Class: LlmCostTracker::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/llm_cost_tracker/configuration.rb

Constant Summary collapse

OPENAI_COMPATIBLE_PROVIDERS =
{
  "openrouter.ai" => "openrouter",
  "api.deepseek.com" => "deepseek",
  "api.groq.com" => "groq"
}.freeze
BUDGET_EXCEEDED_BEHAVIORS =
%i[notify raise block_requests].freeze
UNKNOWN_PRICING_BEHAVIORS =
%i[ignore warn raise].freeze
INGESTION_MODES =
%i[inline async].freeze
SCALAR_ATTRIBUTES =
%i[enabled default_tags on_budget_exceeded monthly_budget daily_budget per_call_budget
log_level prices_file max_tag_count max_tag_value_bytesize
ingestion_pool_size auto_enable_stream_usage cache_rollups
reconciliation_enabled].freeze
ENUM_ATTRIBUTES =
{
  budget_exceeded_behavior: [BUDGET_EXCEEDED_BEHAVIORS, :notify],
  unknown_pricing_behavior: [UNKNOWN_PRICING_BEHAVIORS, :warn],
  ingestion: [INGESTION_MODES, :inline]
}.freeze
DEFAULT_REDACTED_TAG_KEYS =
%w[api_key access_token authorization credential password refresh_token secret].freeze

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Returns a new instance of Configuration.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/llm_cost_tracker/configuration.rb', line 42

def initialize
  @enabled = true
  @default_tags       = {}
  @on_budget_exceeded = nil
  @monthly_budget     = nil
  @daily_budget       = nil
  @per_call_budget    = nil
  self.budget_exceeded_behavior = :notify
  self.unknown_pricing_behavior = :warn
  @log_level          = :info
  @prices_file        = nil
  @max_tag_count      = 50
  @max_tag_value_bytesize = 1024
  @ingestion_pool_size = nil
  self.pricing_overrides = {}
  @instrumented_integrations = Set.new
  @report_tag_breakdowns = []
  @redacted_tag_keys = DEFAULT_REDACTED_TAG_KEYS.dup
  self.openai_compatible_providers = OPENAI_COMPATIBLE_PROVIDERS
  @reconciliation_importers = {}
  @reconciliation_enabled = false
  @auto_enable_stream_usage = true
  self.ingestion = :inline
  @cache_rollups = false
  @finalized = false
end

Instance Method Details

#finalize!Object



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/llm_cost_tracker/configuration.rb', line 136

def finalize!
  @default_tags = deep_freeze(@default_tags || {})
  @pricing_overrides = deep_freeze(@pricing_overrides || {})
  @instrumented_integrations = deep_freeze(@instrumented_integrations || Set.new)
  @report_tag_breakdowns = deep_freeze(Array(@report_tag_breakdowns))
  @redacted_tag_keys = deep_freeze(Array(@redacted_tag_keys))
  @openai_compatible_providers = deep_freeze(
    normalize_openai_compatible_providers(@openai_compatible_providers)
  )
  @finalized = true
end

#finalized?Boolean

Returns:

  • (Boolean)


153
154
155
# File 'lib/llm_cost_tracker/configuration.rb', line 153

def finalized?
  @finalized
end

#instrument(*names) ⇒ Object



113
114
115
116
# File 'lib/llm_cost_tracker/configuration.rb', line 113

def instrument(*names)
  ensure_mutable!
  @instrumented_integrations.merge(normalize_instrumentation_names(names))
end

#instrumented?(name) ⇒ Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/llm_cost_tracker/configuration.rb', line 118

def instrumented?(name)
  @instrumented_integrations.include?(name)
end

#normalized_redacted_tag_keysObject



148
149
150
151
# File 'lib/llm_cost_tracker/configuration.rb', line 148

def normalized_redacted_tag_keys
  @normalized_redacted_tag_keys ||=
    Array(@redacted_tag_keys).map { |key| Tags::Sanitizer.normalized_key(key) }.freeze
end

#openai_compatible_providers=(providers) ⇒ Object



91
92
93
94
# File 'lib/llm_cost_tracker/configuration.rb', line 91

def openai_compatible_providers=(providers)
  ensure_mutable!
  @openai_compatible_providers = normalize_openai_compatible_providers(providers)
end

#pricing_overrides=(value) ⇒ Object



96
97
98
99
100
101
# File 'lib/llm_cost_tracker/configuration.rb', line 96

def pricing_overrides=(value)
  ensure_mutable!
  @pricing_overrides = Pricing::Registry.normalize_price_table(value || {})
rescue ArgumentError => e
  raise Error, "invalid pricing_overrides: #{e.message}"
end

#reconciliation_importers=(importers) ⇒ Object

Raises:



69
70
71
72
73
74
75
76
77
78
# File 'lib/llm_cost_tracker/configuration.rb', line 69

def reconciliation_importers=(importers)
  ensure_mutable!
  raise Error, RECONCILIATION_DISABLED_MESSAGE unless @reconciliation_enabled

  @reconciliation_importers = (importers || {}).to_h do |source, importer|
    raise Error, "reconciliation_importers[#{source}] must respond to call" unless importer.respond_to?(:call)

    [source.to_sym, importer]
  end
end

#redacted_tag_keys=(value) ⇒ Object



108
109
110
111
# File 'lib/llm_cost_tracker/configuration.rb', line 108

def redacted_tag_keys=(value)
  ensure_mutable!
  @redacted_tag_keys = Array(value).map(&:to_s)
end

#register_reconciliation_importer(source, &block) ⇒ Object

Raises:



80
81
82
83
84
85
86
# File 'lib/llm_cost_tracker/configuration.rb', line 80

def register_reconciliation_importer(source, &block)
  ensure_mutable!
  raise Error, RECONCILIATION_DISABLED_MESSAGE unless @reconciliation_enabled
  raise Error, "register_reconciliation_importer requires a block" unless block

  @reconciliation_importers[source.to_sym] = block
end

#report_tag_breakdowns=(value) ⇒ Object



103
104
105
106
# File 'lib/llm_cost_tracker/configuration.rb', line 103

def report_tag_breakdowns=(value)
  ensure_mutable!
  @report_tag_breakdowns = Array(value).map { |key| Tags::Key.validate!(key, error_class: Error) }
end