Changeset 0.10.0 (#56)
This commit is contained in:
89
CHANGELOG.md
89
CHANGELOG.md
@@ -2,6 +2,95 @@
|
||||
|
||||
All notable changes to Chat Switchboard.
|
||||
|
||||
## [0.10.0] — 2026-02-24
|
||||
|
||||
### Added
|
||||
- **Model Roles** — Named model slots (`utility`, `embedding`, `generation`)
|
||||
with primary + fallback bindings. Stored in `global_settings['model_roles']`.
|
||||
Team-level overrides via `teams.settings` JSONB. New `server/roles/` package
|
||||
with `Resolver.Complete()` and `Resolver.Embed()` for automatic failover.
|
||||
Admin API: `GET/PUT /admin/roles/:role`, `POST /admin/roles/:role/test`.
|
||||
Team API: `GET/PUT/DELETE /teams/:id/roles/:role`.
|
||||
- **Provider Embed() interface** — All providers implement `Embed()`. OpenAI,
|
||||
Venice, OpenRouter support `/v1/embeddings`; Anthropic returns `ErrNotSupported`.
|
||||
New types: `EmbeddingRequest`, `EmbeddingResponse`.
|
||||
- **Usage Tracking** — Every completion (streaming and non-streaming) logs
|
||||
token counts and cost to `usage_log` table. Cost calculated at insert time
|
||||
from `model_pricing` (no retroactive recalculation). Provider scope
|
||||
denormalized for efficient admin filtering. Admin views exclude BYOK.
|
||||
New stores: `UsageStore`, `PricingStore`. Admin API:
|
||||
`GET /admin/usage`, `GET /admin/usage/users/:id`, `GET /admin/usage/teams/:id`.
|
||||
User API: `GET /usage` (personal summary).
|
||||
- **Model Pricing** — `model_pricing` table with catalog sync and manual admin
|
||||
overrides. Sync from provider APIs (Venice, OpenRouter) auto-populates
|
||||
pricing with `source='catalog'`; manual entries never overwritten. Admin API:
|
||||
`GET/PUT/DELETE /admin/pricing`.
|
||||
- **Streaming token capture** — OpenAI: `stream_options.include_usage` +
|
||||
parse usage/cache from final chunk. Anthropic: parse `message_start` for
|
||||
input/cache tokens, `message_delta` for output tokens. Cache token fields
|
||||
(`CacheCreationTokens`, `CacheReadTokens`) on `CompletionResponse`,
|
||||
`StreamEvent`, and `streamResult`.
|
||||
- **Admin Roles tab** — Configure primary/fallback provider+model per role,
|
||||
test-fire from UI.
|
||||
- **Admin Usage dashboard** — Period selector (7d/30d/90d), group-by
|
||||
(model/user/day/provider), totals cards, breakdown table, pricing table.
|
||||
- **Team Usage dashboard** — Team admins can view usage against their
|
||||
team-owned providers via `GET /teams/:teamId/usage`. Filters to
|
||||
`provider_configs WHERE scope='team' AND owner_id=teamId`. Integrated
|
||||
into team management panel with period/group-by selectors.
|
||||
- **Admin Reset Password** — Button in user list with vault destruction
|
||||
warning dialog (two-step confirmation).
|
||||
- **User Usage tab** — Settings modal "Usage" tab shows personal token
|
||||
consumption and estimated costs across all conversations, including BYOK.
|
||||
Period selector (7d/30d/90d) and group-by (model/day).
|
||||
- **Live Venice integration tests** — Gated behind `VENICE_API_KEY` env var.
|
||||
Tests: non-streaming completion, streaming completion, usage logging for
|
||||
both modes, pricing from catalog sync, and direct embeddings via BGE-M3.
|
||||
Uses `qwen3-4b` (Venice Small) — cheapest at $0.05/$0.15 per 1M tokens.
|
||||
- **Migration 004** — `usage_log` table, `model_pricing` table, `model_roles`
|
||||
seed in `global_settings`.
|
||||
|
||||
### Fixed
|
||||
- **UEK re-wrap on password change** — `ChangePassword` now decrypts UEK
|
||||
with old password and re-encrypts with new password + fresh salt. Previously,
|
||||
password changes silently broke all personal BYOK keys.
|
||||
- **UEK destruction on admin password reset** — `ResetPassword` now nullifies
|
||||
vault columns, evicts UEK from cache, deletes personal provider configs,
|
||||
and logs an audit event. Previously, admin resets left orphaned encrypted
|
||||
UEK that silently broke personal keys.
|
||||
- **Streaming completions logged zero tokens** — `StreamEvent` lacked token
|
||||
fields and providers discarded usage data from final chunks. Both OpenAI
|
||||
and Anthropic streaming parsers now capture and propagate token counts.
|
||||
- **OpenAI streaming usage race condition** — The OpenAI protocol sends
|
||||
chunks in order: content → finish_reason → usage → [DONE]. The parser
|
||||
sent `Done=true` on the finish_reason chunk (step 2) before the usage
|
||||
chunk arrived (step 3, with `choices:[]`). The receiver returned
|
||||
immediately on Done, so usage was captured but never delivered.
|
||||
Fix: defer the Done event as `pendingFinish` until the usage chunk
|
||||
arrives, then flush with tokens attached. Tool-call finishes flush
|
||||
immediately (tool loop needs the event). Affects all OpenAI-compatible
|
||||
providers (OpenAI, Venice, OpenRouter).
|
||||
- **Token accumulation across tool iterations** — `streamResult` in
|
||||
`stream_loop.go` now accumulates input/output/cache tokens across
|
||||
multi-tool-call iterations instead of only capturing the final iteration.
|
||||
- **Admin pricing leaked BYOK entries** — `PricingStore.List()` returned
|
||||
all model_pricing rows including those from personal BYOK providers.
|
||||
Admin panel showed pricing entries for user-private providers they
|
||||
shouldn't see, with O(users × models) scale explosion. Now joins on
|
||||
`provider_configs` and filters `scope != 'personal'`. Admin `UpsertPricing`
|
||||
also validates provider scope, rejecting manual pricing on personal
|
||||
providers.
|
||||
- **Team role handlers used wrong param name** — `ListTeamRoles`,
|
||||
`UpdateTeamRole`, `DeleteTeamRole` read `c.Param("id")` but routes use
|
||||
`:teamId`. Every team role operation silently got an empty team ID.
|
||||
- **Usage logging suppressed for zero-token streams** — `logUsage` had an
|
||||
early exit when `inputTokens == 0 && outputTokens == 0`. Combined with the
|
||||
OpenAI streaming parser bug (below), this silently dropped all streaming
|
||||
usage rows. Removed the guard — requests are always recorded.
|
||||
- **Live chat completion test used wrong field names** — `TestLive_VeniceChatCompletion`
|
||||
sent `messages` and `config_id` but the handler expects `content` and
|
||||
`provider_config_id`. Test silently skipped on the binding error.
|
||||
|
||||
## [0.9.4] — 2026-02-24
|
||||
|
||||
### Added
|
||||
|
||||
Reference in New Issue
Block a user