fix(routers): hardening batch - adapter, catalog route, and stale selections
Five smaller router-stream fixes that share one theme: the edges of the
feature (proxies, truncated streams, renamed ids, removed selections)
must degrade predictably instead of silently doing the wrong thing.
1. CATALOG ROUTE HONORS OPENROUTER_BASE_URL
The chat adapter already routes through the override (proxies, test
doubles, region mirrors); the catalog route hardcoded the public URL,
so a proxied deployment listed models it could not reach. The route
now reads the env per request, exactly like the Vercel route.
2. TRUNCATED TOOL-CALL ARGUMENTS FAIL THE STREAM
WHY THIS MATTERS: tool-call arguments stream as JSON fragments; when
the connection dies mid-call the fragment can never parse. Coercing
it to {} EXECUTED the tool - a side-effecting document edit could run
with empty input because a proxy hiccuped. parseToolCalls now throws
a descriptive error into the same failure path as a mid-stream
{"error"} chunk. Absent arguments ("" - parameter-less tools) still
mean {}: only present-but-unparseable input is fatal.
3. FLUSH THE DECODER AND RESIDUAL SSE LINE AT END-OF-STREAM
HOW SSE FRAMING BREAKS: the parser split on "\n", so a proxy that
closes the connection without a trailing newline stranded the final
"data:" line - and its content delta - in the buffer. On done, the
loop now flushes the TextDecoder (multi-byte sequences included) and
processes the residual line through the same extracted line handler.
4. LEGACY_MODEL_IDS KEEPS RENAMED STATIC IDS WORKING
gemini-3.1-flash-lite-preview → gemini-3.5-flash-lite and
gpt-5.4-lite → gpt-5.4-mini are mapped on read in backend
resolveModel, in the settings/models page, and in useSelectedModel -
a preference saved before the rename resolves to the model's new id
instead of silently degrading to the fallback.
5. STALE ROUTER SELECTIONS RESET TO THE DEFAULT
useSelectedModel now takes the loaded router lists; a stored
`openrouter/*`/`vercel/*` selection no longer in them resets to the
default model (persisted), mirroring how invalid first-party ids are
replaced on read - the composer no longer silently sends an id the
backend will reject-and-degrade. While the lists are loading, the
selection is left untouched.
Tests (each fails on the ported code, verified via stash - 4 backend +
3 frontend failures): models.test.ts (base-URL override),
openrouter.test.ts (truncated arguments reject and runTools is never
called; final newline-less delta is delivered), llmModels.test.ts
(legacy ids resolve to their new ids), settings models page.test.tsx
(legacy titleModel renders the renamed option), useSelectedModel.test.ts
(missing router selection resets, present one survives, loading leaves
it alone).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Repository | open-legal-products/mike |
|---|---|
| Author | Amal <mamalanand3@gmail.com> |
| Authored | |
| Committed | |
| Parents | 728c4a9f |
| Stats | 12 files changed , +453 , -121 |
| Part of | Harden model-router selection and failure handling |
Capture this commit into my fork
Download a Markdown prompt that tells Claude how to port this
exact commit into your working tree. Run it via
claude -p < capture-commit-3d43970e.md
from inside the repo you want the change in.