You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
First of three stages splitting the admin-UI umbrella task into
Usage+Cost infrastructure (A.1), remaining-provider capture (A.2), and
Usage/Cost admin sections (A.3).
What lands here:
* internal/llm/usage.go
- UsageLog struct (one row per LLM call — agentic loops produce N rows
sharing the same user_message_id)
- UsageStore interface with PutUsage + UpdateAssistantMessageID
- Usage struct attached to llm.Response, populated by providers that
report token counts; zero-value otherwise
- TurnMeta + WithTurnMeta/TurnMetaFrom context helpers for threading
chat_id / user_message_id / role-hint from agent down to router
without changing every provider's Chat signature
- ClassifyErrorClass bucketing for dashboards (rate_limit / 5xx / 4xx
/ network / timeout / other)
* Schema in usage_log (SQLite migration + Postgres CREATE IF NOT EXISTS)
- id, ts, provider, model_id, role, chat_id
- prompt_tokens, completion_tokens, cached_prompt_tokens,
reasoning_tokens (prompt caching + thinking overhead accounted
from the start, per API reference)
- latency_ms, success, error_class, request_id, tool_call_count
- user_message_id, assistant_message_id (nullable FKs to messages.id)
- indexes on (ts, model_id), (chat_id, ts), (user_message_id)
* Store implementations — PutUsage + UpdateAssistantMessageID for both
SQLite and Postgres backends, wired through main.go via type assertion
to llm.UsageStore (works with either backend the bot is configured to
use).
* openai_compat.go — parse OpenRouter usage block including
prompt_tokens_details.cached_tokens (Anthropic caching via bridge /
OpenAI prompt caching) and completion_tokens_details.reasoning_tokens
(thinking models). Also captures the provider's request id for
cross-referencing with their dashboard when debugging a call.
* Router.Chat records a UsageLog row after every provider call (primary
AND each fallback hop), best-effort with a 3s timeout on a background
ctx so usage logging never blocks or fails a user request. Error
class is auto-classified; turn meta is pulled from ctx when present.
Scope covered: OpenRouter traffic (~90% of real requests) is now
logged end-to-end with full token breakdown. Other providers (gemini /
claude_bridge / ollama / local) still land rows but with zero tokens
until their response parsers are updated — that's Stage A.2.
Tests added:
- ClassifyErrorClass bucket mapping (8 cases)
- TurnMeta round-trip through context
- rawChatResponse parses usage block + cached + reasoning + request_id
- SQLite round-trip: insert with all fields, backfill
assistant_message_id, verify NULL handling for background tasks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
0 commit comments