llm_cost_tracker 0.6.0 → 0.7.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 +14 -0
- data/README.md +2 -3
- data/app/services/llm_cost_tracker/dashboard/tag_key_explorer.rb +1 -17
- data/config/routes.rb +1 -1
- data/lib/llm_cost_tracker/active_record_adapter.rb +9 -5
- data/lib/llm_cost_tracker/assets.rb +0 -6
- data/lib/llm_cost_tracker/budget.rb +3 -5
- data/lib/llm_cost_tracker/capture_verifier.rb +3 -10
- data/lib/llm_cost_tracker/configuration.rb +2 -10
- data/lib/llm_cost_tracker/doctor/ingestion_check.rb +1 -3
- data/lib/llm_cost_tracker/doctor.rb +8 -13
- data/lib/llm_cost_tracker/engine.rb +0 -3
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_period_totals_to_llm_cost_tracker.rb.erb +2 -2
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/create_llm_api_calls.rb.erb +7 -1
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/initializer.rb.erb +1 -17
- data/lib/llm_cost_tracker/integrations/registry.rb +0 -2
- data/lib/llm_cost_tracker/period_grouping.rb +3 -5
- data/lib/llm_cost_tracker/railtie.rb +2 -4
- data/lib/llm_cost_tracker/report.rb +2 -4
- data/lib/llm_cost_tracker/storage/active_record_backend.rb +2 -1
- data/lib/llm_cost_tracker/storage/active_record_inbox.rb +1 -6
- data/lib/llm_cost_tracker/storage/active_record_rollup_upsert_sql.rb +1 -1
- data/lib/llm_cost_tracker/storage/{dispatcher.rb → writer.rb} +4 -14
- data/lib/llm_cost_tracker/tag_sql.rb +1 -1
- data/lib/llm_cost_tracker/tags_column.rb +2 -0
- data/lib/llm_cost_tracker/tracker.rb +2 -2
- data/lib/llm_cost_tracker/version.rb +1 -1
- data/lib/llm_cost_tracker.rb +2 -14
- data/lib/tasks/llm_cost_tracker.rake +1 -1
- metadata +33 -60
- data/docs/architecture.md +0 -28
- data/docs/budgets.md +0 -45
- data/docs/configuration.md +0 -65
- data/docs/cookbook.md +0 -185
- data/docs/dashboard-overview.png +0 -0
- data/docs/dashboard.md +0 -38
- data/docs/extending.md +0 -32
- data/docs/operations.md +0 -44
- data/docs/pricing.md +0 -94
- data/docs/querying.md +0 -36
- data/docs/streaming.md +0 -70
- data/docs/technical/README.md +0 -10
- data/docs/technical/data-flow.md +0 -70
- data/docs/technical/extension-points.md +0 -111
- data/docs/technical/module-map.md +0 -197
- data/docs/technical/operational-notes.md +0 -97
- data/docs/upgrading.md +0 -47
- data/lib/llm_cost_tracker/configuration/storage_backend.rb +0 -26
- data/lib/llm_cost_tracker/engine_compatibility.rb +0 -15
- data/lib/llm_cost_tracker/storage/custom_backend.rb +0 -32
- data/lib/llm_cost_tracker/storage/log_backend.rb +0 -38
- data/lib/llm_cost_tracker/storage/registry.rb +0 -63
data/docs/upgrading.md
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# Upgrading
|
|
2
|
-
|
|
3
|
-
LLM Cost Tracker is still moving quickly, so upgrades should be explicit:
|
|
4
|
-
inspect the changelog, run doctor, and apply only the generators your schema is
|
|
5
|
-
missing.
|
|
6
|
-
|
|
7
|
-
The version-by-version upgrade guide is moving here from the README.
|
|
8
|
-
|
|
9
|
-
## Canonical Sources
|
|
10
|
-
|
|
11
|
-
Until this page is expanded, use:
|
|
12
|
-
|
|
13
|
-
- [Changelog](../CHANGELOG.md)
|
|
14
|
-
- [Quickstart](../README.md#quickstart)
|
|
15
|
-
- [Operations](operations.md)
|
|
16
|
-
|
|
17
|
-
## Schema Generators
|
|
18
|
-
|
|
19
|
-
Existing installs can add newer optional columns through focused generators:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
bin/rails generate llm_cost_tracker:add_period_totals
|
|
23
|
-
bin/rails generate llm_cost_tracker:add_ingestion
|
|
24
|
-
bin/rails generate llm_cost_tracker:add_streaming
|
|
25
|
-
bin/rails generate llm_cost_tracker:add_provider_response_id
|
|
26
|
-
bin/rails generate llm_cost_tracker:add_usage_breakdown
|
|
27
|
-
bin/rails generate llm_cost_tracker:upgrade_tags_to_jsonb
|
|
28
|
-
bin/rails generate llm_cost_tracker:upgrade_cost_precision
|
|
29
|
-
bin/rails generate llm_cost_tracker:add_latency_ms
|
|
30
|
-
bin/rails db:migrate
|
|
31
|
-
bin/rails llm_cost_tracker:doctor
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
On PostgreSQL, `upgrade_tags_to_jsonb` rewrites `llm_api_calls`. For large
|
|
35
|
-
tables, run it during a maintenance window or replace it with a two-phase
|
|
36
|
-
backfill.
|
|
37
|
-
|
|
38
|
-
## Upgrade Habit
|
|
39
|
-
|
|
40
|
-
Run:
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
bin/rails llm_cost_tracker:doctor
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Doctor tells you which optional columns and production-hardening pieces are still
|
|
47
|
-
missing.
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module LlmCostTracker
|
|
4
|
-
module ConfigurationStorageBackend
|
|
5
|
-
def storage_backend=(value)
|
|
6
|
-
ensure_shared_configuration_mutable!
|
|
7
|
-
@storage_backend = normalize_storage_backend(value)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
def normalize_storage_backend(value)
|
|
13
|
-
value = :log if value.nil?
|
|
14
|
-
value = value.to_sym
|
|
15
|
-
return value if self.class::STORAGE_BACKENDS.include?(value)
|
|
16
|
-
return value if defined?(Storage::Registry) && Storage::Registry.registered?(value)
|
|
17
|
-
|
|
18
|
-
names = if defined?(Storage::Registry)
|
|
19
|
-
Storage::Registry.names
|
|
20
|
-
else
|
|
21
|
-
self.class::STORAGE_BACKENDS
|
|
22
|
-
end
|
|
23
|
-
raise Error, "Unknown storage_backend: #{value.inspect}. Use one of: #{names.join(', ')}"
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module LlmCostTracker
|
|
4
|
-
module EngineCompatibility
|
|
5
|
-
REQUIRED_RAILS_VERSION = Gem::Version.new("7.1.0")
|
|
6
|
-
|
|
7
|
-
class << self
|
|
8
|
-
def check_rails_version!(version)
|
|
9
|
-
return if Gem::Version.new(version) >= REQUIRED_RAILS_VERSION
|
|
10
|
-
|
|
11
|
-
raise LlmCostTracker::Error, "LlmCostTracker::Engine requires Rails 7.1+"
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "registry"
|
|
4
|
-
|
|
5
|
-
module LlmCostTracker
|
|
6
|
-
module Storage
|
|
7
|
-
class CustomBackend
|
|
8
|
-
class << self
|
|
9
|
-
def save(event)
|
|
10
|
-
result = LlmCostTracker.configuration.custom_storage&.call(event)
|
|
11
|
-
result == false ? false : event
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def verify
|
|
15
|
-
if LlmCostTracker.configuration.custom_storage.respond_to?(:call)
|
|
16
|
-
return [
|
|
17
|
-
VerificationResult.new(
|
|
18
|
-
:ok,
|
|
19
|
-
"storage",
|
|
20
|
-
"custom storage callable configured; external sink was not invoked"
|
|
21
|
-
)
|
|
22
|
-
]
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
[
|
|
26
|
-
VerificationResult.new(:error, "storage", "custom storage backend requires config.custom_storage")
|
|
27
|
-
]
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "../logging"
|
|
4
|
-
require_relative "registry"
|
|
5
|
-
|
|
6
|
-
module LlmCostTracker
|
|
7
|
-
module Storage
|
|
8
|
-
class LogBackend
|
|
9
|
-
class << self
|
|
10
|
-
def save(event)
|
|
11
|
-
config = LlmCostTracker.configuration
|
|
12
|
-
message = "#{event.provider}/#{event.model} " \
|
|
13
|
-
"tokens=#{event.total_tokens} " \
|
|
14
|
-
"cost=#{cost_label(event)}"
|
|
15
|
-
message += " latency=#{event.latency_ms}ms" if event.latency_ms
|
|
16
|
-
message += " stream=#{event.stream}" if event.stream
|
|
17
|
-
message += " source=#{event.usage_source}" if event.usage_source
|
|
18
|
-
message += " tags=#{event.tags}" unless event.tags.empty?
|
|
19
|
-
|
|
20
|
-
Logging.log(config.log_level, message)
|
|
21
|
-
event
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def verify
|
|
25
|
-
[
|
|
26
|
-
VerificationResult.new(:ok, "storage", "log backend configured; capture writes to logs only")
|
|
27
|
-
]
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def cost_label(event)
|
|
33
|
-
event.cost ? "$#{format('%.6f', event.cost.total_cost)}" : "unknown"
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "monitor"
|
|
4
|
-
|
|
5
|
-
require_relative "../errors"
|
|
6
|
-
|
|
7
|
-
module LlmCostTracker
|
|
8
|
-
module Storage
|
|
9
|
-
VerificationResult = Data.define(:status, :name, :message)
|
|
10
|
-
|
|
11
|
-
module Registry
|
|
12
|
-
MUTEX = Monitor.new
|
|
13
|
-
|
|
14
|
-
class << self
|
|
15
|
-
def register(name, backend)
|
|
16
|
-
name = normalize_name(name)
|
|
17
|
-
validate_backend!(backend)
|
|
18
|
-
MUTEX.synchronize { @backends = backends.merge(name => backend).freeze }
|
|
19
|
-
backend
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def fetch(name)
|
|
23
|
-
key = normalize_name(name)
|
|
24
|
-
backends.fetch(key) do
|
|
25
|
-
raise Error, "Unknown storage_backend: #{key.inspect}. Use one of: #{names.join(', ')}"
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def registered?(name)
|
|
30
|
-
backends.key?(normalize_name(name))
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def names
|
|
34
|
-
backends.keys
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
def backends
|
|
40
|
-
@backends || MUTEX.synchronize { @backends ||= {}.freeze }
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def normalize_name(name)
|
|
44
|
-
name.to_sym
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def validate_backend!(backend)
|
|
48
|
-
return if backend.respond_to?(:save)
|
|
49
|
-
|
|
50
|
-
raise ArgumentError, "storage backend must respond to save"
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def self.register(name, backend)
|
|
56
|
-
Registry.register(name, backend)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def self.backends
|
|
60
|
-
Registry.names
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|