llm_cost_tracker 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +14 -1
- data/app/assets/llm_cost_tracker/application.css +1 -4
- data/app/controllers/llm_cost_tracker/calls_controller.rb +9 -13
- data/app/controllers/llm_cost_tracker/dashboard_controller.rb +8 -19
- data/app/controllers/llm_cost_tracker/data_quality_controller.rb +1 -2
- data/app/controllers/llm_cost_tracker/models_controller.rb +5 -2
- data/app/controllers/llm_cost_tracker/tags_controller.rb +2 -4
- data/app/helpers/llm_cost_tracker/dashboard_filter_options_helper.rb +1 -7
- data/app/helpers/llm_cost_tracker/dashboard_query_helper.rb +5 -9
- data/app/services/llm_cost_tracker/dashboard/data_quality.rb +10 -10
- data/app/services/llm_cost_tracker/dashboard/filter.rb +6 -26
- data/app/services/llm_cost_tracker/dashboard/provider_breakdown.rb +0 -3
- data/app/services/llm_cost_tracker/dashboard/tag_breakdown.rb +0 -2
- data/app/services/llm_cost_tracker/pagination.rb +1 -9
- data/app/views/layouts/llm_cost_tracker/application.html.erb +1 -16
- data/app/views/llm_cost_tracker/calls/index.html.erb +13 -13
- data/app/views/llm_cost_tracker/calls/show.html.erb +8 -3
- data/app/views/llm_cost_tracker/dashboard/index.html.erb +1 -1
- data/app/views/llm_cost_tracker/data_quality/index.html.erb +36 -14
- data/app/views/llm_cost_tracker/models/index.html.erb +10 -9
- data/app/views/llm_cost_tracker/shared/_spend_chart.html.erb +0 -1
- data/app/views/llm_cost_tracker/shared/_tag_chips.html.erb +0 -1
- data/app/views/llm_cost_tracker/tags/index.html.erb +1 -1
- data/app/views/llm_cost_tracker/tags/show.html.erb +1 -1
- data/lib/llm_cost_tracker/configuration.rb +0 -1
- data/lib/llm_cost_tracker/event.rb +1 -0
- data/lib/llm_cost_tracker/event_metadata.rb +1 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/add_provider_response_id_generator.rb +29 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_provider_response_id_to_llm_api_calls.rb.erb +15 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/create_llm_api_calls.rb.erb +2 -0
- data/lib/llm_cost_tracker/llm_api_call.rb +6 -2
- data/lib/llm_cost_tracker/middleware/faraday.rb +1 -0
- data/lib/llm_cost_tracker/parameter_hash.rb +33 -0
- data/lib/llm_cost_tracker/parsed_usage.rb +14 -3
- data/lib/llm_cost_tracker/parsers/anthropic.rb +47 -28
- data/lib/llm_cost_tracker/parsers/gemini.rb +28 -4
- data/lib/llm_cost_tracker/parsers/openai_compatible.rb +5 -6
- data/lib/llm_cost_tracker/parsers/openai_usage.rb +14 -0
- data/lib/llm_cost_tracker/price_registry.rb +22 -7
- data/lib/llm_cost_tracker/price_sync/refresh_plan_builder.rb +162 -0
- data/lib/llm_cost_tracker/price_sync/registry_loader.rb +55 -0
- data/lib/llm_cost_tracker/price_sync/registry_writer.rb +25 -0
- data/lib/llm_cost_tracker/price_sync.rb +16 -184
- data/lib/llm_cost_tracker/pricing.rb +0 -11
- data/lib/llm_cost_tracker/railtie.rb +0 -1
- data/lib/llm_cost_tracker/report.rb +0 -5
- data/lib/llm_cost_tracker/storage/active_record_store.rb +10 -11
- data/lib/llm_cost_tracker/stream_collector.rb +17 -13
- data/lib/llm_cost_tracker/tags_column.rb +4 -0
- data/lib/llm_cost_tracker/tracker.rb +10 -2
- data/lib/llm_cost_tracker/version.rb +1 -1
- data/lib/llm_cost_tracker.rb +6 -14
- metadata +7 -1
|
@@ -8,10 +8,11 @@ module LlmCostTracker
|
|
|
8
8
|
class StreamCollector
|
|
9
9
|
attr_reader :provider
|
|
10
10
|
|
|
11
|
-
def initialize(provider:, model:, latency_ms: nil, metadata: {})
|
|
11
|
+
def initialize(provider:, model:, latency_ms: nil, provider_response_id: nil, metadata: {})
|
|
12
12
|
@provider = provider.to_s
|
|
13
13
|
@model = model
|
|
14
14
|
@latency_ms = latency_ms
|
|
15
|
+
@provider_response_id = provider_response_id
|
|
15
16
|
@metadata = ValueHelpers.deep_dup(metadata || {})
|
|
16
17
|
@events = []
|
|
17
18
|
@explicit_usage = nil
|
|
@@ -20,13 +21,11 @@ module LlmCostTracker
|
|
|
20
21
|
@monitor = Monitor.new
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
def model
|
|
24
|
-
@monitor.synchronize { @model }
|
|
25
|
-
end
|
|
24
|
+
def model = @monitor.synchronize { @model }
|
|
26
25
|
|
|
27
|
-
def metadata
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
def metadata = @monitor.synchronize { ValueHelpers.deep_dup(@metadata) }
|
|
27
|
+
|
|
28
|
+
def provider_response_id = @monitor.synchronize { @provider_response_id }
|
|
30
29
|
|
|
31
30
|
def model=(value)
|
|
32
31
|
@monitor.synchronize do
|
|
@@ -35,6 +34,13 @@ module LlmCostTracker
|
|
|
35
34
|
end
|
|
36
35
|
end
|
|
37
36
|
|
|
37
|
+
def provider_response_id=(value)
|
|
38
|
+
@monitor.synchronize do
|
|
39
|
+
ensure_open!
|
|
40
|
+
@provider_response_id = value
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
38
44
|
def event(data, type: nil)
|
|
39
45
|
@monitor.synchronize do
|
|
40
46
|
ensure_open!
|
|
@@ -67,6 +73,7 @@ module LlmCostTracker
|
|
|
67
73
|
explicit_usage: ValueHelpers.deep_dup(@explicit_usage),
|
|
68
74
|
model: @model,
|
|
69
75
|
latency_ms: @latency_ms,
|
|
76
|
+
provider_response_id: @provider_response_id,
|
|
70
77
|
metadata: ValueHelpers.deep_dup(@metadata)
|
|
71
78
|
}
|
|
72
79
|
end
|
|
@@ -80,6 +87,7 @@ module LlmCostTracker
|
|
|
80
87
|
latency_ms: snapshot[:latency_ms] || elapsed_ms,
|
|
81
88
|
stream: true,
|
|
82
89
|
usage_source: parsed.usage_source,
|
|
90
|
+
provider_response_id: parsed.provider_response_id || snapshot[:provider_response_id],
|
|
83
91
|
metadata: error_metadata(errored).merge(snapshot[:metadata]).merge(parsed.metadata)
|
|
84
92
|
)
|
|
85
93
|
end
|
|
@@ -147,12 +155,8 @@ module LlmCostTracker
|
|
|
147
155
|
)
|
|
148
156
|
end
|
|
149
157
|
|
|
150
|
-
def error_metadata(errored)
|
|
151
|
-
errored ? { stream_errored: true } : {}
|
|
152
|
-
end
|
|
158
|
+
def error_metadata(errored) = errored ? { stream_errored: true } : {}
|
|
153
159
|
|
|
154
|
-
def elapsed_ms
|
|
155
|
-
((Process.clock_gettime(Process::CLOCK_MONOTONIC) - @started_at) * 1000).round
|
|
156
|
-
end
|
|
160
|
+
def elapsed_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - @started_at) * 1000).round
|
|
157
161
|
end
|
|
158
162
|
end
|
|
@@ -13,8 +13,8 @@ module LlmCostTracker
|
|
|
13
13
|
Budget.enforce!
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def record(provider:, model:, input_tokens:, output_tokens:,
|
|
17
|
-
|
|
16
|
+
def record(provider:, model:, input_tokens:, output_tokens:, latency_ms: nil, stream: false,
|
|
17
|
+
usage_source: nil, provider_response_id: nil, metadata: {})
|
|
18
18
|
usage = EventMetadata.usage_data(input_tokens, output_tokens, metadata)
|
|
19
19
|
|
|
20
20
|
cost_data = Pricing.cost_for(
|
|
@@ -39,6 +39,7 @@ module LlmCostTracker
|
|
|
39
39
|
latency_ms: normalized_latency_ms(latency_ms),
|
|
40
40
|
stream: stream ? true : false,
|
|
41
41
|
usage_source: normalized_usage_source(usage_source),
|
|
42
|
+
provider_response_id: normalized_provider_response_id(provider_response_id),
|
|
42
43
|
tracked_at: Time.now.utc
|
|
43
44
|
)
|
|
44
45
|
|
|
@@ -122,6 +123,13 @@ module LlmCostTracker
|
|
|
122
123
|
symbol = value.to_sym
|
|
123
124
|
USAGE_SOURCES.include?(symbol) ? symbol.to_s : nil
|
|
124
125
|
end
|
|
126
|
+
|
|
127
|
+
def normalized_provider_response_id(value)
|
|
128
|
+
return nil if value.nil?
|
|
129
|
+
|
|
130
|
+
string = value.to_s
|
|
131
|
+
string.empty? ? nil : string
|
|
132
|
+
end
|
|
125
133
|
end
|
|
126
134
|
end
|
|
127
135
|
end
|
data/lib/llm_cost_tracker.rb
CHANGED
|
@@ -8,6 +8,7 @@ require_relative "llm_cost_tracker/version"
|
|
|
8
8
|
require_relative "llm_cost_tracker/configuration"
|
|
9
9
|
require_relative "llm_cost_tracker/errors"
|
|
10
10
|
require_relative "llm_cost_tracker/logging"
|
|
11
|
+
require_relative "llm_cost_tracker/parameter_hash"
|
|
11
12
|
require_relative "llm_cost_tracker/cost"
|
|
12
13
|
require_relative "llm_cost_tracker/event"
|
|
13
14
|
require_relative "llm_cost_tracker/parsed_usage"
|
|
@@ -44,10 +45,6 @@ module LlmCostTracker
|
|
|
44
45
|
CONFIGURATION_MUTEX.synchronize { @configuration ||= Configuration.new }
|
|
45
46
|
end
|
|
46
47
|
|
|
47
|
-
# Configure the gem once during application boot.
|
|
48
|
-
#
|
|
49
|
-
# @yieldparam configuration [LlmCostTracker::Configuration]
|
|
50
|
-
# @return [void]
|
|
51
48
|
def configure
|
|
52
49
|
config = CONFIGURATION_MUTEX.synchronize do
|
|
53
50
|
current = @configuration || Configuration.new
|
|
@@ -69,13 +66,8 @@ module LlmCostTracker
|
|
|
69
66
|
Tracker.enforce_budget!
|
|
70
67
|
end
|
|
71
68
|
|
|
72
|
-
def track(provider:, model:, input_tokens:, output_tokens:,
|
|
73
|
-
|
|
74
|
-
stream = options.key?(:stream) ? options.delete(:stream) : false
|
|
75
|
-
usage_source = options.key?(:usage_source) ? options.delete(:usage_source) : :manual
|
|
76
|
-
enforce_budget = options.key?(:enforce_budget) ? options.delete(:enforce_budget) : false
|
|
77
|
-
metadata = options
|
|
78
|
-
|
|
69
|
+
def track(provider:, model:, input_tokens:, output_tokens:, latency_ms: nil, stream: false, usage_source: :manual,
|
|
70
|
+
enforce_budget: false, provider_response_id: nil, **metadata)
|
|
79
71
|
enforce_budget! if enforce_budget
|
|
80
72
|
Tracker.record(
|
|
81
73
|
provider: provider.to_s,
|
|
@@ -85,17 +77,19 @@ module LlmCostTracker
|
|
|
85
77
|
latency_ms: latency_ms,
|
|
86
78
|
stream: stream,
|
|
87
79
|
usage_source: usage_source,
|
|
80
|
+
provider_response_id: provider_response_id,
|
|
88
81
|
metadata: metadata
|
|
89
82
|
)
|
|
90
83
|
end
|
|
91
84
|
|
|
92
|
-
def track_stream(provider:, model:, latency_ms: nil, enforce_budget: false, **metadata)
|
|
85
|
+
def track_stream(provider:, model:, latency_ms: nil, enforce_budget: false, provider_response_id: nil, **metadata)
|
|
93
86
|
require_relative "llm_cost_tracker/stream_collector"
|
|
94
87
|
enforce_budget! if enforce_budget
|
|
95
88
|
collector = StreamCollector.new(
|
|
96
89
|
provider: provider.to_s,
|
|
97
90
|
model: model,
|
|
98
91
|
latency_ms: latency_ms,
|
|
92
|
+
provider_response_id: provider_response_id,
|
|
99
93
|
metadata: metadata
|
|
100
94
|
)
|
|
101
95
|
yield collector
|
|
@@ -116,10 +110,8 @@ module LlmCostTracker
|
|
|
116
110
|
end
|
|
117
111
|
end
|
|
118
112
|
|
|
119
|
-
# Load Railtie if Rails is present
|
|
120
113
|
require_relative "llm_cost_tracker/railtie" if defined?(Rails::Railtie)
|
|
121
114
|
|
|
122
|
-
# Auto-register Faraday middleware
|
|
123
115
|
if defined?(Faraday)
|
|
124
116
|
Faraday::Middleware.register_middleware(
|
|
125
117
|
llm_cost_tracker: LlmCostTracker::Middleware::Faraday
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: llm_cost_tracker
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sergii Khomenko
|
|
@@ -248,10 +248,12 @@ files:
|
|
|
248
248
|
- lib/llm_cost_tracker/event.rb
|
|
249
249
|
- lib/llm_cost_tracker/event_metadata.rb
|
|
250
250
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/add_latency_ms_generator.rb
|
|
251
|
+
- lib/llm_cost_tracker/generators/llm_cost_tracker/add_provider_response_id_generator.rb
|
|
251
252
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/add_streaming_generator.rb
|
|
252
253
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/install_generator.rb
|
|
253
254
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/prices_generator.rb
|
|
254
255
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_latency_ms_to_llm_api_calls.rb.erb
|
|
256
|
+
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_provider_response_id_to_llm_api_calls.rb.erb
|
|
255
257
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_streaming_to_llm_api_calls.rb.erb
|
|
256
258
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/create_llm_api_calls.rb.erb
|
|
257
259
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/initializer.rb.erb
|
|
@@ -263,6 +265,7 @@ files:
|
|
|
263
265
|
- lib/llm_cost_tracker/llm_api_call.rb
|
|
264
266
|
- lib/llm_cost_tracker/logging.rb
|
|
265
267
|
- lib/llm_cost_tracker/middleware/faraday.rb
|
|
268
|
+
- lib/llm_cost_tracker/parameter_hash.rb
|
|
266
269
|
- lib/llm_cost_tracker/parsed_usage.rb
|
|
267
270
|
- lib/llm_cost_tracker/parsers/anthropic.rb
|
|
268
271
|
- lib/llm_cost_tracker/parsers/base.rb
|
|
@@ -279,6 +282,9 @@ files:
|
|
|
279
282
|
- lib/llm_cost_tracker/price_sync/merger.rb
|
|
280
283
|
- lib/llm_cost_tracker/price_sync/model_catalog.rb
|
|
281
284
|
- lib/llm_cost_tracker/price_sync/raw_price.rb
|
|
285
|
+
- lib/llm_cost_tracker/price_sync/refresh_plan_builder.rb
|
|
286
|
+
- lib/llm_cost_tracker/price_sync/registry_loader.rb
|
|
287
|
+
- lib/llm_cost_tracker/price_sync/registry_writer.rb
|
|
282
288
|
- lib/llm_cost_tracker/price_sync/source.rb
|
|
283
289
|
- lib/llm_cost_tracker/price_sync/source_result.rb
|
|
284
290
|
- lib/llm_cost_tracker/price_sync/sources/litellm.rb
|