feat(widget): context-usage meter backed by the agent manager - #58
Conversation
|
PR #58 The implementation is clean, but I think the current naming is misleading. The backend calculates usage by summing We should either:
I would prefer resolving this before merging, because the UI currently presents the value as Also, should the critical threshold use |
Surface per-conversation token usage against the configured
context_max_tokens budget so users see how full the context is before
they hit the 429 wall.
Backend (source of truth):
- ContextUsage domain value object with a ContextSeverity StrEnum and a
from_totals factory that owns the warning/critical thresholds
- GET /conversations/{id}/usage returns used_tokens, max_tokens, percent
and severity; ConversationService.usage sums stored token counts
- schema reuses the domain ContextSeverity enum (no duplicated literals)
Widget (stateless renderer):
- AgentChatClient.getUsage + useConversation.loadUsage fetch usage on
open and after each turn; no client-side token math or thresholds
- ContextMeter ring (severity-coloured, hover popover) shown only when a
budget is set; hidden and non-breaking against backends without /usage
Also: neutral demo copy and a documented CONTEXT_MAX_TOKENS in the
starter example.
Tests: usage endpoint, severity thresholds, and two widget e2e cases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review feedback on #58: the value is a *cumulative* sum of every turn's input + output tokens, not the size of the context window currently sent to the model — history is re-sent each turn, so the same message is counted again every time it is included. Calling it "context usage" told the user something the number does not mean. Rename the whole surface to the thing the backend actually enforces (the per-conversation budget behind ConversationTokenBudgetExceeded / 429): ContextUsage -> TokenBudgetUsage, ContextSeverity -> BudgetSeverity, ContextUsageResponse -> TokenBudgetResponse, ContextMeter -> BudgetMeter, `context-*` CSS -> `budget-*`, and the popover/aria labels now read "Token budget". The GET .../usage route and CONTEXT_MAX_TOKENS setting keep their names (pre-existing API), but both are now documented as cumulative rather than context-window figures. Also make the critical threshold inclusive (>= 85 like the warning's >= 65, not > 85) so the two thresholds behave the same, with the boundary pinned by the test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2093eda to
e4705b0
Compare
|
Good catch — agreed, and fixed in You're right about what the number is: it's the cumulative sum of every turn's I went with option 1 (rename) rather than option 2, because the cumulative figure is the one the backend actually enforces —
And yes on the threshold: warning was assert TokenBudgetUsage.from_totals(849, 1000).severity == "warning"
assert TokenBudgetUsage.from_totals(850, 1000).severity == "critical"Also rebased onto
|
Stack 1/3 · base:
mainAdds a token-budget meter (assistant-ui
ContextDisplayshape) to the widget. All computation is on the backend — the agent manager exposesGET /conversations/{id}/usagereturning used/max tokens, percent, and aBudgetSeverity(normal/warning/critical) as the single source of truth. The FE only renders; it does no token math. Severity is aStrEnumreused across domain + schema so thresholds live in one place.The number is the conversation's cumulative token spend (every turn's input + output, summed) measured against
context_max_tokens— the budget the service actually enforces with a 429. It is deliberately not a context-window gauge, and the naming says so throughout (review feedback,e4705b0).Verified:
make lint+mypyclean, 472 tests pass, widget typecheck + Playwright e2e green.🤖 Generated with Claude Code