Module: LlmCostTracker::Pricing::Registry

Defined in:
lib/llm_cost_tracker/pricing/registry.rb

Constant Summary collapse

DEFAULT_PRICES_PATH =
File.expand_path("../prices.json", __dir__)
EMPTY_PRICES =
{}.freeze
CONTEXT_THRESHOLD_KEY =
:_context_price_threshold_tokens
PRICE_KEYS =
Billing::Components::TOKEN_PRICED.map { |component| component.key.name }.freeze
METADATA_KEYS =
[
  "_source", "_source_version", "_fetched_at", "_updated", "_notes", "_validator_override",
  CONTEXT_THRESHOLD_KEY.name
].freeze
MUTEX =
Mutex.new

Class Method Summary collapse

Class Method Details

.builtin_pricesObject



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/llm_cost_tracker/pricing/registry.rb', line 31

def builtin_prices
  cached = @builtin_prices
  return cached if cached

  MUTEX.synchronize do
    @builtin_prices ||= begin
      registry = @raw_registry ||= load_raw_registry
      normalize_price_table(registry.fetch("models", {})).freeze
    end
  end
end

.file_metadata(path) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/llm_cost_tracker/pricing/registry.rb', line 55

def (path)
  return {} unless path

  registry = YAML.safe_load_file(path, aliases: false) || {}

   = registry.fetch("metadata", {})
  raise ArgumentError, "prices_file metadata must be a hash" unless .is_a?(Hash)

  
rescue Errno::ENOENT, Psych::Exception, ArgumentError, TypeError => e
  raise Error, "Unable to load prices_file #{path.inspect}: #{e.message}"
end

.file_prices(path) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/llm_cost_tracker/pricing/registry.rb', line 72

def file_prices(path)
  return EMPTY_PRICES unless path

  cache_key = [path, File.mtime(path)]
  cached = @file_prices_cache
  return cached[:value] if cached && cached[:key] == cache_key

  MUTEX.synchronize do
    cached = @file_prices_cache
    return cached[:value] if cached && cached[:key] == cache_key

    registry = YAML.safe_load_file(path, aliases: false) || {}
    value = normalize_price_entries(registry.fetch("models", registry), context: path).freeze
    @file_prices_cache = { key: cache_key, value: value }.freeze
    value
  end
rescue Errno::ENOENT, Psych::Exception, ArgumentError, TypeError => e
  raise Error, "Unable to load prices_file #{path.inspect}: #{e.message}"
end

.metadataObject



43
44
45
46
47
48
49
50
51
52
53
# File 'lib/llm_cost_tracker/pricing/registry.rb', line 43

def 
  cached = @metadata
  return cached if cached

  MUTEX.synchronize do
    @metadata ||= begin
      registry = @raw_registry ||= load_raw_registry
      registry.fetch("metadata", {}).freeze
    end
  end
end

.normalize_price_table(table) ⇒ Object



68
69
70
# File 'lib/llm_cost_tracker/pricing/registry.rb', line 68

def normalize_price_table(table)
  normalize_price_entries(table, context: "price table")
end

.reset!Object



22
23
24
25
26
27
28
29
# File 'lib/llm_cost_tracker/pricing/registry.rb', line 22

def reset!
  MUTEX.synchronize do
    @builtin_prices = nil
    @metadata = nil
    @raw_registry = nil
    @file_prices_cache = nil
  end
end