Skip to content

feat(dashboard): label analytics and page-level filters on usage page#461

Open
SantiagoDePolonia wants to merge 3 commits into
mainfrom
feature/ui-labelling
Open

feat(dashboard): label analytics and page-level filters on usage page#461
SantiagoDePolonia wants to merge 3 commits into
mainfrom
feature/ui-labelling

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What

Builds the dashboard UI for the labelling system shipped in #454, centered on the usage page, and restructures the page's filtering along the way.

Label analytics

  • Usage by Label section (chart/table toggle, like the model and user-path breakdowns) showing per-label requests, tokens, and costs. Each label gets one deterministic color, shared between its chart bar and every chip on the page, in light and dark themes.
  • Label chips in the request log — clicking a chip filters the whole page by that label; clicking it again clears the filter. The section and column hide themselves when the period has no labels, so untagged deployments see no clutter.
  • Standard inline ? help on the section: one request can carry several labels, so label rows overlap and don't sum to the period totals.
  • New GET /admin/usage/labels endpoint, implemented in all three storage backends: SQLite json_each, PostgreSQL jsonb_array_elements_text, MongoDB $unwind.

Page-level filters

  • Model / provider / label / user-path filters moved out of the request log into a filter bar under the page header. They now drive every widget: the stat cards, all three breakdowns, the cache cards, and the log. Options facet down with the filtered data, keeping the active selection listed.
  • Search and the hide-cached toggle stay in the Request Log section — search is log-only (applied on top of the page filters), and cached rows never appear in the provider-traffic charts.
  • Backend: model/provider/label filters centralized in UsageQueryParams and each backend's shared condition builder, so summaries, breakdowns, cache overview, and the log filter identically. This deduplicated the hand-rolled filter blocks in the log readers and all three pricing recalculators (RecalculatePricingParams is now a plain embed). All usage aggregate endpoints accept the new query params (OpenAPI regenerated).

