Class: LlmCostTracker::Storage::ActiveRecordStore

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

Class Method Summary collapse

Class Method Details

.attributes_for(event, model = LlmCostTracker::LlmApiCall) ⇒ Object



41
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/storage/active_record_store.rb', line 41

def attributes_for(event, model = LlmCostTracker::LlmApiCall)
  tags = stringify_tags(event.tags || {})
  columns = model.columns_hash

  attributes = {
    provider:      event.provider,
    model:         event.model,
    input_tokens:  event.input_tokens,
    output_tokens: event.output_tokens,
    total_tokens:  event.total_tokens,
    input_cost:    event.cost&.input_cost,
    output_cost:   event.cost&.output_cost,
    total_cost:    event.cost&.total_cost,
    tags:          tags_for_storage(tags, model),
    tracked_at:    event.tracked_at
  }
  attributes[:event_id] = event.event_id if columns.key?("event_id")
  optional_attributes(event).each do |name, value|
    attributes[name] = value if columns.key?(name.to_s)
  end
  attributes[:latency_ms] = event.latency_ms if columns.key?("latency_ms")
  attributes[:stream] = event.stream if columns.key?("stream")
  attributes[:usage_source] = event.usage_source if columns.key?("usage_source")
  attributes[:provider_response_id] = event.provider_response_id if columns.key?("provider_response_id")

  attributes
end

.daily_total(time: Time.now.utc) ⇒ Object



73
74
75
# File 'lib/llm_cost_tracker/storage/active_record_store.rb', line 73

def daily_total(time: Time.now.utc)
  period_totals(%i[daily], time: time).fetch(:daily)
end

.insert_many(events) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/llm_cost_tracker/storage/active_record_store.rb', line 26

def insert_many(events)
  events = Array(events)
  return [] if events.empty?

  model = LlmCostTracker::LlmApiCall
  insertable = new_events(model, events)

  if insertable.any?
    rows = insertable.map { |event| attributes_for(event, model) }
    model.insert_all!(rows, **insert_options)
    ActiveRecordRollups.increment_many!(insertable)
  end
  events
end

.monthly_total(time: Time.now.utc) ⇒ Object



69
70
71
# File 'lib/llm_cost_tracker/storage/active_record_store.rb', line 69

def monthly_total(time: Time.now.utc)
  period_totals(%i[monthly], time: time).fetch(:monthly)
end

.period_totals(periods, time: Time.now.utc) ⇒ Object



77
78
79
# File 'lib/llm_cost_tracker/storage/active_record_store.rb', line 77

def period_totals(periods, time: Time.now.utc)
  ActiveRecordPeriodTotals.call(periods, time: time)
end

.prune(cutoff:, batch_size:) ⇒ Object



81
82
83
84
85
86
87
88
89
# File 'lib/llm_cost_tracker/storage/active_record_store.rb', line 81

def prune(cutoff:, batch_size:)
  deleted = 0
  loop do
    batch = prune_batch(cutoff, batch_size)
    deleted += batch
    break if batch < batch_size
  end
  deleted
end

.reset!Object



11
12
13
# File 'lib/llm_cost_tracker/storage/active_record_store.rb', line 11

def reset!
  ActiveRecordRollups.reset!
end

.save(event) ⇒ Object



15
16
17
18
19
20
21
22
23
24
# File 'lib/llm_cost_tracker/storage/active_record_store.rb', line 15

def save(event)
  model = LlmCostTracker::LlmApiCall
  attributes = attributes_for(event, model)

  model.transaction do
    call = model.create!(attributes)
    ActiveRecordRollups.increment!(event)
    call
  end
end