Commit Graph

17 Commits

Author SHA1 Message Date
31b1ed2c96 feat(gateway): collapse multi-tier failure details into short client message
Clients saw the full per-tier chain error including upstream HTML WAF
pages and JSON quota payloads. The response now carries one capped
one-line reason per tier (quota/cooling skips preserved); full detail
remains in rec.Err / stats API and is logged server-side.
2026-08-24 16:02:31 +08:00
bb3af3bdb3 feat(gateway): pass through upstream finish_reason end-to-end
The gateway hardcoded "stop" on every terminating stream chunk, so
tool-call rounds reported finish_reason=stop and length caps were
invisible to clients. UnifiedChunk now carries finish_reason; adapters
emit it (with empty-string finish reasons like sensenova treated as
non-terminal), standardSSEChunk passes it through for un-adapted
upstreams, [DONE] no longer emits a duplicate reason-less done chunk,
and both streaming paths emit the real reason with "stop" as fallback.

Also vendor sensenova/agentrouter adapters into the repo: they were
WebUI-only uploads and a deploy sync silently removed them while live
AUTO-chain slots still referenced them.
2026-08-24 15:05:50 +08:00
3069cfce4e fix(gateway): pass through upstream token usage in streams for all adapters
The prior usage-passthrough fix only covered openai/opencode; the same
empty-choices+usage drop bug remained in the 5 sibling OpenAI-compatible
adapters, and non-OpenAI providers (anthropic/gemini/ollama) never surfaced
streaming usage at all.

- deepseek/github/groq/kimicode/mistral: preserve usage on empty-choices
  chunks and attach it to normal chunks (same pattern as openai.lua)
- anthropic: emit usage from message_start (prompt) and message_delta
  (completion); gateway merges split usage additively
- gemini: read usageMetadata in the stream path
- ollama: fix non-streaming key (usage -> token_usage, matches
  UnifiedResponse json tag) and read prompt_eval_count/eval_count;
  surface counts from the done stream chunk
- gateway: mergeUsage combines usage across chunks (non-zero fields win,
  total recomputed from prompt+completion) so split usage doesn't lose
  the prompt half; single-chunk case (OpenAI) preserved exactly
- usage-only chunks: done=false (no redundant terminal stop), matching
  the Go fallback standardSSEChunk
2026-08-18 23:24:01 +08:00
83e6d88813 feat(gateway): pass through exact upstream token usage in streams
Streaming responses now carry the upstream's real token usage instead of
gateway estimates:
- UnifiedChunk gains an optional Usage field; adapters (opencode, openai)
  extract usage from upstream stream chunks (including the final chunk with
  empty choices) and pass it through.
- standardSSEChunk preserves usage for passthrough adapters.
- Gateway emits the exact usage in the final stream chunk when available,
  falling back to estimates only when the upstream provided none.

Non-streaming usage was already fixed to emit OpenAI-standard keys.
2026-08-18 19:04:44 +08:00
6f1c806591 fix(gateway): emit OpenAI-standard token usage in responses
- TokenUsage.MarshalJSON now emits both standard (prompt_tokens,
  completion_tokens, total_tokens) and legacy (prompt, completion, total)
  keys, so OpenAI-compatible clients (DSH, DevEco Code, etc.) can read
  token usage.
- ChatChunk gains an optional Usage field; stream responses now send a
  final usage chunk (empty choices) before [DONE].

Refs: usage not visible in clients because the gateway serialized only the
internal short keys and never emitted a streaming usage chunk.
2026-08-18 18:14:37 +08:00
2bc1d0e67a feat: opencode zen adapter + first-run config generation, fix stats/stream bugs
- adapters/opencode.lua: opencode.ai zen free pool adapter — sends the
  opencode client User-Agent (zen fingerprints clients by UA; non-official
  clients hit FreeUsageLimitError); pairs with api_key: public
- config: no config file ships in the repo; first run generates a default
  config at the -config path with a random admin key, loopback listen and a
  keyless zen source (config.EnsureDefault); remove config.example.yaml
- lua: seed bundled adapters from the embedded FS instead of a hardcoded
  name list
- ui: widen model kind select (chat was clipped to 'cha')
- phase 5 bugfixes: stats ms/s bucket mixing, cleanScopes nil, ctx.Err
  guards, direct-path ModelAvailable, empty stream body failure,
  bestImageModel rewrite, transform failure recording, Core.mu, timer,
  effective model for tool-calls
2026-08-13 12:25:07 +08:00
20d2268546 fix: close P10 audit items — P10-1 sources API admin guard, P10-2 scope prefix strip, P10-3 runtime source timeout w/ stream-safe clients
- api.go: handleSourcesAPI now requires admin role (GET leaks upstream api_keys, POST/DELETE mutate routing)
- chat.go: hasScopeModel made a Gateway method that strips source-model/:// prefix strictly via Registry.EffectiveModel (only when the prefix names a real source serving the bare model) so dash-bearing ids like deepseek-v4-flash-free are never corrupted; +TestHasScopeModelWithSourcePrefix
- config.go: DefaultSourceTimeout/QueueTimeout/Concurrency constants shared by YAML ApplyDefaults and runtime sources
- core.go: mergedSources applies the same defaults to runtime sources (JSON never persisted timeout fields); a dead upstream can no longer hold a concurrency slot forever
- provider.go: split non-streaming client{Timeout} vs stream client{} sharing a Transport with ResponseHeaderTimeout, so long SSE bodies are not cut by client.Timeout; ChatStream uses doRawStream
- plan.md: mark P4-4/5/6 done, record P4-7/8 (tier-order, audit export, UI key view, zen upstream diagnosis)
- online verified: user key -> /api/sources 403 (GET+POST), admin 200, AUTO stream/non-stream healthy
2026-08-11 15:22:20 +08:00
854b3e2e37 fix: AUTO tier order ascending + stats/CSV export completeness + UI key-view toggle & exit affordance
- scheduler: BuildChain sorts tiers ascending so tier 1 (highest priority) is tried first; previously descending inverted the chain (P10-1..P10-5)
- stats/api: handleStatsAPI limit→20000; CSV reads full audit via new Stats.AuditRecords (includes *.old rotation); key_names mapped by keyID() masked key; non-admin filter and keys-csv use masked keys
- server: ring buffer NewStats(10000)
- ui: dash-row card scroll area moved to table container (fix overflow below card); key view toggle (re-click same key returns to global) + kpi-exit affordance; rec-exit span
- chat: chain-failure record now surfaces first failed tier; AUTO comment sync
2026-08-11 13:36:23 +08:00
88802f9ef6 feat: AUTO chain rewrite — silent failover+busy skip+pref round-robin+503 tier summary; chain edits reset slot cooldowns (P0/P1); stats by_status + audit jsonl rotation; UI priority-page health badges & status-code card; ctx-menu capture-phase close (outside-press guard); main.go ops warnings; local bundled-Lua verified tests (3 latent bugs fixed); plan.md 2026-08-11 00:03:11 +08:00
41c9b0e14a feat: source-model routing disambiguation (source-model/:/ prefix); same-tier round-robin load balancing; public_base_url for copy config; fmtTok(B/M/K) unit scaling; status column reorder (reachability first); drop emoji from seed-warn modal; fix tests for first-run adapter seeding; install lua5.1 dev lib 2026-08-10 12:55:22 +08:00
d48b993010 feat(auto): AUTO-only priority chain with tiered slots + live source probing; CSV export w/ key names; audit persistence; fix prompt token accounting & deepseek thinking 2026-08-10 00:10:19 +08:00
859d310ad3 feat(auto): quota-aware AUTO slot scheduling (model tiers, hour/week/month resets) + key scope periods; fix key/slot create/delete UX 2026-08-09 11:07:38 +08:00
3408c9cb1f feat(keys): role-based gateway keys with admin management UI and per-user model scope 2026-08-09 10:01:40 +08:00
dec03238dd feat(stats): gateway request stats/audit API + polish scratch sort animations (link chain, reset, cleanup) 2026-08-08 13:34:40 +08:00
5d50b69153 fix: tool call anchor & wire format, streaming chunk passthrough, WebUI narrow-screen, docs bilingual 2026-08-08 11:26:58 +08:00
ccbcede581 feat: LuaJIT worker-pool VM, multimodal/disable-thinking passthrough, WebUI redesign, DeepSeek V4 2026-08-07 13:34:16 +08:00
f7f76e097d feat: ModelRouter — unified OpenAI-compatible multi-source LLM gateway
- Lua adapters per upstream (transform_request/response/stream_chunk, build_headers signing hooks)
- AUTO priority routing with per-model kind (chat/image), explicit source/model routing
- Per-source concurrency caps with queueing, exponential backoff, AUTO failover
- OpenAI-compatible API: chat completions, SSE streaming, image generations, models
- Gateway key auth, web UI for adapter/source management, runtime persistence
- e2e test running the real binary against mocked upstreams
2026-08-05 15:25:47 +08:00