Skip to content

feat(db): add image token counts to hourly stats#1674

Open
steebchen wants to merge 4 commits intomainfrom
feat/add-image-token-counts-to-hourly-stats
Open

feat(db): add image token counts to hourly stats#1674
steebchen wants to merge 4 commits intomainfrom
feat/add-image-token-counts-to-hourly-stats

Conversation

@steebchen
Copy link
Copy Markdown
Member

@steebchen steebchen commented Feb 16, 2026

Summary

  • Adds imageInputTokens and imageOutputTokens columns to all 4 hourly aggregation tables (project_hourly_stats, project_hourly_model_stats, api_key_hourly_stats, api_key_hourly_model_stats)
  • Updates the worker aggregator and test helper to aggregate these fields from the log table

Test plan

  • pnpm build passes
  • pnpm format passes
  • Deploy and verify new columns are populated by the backfill worker

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Image token usage is now tracked separately as input and output counts.
    • These image token metrics are persisted and included in project- and API-key-level reports, including hourly breakdowns.
    • Model-level statistics and hourly model reports now include image input/output token totals for finer-grained usage and billing insights.

Copilot AI review requested due to automatic review settings February 16, 2026 12:19
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds imageInputTokens and imageOutputTokens to aggregation logic, hourly-stats DB schema, and migration so image token sums are produced and persisted for project and API-key hourly stats.

Changes

Cohort / File(s) Summary
Database schema
packages/db/src/schema.ts
Added imageInputTokens and imageOutputTokens (decimal, not null, default "0") to projectHourlyStats, projectHourlyModelStats, apiKeyHourlyStats, and apiKeyHourlyModelStats.
Migrations / journal
packages/db/migrations/1771244411_fearless_roulette.sql, packages/db/migrations/meta/_journal.json
SQL migration adds image_input_tokens and image_output_tokens (NOT NULL DEFAULT 0) to the four hourly stats tables; migration journal updated with new entry and reindexed subsequent entries.
Aggregation logic & tests
apps/worker/src/services/project-stats-aggregator.ts, apps/api/src/testing.ts
Extended getCommonAggregationFields() to include imageInputTokens and imageOutputTokens computed as COALESCE(SUM(CAST(... AS numeric)), 0), propagating these values into hourly aggregations and test helpers.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant LogsDB as Logs DB
    participant Aggregator as Worker Aggregator
    participant HourlyDB as Hourly Stats DB
    participant TestHelper as Test Helper

    Client->>LogsDB: insert log rows with image_input_tokens / image_output_tokens
    Aggregator->>LogsDB: query recent log rows
    Aggregator->>Aggregator: compute COALESCE(SUM(CAST(... AS numeric)), 0) for imageInputTokens / imageOutputTokens
    Aggregator->>HourlyDB: upsert hourly stats including imageInputTokens/imageOutputTokens
    TestHelper->>HourlyDB: read hourly stats for assertions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding image token count fields (imageInputTokens and imageOutputTokens) to hourly stats tables across the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-image-token-counts-to-hourly-stats

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for tracking image token counts in the hourly aggregation statistics tables. It extends the existing hourly stats infrastructure to include imageInputTokens and imageOutputTokens fields, mirroring the structure already present in the log table.

