chore(sync): upstream v0.8.6 — server log viewer, context-window safety margin, xkiro provider, compression perf #7
Loading…
Reference in a new issue
No description provided.
Delete branch "upstream-v0.8.6"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Upstream release v0.8.6 (tashfeenahmed/freellmapi). Synced via branch+PR per ci skill — never stash-and-restore. Conflicts expected in shared server files (our fork diverged); Aexora-layer files untouched.
Supersedes the stacked #922/#923/#924 (each was a strict superset of the last, so merging more than one conflicts). Wires each platform at every entry point, not just the server registry. The original PRs registered the provider and the Platform type but skipped key-parser.ts and the Keys page, which left all of them unreachable: no way to add a key in the dashboard, and no way to import one from a .env or an auth.json. New test covers all four registration points together so a half-wired platform fails CI instead of shipping. Tencent Hunyuan is deliberately NOT included. hunyuan-lite was made free in May 2024, which is where the original PR text came from, but Tencent's current pricing doc no longer lists that model and the free allowance is now a one-time 1M-token grant valid for one year with no replenishment. That does not meet the recurring-free bar this catalog is for. Provider facts corrected against vendor docs while transcribing: - LongCat is Meituan (美团), not 面壁智能. - LongCat's launch free tier is 100K tokens/day; the 50M/day Flash-Lite figure was announced as a future plan, so it is not claimed here. - iFlytek publishes no token ceiling or QPS number for Spark Lite, so neither is asserted. - Volcengine's recurring daily per-model quota (2M tokens/day) is real and is the strongest free tier of the four. All four need Chinese real-name verification before a key serves traffic, so the Keys page labels say so up front rather than letting a user mint a key that 401s on every call (the ModelScope lesson, #581). LongCat is the one that accepts an overseas email signup. Catalog rows are NOT included here: model data ships through the signed hosted catalog, never a migration (catalog/ops/README.md).Claude Desktop's third-party gateway discovery fetched /v1/models over HTTP 200, got the full free catalog back, and still reported "found 0 models". It only accepts ids belonging to a Claude family, so a list of qwen3.5-397b / gpt-oss-120b style ids is rejected wholesale. /v1/messages has always served those ids: classifyClaudeFamily maps any claude-* alias onto the free pool through the operator's family map. Only discovery never mentioned them, so a picker had nothing to select. List one canonical id per family (opus, sonnet, haiku) alongside the real catalog. The display name says where the request actually goes ("Sonnet slot (auto-routed to a free model)", or the pinned model when the operator pinned one), so no entry reads as hosted Claude. They are listed only when some model can actually serve them, otherwise discovery would trade "0 models" for a model that 503s. Co-authored-by: Claude Opus 5 (1M context) <[email protected]>Server: - proxy: capture streaming `usage` from choice-bearing frames too, and emit it to the client exactly once (after the finish chunk). Providers that bundle usage onto the last content frame no longer fall back to the chars/4 estimate in accounting. - proxy: cap GitHub Models output tokens (new per-platform maxTokensCap) and trim oversized histories before a github dispatch instead of burning the hop on a guaranteed 400/413. Attempt-scoped: the next candidate still sees the full history. - ratelimit: throttle the rate_limit_usage sweep to once a minute (it ran an unindexed DELETE on every request), stop the in-memory windows growing unbounded on a healthy DB (memory is now the degraded-mode fallback only), cap 429 cooldown escalation at 10 minutes for routes with no published daily limits, and sweep expired cooldown rows once at startup. - router: re-rank legacy-priority chains to dense positions before adding 429/fail penalties — spaced priorities (e.g. holes left by disabled models) could exceed MAX_PENALTY and make demotion inert. - error-classify: walk err.cause (bounded, cycle-safe) so wrapped undici transport errors (ECONNRESET, EPIPE, socket hang up, UND_ERR_*) classify retryable and fail over instead of 502-ing; client/hedge aborts stay non-retryable. - gemini-wire: translate JSON-Schema type unions ("type": ["number", "null"]) to Gemini's type + nullable, and inline local $ref targets from $defs instead of dropping the whole subschema. - db-backup: upload Hugging Face backups through the commit API as base64 text — the old PUT to /resolve/ never persisted, so HF Spaces restored nothing on cold start. Legacy raw blobs still restore. - providers: Zhipu keys issued on the global z.ai console now validate and route via api.z.ai automatically; domestic keys are unaffected. - proxy lib: pass hostnames through to SOCKS5 proxies unresolved so rule-based clients (Clash) can route by domain. - crypto: maskKey no longer echoes keys of 8 chars or fewer. Docker: - drop the VOLUME declaration; persistence comes from the compose named volume or an explicit bind mount. The anonymous volume broke PaaS builds (Railway, Coolify, Dokploy, CapRover) and shadowed same-path bind mounts. 62 new/updated tests across 13 test files; full suite green. Co-authored-by: Claude Fable 5 <[email protected]>* fix(media): serve transcription usage at /api/media/usage The endpoint's modality enum only allowed 'image' and 'audio', so a request for 'transcription' 400'd even though the data model fully supports it: STT models live in media_models with modality='transcription' and their requests are logged with request_type='transcription' (logMedia writes request_type from the row's modality). The SQL was already parameterized by modality, so the fix is the enum plus a comment. The client's Audio tab already renders an STT section (models + detail page) but with no usage stats, because the usage query could never fetch transcription. Wire it up: fetch modality=transcription for the STT section and show the same UsageSummaryCard the TTS section uses. Adds a regression test pinning that transcription usage is served and that TTS requests for the same model_id are not counted. * fix(media): allow registering custom speech-to-text models The custom media upsert (/api/media/custom) restricted modality to 'image' and 'audio', so a custom STT endpoint could not be registered even though the data model, the /v1/audio/transcriptions handler and the usage endpoint all support 'transcription'. Same enum gap as the usage endpoint; the SQL and priority logic were already modality- agnostic. Adds a regression test registering a transcription model and asserting it lands under the endpoint key. * Only render the STT usage card when there are transcription rows, matching the sibling cards * Make custom speech-to-text endpoints work end to end Registration alone left a custom STT row unreachable: runTranscription dropped the row's key_id so getProviderCredential skipped platform custom, and callTranscriptionProvider had no custom adapter. Thread key_id through SttCandidate, add a custom adapter posting multipart to {baseUrl}/audio/transcriptions, add transcription to ApiKeyModel.kind, the Keys page kind labels and sort order, and the custom-type picker, with the two new i18n keys in all 60 locales. Tests cover the full path against a mocked upstream. --------- Co-authored-by: tashdroid <[email protected]> Co-authored-by: t <t@t> Co-authored-by: Tashfeen <[email protected]>Debugging failover meant shell access and docker logs. The dashboard now has a Logs page (Analytics gains the same chevron menu as Models, listing Analytics and Logs). Server: the existing installLogRedaction console wrapper gains a tap, so every line is redacted once and recorded once — no second console patch. A structured providerLog(level, message, {provider, model, event, requestId}) channel replaces the console lines for key auto-disable / key invalid (health), cooldown recovery (cooldown-probe), and model retirement, so those render as filterable chips rather than grep-bait. Entries live in a 1000-entry ring (all levels); warn/error also persist to a new server_logs table (migration 20260823_000001) with retention folded into the existing prune cycle (SERVER_LOGS_RETENTION_DAYS=7, SERVER_LOGS_MAX_ROWS=50000, 0 disables). Ids are monotonic and stable across restarts; the newest 200 persisted rows preload into the ring at boot. GET /api/logs (levels CSV, q, provider, sinceId cursor, limit, ring-wide counts) and POST /api/logs/clear sit behind the dashboard session like every other /api route. The viewer's own polling lines are dropped at ingest. The 32+ char redaction catch-all gains a low-entropy guard so a run of one repeated character is no longer swallowed. Client: Logs page with multi-toggle level pills (server-side filtering, ring-wide counts), provider select, debounced search, incremental sinceId polling into a 2000-entry buffer, direction-aware live tail with a jump-to-latest pill, visibility-gated auto-refresh with Pause, click-to-expand long messages, and confirm-to-clear. The /models nav split control is generalized to a navMenus lookup now shared by /analytics. 24 new i18n keys translated across all 60 locales (890 total). 26 server route tests + 26 client unit tests; verified in headless Chromium end to end (chevron nav, live entries from streamed traffic, error-only filtering, no-match search, clear), streaming and conversations e2e suites unregressed, full suite green. Co-authored-by: Claude Fable 5 <[email protected]>Pull request closed