Cost was always estimated locally from catalog per-token prices, which
drifts from what OpenRouter actually charges (dynamic routing, BYOK,
promos, provider-specific caching). Request OpenRouter's usage.include
extension and use the returned usage.cost as the authoritative figure
when present, falling back to the per-token estimate for providers that
don't report it.
Admin logs list no longer joins prompt/completion content for every row —
content is fetched per-row only when a log is expanded in the dashboard,
since a table of chat-length blobs was slow to serialize even at low row
counts.
request_logs.user_id (previously unpopulated despite an index existing on
it) is now set from the owning key on every insert, including synthetic
dashboard keys, and log queries filter on it directly instead of a
virtual_keys subquery. Pagination switches from OFFSET to a (created_at, id)
keyset cursor so paging deep into the log stays cheap. Adds indexes on
provider_name/key_prefix used by the admin filters.
- Migration 000003: request_log_content table (messages jsonb, completion text)
- InsertRequestLog now returns the new row's ID so content can be linked
- Chat handler captures prompt messages and completion text (streaming accumulates deltas)
- ListRequestLogs left-joins content; logs API passes key filter through
- KeyStats query aggregates requests/tokens/cost/errors per key prefix
- GET /api/admin/stats/keys endpoint
- Logs page: expandable rows show prompt + completion inline
- Analytics page: per-key usage table below the chart
Add /auth/login and /auth/callback handlers. The backend holds the
client secret and exchanges the authorization code server-side;
the browser never sees the secret. Redirect URI and post-login
fragment destination are derived solely from GATEWAY_SERVER_PUBLIC_URL
(required when OIDC is set) to prevent host-header injection.
Tailwind v4 dark design (DM Sans/Mono, violet accent). Pages: Overview,
Logs, API Keys, Providers, Models, Analytics, Routing, Settings.
Auth gate uses localStorage token validated against /api/admin/providers.
Vite builds to internal/web/dist which is embedded into the binary.
main.go connects all packages: config → pool → migrate → cipher → store
→ providermgr → catalog → optional OIDC → rate limiter → API server.
WriteTimeout is zero to support SSE streaming; no middleware.Timeout.
Config validation now allows OIDC to be optional (static-token fallback).
chi v5 router with virtual-key auth middleware, admin-auth middleware
(OIDC-first with static-token fallback), chat and embeddings proxy
handlers with RPM/TPM rate limiting, admin CRUD endpoints for
providers/keys/models/settings, request log handler, and embedded
SPA handler.