llm_cost_tracker 0.14.2 → 0.15.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 +128 -0
- data/LICENSE.txt +28 -0
- data/README.md +33 -41
- data/app/controllers/llm_cost_tracker/application_controller.rb +7 -1
- data/app/controllers/llm_cost_tracker/calls_controller.rb +12 -94
- data/app/controllers/llm_cost_tracker/dashboard_controller.rb +11 -12
- data/app/controllers/llm_cost_tracker/pricing_controller.rb +15 -4
- data/app/controllers/llm_cost_tracker/tags_controller.rb +21 -14
- data/app/helpers/llm_cost_tracker/application_helper.rb +22 -31
- data/app/helpers/llm_cost_tracker/chart_helper.rb +57 -108
- data/app/helpers/llm_cost_tracker/dashboard_query_helper.rb +30 -17
- data/app/helpers/llm_cost_tracker/sortable_table_helper.rb +18 -25
- data/app/helpers/llm_cost_tracker/token_usage_helper.rb +30 -0
- data/app/models/llm_cost_tracker/call.rb +6 -4
- data/app/models/llm_cost_tracker/call_rollup.rb +6 -6
- data/app/services/llm_cost_tracker/dashboard/call_order.rb +36 -0
- data/app/services/llm_cost_tracker/dashboard/calls_export.rb +68 -0
- data/app/services/llm_cost_tracker/dashboard/data_quality/stats_query.rb +77 -0
- data/app/services/llm_cost_tracker/dashboard/data_quality/summary.rb +47 -0
- data/app/services/llm_cost_tracker/dashboard/data_quality.rb +66 -210
- data/app/services/llm_cost_tracker/dashboard/filter.rb +2 -1
- data/app/services/llm_cost_tracker/dashboard/monthly_budget.rb +42 -35
- data/app/services/llm_cost_tracker/dashboard/percent.rb +12 -0
- data/app/services/llm_cost_tracker/dashboard/sort.rb +9 -0
- data/app/services/llm_cost_tracker/dashboard/spend_anomaly.rb +19 -17
- data/app/services/llm_cost_tracker/dashboard/spend_chart.rb +50 -0
- data/app/services/llm_cost_tracker/dashboard/tag_breakdown.rb +7 -8
- data/app/services/llm_cost_tracker/dashboard/tag_key_explorer.rb +1 -4
- data/app/services/llm_cost_tracker/dashboard/top_models.rb +3 -4
- data/app/views/layouts/llm_cost_tracker/application.html.erb +6 -6
- data/app/views/llm_cost_tracker/calls/index.html.erb +2 -1
- data/app/views/llm_cost_tracker/calls/show.html.erb +3 -14
- data/app/views/llm_cost_tracker/errors/query.html.erb +6 -0
- data/app/views/llm_cost_tracker/shared/_filter_pill_stream.html.erb +1 -1
- data/app/views/llm_cost_tracker/tags/show.html.erb +1 -1
- data/lib/llm_cost_tracker/budget/estimate.rb +35 -0
- data/lib/llm_cost_tracker/budget/global.rb +69 -0
- data/lib/llm_cost_tracker/budget/limit.rb +52 -0
- data/lib/llm_cost_tracker/budget/per_tag.rb +59 -27
- data/lib/llm_cost_tracker/budget/tagged.rb +74 -0
- data/lib/llm_cost_tracker/budget.rb +19 -157
- data/lib/llm_cost_tracker/capture/event_window.rb +16 -13
- data/lib/llm_cost_tracker/capture/sdk_payload.rb +22 -17
- data/lib/llm_cost_tracker/capture/sse.rb +7 -5
- data/lib/llm_cost_tracker/capture/stream_collector.rb +89 -96
- data/lib/llm_cost_tracker/capture/stream_tap.rb +2 -2
- data/lib/llm_cost_tracker/capture/stream_tracker.rb +32 -37
- data/lib/llm_cost_tracker/capture_verifier.rb +2 -3
- data/lib/llm_cost_tracker/charges/cost_status.rb +23 -19
- data/lib/llm_cost_tracker/charges/line_item.rb +31 -33
- data/lib/llm_cost_tracker/configuration/budgets.rb +49 -18
- data/lib/llm_cost_tracker/configuration/capture.rb +7 -1
- data/lib/llm_cost_tracker/configuration/mutability.rb +3 -3
- data/lib/llm_cost_tracker/doctor/ingestion_check.rb +20 -2
- data/lib/llm_cost_tracker/doctor/price_check.rb +24 -35
- data/lib/llm_cost_tracker/doctor/probe.rb +1 -1
- data/lib/llm_cost_tracker/doctor.rb +23 -20
- data/lib/llm_cost_tracker/engine.rb +8 -0
- data/lib/llm_cost_tracker/errors.rb +2 -4
- data/lib/llm_cost_tracker/event.rb +22 -29
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/create_llm_cost_tracker_calls.rb.erb +4 -2
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/initializer.rb.erb +6 -4
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_call_rollups_provider.rb.erb +1 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_call_tags_key_value_index.rb.erb +1 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_indexes.rb.erb +2 -1
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_per_tag_budgets.rb.erb +1 -0
- data/lib/llm_cost_tracker/ingestion/batch.rb +33 -19
- data/lib/llm_cost_tracker/ingestion/inbox.rb +20 -11
- data/lib/llm_cost_tracker/ingestion/worker.rb +58 -37
- data/lib/llm_cost_tracker/ingestion.rb +20 -35
- data/lib/llm_cost_tracker/integrations/anthropic/batch_results_capture.rb +40 -0
- data/lib/llm_cost_tracker/integrations/anthropic/patches.rb +64 -0
- data/lib/llm_cost_tracker/integrations/anthropic.rb +59 -120
- data/lib/llm_cost_tracker/integrations/base/patch_target.rb +35 -0
- data/lib/llm_cost_tracker/integrations/base.rb +65 -81
- data/lib/llm_cost_tracker/integrations/openai/batch_capture.rb +20 -19
- data/lib/llm_cost_tracker/integrations/openai/patches.rb +51 -40
- data/lib/llm_cost_tracker/integrations/openai/websocket_capture.rb +89 -0
- data/lib/llm_cost_tracker/integrations/openai.rb +114 -83
- data/lib/llm_cost_tracker/integrations/ruby_llm/v1/patches.rb +67 -0
- data/lib/llm_cost_tracker/integrations/ruby_llm/v1/reply.rb +155 -0
- data/lib/llm_cost_tracker/integrations/ruby_llm/v1.rb +154 -0
- data/lib/llm_cost_tracker/integrations/ruby_llm/v2/attempt/token_counts.rb +159 -0
- data/lib/llm_cost_tracker/integrations/ruby_llm/v2/attempt.rb +175 -0
- data/lib/llm_cost_tracker/integrations/ruby_llm/v2/frame.rb +75 -0
- data/lib/llm_cost_tracker/integrations/ruby_llm/v2/recorder.rb +70 -0
- data/lib/llm_cost_tracker/integrations/ruby_llm/v2/seams.rb +86 -0
- data/lib/llm_cost_tracker/integrations/ruby_llm/v2.rb +161 -0
- data/lib/llm_cost_tracker/integrations/ruby_llm.rb +7 -408
- data/lib/llm_cost_tracker/ledger/isolation.rb +15 -2
- data/lib/llm_cost_tracker/ledger/period/totals.rb +1 -1
- data/lib/llm_cost_tracker/ledger/rollups.rb +24 -8
- data/lib/llm_cost_tracker/ledger/schema/adapter.rb +23 -6
- data/lib/llm_cost_tracker/ledger/schema/base.rb +9 -5
- data/lib/llm_cost_tracker/ledger/store.rb +17 -35
- data/lib/llm_cost_tracker/ledger/tags/breakdown.rb +2 -2
- data/lib/llm_cost_tracker/middleware/faraday/body.rb +57 -0
- data/lib/llm_cost_tracker/middleware/faraday/exchange.rb +153 -0
- data/lib/llm_cost_tracker/middleware/faraday/response_reader.rb +75 -0
- data/lib/llm_cost_tracker/middleware/faraday/stream_tee.rb +47 -0
- data/lib/llm_cost_tracker/middleware/faraday.rb +2 -301
- data/lib/llm_cost_tracker/parsers.rb +12 -0
- data/lib/llm_cost_tracker/prices.json +1748 -379
- data/lib/llm_cost_tracker/pricing/backfill.rb +59 -29
- data/lib/llm_cost_tracker/pricing/calculation/iterations.rb +49 -0
- data/lib/llm_cost_tracker/pricing/calculation/quantities.rb +40 -0
- data/lib/llm_cost_tracker/pricing/calculation/snapshot.rb +51 -0
- data/lib/llm_cost_tracker/pricing/calculation/totals.rb +59 -0
- data/lib/llm_cost_tracker/pricing/calculation.rb +82 -173
- data/lib/llm_cost_tracker/pricing/matcher.rb +31 -14
- data/lib/llm_cost_tracker/pricing/mode.rb +6 -19
- data/lib/llm_cost_tracker/pricing/off_peak.rb +50 -0
- data/lib/llm_cost_tracker/pricing/rate.rb +1 -0
- data/lib/llm_cost_tracker/pricing/registry/normalizer.rb +110 -0
- data/lib/llm_cost_tracker/pricing/registry.rb +38 -111
- data/lib/llm_cost_tracker/pricing/sync/change_printer.rb +40 -20
- data/lib/llm_cost_tracker/pricing/sync/fetcher.rb +35 -34
- data/lib/llm_cost_tracker/pricing/sync/registry_writer.rb +29 -18
- data/lib/llm_cost_tracker/pricing/sync/remote_snapshot.rb +78 -0
- data/lib/llm_cost_tracker/pricing/sync/snapshot_guard.rb +2 -0
- data/lib/llm_cost_tracker/pricing/sync.rb +7 -53
- data/lib/llm_cost_tracker/pricing/unknown.rb +1 -1
- data/lib/llm_cost_tracker/providers/anthropic/parser.rb +30 -25
- data/lib/llm_cost_tracker/providers/anthropic/response_parser.rb +5 -3
- data/lib/llm_cost_tracker/providers/anthropic/usage_extractor.rb +122 -101
- data/lib/llm_cost_tracker/providers/azure/hosts.rb +1 -1
- data/lib/llm_cost_tracker/providers/azure/parser.rb +3 -6
- data/lib/llm_cost_tracker/providers/gemini/grounding.rb +87 -0
- data/lib/llm_cost_tracker/providers/gemini/parser.rb +120 -183
- data/lib/llm_cost_tracker/providers/gemini/usage_extractor.rb +66 -47
- data/lib/llm_cost_tracker/providers/openai/hosts.rb +11 -0
- data/lib/llm_cost_tracker/providers/openai/model_families.rb +1 -1
- data/lib/llm_cost_tracker/providers/openai/output_item.rb +84 -0
- data/lib/llm_cost_tracker/providers/openai/parser.rb +2 -1
- data/lib/llm_cost_tracker/providers/openai/response_parser.rb +81 -149
- data/lib/llm_cost_tracker/providers/openai/service_charges.rb +133 -156
- data/lib/llm_cost_tracker/providers/openai/stream_parser.rb +127 -0
- data/lib/llm_cost_tracker/providers/openai/usage_extractor.rb +80 -62
- data/lib/llm_cost_tracker/providers/openai_compatible/parser.rb +15 -4
- data/lib/llm_cost_tracker/providers.rb +3 -0
- data/lib/llm_cost_tracker/redaction.rb +13 -0
- data/lib/llm_cost_tracker/report/data.rb +25 -28
- data/lib/llm_cost_tracker/tags/context.rb +22 -7
- data/lib/llm_cost_tracker/tracker.rb +32 -26
- data/lib/llm_cost_tracker/usage/dimensions.yml +24 -0
- data/lib/llm_cost_tracker/usage/token_usage.rb +8 -4
- data/lib/llm_cost_tracker/version.rb +1 -1
- data/lib/llm_cost_tracker.rb +8 -4
- data/lib/tasks/llm_cost_tracker.rake +35 -5
- data/llm_cost_tracker.gemspec +7 -6
- metadata +66 -7
data/lib/llm_cost_tracker.rb
CHANGED
|
@@ -128,12 +128,16 @@ module LlmCostTracker
|
|
|
128
128
|
pricing_mode: pricing_mode,
|
|
129
129
|
metadata: tags
|
|
130
130
|
)
|
|
131
|
+
completed = false
|
|
131
132
|
yield collector
|
|
132
|
-
|
|
133
|
-
collector&.finish!(errored: true)
|
|
134
|
-
raise
|
|
135
|
-
else
|
|
133
|
+
completed = true
|
|
136
134
|
collector.finish!
|
|
135
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
136
|
+
collector&.finish!(errored: true) unless completed
|
|
137
|
+
completed = true
|
|
138
|
+
raise
|
|
139
|
+
ensure
|
|
140
|
+
collector&.finish! unless completed
|
|
137
141
|
end
|
|
138
142
|
end
|
|
139
143
|
end
|
|
@@ -12,6 +12,34 @@ module LlmCostTrackerTasks
|
|
|
12
12
|
FileUtils.mkdir_p(File.dirname(path))
|
|
13
13
|
path
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
def self.batch_size(default)
|
|
17
|
+
text = ENV.fetch("BATCH_SIZE", default.to_s)
|
|
18
|
+
size = Integer(text, 10, exception: false)
|
|
19
|
+
return size if size&.positive?
|
|
20
|
+
|
|
21
|
+
abort("llm_cost_tracker: BATCH_SIZE=#{text} is not a positive integer, e.g. BATCH_SIZE=#{default}")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.backfill_after_refresh(path)
|
|
25
|
+
file = LlmCostTracker.configuration.pricing.file
|
|
26
|
+
return unless file && File.expand_path(file) == File.expand_path(path)
|
|
27
|
+
|
|
28
|
+
if ledger_reachable?
|
|
29
|
+
Rake::Task["llm_cost_tracker:backfill_unknown_pricing"].execute
|
|
30
|
+
else
|
|
31
|
+
puts "llm_cost_tracker: calls ledger not reachable; " \
|
|
32
|
+
"run bin/rails llm_cost_tracker:backfill_unknown_pricing where it is"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.ledger_reachable?
|
|
37
|
+
return false unless Rake::Task.task_defined?("environment")
|
|
38
|
+
|
|
39
|
+
LlmCostTracker::Call.with_connection { |connection| connection.table_exists?(LlmCostTracker::Call.table_name) }
|
|
40
|
+
rescue ActiveRecord::NoDatabaseError, ActiveRecord::ConnectionNotEstablished
|
|
41
|
+
false
|
|
42
|
+
end
|
|
15
43
|
end
|
|
16
44
|
|
|
17
45
|
# rubocop:disable-next Metrics/BlockLength
|
|
@@ -58,7 +86,7 @@ namespace :llm_cost_tracker do
|
|
|
58
86
|
"Use BATCH_SIZE=N to tune."
|
|
59
87
|
task backfill_unknown_pricing: :environment do
|
|
60
88
|
require_relative "../llm_cost_tracker/pricing/backfill"
|
|
61
|
-
batch_size = (
|
|
89
|
+
batch_size = LlmCostTrackerTasks.batch_size(LlmCostTracker::Pricing::Backfill::DEFAULT_BATCH_SIZE)
|
|
62
90
|
result = LlmCostTracker::Pricing::Backfill.call(batch_size: batch_size)
|
|
63
91
|
puts "llm_cost_tracker: examined #{result.examined} calls, recomputed #{result.recomputed}, " \
|
|
64
92
|
"still unknown #{result.still_unknown}"
|
|
@@ -73,7 +101,7 @@ namespace :llm_cost_tracker do
|
|
|
73
101
|
to_text = ENV["TO"].presence
|
|
74
102
|
to = to_text && Time.zone.parse(to_text)
|
|
75
103
|
abort("llm_cost_tracker: TO=#{to_text} is not a date, e.g. TO=2026-10-01") if to_text && !to
|
|
76
|
-
batch_size = (
|
|
104
|
+
batch_size = LlmCostTrackerTasks.batch_size(LlmCostTracker::Pricing::Backfill::DEFAULT_BATCH_SIZE)
|
|
77
105
|
scope = LlmCostTracker::Pricing::Backfill.reprice_scope(from...to)
|
|
78
106
|
result = LlmCostTracker::Pricing::Backfill.call(scope: scope, batch_size: batch_size, reprice: true)
|
|
79
107
|
puts "llm_cost_tracker: examined #{result.examined} calls, repriced #{result.recomputed}"
|
|
@@ -82,7 +110,7 @@ namespace :llm_cost_tracker do
|
|
|
82
110
|
desc "Delete llm_cost_tracker_calls and ingestion inbox rows older than DAYS (default: 90). Use BATCH_SIZE=N to tune."
|
|
83
111
|
task prune: :environment do
|
|
84
112
|
days = (ENV["DAYS"] || 90).to_i
|
|
85
|
-
batch_size = (
|
|
113
|
+
batch_size = LlmCostTrackerTasks.batch_size(LlmCostTracker::Retention::DEFAULT_BATCH_SIZE)
|
|
86
114
|
deleted = LlmCostTracker::Retention.prune(older_than: days, batch_size: batch_size)
|
|
87
115
|
puts "llm_cost_tracker: pruned #{deleted} calls older than #{days} days"
|
|
88
116
|
inbox_pruned = LlmCostTracker::Retention.prune_inbox(older_than: days)
|
|
@@ -105,14 +133,15 @@ namespace :llm_cost_tracker do
|
|
|
105
133
|
abort("llm_cost_tracker: llm_cost_tracker_call_tags is missing the cost columns; " \
|
|
106
134
|
"run the upgrade_per_tag_budgets generator and migrate first")
|
|
107
135
|
end
|
|
108
|
-
batch_size = (
|
|
136
|
+
batch_size = LlmCostTrackerTasks.batch_size(LlmCostTracker::Budget::PerTag::DEFAULT_BACKFILL_BATCH)
|
|
109
137
|
filled = LlmCostTracker::Budget::PerTag.backfill(batch_size: batch_size)
|
|
110
138
|
puts "llm_cost_tracker: filled cost and time on #{filled} tag rows"
|
|
111
139
|
end
|
|
112
140
|
|
|
113
141
|
namespace :prices do
|
|
114
142
|
desc(
|
|
115
|
-
"Refresh the configured pricing file from the maintained LLM Cost Tracker price snapshot
|
|
143
|
+
"Refresh the configured pricing file from the maintained LLM Cost Tracker price snapshot; after writing a new " \
|
|
144
|
+
"config.pricing.file, run llm_cost_tracker:backfill_unknown_pricing if the calls ledger is reachable. " \
|
|
116
145
|
"Review changes first with llm_cost_tracker:prices:check. Use FORCE=1 to accept suspicious price changes, " \
|
|
117
146
|
"URL=... to override the source, or OUTPUT=path/to/file.json."
|
|
118
147
|
)
|
|
@@ -130,6 +159,7 @@ namespace :llm_cost_tracker do
|
|
|
130
159
|
puts " source: #{result.source_url}"
|
|
131
160
|
puts " version: #{result.source_version.inspect}" if result.source_version
|
|
132
161
|
LlmCostTracker::Pricing::Sync::ChangePrinter.call(result.changes, suspicious: result.suspicious)
|
|
162
|
+
LlmCostTrackerTasks.backfill_after_refresh(result.path) if result.written
|
|
133
163
|
end
|
|
134
164
|
|
|
135
165
|
desc "Compare the current pricing file with the maintained LLM Cost Tracker price snapshot."
|
data/llm_cost_tracker.gemspec
CHANGED
|
@@ -8,11 +8,11 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.authors = ["Sergii Khomenko"]
|
|
9
9
|
spec.email = ["sergey@mm.st"]
|
|
10
10
|
|
|
11
|
-
spec.summary = "LLM
|
|
12
|
-
spec.description = "
|
|
13
|
-
"or
|
|
14
|
-
"
|
|
15
|
-
"
|
|
11
|
+
spec.summary = "LLM spend tracking and budgets for Rails, in your own database"
|
|
12
|
+
spec.description = "Records every LLM call your Rails app makes through RubyLLM, the official OpenAI " \
|
|
13
|
+
"and Anthropic SDKs, Faraday, or any OpenAI-compatible API: tokens and cost, attributed " \
|
|
14
|
+
"by user, feature, or any tag. Calls go straight to the provider — no proxy. " \
|
|
15
|
+
"Includes budgets that can block the next call, bundled prices, and a dashboard."
|
|
16
16
|
spec.homepage = "https://github.com/sergey-homenko/llm_cost_tracker"
|
|
17
17
|
spec.license = "MIT"
|
|
18
18
|
|
|
@@ -49,7 +49,8 @@ Gem::Specification.new do |spec|
|
|
|
49
49
|
spec.add_dependency "faraday", ">= 2.0", "< 3.0"
|
|
50
50
|
spec.add_dependency "railties", ">= 8.0", "< 9.0"
|
|
51
51
|
|
|
52
|
-
spec.add_development_dependency "anthropic", "
|
|
52
|
+
spec.add_development_dependency "anthropic", ">= 1.61", "< 3"
|
|
53
|
+
spec.add_development_dependency "aws-eventstream", "~> 1.4"
|
|
53
54
|
spec.add_development_dependency "nokogiri", "~> 1.19"
|
|
54
55
|
spec.add_development_dependency "openai", "~> 0.80"
|
|
55
56
|
spec.add_development_dependency "pg", "~> 1.6"
|
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.
|
|
4
|
+
version: 0.15.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sergii Khomenko
|
|
@@ -107,16 +107,36 @@ dependencies:
|
|
|
107
107
|
name: anthropic
|
|
108
108
|
requirement: !ruby/object:Gem::Requirement
|
|
109
109
|
requirements:
|
|
110
|
-
- - "
|
|
110
|
+
- - ">="
|
|
111
111
|
- !ruby/object:Gem::Version
|
|
112
112
|
version: '1.61'
|
|
113
|
+
- - "<"
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: '3'
|
|
113
116
|
type: :development
|
|
114
117
|
prerelease: false
|
|
115
118
|
version_requirements: !ruby/object:Gem::Requirement
|
|
116
119
|
requirements:
|
|
117
|
-
- - "
|
|
120
|
+
- - ">="
|
|
118
121
|
- !ruby/object:Gem::Version
|
|
119
122
|
version: '1.61'
|
|
123
|
+
- - "<"
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '3'
|
|
126
|
+
- !ruby/object:Gem::Dependency
|
|
127
|
+
name: aws-eventstream
|
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - "~>"
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '1.4'
|
|
133
|
+
type: :development
|
|
134
|
+
prerelease: false
|
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
136
|
+
requirements:
|
|
137
|
+
- - "~>"
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: '1.4'
|
|
120
140
|
- !ruby/object:Gem::Dependency
|
|
121
141
|
name: nokogiri
|
|
122
142
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -263,9 +283,10 @@ dependencies:
|
|
|
263
283
|
- - "~>"
|
|
264
284
|
- !ruby/object:Gem::Version
|
|
265
285
|
version: '3.0'
|
|
266
|
-
description: '
|
|
267
|
-
or
|
|
268
|
-
|
|
286
|
+
description: 'Records every LLM call your Rails app makes through RubyLLM, the official
|
|
287
|
+
OpenAI and Anthropic SDKs, Faraday, or any OpenAI-compatible API: tokens and cost,
|
|
288
|
+
attributed by user, feature, or any tag. Calls go straight to the provider — no
|
|
289
|
+
proxy. Includes budgets that can block the next call, bundled prices, and a dashboard.'
|
|
269
290
|
email:
|
|
270
291
|
- sergey@mm.st
|
|
271
292
|
executables: []
|
|
@@ -303,7 +324,11 @@ files:
|
|
|
303
324
|
- app/models/llm_cost_tracker/call_tag.rb
|
|
304
325
|
- app/models/llm_cost_tracker/ingestion/inbox_entry.rb
|
|
305
326
|
- app/models/llm_cost_tracker/ingestion/lease.rb
|
|
327
|
+
- app/services/llm_cost_tracker/dashboard/call_order.rb
|
|
328
|
+
- app/services/llm_cost_tracker/dashboard/calls_export.rb
|
|
306
329
|
- app/services/llm_cost_tracker/dashboard/data_quality.rb
|
|
330
|
+
- app/services/llm_cost_tracker/dashboard/data_quality/stats_query.rb
|
|
331
|
+
- app/services/llm_cost_tracker/dashboard/data_quality/summary.rb
|
|
307
332
|
- app/services/llm_cost_tracker/dashboard/date_range.rb
|
|
308
333
|
- app/services/llm_cost_tracker/dashboard/filter.rb
|
|
309
334
|
- app/services/llm_cost_tracker/dashboard/masking.rb
|
|
@@ -311,11 +336,13 @@ files:
|
|
|
311
336
|
- app/services/llm_cost_tracker/dashboard/overview_stats.rb
|
|
312
337
|
- app/services/llm_cost_tracker/dashboard/pagination.rb
|
|
313
338
|
- app/services/llm_cost_tracker/dashboard/params.rb
|
|
339
|
+
- app/services/llm_cost_tracker/dashboard/percent.rb
|
|
314
340
|
- app/services/llm_cost_tracker/dashboard/pricing_overview.rb
|
|
315
341
|
- app/services/llm_cost_tracker/dashboard/provider_breakdown.rb
|
|
316
342
|
- app/services/llm_cost_tracker/dashboard/setup_state.rb
|
|
317
343
|
- app/services/llm_cost_tracker/dashboard/sort.rb
|
|
318
344
|
- app/services/llm_cost_tracker/dashboard/spend_anomaly.rb
|
|
345
|
+
- app/services/llm_cost_tracker/dashboard/spend_chart.rb
|
|
319
346
|
- app/services/llm_cost_tracker/dashboard/tag_breakdown.rb
|
|
320
347
|
- app/services/llm_cost_tracker/dashboard/tag_key_explorer.rb
|
|
321
348
|
- app/services/llm_cost_tracker/dashboard/time_series.rb
|
|
@@ -328,6 +355,7 @@ files:
|
|
|
328
355
|
- app/views/llm_cost_tracker/errors/database.html.erb
|
|
329
356
|
- app/views/llm_cost_tracker/errors/invalid_filter.html.erb
|
|
330
357
|
- app/views/llm_cost_tracker/errors/not_found.html.erb
|
|
358
|
+
- app/views/llm_cost_tracker/errors/query.html.erb
|
|
331
359
|
- app/views/llm_cost_tracker/models/index.html.erb
|
|
332
360
|
- app/views/llm_cost_tracker/pricing/index.html.erb
|
|
333
361
|
- app/views/llm_cost_tracker/shared/_bar.html.erb
|
|
@@ -345,7 +373,11 @@ files:
|
|
|
345
373
|
- lib/llm_cost_tracker.rb
|
|
346
374
|
- lib/llm_cost_tracker/assets.rb
|
|
347
375
|
- lib/llm_cost_tracker/budget.rb
|
|
376
|
+
- lib/llm_cost_tracker/budget/estimate.rb
|
|
377
|
+
- lib/llm_cost_tracker/budget/global.rb
|
|
378
|
+
- lib/llm_cost_tracker/budget/limit.rb
|
|
348
379
|
- lib/llm_cost_tracker/budget/per_tag.rb
|
|
380
|
+
- lib/llm_cost_tracker/budget/tagged.rb
|
|
349
381
|
- lib/llm_cost_tracker/capture/event_window.rb
|
|
350
382
|
- lib/llm_cost_tracker/capture/sdk_payload.rb
|
|
351
383
|
- lib/llm_cost_tracker/capture/sse.rb
|
|
@@ -401,11 +433,24 @@ files:
|
|
|
401
433
|
- lib/llm_cost_tracker/ingestion/worker.rb
|
|
402
434
|
- lib/llm_cost_tracker/integrations.rb
|
|
403
435
|
- lib/llm_cost_tracker/integrations/anthropic.rb
|
|
436
|
+
- lib/llm_cost_tracker/integrations/anthropic/batch_results_capture.rb
|
|
437
|
+
- lib/llm_cost_tracker/integrations/anthropic/patches.rb
|
|
404
438
|
- lib/llm_cost_tracker/integrations/base.rb
|
|
439
|
+
- lib/llm_cost_tracker/integrations/base/patch_target.rb
|
|
405
440
|
- lib/llm_cost_tracker/integrations/openai.rb
|
|
406
441
|
- lib/llm_cost_tracker/integrations/openai/batch_capture.rb
|
|
407
442
|
- lib/llm_cost_tracker/integrations/openai/patches.rb
|
|
443
|
+
- lib/llm_cost_tracker/integrations/openai/websocket_capture.rb
|
|
408
444
|
- lib/llm_cost_tracker/integrations/ruby_llm.rb
|
|
445
|
+
- lib/llm_cost_tracker/integrations/ruby_llm/v1.rb
|
|
446
|
+
- lib/llm_cost_tracker/integrations/ruby_llm/v1/patches.rb
|
|
447
|
+
- lib/llm_cost_tracker/integrations/ruby_llm/v1/reply.rb
|
|
448
|
+
- lib/llm_cost_tracker/integrations/ruby_llm/v2.rb
|
|
449
|
+
- lib/llm_cost_tracker/integrations/ruby_llm/v2/attempt.rb
|
|
450
|
+
- lib/llm_cost_tracker/integrations/ruby_llm/v2/attempt/token_counts.rb
|
|
451
|
+
- lib/llm_cost_tracker/integrations/ruby_llm/v2/frame.rb
|
|
452
|
+
- lib/llm_cost_tracker/integrations/ruby_llm/v2/recorder.rb
|
|
453
|
+
- lib/llm_cost_tracker/integrations/ruby_llm/v2/seams.rb
|
|
409
454
|
- lib/llm_cost_tracker/ledger.rb
|
|
410
455
|
- lib/llm_cost_tracker/ledger/isolation.rb
|
|
411
456
|
- lib/llm_cost_tracker/ledger/period.rb
|
|
@@ -427,18 +472,28 @@ files:
|
|
|
427
472
|
- lib/llm_cost_tracker/ledger/tags/query.rb
|
|
428
473
|
- lib/llm_cost_tracker/logging.rb
|
|
429
474
|
- lib/llm_cost_tracker/middleware/faraday.rb
|
|
475
|
+
- lib/llm_cost_tracker/middleware/faraday/body.rb
|
|
476
|
+
- lib/llm_cost_tracker/middleware/faraday/exchange.rb
|
|
477
|
+
- lib/llm_cost_tracker/middleware/faraday/response_reader.rb
|
|
478
|
+
- lib/llm_cost_tracker/middleware/faraday/stream_tee.rb
|
|
430
479
|
- lib/llm_cost_tracker/parsers.rb
|
|
431
480
|
- lib/llm_cost_tracker/prices.json
|
|
432
481
|
- lib/llm_cost_tracker/pricing.rb
|
|
433
482
|
- lib/llm_cost_tracker/pricing/backfill.rb
|
|
434
483
|
- lib/llm_cost_tracker/pricing/calculation.rb
|
|
484
|
+
- lib/llm_cost_tracker/pricing/calculation/iterations.rb
|
|
485
|
+
- lib/llm_cost_tracker/pricing/calculation/quantities.rb
|
|
486
|
+
- lib/llm_cost_tracker/pricing/calculation/snapshot.rb
|
|
487
|
+
- lib/llm_cost_tracker/pricing/calculation/totals.rb
|
|
435
488
|
- lib/llm_cost_tracker/pricing/effective_prices.rb
|
|
436
489
|
- lib/llm_cost_tracker/pricing/estimator.rb
|
|
437
490
|
- lib/llm_cost_tracker/pricing/matcher.rb
|
|
438
491
|
- lib/llm_cost_tracker/pricing/mode.rb
|
|
492
|
+
- lib/llm_cost_tracker/pricing/off_peak.rb
|
|
439
493
|
- lib/llm_cost_tracker/pricing/price_key.rb
|
|
440
494
|
- lib/llm_cost_tracker/pricing/rate.rb
|
|
441
495
|
- lib/llm_cost_tracker/pricing/registry.rb
|
|
496
|
+
- lib/llm_cost_tracker/pricing/registry/normalizer.rb
|
|
442
497
|
- lib/llm_cost_tracker/pricing/service_rates.rb
|
|
443
498
|
- lib/llm_cost_tracker/pricing/source.rb
|
|
444
499
|
- lib/llm_cost_tracker/pricing/sync.rb
|
|
@@ -446,6 +501,7 @@ files:
|
|
|
446
501
|
- lib/llm_cost_tracker/pricing/sync/fetcher.rb
|
|
447
502
|
- lib/llm_cost_tracker/pricing/sync/registry_diff.rb
|
|
448
503
|
- lib/llm_cost_tracker/pricing/sync/registry_writer.rb
|
|
504
|
+
- lib/llm_cost_tracker/pricing/sync/remote_snapshot.rb
|
|
449
505
|
- lib/llm_cost_tracker/pricing/sync/snapshot_guard.rb
|
|
450
506
|
- lib/llm_cost_tracker/pricing/unknown.rb
|
|
451
507
|
- lib/llm_cost_tracker/providers.rb
|
|
@@ -454,14 +510,17 @@ files:
|
|
|
454
510
|
- lib/llm_cost_tracker/providers/anthropic/usage_extractor.rb
|
|
455
511
|
- lib/llm_cost_tracker/providers/azure/hosts.rb
|
|
456
512
|
- lib/llm_cost_tracker/providers/azure/parser.rb
|
|
513
|
+
- lib/llm_cost_tracker/providers/gemini/grounding.rb
|
|
457
514
|
- lib/llm_cost_tracker/providers/gemini/model_families.rb
|
|
458
515
|
- lib/llm_cost_tracker/providers/gemini/parser.rb
|
|
459
516
|
- lib/llm_cost_tracker/providers/gemini/usage_extractor.rb
|
|
460
517
|
- lib/llm_cost_tracker/providers/openai/hosts.rb
|
|
461
518
|
- lib/llm_cost_tracker/providers/openai/model_families.rb
|
|
519
|
+
- lib/llm_cost_tracker/providers/openai/output_item.rb
|
|
462
520
|
- lib/llm_cost_tracker/providers/openai/parser.rb
|
|
463
521
|
- lib/llm_cost_tracker/providers/openai/response_parser.rb
|
|
464
522
|
- lib/llm_cost_tracker/providers/openai/service_charges.rb
|
|
523
|
+
- lib/llm_cost_tracker/providers/openai/stream_parser.rb
|
|
465
524
|
- lib/llm_cost_tracker/providers/openai/usage_extractor.rb
|
|
466
525
|
- lib/llm_cost_tracker/providers/openai_compatible/parser.rb
|
|
467
526
|
- lib/llm_cost_tracker/railtie.rb
|
|
@@ -508,5 +567,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
508
567
|
requirements: []
|
|
509
568
|
rubygems_version: 3.6.9
|
|
510
569
|
specification_version: 4
|
|
511
|
-
summary: LLM
|
|
570
|
+
summary: LLM spend tracking and budgets for Rails, in your own database
|
|
512
571
|
test_files: []
|