Add GET /v1/users/me/llm-usage/daily: per-day LLM usage drill-down#9061
Add GET /v1/users/me/llm-usage/daily: per-day LLM usage drill-down#9061ZachL111 wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
2 issues found across 3 files
Confidence score: 3/5
- In
backend/routers/llm_usage.py, the daily usage normalization loop re-implements aggregation logic that already exists indatabase.llm_usage.get_usage_summary(andget_global_top_features), which creates a drift risk where usage totals or feature rankings can silently diverge after future rule changes — centralize this mapping in one shared helper before merging. - In
backend/routers/llm_usage.py, declaringdateasOptional[str]and parsing withdatetime.strptimecan accept non-canonical inputs (for%Y-%m-%d) and shifts validation out of Pydantic, which can lead to inconsistent request handling at the API boundary — switch to a typed date parameter (or stricter validation) before merging.
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
kodjima33
left a comment
There was a problem hiding this comment.
Additive read endpoint (per-day LLM usage). Backend feature — approve only.
331f7fa to
4d89300
Compare
|
Addressed both. Extracted a shared _sum_model_tokens helper in database/llm_usage.py and routed both get_usage_summary and the new get_daily_usage_summary through it, so the per-feature token aggregation lives in one place and cannot drift. The router is now a thin delegator. Also switched the date query param to a typed date so Pydantic validates it at the boundary (a malformed value returns 422 instead of a hand-rolled 400). The existing get_usage_summary tests still pass, and I added tests for the shared helper, the daily summary, and the endpoint delegation. |
Git-on-my-level
left a comment
There was a problem hiding this comment.
Thanks for the focused endpoint addition and for addressing the earlier validation/aggregation feedback.
I found one blocker before this can merge:
- The new daily summary currently drops the bucket-based usage schema written by
record_llm_usage_bucket. Those documents store buckets likedesktop_chatas flat dicts (input_tokens,output_tokens,cache_read_tokens,total_tokens,cost_usd,call_count), but_sum_model_tokens()treats each inner scalar as a non-dict and returns zeros, so/v1/users/me/llm-usage/dailycan reporthas_data=falseand empty totals for real daily desktop/chat usage. Please either support both the nested per-model schema and the flat bucket schema inget_daily_usage_summary, or make the route explicitly scoped to the nested schema only if that is the intended product surface.
Also, the current Backend unit suite check is failing, so I’m leaving this as changes requested rather than approval. A regression test with a flat desktop_chat/bucket document would cover the issue above.
No security attack/supply-chain concern spotted, but because this is an authenticated user-usage/data endpoint it should get maintainer review once the behavior and checks are fixed.
The existing /v1/users/me/llm-usage returns only a 30-day aggregate; there was no way to inspect a single day. Wire the tested-but-unwired get_daily_usage helper to a read endpoint returning the caller's token usage for one UTC day, by feature (input/output/total tokens plus call_count) with day-level totals and has_data. New light router because the natural host routers/users.py is large.
…eview) cubic flagged that the router re-implemented aggregation that already exists in get_usage_summary (drift risk), and that the string date param moved validation out of Pydantic. Extract a shared _sum_model_tokens helper (used by both get_usage_summary and the new get_daily_usage_summary), move the daily normalization into the database layer, and make the router a thin delegator with a typed date query param (Pydantic returns 422 on a malformed date).
…count aliases
get_daily_usage_summary treated each scalar in a flat desktop_chat-style bucket as
a non-dict model and returned zeros, so /v1/users/me/llm-usage/daily could report
has_data=false and empty totals for real desktop/chat usage. Detect the flat bucket
schema (token fields on the feature dict) alongside the nested per-model schema, and
skip the "{bucket}_{account}" aliases that restate the same totals so they cannot
double-count. Adds regression tests for a mixed day and a bucket-only day.
4d89300 to
87944d7
Compare
|
Good catch, fixed in the latest push.
A feature is treated as a flat bucket when it carries Regression tests in
On the current red check: that is the separate main-CI breakage the full unit suite is hitting right now (the dev-read rate-limit guard, tracked in #9065), not this change. The llm-usage suites here pass locally. Once #9065 lands I will rebase and it should go green. |
|
Thanks for the follow-up here. I re-reviewed the latest head ( I’m not approving this yet because the PR still has maintainer-review/test guardrails active and the Backend unit suite is red. I checked the failing job and the visible failure is in the existing developer read rate-limit test, not in the new LLM-usage tests, but this still needs a green run/rebase before merge. Because this is an authenticated user-usage endpoint, a human maintainer should also confirm the product/API shape before landing. No security attack or supply-chain concern spotted in this diff. |
What
Adds
GET /v1/users/me/llm-usage/daily, returning the current user's LLM token usage for a single UTC day.Why
The existing
GET /v1/users/me/llm-usagereturns only a 30-day aggregate (totals plus top features), so there is no way to inspect one day. The backing helperget_daily_usageis already unit-tested in the database layer but was never wired to a route. This exposes it.Details
has_dataflag.dateis an optionalYYYY-MM-DDquery param defaulting to today (UTC); a malformed date returns 400.users/{uid}/llm_usage/{date}(self-scoped, authenticated), with no new database code.get_usage_summary: it sums only the nested{feature}.{model}token dicts, skips scalar meta fields, and drops zero-sum buckets for a clean payload.routers/users.pywhere the sibling llm-usage routes live.Test
backend/tests/unit/test_llm_usage_daily.py: no-data zeros withhas_data=false, per-feature and total normalization, skipping of cost-only and zero-sum buckets, and a 400 for a malformed date (raised before any database access). 4 passed locally.