llm_cost_tracker 0.4.1 → 0.5.0
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 +19 -0
- data/README.md +182 -100
- data/lib/llm_cost_tracker/configuration/instrumentation.rb +37 -0
- data/lib/llm_cost_tracker/configuration.rb +10 -5
- data/lib/llm_cost_tracker/doctor.rb +166 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/install_generator.rb +33 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/prices_generator.rb +12 -6
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/initializer.rb.erb +53 -21
- data/lib/llm_cost_tracker/integrations/anthropic.rb +75 -0
- data/lib/llm_cost_tracker/integrations/base.rb +72 -0
- data/lib/llm_cost_tracker/integrations/object_reader.rb +56 -0
- data/lib/llm_cost_tracker/integrations/openai.rb +95 -0
- data/lib/llm_cost_tracker/integrations/registry.rb +41 -0
- data/lib/llm_cost_tracker/middleware/faraday.rb +4 -3
- data/lib/llm_cost_tracker/parsed_usage.rb +8 -1
- data/lib/llm_cost_tracker/parsers/base.rb +1 -1
- data/lib/llm_cost_tracker/parsers/openai_usage.rb +1 -1
- data/lib/llm_cost_tracker/price_freshness.rb +38 -0
- data/lib/llm_cost_tracker/price_registry.rb +14 -0
- data/lib/llm_cost_tracker/price_sync/fetcher.rb +2 -1
- data/lib/llm_cost_tracker/price_sync/refresh_plan_builder.rb +4 -2
- data/lib/llm_cost_tracker/price_sync.rb +10 -0
- data/lib/llm_cost_tracker/prices.json +394 -41
- data/lib/llm_cost_tracker/pricing.rb +8 -1
- data/lib/llm_cost_tracker/request_url.rb +20 -0
- data/lib/llm_cost_tracker/stream_collector.rb +3 -3
- data/lib/llm_cost_tracker/tag_context.rb +52 -0
- data/lib/llm_cost_tracker/tracker.rb +5 -2
- data/lib/llm_cost_tracker/version.rb +1 -1
- data/lib/llm_cost_tracker.rb +14 -4
- data/lib/tasks/llm_cost_tracker.rake +21 -3
- metadata +12 -3
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/llm_cost_tracker_prices.yml.erb +0 -51
data/lib/llm_cost_tracker.rb
CHANGED
|
@@ -25,9 +25,11 @@ require_relative "llm_cost_tracker/parsers/gemini"
|
|
|
25
25
|
require_relative "llm_cost_tracker/parsers/sse"
|
|
26
26
|
require_relative "llm_cost_tracker/parsers/registry"
|
|
27
27
|
require_relative "llm_cost_tracker/middleware/faraday"
|
|
28
|
+
require_relative "llm_cost_tracker/integrations/registry"
|
|
28
29
|
require_relative "llm_cost_tracker/budget"
|
|
29
30
|
require_relative "llm_cost_tracker/unknown_pricing"
|
|
30
31
|
require_relative "llm_cost_tracker/event_metadata"
|
|
32
|
+
require_relative "llm_cost_tracker/tag_context"
|
|
31
33
|
require_relative "llm_cost_tracker/tags_column"
|
|
32
34
|
require_relative "llm_cost_tracker/tag_key"
|
|
33
35
|
require_relative "llm_cost_tracker/tag_query"
|
|
@@ -37,6 +39,7 @@ require_relative "llm_cost_tracker/retention"
|
|
|
37
39
|
require_relative "llm_cost_tracker/report_data"
|
|
38
40
|
require_relative "llm_cost_tracker/report_formatter"
|
|
39
41
|
require_relative "llm_cost_tracker/report"
|
|
42
|
+
require_relative "llm_cost_tracker/doctor"
|
|
40
43
|
|
|
41
44
|
module LlmCostTracker
|
|
42
45
|
CONFIGURATION_MUTEX = Monitor.new
|
|
@@ -52,10 +55,11 @@ module LlmCostTracker
|
|
|
52
55
|
current = current.dup_for_configuration if current.finalized?
|
|
53
56
|
@configuration = current
|
|
54
57
|
yield(current)
|
|
55
|
-
current.
|
|
58
|
+
current.openai_compatible_providers = current.openai_compatible_providers.dup
|
|
56
59
|
current.finalize!
|
|
57
60
|
current
|
|
58
61
|
end
|
|
62
|
+
Integrations.install!
|
|
59
63
|
warn_for_configuration!(config)
|
|
60
64
|
end
|
|
61
65
|
|
|
@@ -63,14 +67,20 @@ module LlmCostTracker
|
|
|
63
67
|
CONFIGURATION_MUTEX.synchronize { @configuration = Configuration.new }
|
|
64
68
|
UnknownPricing.reset! if defined?(UnknownPricing)
|
|
65
69
|
Storage::ActiveRecordStore.reset! if defined?(Storage::ActiveRecordStore)
|
|
70
|
+
TagContext.clear! if defined?(TagContext)
|
|
66
71
|
end
|
|
67
72
|
|
|
68
73
|
def enforce_budget!
|
|
69
74
|
Tracker.enforce_budget!
|
|
70
75
|
end
|
|
71
76
|
|
|
72
|
-
def
|
|
73
|
-
|
|
77
|
+
def with_tags(tags = nil, **kwargs, &)
|
|
78
|
+
merged = (tags || {}).to_h.merge(kwargs)
|
|
79
|
+
TagContext.with(merged, &)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def track(provider:, input_tokens:, output_tokens:, model: nil, latency_ms: nil, stream: false,
|
|
83
|
+
usage_source: :manual, enforce_budget: false, provider_response_id: nil, pricing_mode: nil, **metadata)
|
|
74
84
|
enforce_budget! if enforce_budget
|
|
75
85
|
Tracker.record(
|
|
76
86
|
provider: provider.to_s,
|
|
@@ -86,7 +96,7 @@ module LlmCostTracker
|
|
|
86
96
|
)
|
|
87
97
|
end
|
|
88
98
|
|
|
89
|
-
def track_stream(provider:, model
|
|
99
|
+
def track_stream(provider:, model: nil, latency_ms: nil, enforce_budget: false, provider_response_id: nil,
|
|
90
100
|
pricing_mode: nil, **metadata)
|
|
91
101
|
require_relative "llm_cost_tracker/stream_collector"
|
|
92
102
|
enforce_budget! if enforce_budget
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
# rubocop:disable Metrics/BlockLength
|
|
4
4
|
namespace :llm_cost_tracker do
|
|
5
|
+
desc "Check LLM Cost Tracker setup"
|
|
6
|
+
task :doctor do
|
|
7
|
+
Rake::Task["environment"].invoke if Rake::Task.task_defined?("environment")
|
|
8
|
+
checks = LlmCostTracker::Doctor.call
|
|
9
|
+
puts LlmCostTracker::Doctor.report(checks)
|
|
10
|
+
abort("llm_cost_tracker: doctor found setup errors") unless LlmCostTracker::Doctor.healthy?(checks)
|
|
11
|
+
end
|
|
12
|
+
|
|
5
13
|
desc "Print an LLM cost report from ActiveRecord storage"
|
|
6
14
|
task report: :environment do
|
|
7
15
|
days = (ENV["DAYS"] || LlmCostTracker::Report::DEFAULT_DAYS).to_i
|
|
@@ -18,7 +26,7 @@ namespace :llm_cost_tracker do
|
|
|
18
26
|
|
|
19
27
|
namespace :prices do
|
|
20
28
|
desc(
|
|
21
|
-
"Sync
|
|
29
|
+
"Sync the configured pricing file from LiteLLM/OpenRouter JSON sources. " \
|
|
22
30
|
"Use PREVIEW=1 to preview, STRICT=1 to fail on provider errors, " \
|
|
23
31
|
"or OUTPUT=path/to/file.json."
|
|
24
32
|
)
|
|
@@ -26,7 +34,7 @@ namespace :llm_cost_tracker do
|
|
|
26
34
|
Rake::Task["environment"].invoke if Rake::Task.task_defined?("environment")
|
|
27
35
|
require_relative "../llm_cost_tracker"
|
|
28
36
|
|
|
29
|
-
output_path =
|
|
37
|
+
output_path = price_sync_output_path
|
|
30
38
|
strict = ENV["STRICT"] == "1" || ARGV.include?("--strict")
|
|
31
39
|
result = LlmCostTracker::PriceSync.sync(
|
|
32
40
|
path: output_path,
|
|
@@ -57,7 +65,7 @@ namespace :llm_cost_tracker do
|
|
|
57
65
|
Rake::Task["environment"].invoke if Rake::Task.task_defined?("environment")
|
|
58
66
|
require_relative "../llm_cost_tracker"
|
|
59
67
|
|
|
60
|
-
output_path =
|
|
68
|
+
output_path = price_sync_output_path
|
|
61
69
|
result = LlmCostTracker::PriceSync.check(path: output_path)
|
|
62
70
|
|
|
63
71
|
puts "llm_cost_tracker: checked pricing file #{result.path}"
|
|
@@ -131,3 +139,13 @@ def print_failures(failed_sources, heading:)
|
|
|
131
139
|
puts " - #{source}: #{message}"
|
|
132
140
|
end
|
|
133
141
|
end
|
|
142
|
+
|
|
143
|
+
def price_sync_output_path
|
|
144
|
+
path = LlmCostTracker::PriceSync.configured_output_path
|
|
145
|
+
return path if path
|
|
146
|
+
|
|
147
|
+
abort(
|
|
148
|
+
"llm_cost_tracker: configure prices_file, run bin/rails generate llm_cost_tracker:prices, " \
|
|
149
|
+
"or set OUTPUT=config/llm_cost_tracker_prices.yml"
|
|
150
|
+
)
|
|
151
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: llm_cost_tracker
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sergii Khomenko
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -272,7 +272,9 @@ files:
|
|
|
272
272
|
- lib/llm_cost_tracker/assets.rb
|
|
273
273
|
- lib/llm_cost_tracker/budget.rb
|
|
274
274
|
- lib/llm_cost_tracker/configuration.rb
|
|
275
|
+
- lib/llm_cost_tracker/configuration/instrumentation.rb
|
|
275
276
|
- lib/llm_cost_tracker/cost.rb
|
|
277
|
+
- lib/llm_cost_tracker/doctor.rb
|
|
276
278
|
- lib/llm_cost_tracker/engine.rb
|
|
277
279
|
- lib/llm_cost_tracker/engine_compatibility.rb
|
|
278
280
|
- lib/llm_cost_tracker/errors.rb
|
|
@@ -292,11 +294,15 @@ files:
|
|
|
292
294
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_usage_breakdown_to_llm_api_calls.rb.erb
|
|
293
295
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/create_llm_api_calls.rb.erb
|
|
294
296
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/initializer.rb.erb
|
|
295
|
-
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/llm_cost_tracker_prices.yml.erb
|
|
296
297
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_llm_api_call_cost_precision.rb.erb
|
|
297
298
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_llm_api_call_tags_to_jsonb.rb.erb
|
|
298
299
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/upgrade_cost_precision_generator.rb
|
|
299
300
|
- lib/llm_cost_tracker/generators/llm_cost_tracker/upgrade_tags_to_jsonb_generator.rb
|
|
301
|
+
- lib/llm_cost_tracker/integrations/anthropic.rb
|
|
302
|
+
- lib/llm_cost_tracker/integrations/base.rb
|
|
303
|
+
- lib/llm_cost_tracker/integrations/object_reader.rb
|
|
304
|
+
- lib/llm_cost_tracker/integrations/openai.rb
|
|
305
|
+
- lib/llm_cost_tracker/integrations/registry.rb
|
|
300
306
|
- lib/llm_cost_tracker/llm_api_call.rb
|
|
301
307
|
- lib/llm_cost_tracker/logging.rb
|
|
302
308
|
- lib/llm_cost_tracker/middleware/faraday.rb
|
|
@@ -312,6 +318,7 @@ files:
|
|
|
312
318
|
- lib/llm_cost_tracker/parsers/sse.rb
|
|
313
319
|
- lib/llm_cost_tracker/period_grouping.rb
|
|
314
320
|
- lib/llm_cost_tracker/period_total.rb
|
|
321
|
+
- lib/llm_cost_tracker/price_freshness.rb
|
|
315
322
|
- lib/llm_cost_tracker/price_registry.rb
|
|
316
323
|
- lib/llm_cost_tracker/price_sync.rb
|
|
317
324
|
- lib/llm_cost_tracker/price_sync/fetcher.rb
|
|
@@ -332,11 +339,13 @@ files:
|
|
|
332
339
|
- lib/llm_cost_tracker/report.rb
|
|
333
340
|
- lib/llm_cost_tracker/report_data.rb
|
|
334
341
|
- lib/llm_cost_tracker/report_formatter.rb
|
|
342
|
+
- lib/llm_cost_tracker/request_url.rb
|
|
335
343
|
- lib/llm_cost_tracker/retention.rb
|
|
336
344
|
- lib/llm_cost_tracker/storage/active_record_rollups.rb
|
|
337
345
|
- lib/llm_cost_tracker/storage/active_record_store.rb
|
|
338
346
|
- lib/llm_cost_tracker/stream_collector.rb
|
|
339
347
|
- lib/llm_cost_tracker/tag_accessors.rb
|
|
348
|
+
- lib/llm_cost_tracker/tag_context.rb
|
|
340
349
|
- lib/llm_cost_tracker/tag_key.rb
|
|
341
350
|
- lib/llm_cost_tracker/tag_query.rb
|
|
342
351
|
- lib/llm_cost_tracker/tags_column.rb
|
data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/llm_cost_tracker_prices.yml.erb
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# Local LlmCostTracker price overrides.
|
|
2
|
-
#
|
|
3
|
-
# Add only the models you want to override or add. Built-in prices still come
|
|
4
|
-
# from the gem's prices.json, and Ruby pricing_overrides still take precedence.
|
|
5
|
-
#
|
|
6
|
-
# Units: USD per 1M tokens.
|
|
7
|
-
#
|
|
8
|
-
# Supported price keys:
|
|
9
|
-
# - input
|
|
10
|
-
# - output
|
|
11
|
-
# - cache_read_input
|
|
12
|
-
# - cache_write_input
|
|
13
|
-
# - mode_input / mode_output / mode_cache_read_input / mode_cache_write_input
|
|
14
|
-
#
|
|
15
|
-
# Optional metadata keys, ignored by cost calculation:
|
|
16
|
-
# - _source
|
|
17
|
-
# - _source_version
|
|
18
|
-
# - _fetched_at
|
|
19
|
-
# - _updated
|
|
20
|
-
# - _notes
|
|
21
|
-
# - _validator_override
|
|
22
|
-
#
|
|
23
|
-
# Example: custom fine-tune
|
|
24
|
-
# models:
|
|
25
|
-
# "ft:gpt-4o-mini:my-org":
|
|
26
|
-
# input: 0.30
|
|
27
|
-
# cache_read_input: 0.15
|
|
28
|
-
# output: 1.20
|
|
29
|
-
# _notes: "Internal fine-tune rate"
|
|
30
|
-
#
|
|
31
|
-
# Example: alternate pricing mode
|
|
32
|
-
# models:
|
|
33
|
-
# "batchable-model":
|
|
34
|
-
# input: 1.00
|
|
35
|
-
# output: 2.00
|
|
36
|
-
# batch_input: 0.50
|
|
37
|
-
# batch_output: 1.00
|
|
38
|
-
#
|
|
39
|
-
# Example: negotiated provider discount
|
|
40
|
-
# models:
|
|
41
|
-
# "gpt-4o":
|
|
42
|
-
# input: 2.00
|
|
43
|
-
# output: 8.00
|
|
44
|
-
# _source: "manual"
|
|
45
|
-
# _updated: "2026-04-18"
|
|
46
|
-
#
|
|
47
|
-
# Use _source: "manual" for custom or orphaned entries you never want sync to touch.
|
|
48
|
-
# Use _validator_override: ["skip_relative_change"] if a negotiated price would
|
|
49
|
-
# otherwise trip the >3x sync warning.
|
|
50
|
-
|
|
51
|
-
models:
|