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.



102
103
104
105
# File 'lib/active_harness/costs.rb', line 102

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

.available_providersObject

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



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

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.



157
158
159
# File 'lib/active_harness/costs.rb', line 157

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.



108
109
110
111
112
# File 'lib/active_harness/costs.rb', line 108

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.



120
121
122
123
124
125
# File 'lib/active_harness/costs.rb', line 120

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.



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

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.



115
116
117
# File 'lib/active_harness/costs.rb', line 115

def providers
  @providers_proxy ||= ProvidersProxy.new
end

.reload!Object

Reloads registry from disk on next access.



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

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.



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

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