Changes:

  • Added imageInputTokens and imageOutputTokens columns to all 4 hourly stats tables with default value "0"
  • Updated worker aggregation logic to sum image token counts from the log table
  • Updated test helper aggregation function to include image token fields

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
packages/db/src/schema.ts Added imageInputTokens and imageOutputTokens decimal columns to all 4 hourly stats tables
apps/worker/src/services/project-stats-aggregator.ts Extended getCommonAggregationFields() to aggregate image token counts from log table
apps/api/src/testing.ts Extended test helper's getCommonAggregationFields() to include image token aggregation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/db/src/schema.ts
Comment on lines +1229 to +1230
imageInputTokens: decimal().notNull().default("0"),
imageOutputTokens: decimal().notNull().default("0"),
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing database migration file for adding imageInputTokens and imageOutputTokens columns. Schema changes need corresponding migrations to be applied to the database. Run pnpm migrations in the packages/db directory to generate the migration file for these new columns across all four hourly stats tables.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/db/src/schema.ts`:
- Around line 1229-1230: The schema defines imageInputTokens/imageOutputTokens
(imageInputTokens/imageOutputTokens in schema.ts) but the migration
1770740952_same_lake.sql only creates image_input_cost/image_output_cost; update
that migration by adding the two missing columns "image_input_tokens" numeric
DEFAULT '0' NOT NULL and "image_output_tokens" numeric DEFAULT '0' NOT NULL to
each of the four CREATE TABLE statements, placing them immediately after the
"cached_tokens" column so the migration matches the schema.

Comment thread packages/db/src/schema.ts
Comment on lines +1229 to +1230
imageInputTokens: decimal().notNull().default("0"),
imageOutputTokens: decimal().notNull().default("0"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's check the schema file at the specified lines
wc -l packages/db/src/schema.ts

Repository: theopenco/llmgateway

Length of output: 94


🏁 Script executed:

# Read the schema file around the specified lines to verify the changes
sed -n '1225,1235p' packages/db/src/schema.ts
echo "---"
sed -n '1293,1303p' packages/db/src/schema.ts
echo "---"
sed -n '1372,1382p' packages/db/src/schema.ts
echo "---"
sed -n '1451,1461p' packages/db/src/schema.ts

Repository: theopenco/llmgateway

Length of output: 2027


🏁 Script executed:

# Search for migration files that might reference these new columns
find . -type f \( -name "*.sql" -o -name "*.ts" -o -name "*.js" \) -path "*/migration*" 2>/dev/null | head -20

Repository: theopenco/llmgateway

Length of output: 1223


🏁 Script executed:

# Search for any reference to imageInputTokens or imageOutputTokens in the codebase
rg 'imageInputTokens|imageOutputTokens' --type-list | head -5
echo "---"
rg 'imageInputTokens|imageOutputTokens' -g '*.sql' -g '*.ts' --max-count=50

Repository: theopenco/llmgateway

Length of output: 7886


🏁 Script executed:

# Check the most recent migration files
cat packages/db/migrations/1749327353_white_vivisector.sql
echo "---"
cat packages/db/migrations/1749323043_panoramic_gorilla_man.sql

Repository: theopenco/llmgateway

Length of output: 1128


🏁 Script executed:

# Search more specifically for ALTER TABLE statements that might add these columns
rg 'ALTER TABLE.*image' --type sql
echo "---"
rg 'imageInputTokens|imageOutputTokens' -g '*.sql'

Repository: theopenco/llmgateway

Length of output: 696


🏁 Script executed:

# Check if there's a drizzle config and if migrations are auto-generated
find . -name "drizzle.config.*" -o -name ".drizzlerc*" 2>/dev/null
echo "---"
# Check recent git changes to migration files
git log --oneline packages/db/migrations/ 2>/dev/null | head -10

Repository: theopenco/llmgateway

Length of output: 157


🏁 Script executed:

# Check the migration file that was just found
cat packages/db/migrations/1770626225_clever_silhouette.sql

Repository: theopenco/llmgateway

Length of output: 363


🏁 Script executed:

# Look for any other recent migrations that might add columns to the hourly stats tables
rg 'projectHourlyStats|projectHourlyModelStats|apiKeyHourlyStats|apiKeyHourlyModelStats' --type sql
echo "---"
# Check for any ALTER TABLE statements on these tables in migrations
rg 'ALTER TABLE "project_hourly|ALTER TABLE "api_key_hourly' --type sql

Repository: theopenco/llmgateway

Length of output: 67


🏁 Script executed:

# Get the most recent migrations to see the overall timeline
ls -lt packages/db/migrations/ | head -20

Repository: theopenco/llmgateway

Length of output: 1600


🏁 Script executed:

# Let's check if the migration file 1770626225 exists and what it contains entirely
cat packages/db/migrations/1770626225_clever_silhouette.sql 2>/dev/null || echo "File not found in 1749xxx range, checking full directory..."

Repository: theopenco/llmgateway

Length of output: 363


🏁 Script executed:

# Let's be more thorough - check all migration files for any mention of hourly stats tables
ls packages/db/migrations/*.sql | while read f; do
  if rg -l 'project_hourly|api_key_hourly' "$f" 2>/dev/null; then
    echo "Found in: $f"
    cat "$f"
  fi
done

Repository: theopenco/llmgateway

Length of output: 10110


🏁 Script executed:

# Also check git history for the schema changes
git log --oneline --all packages/db/src/schema.ts 2>/dev/null | head -5
echo "---"
git log --oneline --all packages/db/migrations/ 2>/dev/null | head -5

Repository: theopenco/llmgateway

Length of output: 281


🏁 Script executed:

# Let's check the schema file to see all the columns defined for one of these tables
sed -n '1200,1240p' packages/db/src/schema.ts

Repository: theopenco/llmgateway

Length of output: 1849


🏁 Script executed:

# Let's get the entire definition of projectHourlyStats table from schema
rg -A 50 'export const projectHourlyStats = pgTable' packages/db/src/schema.ts | head -70

Repository: theopenco/llmgateway

Length of output: 2253


🏁 Script executed:

# Let me check the migration file more carefully - search for all columns in the CREATE TABLE statements
cat packages/db/migrations/1770740952_same_lake.sql | grep -E 'image_|tokens'

Repository: theopenco/llmgateway

Length of output: 1371


🏁 Script executed:

# Also check if there's an ALTER TABLE to add these columns to existing tables
rg 'ALTER TABLE.*project_hourly.*image_.*_tokens|ALTER TABLE.*api_key_hourly.*image_.*_tokens' --type sql

Repository: theopenco/llmgateway

Length of output: 46


🏁 Script executed:

# Let me see the full content of the migration to understand what it does
wc -l packages/db/migrations/1770740952_same_lake.sql

Repository: theopenco/llmgateway

Length of output: 115


🏁 Script executed:

# Let me view the entire migration file
cat packages/db/migrations/1770740952_same_lake.sql

Repository: theopenco/llmgateway

Length of output: 10004


Schema and migration are misaligned — the imageInputTokens and imageOutputTokens columns are defined in schema.ts but missing from the corresponding migration.

The migration file 1770740952_same_lake.sql creates the four hourly stats tables but excludes image_input_tokens and image_output_tokens columns. The migration only includes image_input_cost and image_output_cost (cost columns, not token columns). This mismatch will cause runtime failures when the code attempts to read or write these token columns to the database.

Add "image_input_tokens" numeric DEFAULT '0' NOT NULL and "image_output_tokens" numeric DEFAULT '0' NOT NULL to all four CREATE TABLE statements in the migration, positioned after "cached_tokens" to match the schema definition.

🤖 Prompt for AI Agents
In `@packages/db/src/schema.ts` around lines 1229 - 1230, The schema defines
imageInputTokens/imageOutputTokens (imageInputTokens/imageOutputTokens in
schema.ts) but the migration 1770740952_same_lake.sql only creates
image_input_cost/image_output_cost; update that migration by adding the two
missing columns "image_input_tokens" numeric DEFAULT '0' NOT NULL and
"image_output_tokens" numeric DEFAULT '0' NOT NULL to each of the four CREATE
TABLE statements, placing them immediately after the "cached_tokens" column so
the migration matches the schema.

steebchen and others added 2 commits February 17, 2026 13:33
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@steebchen steebchen force-pushed the feat/add-image-token-counts-to-hourly-stats branch from 39ca807 to 013bb50 Compare February 17, 2026 06:33
@steebchen steebchen self-assigned this Feb 22, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/db/migrations/meta/_journal.json (1)

727-741: ⚠️ Potential issue | 🟡 Minor

Duplicate idx: 103 entries in migration journal.

There are two consecutive entries with idx: 103 (lines 727-732 and 733-739). This appears to be a pre-existing issue in the journal, but it should be corrected to maintain journal integrity. The second entry should likely be idx: 104, with subsequent entries renumbered accordingly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/db/migrations/meta/_journal.json` around lines 727 - 741, Two
consecutive migration journal entries share the same idx (idx: 103) for the
entries with tag "1773841690_keen_toad_men" and "1773841805_dashing_sentinels";
update the second duplicate entry to idx: 104 and then renumber all subsequent
entries' idx values incrementally to restore a unique, sequential idx sequence
(ensure you update any following entry that currently starts with idx: 104 to
105, etc., so the journal's idx values remain continuous).
🧹 Nitpick comments (1)
apps/api/src/testing.ts (1)

50-198: Consider extracting shared aggregation fields to reduce duplication.

getCommonAggregationFields() is duplicated between this file and apps/worker/src/services/project-stats-aggregator.ts. Both implementations are nearly identical (~45 fields). This duplication increases maintenance burden—any future field additions must be synchronized across both files.

Consider extracting the shared aggregation logic to @llmgateway/db or a shared utility module.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/testing.ts` around lines 50 - 198, getCommonAggregationFields()
is duplicated here and in apps/worker/src/services/project-stats-aggregator.ts;
extract it into a single shared module (e.g., export a function from
`@llmgateway/db` or a new shared util) and have both places import it. Move the
full implementation (all aggregation fields) into the new module as an exported
function (retain the name getCommonAggregationFields or a clear alias), update
both callers to import and use that exported function, and remove the local
duplicate definitions; ensure the module exports the same typed SQL fragments so
existing callers (in testing.ts and project-stats-aggregator.ts) compile without
other changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/db/migrations/meta/_journal.json`:
- Around line 727-741: Two consecutive migration journal entries share the same
idx (idx: 103) for the entries with tag "1773841690_keen_toad_men" and
"1773841805_dashing_sentinels"; update the second duplicate entry to idx: 104
and then renumber all subsequent entries' idx values incrementally to restore a
unique, sequential idx sequence (ensure you update any following entry that
currently starts with idx: 104 to 105, etc., so the journal's idx values remain
continuous).

---

Nitpick comments:
In `@apps/api/src/testing.ts`:
- Around line 50-198: getCommonAggregationFields() is duplicated here and in
apps/worker/src/services/project-stats-aggregator.ts; extract it into a single
shared module (e.g., export a function from `@llmgateway/db` or a new shared util)
and have both places import it. Move the full implementation (all aggregation
fields) into the new module as an exported function (retain the name
getCommonAggregationFields or a clear alias), update both callers to import and
use that exported function, and remove the local duplicate definitions; ensure
the module exports the same typed SQL fragments so existing callers (in
testing.ts and project-stats-aggregator.ts) compile without other changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a5adc6a1-e331-4fc3-a363-9b0440803e21

📥 Commits

Reviewing files that changed from the base of the PR and between ce4f771 and caa613b.

📒 Files selected for processing (4)
  • apps/api/src/testing.ts
  • apps/worker/src/services/project-stats-aggregator.ts
  • packages/db/migrations/meta/_journal.json
  • packages/db/src/schema.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants