- Removed gemini-3.5-flash-lite from dustins_stack targets (7→6 models)
- Replaced Gemini fallbacks in multimodal_tier, ultra_long_context, fast_flow_tier
- Added dustins_stack seed data to db.go for fresh deploys
- Also commits pending Gemini role=function→user fix for ChatCompletion and ChatCompletionStream
- Sidebar becomes slide-in overlay under 768px with backdrop
- Hamburger button in top-bar for mobile nav toggle
- Grids stack single-column, charts shrink to 280px
- Tables scroll horizontally with touch momentum
- Status text hidden on mobile (dot stays visible)
- Card headers/actions stack vertically
- Period selectors scroll horizontally
also: image size validation clamp in handleImageGenerations
- Strip additionalProperties and $schema from tool parameter schemas
(Gemini API rejects these unsupported JSON Schema fields)
- Add thoughtSignature to functionCall parts for multi-turn tool calling
(Gemini 3 models require thought signatures on function call history)
- Ensure functionResponse.response is always a JSON object, never an
array or primitive (wrap non-objects in {"result": ...})
- Resolve tool names from tool_call_id or positional index when the
tool message doesn't carry a name field
- Add functionCall parsing in streaming responses (emitGeminiChunk)
to properly relay tool calls via SSE
- Add error logging for Gemini stream failures with request body dump
- Add unit tests for schema cleaning and streaming tool call emission
- Added tier boundary descriptions (fast/standard/heavy) to the classifier
system prompt so gpt-5.4-nano understands what each complexity band means
- Added signal keywords for higher/lower ratings to reduce misclassification
of simple requests as complex and vice versa
Upgrades the routing engine to support tag, token limit, multimodal, reasoning, and tool calling conditions. Adds unit tests for the new routing features.
- Use resp.Body() instead of resp.RawBody() for non-streaming error responses
- Fall back to RawBody() for streaming responses
- Log the full request body on API errors for debugging
- Add promo discount system for deepseek-v4-pro (75% off until 2026-05-31)
- Rewrite StreamGemini to handle both SSE and JSON array response formats,
fixing 0-token logging for gemini-3-flash and gemini-3-flash-preview
- Fall back to model group name for cost lookup when concrete model
isnt in the registry (fixes $0 cost on deepseek-auto entries)
- Move registry lock before FindModel call to fix data race
The 40-character truncation of tool call IDs in helper.go caused collisions
when models (like deepseek-v4-flash) generated longer IDs, leading to
"Duplicate value for 'tool_call_id'" errors. Removed the limit to allow
full unique IDs.
DeepSeek: updated reasoning_content injection to use an empty string
instead of a space, better matching provider expectations for history.
Improved API error reporting across all providers by capturing raw body
content when response parsing fails or returns empty strings.
FindModel iterates providers in random map order, so when deepseek-v4-pro
exists in both 'deepseek' (output=384000) and 'ollama-cloud' (output=1048576),
it sometimes returned the wrong metadata. The proxy then injected
max_tokens=1048576 into DeepSeek's API, which rejected it with 400
(valid range is [1, 393216]).
Fix: define CanonicalProviders list (deepseek, openai, google, xai, etc.)
and search them in priority order before falling back to all providers.
Each of the four lookup strategies (exact key, metadata ID, reverse fuzzy,
forward fuzzy) checks canonical providers first.
README: Added hierarchical routing, classifier bucket mapping, two-level
dispatch, model groups table, DeepSeek language note, deploy script, and
updated model names to match current models.dev registry.
TODO: Added 15 completed items covering model groups, routing, dispatch,
and provider fixes from May 7 session.
deployment.md: Added deploy.sh instructions.
DeepSeek models default to Chinese for some prompts. The ensureEnglish()
function prepends 'Always respond in English' as a system message when
no system prompt is already set. Applied to both ChatCompletion and
ChatCompletionStream paths.
Previously, provider selection happened on the raw client-requested model
name (e.g. 'dispatcher') which defaulted to OpenAI. After routing resolved
it to 'deepseek-v4-flash', the provider was never re-selected.
Now prefix-stripping + routing runs first, then selectProvider() picks
the correct provider based on the resolved concrete model.
Extracted selectProvider() method from handleChatCompletions' inline
logic. The classifier callback now calls selectProvider(selectorModel)
instead of hardcoding openaiProvider.
This fixes the 'circuit breaker is open' error when dispatcher tries
to use deepseek-v4-flash as its selector model.
Classifier: When complexity_threshold is set (e.g. 10), uses it as the
rating scale and maps ratings proportionally to target buckets instead
of 1:1. Formula: idx = rating * len(targets) / (threshold + 1).
With threshold=10 and 3 targets: 1-3→target[0], 4-7→target[1], 8-10→target[2].
Seed: Added 'dispatcher' group (classifier, threshold=10, selector=deepseek-v4-flash)
that auto-routes to fast-flow/standard-pro/heavy-logic by complexity score.
Combined with hierarchical routing, this enables two-level dispatch:
dispatcher scores 1-10 → routes to tier group → tier picks concrete model.
RouteToConcrete() recursively resolves group chains until a concrete
model is reached, with cycle detection and max depth (10) guard.
Example: all-purpose -> fast-flow -> deepseek-v4-flash
The dashboard log shows the full chain: 'deepseek-v4-flash (hierarchical:
fast-flow (default (first target)) -> deepseek-v4-flash (default (first target)))'
Schema: Added logic_level (INTEGER) and primary_use (TEXT) columns
to model_groups table with auto-migration for existing databases.
Seed: Three new default groups:
heavy-logic (level 9) — Complex Coding, Logic, Agents
standard-pro (level 5) — General Assistant, Long Docs
fast-flow (level 2) — Classification, JSON, Basic Q&A
Admin API: INSERT/UPDATE handlers now accept and persist the new fields.
Dashboard: Table shows Level and Primary Use columns; form includes
both fields with appropriate inputs and placeholders.
Add Groups() method to Router so handleListModels can append model
group IDs (e.g. 'deepseek-auto', 'openai-auto') to the model list,
marked with owned_by: 'gophergate'. This lets clients discover and
use groups via the standard OpenAI /v1/models endpoint.
When using model groups (e.g. 'deepseek-auto'), the dashboard logged the
group name instead of the concrete resolved model (e.g. 'deepseek-reasoner').
Now:
- logRequest passes the resolved modelID (concrete) + modelGroup (group name)
- RequestLog struct has a new ModelGroup field (omitempty)
- Dashboard displays resolved model (via group) when a group was used
Files changed:
internal/server/logging.go - add ModelGroup field
internal/server/server.go - pass resolved modelID, capture modelGroup
static/js/websocket.js - show group annotation in Recent Activity
static/js/pages/overview.js - show group annotation in overview table
static/js/pages/monitoring.js - show group annotation in stream
30-second resty client timeout was killing long streaming responses
mid-generation. Models with large output windows (e.g. deepseek-v4-pro
at 384K max_tokens) routinely exceed 30s. Raised all providers to
10 minutes (Ollama already at 15min, unchanged). Circuit breaker
recovery timeout raised from 30s to 5min.