llm_cost_tracker 0.7.3 → 0.9.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/.ruby-version +1 -0
- data/CHANGELOG.md +173 -0
- data/README.md +60 -220
- data/app/assets/llm_cost_tracker/application.css +282 -45
- data/app/controllers/llm_cost_tracker/application_controller.rb +25 -20
- data/app/controllers/llm_cost_tracker/assets_controller.rb +11 -1
- data/app/controllers/llm_cost_tracker/calls_controller.rb +22 -19
- data/app/controllers/llm_cost_tracker/data_quality_controller.rb +14 -2
- data/app/controllers/llm_cost_tracker/reconciliation_controller.rb +106 -0
- data/app/controllers/llm_cost_tracker/tags_controller.rb +15 -1
- data/app/helpers/llm_cost_tracker/application_helper.rb +18 -21
- data/app/helpers/llm_cost_tracker/dashboard_filter_helper.rb +3 -21
- data/app/helpers/llm_cost_tracker/dashboard_filter_options_helper.rb +4 -4
- data/app/helpers/llm_cost_tracker/dashboard_query_helper.rb +1 -1
- data/app/helpers/llm_cost_tracker/inline_style_helper.rb +28 -0
- data/app/helpers/llm_cost_tracker/reconciliation_helper.rb +13 -0
- data/app/helpers/llm_cost_tracker/token_usage_helper.rb +24 -7
- data/app/models/llm_cost_tracker/call.rb +166 -0
- data/app/models/llm_cost_tracker/call_line_item.rb +18 -0
- data/app/models/llm_cost_tracker/call_rollup.rb +6 -0
- data/app/models/llm_cost_tracker/call_tag.rb +12 -0
- data/app/models/llm_cost_tracker/ingestion/inbox_entry.rb +9 -0
- data/app/models/llm_cost_tracker/ingestion/lease.rb +0 -3
- data/app/models/llm_cost_tracker/provider_invoice.rb +13 -0
- data/app/models/llm_cost_tracker/provider_invoice_import.rb +24 -0
- data/app/services/llm_cost_tracker/dashboard/data_quality.rb +152 -32
- data/app/services/llm_cost_tracker/dashboard/date_range.rb +1 -1
- data/app/services/llm_cost_tracker/dashboard/filter.rb +8 -6
- data/app/services/llm_cost_tracker/dashboard/overview_stats.rb +74 -21
- data/app/services/llm_cost_tracker/dashboard/pagination.rb +6 -4
- data/app/services/llm_cost_tracker/dashboard/params.rb +8 -2
- data/app/services/llm_cost_tracker/dashboard/provider_breakdown.rb +1 -1
- data/app/services/llm_cost_tracker/dashboard/spend_anomaly.rb +4 -3
- data/app/services/llm_cost_tracker/dashboard/tag_breakdown.rb +42 -9
- data/app/services/llm_cost_tracker/dashboard/tag_key_explorer.rb +14 -37
- data/app/services/llm_cost_tracker/dashboard/time_series.rb +1 -1
- data/app/services/llm_cost_tracker/dashboard/top_models.rb +1 -1
- data/app/views/layouts/llm_cost_tracker/application.html.erb +6 -1
- data/app/views/llm_cost_tracker/calls/index.html.erb +33 -75
- data/app/views/llm_cost_tracker/calls/show.html.erb +73 -33
- data/app/views/llm_cost_tracker/dashboard/index.html.erb +16 -57
- data/app/views/llm_cost_tracker/data_quality/index.html.erb +183 -167
- data/app/views/llm_cost_tracker/errors/database.html.erb +1 -1
- data/app/views/llm_cost_tracker/models/index.html.erb +18 -50
- data/app/views/llm_cost_tracker/reconciliation/index.html.erb +183 -0
- data/app/views/llm_cost_tracker/shared/_bar.html.erb +1 -1
- data/app/views/llm_cost_tracker/shared/_filters.html.erb +66 -0
- data/app/views/llm_cost_tracker/shared/_metric_stack.html.erb +1 -1
- data/app/views/llm_cost_tracker/shared/_sort.html.erb +13 -0
- data/app/views/llm_cost_tracker/shared/setup_required.html.erb +1 -1
- data/app/views/llm_cost_tracker/tags/index.html.erb +3 -34
- data/app/views/llm_cost_tracker/tags/show.html.erb +64 -36
- data/config/routes.rb +3 -2
- data/lib/llm_cost_tracker/billing/components.rb +95 -0
- data/lib/llm_cost_tracker/billing/components.yml +188 -0
- data/lib/llm_cost_tracker/billing/cost_status.rb +45 -0
- data/lib/llm_cost_tracker/billing/line_item.rb +189 -0
- data/lib/llm_cost_tracker/budget.rb +26 -36
- data/lib/llm_cost_tracker/capture/stream_collector.rb +125 -38
- data/lib/llm_cost_tracker/capture/stream_tracker.rb +40 -5
- data/lib/llm_cost_tracker/configuration.rb +86 -17
- data/lib/llm_cost_tracker/dashboard_setup_state.rb +109 -0
- data/lib/llm_cost_tracker/doctor/cost_drift_check.rb +56 -0
- data/lib/llm_cost_tracker/doctor/ingestion_check.rb +48 -30
- data/lib/llm_cost_tracker/doctor/invoice_reconciliation_check.rb +164 -0
- data/lib/llm_cost_tracker/doctor/legacy_audit_check.rb +36 -0
- data/lib/llm_cost_tracker/doctor/legacy_billing_status_check.rb +22 -0
- data/lib/llm_cost_tracker/doctor/price_check.rb +2 -2
- data/lib/llm_cost_tracker/doctor/pricing_snapshot_drift_check.rb +85 -0
- data/lib/llm_cost_tracker/doctor/probe.rb +17 -0
- data/lib/llm_cost_tracker/doctor/schema_check.rb +34 -0
- data/lib/llm_cost_tracker/doctor.rb +111 -44
- data/lib/llm_cost_tracker/engine.rb +9 -0
- data/lib/llm_cost_tracker/errors.rb +5 -19
- data/lib/llm_cost_tracker/event.rb +11 -3
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/call_rollups_generator.rb +43 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/durable_ingestion_generator.rb +43 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/install_generator.rb +17 -5
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/prices_generator.rb +2 -6
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/reconciliation_generator.rb +34 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/create_llm_cost_tracker_call_rollups.rb.erb +15 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/create_llm_cost_tracker_calls.rb.erb +104 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/create_llm_cost_tracker_durable_ingestion.rb.erb +29 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/create_llm_cost_tracker_reconciliation.rb.erb +55 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/initializer.rb.erb +28 -25
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_call_rollups_provider.rb.erb +20 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_call_tags_key_value_index.rb.erb +32 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_image_tokens.rb.erb +18 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/upgrade_call_rollups_provider_generator.rb +38 -0
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/{add_provider_response_id_generator.rb → upgrade_call_tags_key_value_index_generator.rb} +5 -4
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/{add_streaming_generator.rb → upgrade_image_tokens_generator.rb} +4 -4
- data/lib/llm_cost_tracker/ingestion/batch.rb +11 -12
- data/lib/llm_cost_tracker/ingestion/inbox.rb +39 -24
- data/lib/llm_cost_tracker/ingestion/inline.rb +22 -0
- data/lib/llm_cost_tracker/ingestion/worker.rb +24 -7
- data/lib/llm_cost_tracker/ingestion.rb +66 -22
- data/lib/llm_cost_tracker/integrations/anthropic.rb +68 -42
- data/lib/llm_cost_tracker/integrations/base.rb +56 -32
- data/lib/llm_cost_tracker/integrations/openai.rb +342 -63
- data/lib/llm_cost_tracker/integrations/ruby_llm.rb +110 -11
- data/lib/llm_cost_tracker/integrations.rb +21 -3
- data/lib/llm_cost_tracker/ledger/period/totals.rb +30 -11
- data/lib/llm_cost_tracker/ledger/period.rb +5 -5
- data/lib/llm_cost_tracker/ledger/rollups/upsert_sql.rb +2 -2
- data/lib/llm_cost_tracker/ledger/rollups.rb +90 -25
- data/lib/llm_cost_tracker/ledger/schema/adapter.rb +18 -0
- data/lib/llm_cost_tracker/ledger/schema/call_line_items.rb +79 -0
- data/lib/llm_cost_tracker/ledger/schema/call_rollups.rb +37 -0
- data/lib/llm_cost_tracker/ledger/schema/call_tags.rb +41 -0
- data/lib/llm_cost_tracker/ledger/schema/calls.rb +36 -23
- data/lib/llm_cost_tracker/ledger/schema/ingestion_inbox_entries.rb +47 -0
- data/lib/llm_cost_tracker/ledger/schema/ingestion_leases.rb +42 -0
- data/lib/llm_cost_tracker/ledger/schema/provider_invoice_imports.rb +46 -0
- data/lib/llm_cost_tracker/ledger/schema/provider_invoices.rb +57 -0
- data/lib/llm_cost_tracker/ledger/store.rb +103 -20
- data/lib/llm_cost_tracker/ledger/tags/encoding.rb +37 -0
- data/lib/llm_cost_tracker/ledger/tags/query.rb +6 -11
- data/lib/llm_cost_tracker/ledger/tags/sql.rb +27 -15
- data/lib/llm_cost_tracker/ledger.rb +5 -2
- data/lib/llm_cost_tracker/logging.rb +2 -5
- data/lib/llm_cost_tracker/masking.rb +39 -0
- data/lib/llm_cost_tracker/middleware/faraday.rb +95 -35
- data/lib/llm_cost_tracker/parsers/anthropic.rb +74 -14
- data/lib/llm_cost_tracker/parsers/base.rb +13 -4
- data/lib/llm_cost_tracker/parsers/gemini.rb +105 -15
- data/lib/llm_cost_tracker/parsers/openai.rb +16 -2
- data/lib/llm_cost_tracker/parsers/openai_compatible.rb +15 -3
- data/lib/llm_cost_tracker/parsers/openai_service_charges.rb +126 -0
- data/lib/llm_cost_tracker/parsers/openai_usage.rb +157 -59
- data/lib/llm_cost_tracker/parsers/sse.rb +1 -1
- data/lib/llm_cost_tracker/parsers.rb +1 -1
- data/lib/llm_cost_tracker/prices.json +198 -22
- data/lib/llm_cost_tracker/pricing/effective_prices.rb +28 -21
- data/lib/llm_cost_tracker/pricing/explainer.rb +4 -5
- data/lib/llm_cost_tracker/pricing/lookup.rb +73 -36
- data/lib/llm_cost_tracker/pricing/mode.rb +76 -0
- data/lib/llm_cost_tracker/pricing/registry.rb +67 -45
- data/lib/llm_cost_tracker/pricing/service_charges.rb +210 -0
- data/lib/llm_cost_tracker/pricing/sync/fetcher.rb +26 -17
- data/lib/llm_cost_tracker/pricing/sync/registry_diff.rb +6 -15
- data/lib/llm_cost_tracker/pricing/sync/registry_writer.rb +50 -1
- data/lib/llm_cost_tracker/pricing/sync.rb +59 -10
- data/lib/llm_cost_tracker/pricing/sync_change_printer.rb +32 -0
- data/lib/llm_cost_tracker/pricing.rb +220 -28
- data/lib/llm_cost_tracker/railtie.rb +6 -8
- data/lib/llm_cost_tracker/reconcile_tasks.rb +134 -0
- data/lib/llm_cost_tracker/reconciliation/diff.rb +428 -0
- data/lib/llm_cost_tracker/reconciliation/diff_result.rb +48 -0
- data/lib/llm_cost_tracker/reconciliation/import_result.rb +19 -0
- data/lib/llm_cost_tracker/reconciliation/importer.rb +253 -0
- data/lib/llm_cost_tracker/reconciliation/sources/anthropic_usage.rb +171 -0
- data/lib/llm_cost_tracker/reconciliation/sources/fingerprint.rb +20 -0
- data/lib/llm_cost_tracker/reconciliation/sources/openai_usage.rb +142 -0
- data/lib/llm_cost_tracker/reconciliation.rb +118 -0
- data/lib/llm_cost_tracker/report/data.rb +19 -8
- data/lib/llm_cost_tracker/report.rb +0 -4
- data/lib/llm_cost_tracker/retention.rb +22 -9
- data/lib/llm_cost_tracker/tags/context.rb +2 -5
- data/lib/llm_cost_tracker/tags/key.rb +4 -0
- data/lib/llm_cost_tracker/tags/sanitizer.rb +71 -20
- data/lib/llm_cost_tracker/timing.rb +15 -0
- data/lib/llm_cost_tracker/token_usage.rb +64 -42
- data/lib/llm_cost_tracker/tracker.rb +97 -27
- data/lib/llm_cost_tracker/usage_capture.rb +29 -8
- data/lib/llm_cost_tracker/version.rb +1 -1
- data/lib/llm_cost_tracker.rb +45 -35
- data/lib/tasks/llm_cost_tracker.rake +45 -17
- metadata +71 -41
- data/app/models/llm_cost_tracker/ingestion/event.rb +0 -13
- data/app/models/llm_cost_tracker/ledger/call.rb +0 -45
- data/app/models/llm_cost_tracker/ledger/call_metrics.rb +0 -66
- data/app/models/llm_cost_tracker/ledger/period/grouping.rb +0 -71
- data/app/models/llm_cost_tracker/ledger/period/total.rb +0 -13
- data/app/models/llm_cost_tracker/ledger/tags/accessors.rb +0 -19
- data/lib/llm_cost_tracker/configuration/instrumentation.rb +0 -33
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/add_ingestion_generator.rb +0 -29
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/add_latency_ms_generator.rb +0 -29
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/add_period_totals_generator.rb +0 -29
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/add_token_usage_generator.rb +0 -42
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_ingestion_to_llm_cost_tracker.rb.erb +0 -33
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_latency_ms_to_llm_api_calls.rb.erb +0 -9
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_period_totals_to_llm_cost_tracker.rb.erb +0 -104
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_provider_response_id_to_llm_api_calls.rb.erb +0 -15
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_streaming_to_llm_api_calls.rb.erb +0 -21
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/add_token_usage_to_llm_api_calls.rb.erb +0 -22
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/create_llm_api_calls.rb.erb +0 -83
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_llm_api_call_cost_precision.rb.erb +0 -26
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/upgrade_llm_api_call_tags_to_jsonb.rb.erb +0 -44
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/upgrade_cost_precision_generator.rb +0 -29
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/upgrade_tags_to_jsonb_generator.rb +0 -29
- data/lib/llm_cost_tracker/ledger/rollups/batch.rb +0 -43
- data/lib/llm_cost_tracker/ledger/schema/period_totals.rb +0 -32
- data/lib/llm_cost_tracker/pricing/components.rb +0 -37
- data/lib/llm_cost_tracker/pricing/sync/registry_loader.rb +0 -63
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "llm_cost_tracker/ledger/schema/adapter"
|
|
4
|
-
|
|
5
|
-
module LlmCostTracker
|
|
6
|
-
module Ledger
|
|
7
|
-
module Period
|
|
8
|
-
module Grouping
|
|
9
|
-
PERIOD_FORMATS = {
|
|
10
|
-
day: {
|
|
11
|
-
postgres: "YYYY-MM-DD",
|
|
12
|
-
mysql: "%Y-%m-%d"
|
|
13
|
-
},
|
|
14
|
-
month: {
|
|
15
|
-
postgres: "YYYY-MM",
|
|
16
|
-
mysql: "%Y-%m"
|
|
17
|
-
}
|
|
18
|
-
}.freeze
|
|
19
|
-
|
|
20
|
-
private_constant :PERIOD_FORMATS
|
|
21
|
-
|
|
22
|
-
def group_by_period(period, column: :tracked_at)
|
|
23
|
-
group(Arel.sql(period_group_expression(period, column: column)))
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def daily_costs(days: 30)
|
|
27
|
-
where(tracked_at: days.days.ago..)
|
|
28
|
-
.group_by_period(:day)
|
|
29
|
-
.sum(:total_cost)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
private
|
|
33
|
-
|
|
34
|
-
def period_group_expression(period, column:)
|
|
35
|
-
period = validated_period(period)
|
|
36
|
-
column = period_column_expression(column)
|
|
37
|
-
formats = PERIOD_FORMATS.fetch(period)
|
|
38
|
-
|
|
39
|
-
if Ledger::Schema::Adapter.postgresql?(connection)
|
|
40
|
-
postgres_period_expression(period, column, formats)
|
|
41
|
-
elsif Ledger::Schema::Adapter.mysql?(connection)
|
|
42
|
-
"DATE_FORMAT(#{column}, #{connection.quote(formats.fetch(:mysql))})"
|
|
43
|
-
else
|
|
44
|
-
Ledger::Schema::Adapter.ensure_supported!(connection)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def postgres_period_expression(period, column, formats)
|
|
49
|
-
"TO_CHAR(" \
|
|
50
|
-
"DATE_TRUNC(#{connection.quote(period.to_s)}, #{column}), " \
|
|
51
|
-
"#{connection.quote(formats.fetch(:postgres))}" \
|
|
52
|
-
")"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def validated_period(period)
|
|
56
|
-
normalized_period = period.try(:to_sym)
|
|
57
|
-
return normalized_period if PERIOD_FORMATS.key?(normalized_period)
|
|
58
|
-
|
|
59
|
-
raise ArgumentError, "invalid period: #{period.inspect}"
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def period_column_expression(column)
|
|
63
|
-
column = column.to_s
|
|
64
|
-
return "#{quoted_table_name}.#{connection.quote_column_name(column)}" if column_names.include?(column)
|
|
65
|
-
|
|
66
|
-
raise ArgumentError, "invalid period column: #{column.inspect}"
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
|
-
module LlmCostTracker
|
|
6
|
-
module Ledger
|
|
7
|
-
module Tags
|
|
8
|
-
module Accessors
|
|
9
|
-
def parsed_tags
|
|
10
|
-
return tags.transform_keys(&:to_s) if tags.is_a?(Hash)
|
|
11
|
-
|
|
12
|
-
JSON.parse(tags || "{}")
|
|
13
|
-
rescue JSON::ParserError
|
|
14
|
-
{}
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module LlmCostTracker
|
|
4
|
-
module ConfigurationInstrumentation
|
|
5
|
-
def instrument(*names)
|
|
6
|
-
ensure_shared_configuration_mutable!
|
|
7
|
-
@instrumented_integrations = (@instrumented_integrations + normalize_instrumentation_names(names)).uniq
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def instrumented?(name)
|
|
11
|
-
@instrumented_integrations.include?(name.to_sym)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
def normalize_instrumentation_names(names)
|
|
17
|
-
names.flatten.flat_map do |name|
|
|
18
|
-
key = name.to_sym
|
|
19
|
-
next Integrations.names if key == :all
|
|
20
|
-
|
|
21
|
-
validate_instrumentation_name!(key)
|
|
22
|
-
key
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def validate_instrumentation_name!(name)
|
|
27
|
-
return if Integrations.names.include?(name)
|
|
28
|
-
|
|
29
|
-
raise Error, "Unknown integration: #{name.inspect}. " \
|
|
30
|
-
"Use one of: #{Integrations.names.join(', ')}"
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "rails/generators"
|
|
4
|
-
require "rails/generators/active_record"
|
|
5
|
-
|
|
6
|
-
module LlmCostTracker
|
|
7
|
-
module Generators
|
|
8
|
-
class AddIngestionGenerator < Rails::Generators::Base
|
|
9
|
-
include ActiveRecord::Generators::Migration
|
|
10
|
-
|
|
11
|
-
source_root File.expand_path("templates", __dir__)
|
|
12
|
-
|
|
13
|
-
desc "Creates a migration to add durable ActiveRecord ingestion"
|
|
14
|
-
|
|
15
|
-
def create_migration_file
|
|
16
|
-
migration_template(
|
|
17
|
-
"add_ingestion_to_llm_cost_tracker.rb.erb",
|
|
18
|
-
"db/migrate/add_ingestion_to_llm_cost_tracker.rb"
|
|
19
|
-
)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
def migration_version
|
|
25
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "rails/generators"
|
|
4
|
-
require "rails/generators/active_record"
|
|
5
|
-
|
|
6
|
-
module LlmCostTracker
|
|
7
|
-
module Generators
|
|
8
|
-
class AddLatencyMsGenerator < Rails::Generators::Base
|
|
9
|
-
include ActiveRecord::Generators::Migration
|
|
10
|
-
|
|
11
|
-
source_root File.expand_path("templates", __dir__)
|
|
12
|
-
|
|
13
|
-
desc "Creates a migration to add llm_api_calls.latency_ms"
|
|
14
|
-
|
|
15
|
-
def create_migration_file
|
|
16
|
-
migration_template(
|
|
17
|
-
"add_latency_ms_to_llm_api_calls.rb.erb",
|
|
18
|
-
"db/migrate/add_latency_ms_to_llm_api_calls.rb"
|
|
19
|
-
)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
def migration_version
|
|
25
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "rails/generators"
|
|
4
|
-
require "rails/generators/active_record"
|
|
5
|
-
|
|
6
|
-
module LlmCostTracker
|
|
7
|
-
module Generators
|
|
8
|
-
class AddPeriodTotalsGenerator < Rails::Generators::Base
|
|
9
|
-
include ActiveRecord::Generators::Migration
|
|
10
|
-
|
|
11
|
-
source_root File.expand_path("templates", __dir__)
|
|
12
|
-
|
|
13
|
-
desc "Creates a migration to add llm_cost_tracker_period_totals"
|
|
14
|
-
|
|
15
|
-
def create_migration_file
|
|
16
|
-
migration_template(
|
|
17
|
-
"add_period_totals_to_llm_cost_tracker.rb.erb",
|
|
18
|
-
"db/migrate/add_period_totals_to_llm_cost_tracker.rb"
|
|
19
|
-
)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
def migration_version
|
|
25
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "rails/generators"
|
|
4
|
-
require "rails/generators/active_record"
|
|
5
|
-
|
|
6
|
-
module LlmCostTracker
|
|
7
|
-
module Generators
|
|
8
|
-
class AddTokenUsageGenerator < Rails::Generators::Base
|
|
9
|
-
include ActiveRecord::Generators::Migration
|
|
10
|
-
|
|
11
|
-
TOKEN_COLUMNS = %w[
|
|
12
|
-
cache_read_input_tokens
|
|
13
|
-
cache_write_input_tokens
|
|
14
|
-
cache_write_1h_input_tokens
|
|
15
|
-
hidden_output_tokens
|
|
16
|
-
].freeze
|
|
17
|
-
COST_COLUMNS = %w[
|
|
18
|
-
cache_read_input_cost
|
|
19
|
-
cache_write_input_cost
|
|
20
|
-
cache_write_1h_input_cost
|
|
21
|
-
].freeze
|
|
22
|
-
COLUMN_NAMES = (TOKEN_COLUMNS + COST_COLUMNS + %w[pricing_mode]).freeze
|
|
23
|
-
|
|
24
|
-
source_root File.expand_path("templates", __dir__)
|
|
25
|
-
|
|
26
|
-
desc "Creates a migration to add token usage and token cost columns to llm_api_calls"
|
|
27
|
-
|
|
28
|
-
def create_migration_file
|
|
29
|
-
migration_template(
|
|
30
|
-
"add_token_usage_to_llm_api_calls.rb.erb",
|
|
31
|
-
"db/migrate/add_token_usage_to_llm_api_calls.rb"
|
|
32
|
-
)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def migration_version
|
|
38
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
class AddIngestionToLlmCostTracker < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
-
def change
|
|
3
|
-
add_column :llm_api_calls, :event_id, :string unless column_exists?(:llm_api_calls, :event_id)
|
|
4
|
-
add_index :llm_api_calls, :event_id, unique: true if column_exists?(:llm_api_calls, :event_id) &&
|
|
5
|
-
!index_exists?(:llm_api_calls, :event_id)
|
|
6
|
-
|
|
7
|
-
create_table :llm_cost_tracker_inbox_events do |t|
|
|
8
|
-
t.string :event_id, null: false
|
|
9
|
-
t.decimal :total_cost, precision: 20, scale: 8
|
|
10
|
-
t.datetime :tracked_at, null: false
|
|
11
|
-
t.text :payload, null: false
|
|
12
|
-
t.datetime :locked_at
|
|
13
|
-
t.string :locked_by
|
|
14
|
-
t.integer :attempts, null: false, default: 0
|
|
15
|
-
t.text :last_error
|
|
16
|
-
|
|
17
|
-
t.timestamps
|
|
18
|
-
end unless table_exists?(:llm_cost_tracker_inbox_events)
|
|
19
|
-
|
|
20
|
-
create_table :llm_cost_tracker_ingestor_leases do |t|
|
|
21
|
-
t.string :name, null: false
|
|
22
|
-
t.string :locked_by
|
|
23
|
-
t.datetime :locked_until
|
|
24
|
-
|
|
25
|
-
t.timestamps
|
|
26
|
-
end unless table_exists?(:llm_cost_tracker_ingestor_leases)
|
|
27
|
-
|
|
28
|
-
add_index :llm_cost_tracker_inbox_events, :event_id, unique: true unless index_exists?(:llm_cost_tracker_inbox_events, :event_id)
|
|
29
|
-
add_index :llm_cost_tracker_inbox_events, :tracked_at unless index_exists?(:llm_cost_tracker_inbox_events, :tracked_at)
|
|
30
|
-
add_index :llm_cost_tracker_inbox_events, [:locked_at, :id] unless index_exists?(:llm_cost_tracker_inbox_events, [:locked_at, :id])
|
|
31
|
-
add_index :llm_cost_tracker_ingestor_leases, :name, unique: true unless index_exists?(:llm_cost_tracker_ingestor_leases, :name)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
class AddLatencyMsToLlmApiCalls < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
-
def up
|
|
3
|
-
add_column :llm_api_calls, :latency_ms, :integer unless column_exists?(:llm_api_calls, :latency_ms)
|
|
4
|
-
end
|
|
5
|
-
|
|
6
|
-
def down
|
|
7
|
-
remove_column :llm_api_calls, :latency_ms if column_exists?(:llm_api_calls, :latency_ms)
|
|
8
|
-
end
|
|
9
|
-
end
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
require "llm_cost_tracker/ledger/schema/adapter"
|
|
2
|
-
|
|
3
|
-
class AddPeriodTotalsToLlmCostTracker < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
-
def up
|
|
5
|
-
create_table :llm_cost_tracker_period_totals do |t|
|
|
6
|
-
t.string :period, null: false
|
|
7
|
-
t.date :period_start, null: false
|
|
8
|
-
t.decimal :total_cost, precision: 20, scale: 8, null: false, default: 0
|
|
9
|
-
|
|
10
|
-
t.timestamps
|
|
11
|
-
end unless table_exists?(:llm_cost_tracker_period_totals)
|
|
12
|
-
|
|
13
|
-
backfill_period_totals
|
|
14
|
-
|
|
15
|
-
add_index :llm_cost_tracker_period_totals, [:period, :period_start],
|
|
16
|
-
unique: true unless index_exists?(:llm_cost_tracker_period_totals, [:period, :period_start])
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def down
|
|
20
|
-
remove_index :llm_cost_tracker_period_totals, [:period, :period_start] if index_exists?(:llm_cost_tracker_period_totals, [:period, :period_start])
|
|
21
|
-
drop_table :llm_cost_tracker_period_totals if table_exists?(:llm_cost_tracker_period_totals)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
private
|
|
25
|
-
|
|
26
|
-
def backfill_period_totals
|
|
27
|
-
backfill_legacy_monthly_totals if table_exists?(:llm_cost_tracker_monthly_totals)
|
|
28
|
-
return unless table_exists?(:llm_api_calls)
|
|
29
|
-
|
|
30
|
-
backfill_period_total("day", day_bucket_sql)
|
|
31
|
-
backfill_period_total("month", month_bucket_sql)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def backfill_legacy_monthly_totals
|
|
35
|
-
execute <<~SQL
|
|
36
|
-
INSERT INTO llm_cost_tracker_period_totals (period, period_start, total_cost, created_at, updated_at)
|
|
37
|
-
SELECT #{connection.quote("month")} AS period,
|
|
38
|
-
month AS period_start,
|
|
39
|
-
total_cost,
|
|
40
|
-
CURRENT_TIMESTAMP,
|
|
41
|
-
CURRENT_TIMESTAMP
|
|
42
|
-
FROM llm_cost_tracker_monthly_totals legacy
|
|
43
|
-
WHERE NOT EXISTS (
|
|
44
|
-
SELECT 1
|
|
45
|
-
FROM llm_cost_tracker_period_totals existing
|
|
46
|
-
WHERE existing.period = #{connection.quote("month")}
|
|
47
|
-
AND existing.period_start = legacy.month
|
|
48
|
-
)
|
|
49
|
-
SQL
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def backfill_period_total(period, bucket_sql)
|
|
53
|
-
execute <<~SQL
|
|
54
|
-
INSERT INTO llm_cost_tracker_period_totals (period, period_start, total_cost, created_at, updated_at)
|
|
55
|
-
SELECT aggregated.period,
|
|
56
|
-
aggregated.period_start,
|
|
57
|
-
aggregated.total_cost,
|
|
58
|
-
CURRENT_TIMESTAMP,
|
|
59
|
-
CURRENT_TIMESTAMP
|
|
60
|
-
FROM (
|
|
61
|
-
SELECT #{connection.quote(period)} AS period,
|
|
62
|
-
#{bucket_sql} AS period_start,
|
|
63
|
-
SUM(total_cost) AS total_cost
|
|
64
|
-
FROM llm_api_calls
|
|
65
|
-
WHERE total_cost IS NOT NULL
|
|
66
|
-
GROUP BY #{bucket_sql}
|
|
67
|
-
) aggregated
|
|
68
|
-
WHERE NOT EXISTS (
|
|
69
|
-
SELECT 1
|
|
70
|
-
FROM llm_cost_tracker_period_totals existing
|
|
71
|
-
WHERE existing.period = aggregated.period
|
|
72
|
-
AND existing.period_start = aggregated.period_start
|
|
73
|
-
)
|
|
74
|
-
SQL
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def day_bucket_sql
|
|
78
|
-
if postgresql?
|
|
79
|
-
"DATE_TRUNC('day', tracked_at)::date"
|
|
80
|
-
elsif mysql?
|
|
81
|
-
"DATE(tracked_at)"
|
|
82
|
-
else
|
|
83
|
-
raise "LLM Cost Tracker supports PostgreSQL and MySQL only"
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def month_bucket_sql
|
|
88
|
-
if postgresql?
|
|
89
|
-
"DATE_TRUNC('month', tracked_at)::date"
|
|
90
|
-
elsif mysql?
|
|
91
|
-
"DATE_FORMAT(tracked_at, '%Y-%m-01')"
|
|
92
|
-
else
|
|
93
|
-
raise "LLM Cost Tracker supports PostgreSQL and MySQL only"
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def postgresql?
|
|
98
|
-
LlmCostTracker::Ledger::Schema::Adapter.postgresql?(connection)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def mysql?
|
|
102
|
-
LlmCostTracker::Ledger::Schema::Adapter.mysql?(connection)
|
|
103
|
-
end
|
|
104
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
class AddProviderResponseIdToLlmApiCalls < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
-
def up
|
|
3
|
-
return if column_exists?(:llm_api_calls, :provider_response_id)
|
|
4
|
-
|
|
5
|
-
add_column :llm_api_calls, :provider_response_id, :string
|
|
6
|
-
add_index :llm_api_calls, :provider_response_id
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def down
|
|
10
|
-
return unless column_exists?(:llm_api_calls, :provider_response_id)
|
|
11
|
-
|
|
12
|
-
remove_index :llm_api_calls, :provider_response_id if index_exists?(:llm_api_calls, :provider_response_id)
|
|
13
|
-
remove_column :llm_api_calls, :provider_response_id
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
class AddStreamingToLlmApiCalls < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
-
def up
|
|
3
|
-
unless column_exists?(:llm_api_calls, :stream)
|
|
4
|
-
add_column :llm_api_calls, :stream, :boolean, null: false, default: false
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
unless column_exists?(:llm_api_calls, :usage_source)
|
|
8
|
-
add_column :llm_api_calls, :usage_source, :string
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def down
|
|
13
|
-
if column_exists?(:llm_api_calls, :usage_source)
|
|
14
|
-
remove_column :llm_api_calls, :usage_source
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
if column_exists?(:llm_api_calls, :stream)
|
|
18
|
-
remove_column :llm_api_calls, :stream
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
class AddTokenUsageToLlmApiCalls < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
-
def up
|
|
3
|
-
<% LlmCostTracker::Generators::AddTokenUsageGenerator::TOKEN_COLUMNS.each do |column| -%>
|
|
4
|
-
unless column_exists?(:llm_api_calls, :<%= column %>)
|
|
5
|
-
add_column :llm_api_calls, :<%= column %>, :integer, null: false, default: 0
|
|
6
|
-
end
|
|
7
|
-
<% end -%>
|
|
8
|
-
<% LlmCostTracker::Generators::AddTokenUsageGenerator::COST_COLUMNS.each do |column| -%>
|
|
9
|
-
unless column_exists?(:llm_api_calls, :<%= column %>)
|
|
10
|
-
add_column :llm_api_calls, :<%= column %>, :decimal, precision: 20, scale: 8
|
|
11
|
-
end
|
|
12
|
-
<% end -%>
|
|
13
|
-
add_column :llm_api_calls, :pricing_mode, :string unless column_exists?(:llm_api_calls, :pricing_mode)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def down
|
|
17
|
-
remove_column :llm_api_calls, :pricing_mode if column_exists?(:llm_api_calls, :pricing_mode)
|
|
18
|
-
<% (LlmCostTracker::Generators::AddTokenUsageGenerator::COST_COLUMNS + LlmCostTracker::Generators::AddTokenUsageGenerator::TOKEN_COLUMNS).reverse.each do |column| -%>
|
|
19
|
-
remove_column :llm_api_calls, :<%= column %> if column_exists?(:llm_api_calls, :<%= column %>)
|
|
20
|
-
<% end -%>
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
require "llm_cost_tracker/ledger/schema/adapter"
|
|
2
|
-
|
|
3
|
-
class CreateLlmApiCalls < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
-
def change
|
|
5
|
-
create_table :llm_api_calls do |t|
|
|
6
|
-
t.string :event_id, null: false
|
|
7
|
-
t.string :provider, null: false
|
|
8
|
-
t.string :model, null: false
|
|
9
|
-
<% LlmCostTracker::TokenUsage::STORED_KEYS.each do |column| -%>
|
|
10
|
-
t.integer :<%= column %>, null: false, default: 0
|
|
11
|
-
<% end -%>
|
|
12
|
-
<% LlmCostTracker::Pricing::COST_KEYS.each do |column| -%>
|
|
13
|
-
t.decimal :<%= column %>, precision: 20, scale: 8
|
|
14
|
-
<% end -%>
|
|
15
|
-
t.integer :latency_ms
|
|
16
|
-
t.boolean :stream, null: false, default: false
|
|
17
|
-
t.string :usage_source
|
|
18
|
-
t.string :provider_response_id
|
|
19
|
-
t.string :pricing_mode
|
|
20
|
-
if postgresql?
|
|
21
|
-
t.jsonb :tags, null: false, default: {}
|
|
22
|
-
elsif mysql?
|
|
23
|
-
t.json :tags, null: false
|
|
24
|
-
else
|
|
25
|
-
raise "LLM Cost Tracker supports PostgreSQL and MySQL only"
|
|
26
|
-
end
|
|
27
|
-
t.datetime :tracked_at, null: false
|
|
28
|
-
|
|
29
|
-
t.timestamps
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
create_table :llm_cost_tracker_period_totals do |t|
|
|
33
|
-
t.string :period, null: false
|
|
34
|
-
t.date :period_start, null: false
|
|
35
|
-
t.decimal :total_cost, precision: 20, scale: 8, null: false, default: 0
|
|
36
|
-
|
|
37
|
-
t.timestamps
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
create_table :llm_cost_tracker_inbox_events do |t|
|
|
41
|
-
t.string :event_id, null: false
|
|
42
|
-
t.decimal :total_cost, precision: 20, scale: 8
|
|
43
|
-
t.datetime :tracked_at, null: false
|
|
44
|
-
t.text :payload, null: false
|
|
45
|
-
t.datetime :locked_at
|
|
46
|
-
t.string :locked_by
|
|
47
|
-
t.integer :attempts, null: false, default: 0
|
|
48
|
-
t.text :last_error
|
|
49
|
-
|
|
50
|
-
t.timestamps
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
create_table :llm_cost_tracker_ingestor_leases do |t|
|
|
54
|
-
t.string :name, null: false
|
|
55
|
-
t.string :locked_by
|
|
56
|
-
t.datetime :locked_until
|
|
57
|
-
|
|
58
|
-
t.timestamps
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
add_index :llm_api_calls, :event_id, unique: true
|
|
62
|
-
add_index :llm_api_calls, :tracked_at
|
|
63
|
-
add_index :llm_api_calls, [:provider, :tracked_at]
|
|
64
|
-
add_index :llm_api_calls, [:model, :tracked_at]
|
|
65
|
-
add_index :llm_api_calls, :provider_response_id
|
|
66
|
-
add_index :llm_api_calls, :tags, using: :gin if postgresql?
|
|
67
|
-
add_index :llm_cost_tracker_period_totals, [:period, :period_start], unique: true
|
|
68
|
-
add_index :llm_cost_tracker_inbox_events, :event_id, unique: true
|
|
69
|
-
add_index :llm_cost_tracker_inbox_events, :tracked_at
|
|
70
|
-
add_index :llm_cost_tracker_inbox_events, [:locked_at, :id]
|
|
71
|
-
add_index :llm_cost_tracker_ingestor_leases, :name, unique: true
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
private
|
|
75
|
-
|
|
76
|
-
def postgresql?
|
|
77
|
-
LlmCostTracker::Ledger::Schema::Adapter.postgresql?(connection)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def mysql?
|
|
81
|
-
LlmCostTracker::Ledger::Schema::Adapter.mysql?(connection)
|
|
82
|
-
end
|
|
83
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
class UpgradeLlmApiCallCostPrecision < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
-
COST_COLUMNS = %i[
|
|
3
|
-
input_cost
|
|
4
|
-
cache_read_input_cost
|
|
5
|
-
cache_write_input_cost
|
|
6
|
-
cache_write_1h_input_cost
|
|
7
|
-
output_cost
|
|
8
|
-
total_cost
|
|
9
|
-
].freeze
|
|
10
|
-
|
|
11
|
-
def up
|
|
12
|
-
COST_COLUMNS.each do |column|
|
|
13
|
-
next unless column_exists?(:llm_api_calls, column)
|
|
14
|
-
|
|
15
|
-
change_column :llm_api_calls, column, :decimal, precision: 20, scale: 8
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def down
|
|
20
|
-
COST_COLUMNS.each do |column|
|
|
21
|
-
next unless column_exists?(:llm_api_calls, column)
|
|
22
|
-
|
|
23
|
-
change_column :llm_api_calls, column, :decimal, precision: 12, scale: 8
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
require "llm_cost_tracker/ledger/schema/adapter"
|
|
2
|
-
|
|
3
|
-
class UpgradeLlmApiCallTagsToJsonb < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
-
def up
|
|
5
|
-
unless postgresql?
|
|
6
|
-
say "Skipping llm_api_calls.tags JSONB upgrade: database adapter is #{connection.adapter_name}."
|
|
7
|
-
return
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
return if tags_jsonb?
|
|
11
|
-
|
|
12
|
-
remove_index :llm_api_calls, :tags if index_exists?(:llm_api_calls, :tags)
|
|
13
|
-
say "Upgrading llm_api_calls.tags to jsonb rewrites the table on PostgreSQL. Run this migration during a maintenance window on large datasets."
|
|
14
|
-
|
|
15
|
-
change_column(
|
|
16
|
-
:llm_api_calls,
|
|
17
|
-
:tags,
|
|
18
|
-
:jsonb,
|
|
19
|
-
using: "CASE WHEN tags IS NULL OR tags = '' THEN '{}'::jsonb ELSE tags::jsonb END",
|
|
20
|
-
default: {},
|
|
21
|
-
null: false
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
add_index :llm_api_calls, :tags, using: :gin unless index_exists?(:llm_api_calls, :tags)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def down
|
|
28
|
-
return unless postgresql?
|
|
29
|
-
|
|
30
|
-
remove_index :llm_api_calls, :tags if index_exists?(:llm_api_calls, :tags)
|
|
31
|
-
change_column :llm_api_calls, :tags, :text, using: "tags::text"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
private
|
|
35
|
-
|
|
36
|
-
def postgresql?
|
|
37
|
-
LlmCostTracker::Ledger::Schema::Adapter.postgresql?(connection)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def tags_jsonb?
|
|
41
|
-
column = connection.columns(:llm_api_calls).find { |candidate| candidate.name == "tags" }
|
|
42
|
-
column&.sql_type.to_s.downcase == "jsonb"
|
|
43
|
-
end
|
|
44
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "rails/generators"
|
|
4
|
-
require "rails/generators/active_record"
|
|
5
|
-
|
|
6
|
-
module LlmCostTracker
|
|
7
|
-
module Generators
|
|
8
|
-
class UpgradeCostPrecisionGenerator < Rails::Generators::Base
|
|
9
|
-
include ActiveRecord::Generators::Migration
|
|
10
|
-
|
|
11
|
-
source_root File.expand_path("templates", __dir__)
|
|
12
|
-
|
|
13
|
-
desc "Creates a migration to widen llm_api_calls cost decimal precision"
|
|
14
|
-
|
|
15
|
-
def create_migration_file
|
|
16
|
-
migration_template(
|
|
17
|
-
"upgrade_llm_api_call_cost_precision.rb.erb",
|
|
18
|
-
"db/migrate/upgrade_llm_api_call_cost_precision.rb"
|
|
19
|
-
)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
def migration_version
|
|
25
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|