fix(routers): hardening batch - adapter, catalog route, and stale selections

↗ view on GitHub · Amal · 2026-08-18 · 3d43970e

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.

⬇ Download capture-commit-3d43970e.md