Module: ActiveHarness::Costs

Defined in:
lib/active_harness/costs.rb

Overview

Provides access to AI model pricing data, filtered to providers supported by ActiveHarness (files present in lib/active_harness/providers/).

Data source priority:

1. {project_root}/tmp/active_harness/costs.json — fetched cache (refreshed once per day)
2. lib/active_harness/data/models.json           — bundled fallback (ships with gem)

Usage:

# Fetch fresh data and save to tmp cache (also called automatically when stale)
ActiveHarness::Costs.update

# All models (auto-updates cache if missing or older than 24h)
ActiveHarness::Costs.all

# Single model by ID
ActiveHarness::Costs.find("gpt-4o")

# By provider — method or bracket syntax
ActiveHarness::Costs.providers.openai
ActiveHarness::Costs.providers[:anthropic]

# List providers that have data
ActiveHarness::Costs.providers.list

Defined Under Namespace

Classes: ModelCost, ProvidersProxy

Constant Summary collapse

BUNDLED_DATA_FILE =
File.expand_path("data/models.json", __dir__).freeze
MODELS_DEV_URL =
"https://models.dev/api.json"
CACHE_TTL =

24 hours in seconds

86_400
MODELS_DEV_PROVIDER_MAP =

Maps models.dev provider keys → ActiveHarness provider names. Only entries whose value matches a file in providers/ will be kept.

{
  "openai"         => "openai",
  "anthropic"      => "anthropic",
  "google"         => "gemini",
  "google-vertex"  => "vertexai",
  "amazon-bedrock" => "bedrock",
  "deepseek"       => "deepseek",
  "mistral"        => "mistral",
  "openrouter"     => "openrouter",
  "perplexity"     => "perplexity",
  "xai"            => "xai",
  "groq"           => "groq",
  "azure"          => "azure"
}.freeze

Class Method Summary collapse

Class Method Details

.allObject

Returns pricing data for all models from supported providers. Automatically fetches fresh data if the cache is missing or older than 24h.



123
124
125
126
# File 'lib/active_harness/costs.rb', line 123

def all
  ensure_fresh_registry
  registry.map { |raw| build_cost(raw) }
end

.available_providersObject

Names of providers supported by ActiveHarness (derived from providers/ directory).



183
184
185
186
187
188
189
190
# File 'lib/active_harness/costs.rb', line 183

def available_providers
  @available_providers ||= begin
    providers_dir = File.expand_path("providers", __dir__)
    Dir.glob("#{providers_dir}/*.rb")
      .map { |f| File.basename(f, ".rb") }
      .reject { |n| %w[base custom].include?(n) }
  end
end

.cache_fileObject

Path to the per-project cache file.



178
179
180
# File 'lib/active_harness/costs.rb', line 178

def cache_file
  File.join(project_root, "tmp", "active_harness", "costs.json")
end

.find(model_id) ⇒ Object

Returns pricing data for a single model by ID, or nil if not found.



129
130
131
132
133
# File 'lib/active_harness/costs.rb', line 129

def find(model_id)
  ensure_fresh_registry
  raw = registry.find { |m| m[:id] == model_id.to_s }
  raw ? build_cost(raw) : nil
end

.for_provider(name) ⇒ Object

Returns pricing data for all models from the given provider.



141
142
143
144
145
146
# File 'lib/active_harness/costs.rb', line 141

def for_provider(name)
  ensure_fresh_registry
  registry
    .select { |m| m[:provider] == name.to_s }
    .map { |m| build_cost(m) }
end

.provider_namesObject

Returns a sorted list of provider names that have data.



149
150
151
152
153
154
# File 'lib/active_harness/costs.rb', line 149

def provider_names
  @provider_names ||= begin
    ensure_fresh_registry
    registry.map { |m| m[:provider] }.uniq.sort
  end
end

.providersObject

Returns a ProvidersProxy for provider-scoped access.



136
137
138
# File 'lib/active_harness/costs.rb', line 136

def providers
  @providers_proxy ||= ProvidersProxy.new
end

.reload!Object

Reloads registry from disk on next access.



171
172
173
174
175
# File 'lib/active_harness/costs.rb', line 171

def reload!
  @registry      = nil
  @provider_names = nil
  nil
end

.updateObject

Fetches fresh pricing data from models.dev, filters to supported providers, and writes the result to project_root/tmp/active_harness/costs.json. Returns the number of models saved, or raises on HTTP failure.



159
160
161
162
163
164
165
166
167
168
# File 'lib/active_harness/costs.rb', line 159

def update
  raw_api = fetch_models_dev
  models  = extract_models(raw_api)

  FileUtils.mkdir_p(File.dirname(cache_file))
  File.write(cache_file, JSON.generate(models))

  reload!
  models.size
end