Period totals

  • Total Requests and Estimated Cost stat cards for the selected period + filters. Requests include locally-cached hits (tooltip shows the provider/cache split, matching the overview page's convention); cost shows an input/output split on hover.

Misc

  • tools/seed-demo-data.sh now labels roughly two thirds of generated traffic and tolerantly adds the labels column to pre-labelling databases.
  • Request log wrapper scrolls horizontally instead of clipping trailing columns on narrow windows.

User-visible impact

Operators using tagging headers can now see where labelled traffic goes and what it costs, filter the entire usage page by any label/model/provider/user-path combination, and read period totals at a glance. Deployments without tagging see an unchanged page (plus the new stat cards).

Testing

  • Go: new SQLite tests for label aggregation, label filtering, and cross-filter aggregates; Mongo match-filter shape tests; handler and route tests. Full suite green, golangci-lint clean.
  • Dashboard: 394 JS unit tests pass, including new coverage for filter query composition, chip toggling, label colors, and the stat-card derivations.
  • Verified end-to-end headlessly against a seeded instance: every endpoint refetches with the active filters, log rows match, chip toggling drives the page, stat cards match the API to the cent across filter and period changes, both themes render correctly.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added “Usage by Label” analytics with a chart/table view and per-label token/cost aggregates.
    • Added page-wide filters for model, provider, label, and user path across usage charts, summaries, cache overview, and request logs.
    • Added admin endpoints for label-aware usage totals and label tagging settings (view/update).
    • Request logs now show label chips and a “Labels” column when available; chips support one-click label filtering.
  • Bug Fixes
    • Improved label chart/table rerendering on filter changes and theme refresh, plus better label chip styling (deterministic colors) and responsive request-log filtering layout.
  • Documentation
    • Updated the admin API specification to reflect label filters and the new endpoints.

- Usage by Label breakdown (chart/table toggle) with one deterministic
  color per label, shared between chart bars and label chips
- GET /admin/usage/labels aggregation implemented in all three storage
  backends (SQLite json_each, PostgreSQL jsonb_array_elements_text,
  MongoDB $unwind); label filter on the request log
- Request log renders clickable label chips; clicking one filters the
  whole page, clicking again clears it
- Filters (model, provider, label, user path) moved out of the request
  log into a page-level bar that drives every widget; search and the
  hide-cached toggle stay log-scoped
- Model/provider/label filters centralized in UsageQueryParams and the
  shared per-backend condition builders, deduplicating the log readers
  and all three pricing recalculators; every usage aggregate endpoint
  now accepts them
- Total Requests and Estimated Cost stat cards for the selected period
  and filters, with provider/cache and input/output tooltips
- Standard inline help on the label section ("One request can have
  multiple labels...")
- Demo seeder labels roughly two thirds of generated traffic

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mintlify

mintlify Bot commented Jul 3, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
gomodel 🟢 Ready View Preview Jul 3, 2026, 5:04 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8fb11f79-3f7a-46b5-9973-f083e0c26d08

📥 Commits

Reviewing files that changed from the base of the PR and between 025cb43 and 09d6eac.

📒 Files selected for processing (7)
  • internal/admin/dashboard/static/js/dashboard.js
  • internal/admin/dashboard/static/js/modules/usage.js
  • internal/admin/dashboard/static/js/modules/usage.test.cjs
  • internal/usage/reader_postgresql.go
  • internal/usage/reader_sqlite.go
  • internal/usage/recalculate_pricing.go
  • internal/usage/recalculate_pricing_sqlite_test.go

📝 Walkthrough

Walkthrough

This PR adds label-based usage analytics and filtering across the admin usage stack, introduces /admin/usage/labels and /admin/tagging/settings, updates dashboard label charts and filters, and extends the API docs and demo seed data to include labels.

Changes

Label-based usage filtering and tagging settings

Layer / File(s) Summary
Usage filter contracts
internal/usage/reader.go, internal/usage/recalculate_pricing.go
Adds Model/Provider/Label to UsageQueryParams, introduces LabelUsage, and embeds shared usage filters into usage-log and recalculation params.
Usage readers and recalculation filters
internal/usage/reader_sqlite.go, internal/usage/reader_postgresql.go, internal/usage/reader_mongodb.go, internal/usage/*_test.go, internal/usage/labels_sqlite_test.go, internal/usage/recalculate_pricing_*.go, internal/usage/recalculate_pricing_*_test.go
Implements GetUsageByLabel, centralizes backend filter construction, and removes duplicate recalculation filter logic.
Admin usage API wiring
internal/admin/handler.go, internal/admin/handler_usage.go, internal/admin/routes.go, internal/admin/*_test.go
Parses usage filters, adds the label-usage handler and route, and updates Swagger docs/tests for label queries.
Dashboard filter state and fetch logic
internal/admin/dashboard/static/js/dashboard.js, .../modules/usage.js, .../modules/live-logs.js, .../*.test.cjs
Reworks usage-page state and fetch URLs around page-wide model/provider/label/user-path filters.
Label usage charts
internal/admin/dashboard/static/js/modules/charts.js, .../charts.test.cjs, .../dashboard-display.test.cjs
Adds label usage chart rendering, label color/chip helpers, and chart rerender support.
Usage page template and styling
internal/admin/dashboard/templates/page-usage.html, .../css/dashboard.css, .../dashboard-layout.test.cjs
Adds page-level filters, the usage-by-label section, request-log label cells, and supporting styling/layout updates.
API docs, tagging settings, and seed data
docs/openapi.json, cmd/gomodel/docs/docs.go, CLAUDE.md, tools/seed-demo-data.sh
Updates generated API specs, tagging settings schemas, usage-label schemas, documentation notes, and seeded usage labels.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Poem

A rabbit hops through labels bright,
and charts now bloom in colored light.
With prod and batch and tea-time trails,
the usage garden tells its tales.
🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: label analytics and page-level filters on the usage page.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 feature/ui-labelling

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.

@codecov-commenter

codecov-commenter commented Jul 3, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 32.02614% with 104 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/usage/reader_mongodb.go 14.70% 58 Missing ⚠️
internal/usage/reader_postgresql.go 0.00% 37 Missing ⚠️
internal/usage/reader_sqlite.go 76.47% 4 Missing and 4 partials ⚠️
internal/usage/recalculate_pricing_postgresql.go 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@SantiagoDePolonia SantiagoDePolonia self-assigned this Jul 3, 2026
@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Safe to merge with low risk.

Backend filters are parameterized across SQLite, PostgreSQL, and MongoDB. The new route is registered and documented. The frontend consistently composes the page-level filter query for affected widgets. No blocking correctness or security issues were identified.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex ran the initial verification to compare before/after states for label analytics endpoints, page filters, and log search caching.
  • T-Rex exercised the Usage by Label dashboard UI flow after the changes, including the chart/table toggle, inline help rendering, and UI state transitions such as the no-label period and dark/narrow themes.
  • T-Rex confirmed the inclusion of the shared stems sqlite-label-aggregation, cross-backend-filter-shape, pricing-filter-embed, and seed-demo-labels in the tests, and noted target tests passing with seed behavior.
  • T-Rex ran the requested verification, but its local artifact references were not uploaded.

View all artifacts

T-Rex Ran code and verified through T-Rex

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
  participant Operator as Dashboard operator
  participant UI as Usage page UI
  participant Admin as Admin API
  participant Reader as UsageReader backend
  participant DB as Storage backend

  Operator->>UI: Select period/model/provider/label/user_path
  UI->>Admin: GET /admin/usage/summary + filters
  UI->>Admin: GET /admin/usage/models + filters
  UI->>Admin: GET /admin/usage/user-paths + filters
  UI->>Admin: GET /admin/usage/labels + filters
  UI->>Admin: GET /admin/cache/overview + filters
  UI->>Admin: GET /admin/usage/log + filters + log options
  Admin->>Reader: UsageQueryParams / UsageLogParams
  Reader->>DB: Apply date, model, provider, label, user_path, cache mode
  DB-->>Reader: Filtered aggregates and rows
  Reader-->>Admin: Summary, breakdowns, cache overview, log entries
  Admin-->>UI: JSON responses
  UI-->>Operator: Stat cards, charts/tables, label chips, filtered log
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
  participant Operator as Dashboard operator
  participant UI as Usage page UI
  participant Admin as Admin API
  participant Reader as UsageReader backend
  participant DB as Storage backend

  Operator->>UI: Select period/model/provider/label/user_path
  UI->>Admin: GET /admin/usage/summary + filters
  UI->>Admin: GET /admin/usage/models + filters
  UI->>Admin: GET /admin/usage/user-paths + filters
  UI->>Admin: GET /admin/usage/labels + filters
  UI->>Admin: GET /admin/cache/overview + filters
  UI->>Admin: GET /admin/usage/log + filters + log options
  Admin->>Reader: UsageQueryParams / UsageLogParams
  Reader->>DB: Apply date, model, provider, label, user_path, cache mode
  DB-->>Reader: Filtered aggregates and rows
  Reader-->>Admin: Summary, breakdowns, cache overview, log entries
  Admin-->>UI: JSON responses
  UI-->>Operator: Stat cards, charts/tables, label chips, filtered log
Loading

Comments Outside Diff (1)

  1. General comment

    P1 Filtered usage stat cards can disagree with matching request log rows when cached traffic is visible

    • Bug
      • For the same active label/model/provider/user_path operator filters, the head server returned total_requests:3 from /admin/usage/summary but /admin/usage/log returned 4 matching rows when cached rows were included. The validation contract requires the usage page Total Requests/Estimated Cost stat cards to match API values and visible request log rows matching those filters. In the demonstrated workflow, one matching cached semantic row appears in the log but is excluded from the summary/stat-card API default, so operators see inconsistent request totals for the same filtered page.
    • Cause
      • The summary endpoint/default usage-page stat-card fetch uses the default uncached cache mode, while the request log workflow can include cached rows via cache_mode=all. The page-level filters are shared, but cache-mode semantics are not aligned across the stat-card summary and log data sources. This is anchored to the changed UI/API contract around internal/admin/dashboard/static/js/modules/usage.js summary/log fetches and internal/admin/handler_usage.go cache_mode query handling.
    • Fix
      • Align cache-mode semantics for the usage-page summary/stat cards and request log. Either have the stat-card summary use the same cache_mode as the visible log when cached rows are shown, or clearly separate provider-request totals from all visible log rows and ensure the UI labels make that distinction. Add an E2E/API regression that applies label/model/provider/user_path filters with cached traffic and asserts the card totals match the intended visible-row scope.

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "feat(dashboard): label analytics and pag..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (3)
internal/usage/recalculate_pricing.go (1)

57-62: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Label filter isn't trimmed like Model/Provider.

normalizedRecalculatePricingParams trims Model and Provider but leaves Label untouched, even though Label now flows through the same embedded UsageQueryParams and is used as an exact-match filter (jsonb_exists/labels match) in the postgres/mongo recalculation queries. The sibling test file explicitly pads Model/Provider with spaces to verify trimming (recalculate_pricing_mongodb_test.go uses " gpt-4o ", " primary-openai "), showing the codebase already guards against this class of bug for exact-match fields — Label is missing the same protection, so a caller passing a label with incidental whitespace would silently match zero rows.

🩹 Proposed fix
 func normalizedRecalculatePricingParams(params RecalculatePricingParams) RecalculatePricingParams {
 	params.Model = strings.TrimSpace(params.Model)
 	params.Provider = strings.TrimSpace(params.Provider)
+	params.Label = strings.TrimSpace(params.Label)
 	params.CacheMode = CacheModeAll
 	return params
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/usage/recalculate_pricing.go` around lines 57 - 62,
normalizedRecalculatePricingParams currently trims Model and Provider but leaves
Label unchanged, so add the same whitespace normalization for Label in
RecalculatePricingParams. Update the normalization path in
normalizedRecalculatePricingParams so Label is trimmed before the recalculation
queries run, keeping it consistent with the exact-match filters used by the
postgres/mongo logic and the existing trimming behavior for Model and Provider.
internal/usage/recalculate_pricing_sqlite_test.go (1)

20-188: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Add Label-filter coverage to recalculation tests The recalculation paths already use the shared usage-condition builders, so the remaining gap is test coverage: none of the sqlite/postgres/mongo recalculation tests exercise UsageQueryParams.Label, leaving that filter without a regression guard.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/usage/recalculate_pricing_sqlite_test.go` around lines 20 - 188, Add
test coverage for the Label filter in the SQLite recalculation tests, since
`UsageQueryParams.Label` is not exercised here. Extend one of the existing
`TestSQLiteStoreRecalculatePricing...` cases to write usage rows with a label
and call `Store.RecalculatePricing` with `UsageQueryParams.Label` set, then
assert only the matching row is recalculated. Use the existing `WriteBatch`,
`RecalculatePricing`, and `UsageQueryParams` symbols so the new assertion
verifies the label-based condition builder path.

Source: Coding guidelines

internal/admin/dashboard/static/js/modules/usage.js (1)

619-650: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Facet dropdowns self-filter usageFilterModelOptions(), usageFilterProviderOptions(), and usageFilterLabelOptions() are built from page-filtered fetches, so each select can collapse to the active value and lose the other choices. Source facet options from data that omits that facet’s own filter while keeping the other page filters.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/admin/dashboard/static/js/modules/usage.js` around lines 619 - 650,
The facet option builders in usage.js are self-filtering because
usageFilterModelOptions(), usageFilterProviderOptions(), and
usageFilterLabelOptions() all derive from already page-filtered data, which can
collapse each dropdown to only the active value. Update these option getters to
source from data filtered by the other page filters only, so each facet
preserves its full choice set while still honoring the remaining active filters.
Keep the existing symbols usageFilterModelOptions, usageFilterProviderOptions,
usageFilterLabelOptions, and the related
usageFilterModel/usageFilterProvider/usageFilterLabel state when adjusting the
data source.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/usage/recalculate_pricing.go`:
- Around line 15-19: The doc comment on RecalculatePricingParams is missing
Label as one of the selector fields used by the embedded UsageQueryParams.
Update the comment to include Label alongside date range, model, provider, and
user path, so the documented recalculation scope matches the filters actually
applied in pgUsageConditions and mongoUsageMatchFilters when params.Label is
set.

---

Outside diff comments:
In `@internal/admin/dashboard/static/js/modules/usage.js`:
- Around line 619-650: The facet option builders in usage.js are self-filtering
because usageFilterModelOptions(), usageFilterProviderOptions(), and
usageFilterLabelOptions() all derive from already page-filtered data, which can
collapse each dropdown to only the active value. Update these option getters to
source from data filtered by the other page filters only, so each facet
preserves its full choice set while still honoring the remaining active filters.
Keep the existing symbols usageFilterModelOptions, usageFilterProviderOptions,
usageFilterLabelOptions, and the related
usageFilterModel/usageFilterProvider/usageFilterLabel state when adjusting the
data source.

In `@internal/usage/recalculate_pricing_sqlite_test.go`:
- Around line 20-188: Add test coverage for the Label filter in the SQLite
recalculation tests, since `UsageQueryParams.Label` is not exercised here.
Extend one of the existing `TestSQLiteStoreRecalculatePricing...` cases to write
usage rows with a label and call `Store.RecalculatePricing` with
`UsageQueryParams.Label` set, then assert only the matching row is recalculated.
Use the existing `WriteBatch`, `RecalculatePricing`, and `UsageQueryParams`
symbols so the new assertion verifies the label-based condition builder path.

In `@internal/usage/recalculate_pricing.go`:
- Around line 57-62: normalizedRecalculatePricingParams currently trims Model
and Provider but leaves Label unchanged, so add the same whitespace
normalization for Label in RecalculatePricingParams. Update the normalization
path in normalizedRecalculatePricingParams so Label is trimmed before the
recalculation queries run, keeping it consistent with the exact-match filters
used by the postgres/mongo logic and the existing trimming behavior for Model
and Provider.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 504ad074-98ab-47c3-a3fc-176abfe9d051

📥 Commits

Reviewing files that changed from the base of the PR and between 4c8e0d7 and 133d824.

📒 Files selected for processing (32)
  • CLAUDE.md
  • cmd/gomodel/docs/docs.go
  • docs/openapi.json
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/dashboard.js
  • internal/admin/dashboard/static/js/modules/charts.js
  • internal/admin/dashboard/static/js/modules/charts.test.cjs
  • internal/admin/dashboard/static/js/modules/dashboard-display.test.cjs
  • internal/admin/dashboard/static/js/modules/dashboard-layout.test.cjs
  • internal/admin/dashboard/static/js/modules/live-logs.js
  • internal/admin/dashboard/static/js/modules/live-logs.test.cjs
  • internal/admin/dashboard/static/js/modules/usage.js
  • internal/admin/dashboard/static/js/modules/usage.test.cjs
  • internal/admin/dashboard/templates/page-usage.html
  • internal/admin/handler.go
  • internal/admin/handler_test.go
  • internal/admin/handler_usage.go
  • internal/admin/routes.go
  • internal/admin/routes_test.go
  • internal/usage/labels_sqlite_test.go
  • internal/usage/reader.go
  • internal/usage/reader_mongodb.go
  • internal/usage/reader_mongodb_test.go
  • internal/usage/reader_postgresql.go
  • internal/usage/reader_sqlite.go
  • internal/usage/recalculate_pricing.go
  • internal/usage/recalculate_pricing_mongodb.go
  • internal/usage/recalculate_pricing_mongodb_test.go
  • internal/usage/recalculate_pricing_postgresql.go
  • internal/usage/recalculate_pricing_sqlite.go
  • internal/usage/recalculate_pricing_sqlite_test.go
  • tools/seed-demo-data.sh
💤 Files with no reviewable changes (1)
  • internal/usage/recalculate_pricing_sqlite.go

Comment thread internal/usage/recalculate_pricing.go
SantiagoDePolonia and others added 2 commits July 3, 2026 19:32
The Total Requests card derived its cached-hit count from the cache
overview endpoint, which is unavailable when cache analytics is
disabled — so with historical cached rows in storage the card silently
undercounted versus the log's default all-mode view (and overcounted
when "Hide cached requests" was on).

The stat-card summary now fetches both cache modes: the requests card
follows the log's visible scope (all rows by default, provider rows
when hide-cached is on) with the provider/cache split derived as the
difference of the two summaries, independent of the cache-analytics
flag. Estimated Cost stays on uncached mode, since cached rows carry
the avoided cost that must not inflate real spend.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Facet dropdown options now follow the faceted-search rule: each facet's
choices honor every active filter except its own, fetched from dedicated
per-facet aggregate queries (shared when the by-model queries coincide).
Previously the options derived from the fully filtered chart data, so
selecting a model collapsed the model dropdown to that single value and
switching required clearing first.

Review nits: RecalculatePricingParams doc comment lists label as a
selector; the label filter is trimmed like model/provider; a sqlite
recalculation test covers label-scoped recalculation (with a padded
label exercising the trim). Also inlined the single-use
*ByUserPathConditions wrapper aliases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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