fix(admin): use model history for cost data - #1816
Conversation
Add totalCost column to modelHistory and modelProviderMappingHistory tables, aggregate log.cost in the stats-calculator worker, and switch admin /models endpoints to query these tables instead of projectHourlyModelStats. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WalkthroughReplaces hourly aggregations with minute-level history sources and renames response keys from usedModel/usedProvider to modelId/providerId across admin routes; adds a new totalCost metric to minutely model and model-provider mapping histories and persists it via schema migration and worker upserts. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/db/migrations/1773263792_boring_luminals.sql`:
- Around line 1-2: The migration adds total_cost to model_history and
model_provider_mapping_history but leaves existing rows at 0; update the
migration to either backfill historical values from the log table or add
documentation noting the data gap. Specifically, add an UPDATE step after adding
the total_cost column that aggregates historical cost data from the log (or
whichever source the stats-calculator uses) and writes it into
model_history.total_cost and model_provider_mapping_history.total_cost for
matching records, or alternatively add a migration note in your docs describing
that pre-migration rows have zeroed total_cost and that admin queries should
exclude pre-migration time ranges; reference the table names model_history,
model_provider_mapping_history and the total_cost column when implementing the
fix.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 4e5a6508-ff5d-4e8f-a9c8-6ab053a2ca6c
📒 Files selected for processing (3)
packages/db/migrations/1773263792_boring_luminals.sqlpackages/db/migrations/meta/1773263792_snapshot.jsonpackages/db/migrations/meta/_journal.json
| ALTER TABLE "model_history" ADD COLUMN "total_cost" real DEFAULT 0 NOT NULL;--> statement-breakpoint | ||
| ALTER TABLE "model_provider_mapping_history" ADD COLUMN "total_cost" real DEFAULT 0 NOT NULL; No newline at end of file |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Looking for total_cost / totalCost writes in migrations and app code..."
rg -n -C3 'total_cost|totalCost' packages/db apps
echo
echo "Looking for backfill updates on existing history tables..."
rg -n -C3 'UPDATE\s+"?(model_history|model_provider_mapping_history)"?' packages/db
echo
echo "Looking for history upserts/inserts that only affect new rows..."
rg -n -C3 'modelHistory|modelProviderMappingHistory' packages/db appsRepository: theopenco/llmgateway
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Checking how history tables are populated (INSERT/UPSERT logic) ==="
rg -A5 'insert.*modelHistory|insert.*modelProviderMappingHistory' packages/db apps --multiline
echo
echo "=== Checking if there are any backfill/migration scripts or scheduled jobs ==="
rg -l 'backfill|historical|aggregate' packages/db apps
echo
echo "=== Checking admin endpoint implementations for /models endpoints ==="
fd -e ts -e tsx 'admin|models' apps | head -20 | xargs rg -l 'model_history|modelHistory' 2>/dev/null || true
echo
echo "=== Looking for any references to migration or data sync scripts ==="
rg 'pnpm|npm|yarn' package.json | grep -i 'setup\|migrate\|sync' || true
fd -e ts -e js 'migrate\|sync\|seed' packages | head -10Repository: theopenco/llmgateway
Length of output: 7955
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Checking stats-calculator.ts INSERT logic for totalCost ==="
sed -n '1,100p' apps/worker/src/services/stats-calculator.ts
echo "..."
rg -A20 '\.insert\(modelHistory\)' apps/worker/src/services/stats-calculator.ts | head -35
echo
echo "=== Checking admin.ts /models endpoints query logic ==="
rg -B5 -A30 'models.*history|modelHistory.*history' apps/api/src/routes/admin.ts | head -80Repository: theopenco/llmgateway
Length of output: 5553
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Checking when modelHistory and modelProviderMappingHistory tables were created ==="
rg -A3 'createTable.*modelHistory|createTable.*modelProviderMappingHistory' packages/db/src/schema.ts | head -20
echo
echo "=== Looking at migration chronology to see table creation date ==="
ls -la packages/db/migrations/*.sql | tail -20
echo
echo "=== Checking if history tables existed before this migration ==="
git log --oneline packages/db/src/schema.ts | head -10 2>/dev/null || echo "Git not available; checking migration sequence instead"
</parameter>Repository: theopenco/llmgateway
Length of output: 150
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Listing migration files to understand when tables were added ==="
ls -1 packages/db/migrations/*.sql | sort
echo
echo "=== Searching for modelHistory table definition in schema ==="
rg -B2 -A8 'modelHistory.*=' packages/db/src/schema.ts | head -40
echo
echo "=== Checking the previous migration before this one ==="
ls -1 packages/db/migrations/*.sql | sort | tail -5
echo
echo "=== Reading the second-to-last migration to see what existed before ==="
SECOND_LAST=$(ls -1 packages/db/migrations/*.sql | sort | tail -2 | head -1)
echo "File: $SECOND_LAST"
cat "$SECOND_LAST"Repository: theopenco/llmgateway
Length of output: 6517
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Searching for creation of modelHistory table in migrations ==="
rg -l 'CREATE TABLE.*model_history' packages/db/migrations/*.sql
echo
echo "=== Reading the migration that created modelHistory table ==="
HIST_CREATION=$(rg -l 'CREATE TABLE.*model_history' packages/db/migrations/*.sql | head -1)
if [ -n "$HIST_CREATION" ]; then
echo "File: $HIST_CREATION"
cat "$HIST_CREATION"
fi
echo
echo "=== Checking if there's any backfill job or one-time script ==="
find packages apps -name '*.ts' -o -name '*.sql' | xargs rg -l 'backfill|UPDATE.*model_history|UPDATE.*model_provider_mapping_history' 2>/dev/null | head -10
echo
echo "=== Checking when history tables started being populated (git history of stats-calculator) ==="
git log --oneline packages/worker/src/services/stats-calculator.ts 2>/dev/null | head -5 || echo "Git history not available"Repository: theopenco/llmgateway
Length of output: 5114
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Checking the full stats-calculator totalCost calculation ==="
rg -B10 -A5 'totalCost:' apps/worker/src/services/stats-calculator.ts | head -80
echo
echo "=== Verifying if totalCost is calculated for every insert ==="
rg -B50 '\.insert\(modelHistory\)' apps/worker/src/services/stats-calculator.ts | grep -A50 'const modelStats'Repository: theopenco/llmgateway
Length of output: 1136
Consider backfilling historical total_cost values or documenting the data gap.
The migration adds total_cost DEFAULT 0 to existing history rows without populating actual values. While new records will have correct costs (stats-calculator calculates and inserts them each minute), any pre-migration history rows will report zero cost. If admin endpoints query time windows that include pre-migration data, cost metrics will be underreported for those ranges. Either run an UPDATE to backfill from the log table, or add documentation confirming query windows won't span pre-migration records.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/db/migrations/1773263792_boring_luminals.sql` around lines 1 - 2,
The migration adds total_cost to model_history and
model_provider_mapping_history but leaves existing rows at 0; update the
migration to either backfill historical values from the log table or add
documentation noting the data gap. Specifically, add an UPDATE step after adding
the total_cost column that aggregates historical cost data from the log (or
whichever source the stats-calculator uses) and writes it into
model_history.total_cost and model_provider_mapping_history.total_cost for
matching records, or alternatively add a migration note in your docs describing
that pre-migration rows have zeroed total_cost and that admin queries should
exclude pre-migration time ranges; reference the table names model_history,
model_provider_mapping_history and the total_cost column when implementing the
fix.
Summary
totalCostcolumn tomodelHistoryandmodelProviderMappingHistorytableslog.costin the stats-calculator worker when computing minute-level history/modelslist endpoint to querymodelHistoryinstead ofprojectHourlyModelStatsfor date-range stats/models/{modelId}detail endpoint to querymodelProviderMappingHistoryinstead ofprojectHourlyModelStatsfor per-provider statsThe admin models page was broken because it relied on
projectHourlyModelStats(a per-project hourly table) which doesn't properly aggregate costs at the model level. ThemodelHistoryandmodelProviderMappingHistorytables are the correct source for model-level stats but were missing cost tracking.Test plan
pnpm buildto verify compilationpnpm run setupto push new schema columns🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Refactor