Module: LlmCostTracker::Budget::PerTag
- Defined in:
- lib/llm_cost_tracker/budget/per_tag.rb
Defined Under Namespace
Classes: Rule
Constant Summary collapse
- COST_COLUMN =
"total_cost"- TIME_COLUMN =
"tracked_at"- WINDOW_STARTS =
{ daily: :beginning_of_day, weekly: :beginning_of_week, monthly: :beginning_of_month }.freeze
- WINDOW_NEXTS =
{ daily: :next_day, weekly: :next_week, monthly: :next_month }.freeze
- SLOW_READ_SECONDS =
0.1- DEFAULT_BACKFILL_BATCH =
5_000
Class Method Summary collapse
- .active? ⇒ Boolean
- .backfill(batch_size: DEFAULT_BACKFILL_BATCH) ⇒ Object
- .blocking? ⇒ Boolean
- .columns? ⇒ Boolean
- .configured ⇒ Object
- .rules_for(tags, blocking_only: false) ⇒ Object
- .rules_for_events(events) ⇒ Object
- .spend(key, value, window, time:) ⇒ Object
- .spend_by_value(key, values, window, bucket) ⇒ Object
- .window_start(window, time) ⇒ Object
Class Method Details
.active? ⇒ Boolean
23 24 25 26 27 28 29 |
# File 'lib/llm_cost_tracker/budget/per_tag.rb', line 23 def active? return false if configured.empty? return true if columns? warn_missing_columns false end |
.backfill(batch_size: DEFAULT_BACKFILL_BATCH) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/llm_cost_tracker/budget/per_tag.rb', line 77 def backfill(batch_size: DEFAULT_BACKFILL_BATCH) return 0 unless columns? filled = 0 loop do copied = copy_next_batch(batch_size) break if copied.zero? filled += copied end filled end |
.blocking? ⇒ Boolean
31 32 33 |
# File 'lib/llm_cost_tracker/budget/per_tag.rb', line 31 def blocking? active? && configured.each_value.any? { |entry| behavior_for(entry) == :block_requests } end |
.columns? ⇒ Boolean
90 91 92 |
# File 'lib/llm_cost_tracker/budget/per_tag.rb', line 90 def columns? (LlmCostTracker::CallTag.column_names & [COST_COLUMN, TIME_COLUMN]).size == 2 end |
.configured ⇒ Object
19 20 21 |
# File 'lib/llm_cost_tracker/budget/per_tag.rb', line 19 def configured LlmCostTracker.configuration.budgets.per_tag end |
.rules_for(tags, blocking_only: false) ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/llm_cost_tracker/budget/per_tag.rb', line 35 def rules_for(, blocking_only: false) return [] unless active? normalized = ( || {}).to_h.transform_keys(&:to_s) configured.filter_map do |key, entry| raw = normalized[key] next if raw.nil? next if blocking_only && behavior_for(entry) != :block_requests Rule.new( key: key, value: Ledger::Tags::Encoding.encode(raw), windows: entry[:windows], behavior: behavior_for(entry), on_exceeded: on_exceeded_for(entry) ) end end |
.rules_for_events(events) ⇒ Object
54 55 56 57 58 |
# File 'lib/llm_cost_tracker/budget/per_tag.rb', line 54 def rules_for_events(events) events.each_with_object({}) do |event, grouped| rules_for(event.).each { |rule| (grouped[rule] ||= []) << event } end end |
.spend(key, value, window, time:) ⇒ Object
60 61 62 |
# File 'lib/llm_cost_tracker/budget/per_tag.rb', line 60 def spend(key, value, window, time:) spend_by_value(key, [value], window, window_start(window, time)).fetch(value, 0).to_d end |
.spend_by_value(key, values, window, bucket) ⇒ Object
64 65 66 67 68 69 70 71 |
# File 'lib/llm_cost_tracker/budget/per_tag.rb', line 64 def spend_by_value(key, values, window, bucket) started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) totals = LlmCostTracker::CallTag .where(key: key, value: values, TIME_COLUMN => window_range(window, bucket)) .group(:value).sum(COST_COLUMN) warn_slow_read(key, window, Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at) totals end |
.window_start(window, time) ⇒ Object
73 74 75 |
# File 'lib/llm_cost_tracker/budget/per_tag.rb', line 73 def window_start(window, time) time.to_time.utc.public_send(WINDOW_STARTS.fetch(window)) end |