Repository navigation
fix(stream): harden streaming external tool calls, error wire shapes, and 503 retries - #34
Conversation
…l tool calls - Track per-part stream state with sequence-based snapshot and delta reconciliation - Resolve a single authoritative stream source across Chat, Messages, and Responses - Add targeted single snapshot repair when raw tool markup is malformed - Derive visible assistant text in one pass under external tool buffering - Replace invalid JSON placeholder in external tool reminder with valid JSON - Add deterministic collector, source resolution, and route snapshot regression tests
…r termination - Write bare keepalive comments without trailing newline to prevent phantom empty SSE events - Add X-Accel-Buffering: no header across all streaming endpoints - Close open Anthropic content and thinking blocks before emitting stream errors - Ensure outer catch handlers across all four streaming routes close open sockets with res.end() - Finalize natural event-stream iterator completion and detect cut streams in collector - Preserve protocol-specific streaming error wire shapes without stray [DONE] markers - Add comprehensive Phase 2 regression tests
…, and security hardening - Add pure HTTP error classifier and terminal 4-argument Express error middleware - Mount auth middleware before body parsing to block unauthenticated payload processing - Disable Express X-Powered-By header and add NODE_ENV=production in runtime container - Set explicit 404 status code and code in resolveRequestedModel - Add comprehensive HTTP error, security non-leakage, and protocol-shaped wire tests
… fast-fail model resolution - Add transient 503 retry loops on Responses stream, Messages stream, and Interactions before output - Preserve multi-turn continuation parent sessions during stream retry - Enforce fail-closed termination after partial output has streamed - Recognize data.statusCode: 503 and Endpoint is unavailable in isTransientUpstreamError - Cache provider models list for 30s with reconnect and error invalidation - Resolve requested models before acquiring the global lock to fail fast on 404 - Add comprehensive streaming retry, lock-free fast fail, and provider caching tests
…ease changes - Align opencode-ai pin in AI workflows to 1.18.35 matching Dockerfile - Add automated verification in tests/dockerfile-pin.test.js - Document all four phases of streaming, error, retry, and hardening fixes in CHANGELOG.md
Verdict matrix
Sensitive paths
Deterministic pre-checks
gateway_safetyVERDICT: APPROVEThe changes harden stream lifecycle management, external tool assembly, and error handling across all protocols while maintaining all [provider] safety invariants, tenant isolation, and wire protocol shapes. BLOCKING(None) SHOULD_FIX(None) NITS
Checklist Verification Summary
api_compatAPI Compatibility ReviewI'm reviewing this large PR against the multi-protocol wire-shape compatibility checklist. The diff is truncated, so I'll note where evidence is incomplete. VERDICT: NEEDS_CHANGESBLOCKING:
SHOULD_FIX:
NITS:
Test Gaps:
Cross-Protocol Regression Risks:
docs_configVerdict: APPROVEThe PR maintains documentation consistency, respects the EN-first convention for root documentation and changelog entries, and does not alter or break the six-way config matrix (no new environment configuration keys were introduced; all idempotency and cache limits are in-memory constants). BLOCKING(None) SHOULD_FIX(None) NITS
Config Matrix Status
FINAL_VERDICT: COMMENT Session: response_id=resp_6W7caqDBPI331e8PqtnXsAM,resp_req_vrtx_011CgLkzFC3daC5etqdLYUnE,resp_aW7careYI8aZ0-kPx4Sj4A0 (chain via previous_response_id, 30min TTL). model=[model] provider=[provider]. |
PR #34 — Independent Deep Re-review(2026-10-24,更新原留言)Reviewed head: 本次從最新原始碼獨立複核前一輪 2 項 blocker、 ✅ 前次 Blocking #1:Jest failed — RESOLVED最新 CI run #40078760373 = SUCCESS。 上次 ✅ 前次 Blocking #2:Pre-submit 暫時性 503 被 sticky-cache — RESOLVED目前 這次補上 actual route-level 回歸( ✅ 本 PR 的跨 HTTP continuation / streaming 核心行為複核
|
…d eliminate stale raw tool calls
…llback state and error classification
…s, and coalesce provider caching
…, and error shape fixes
…fered text divergence
…ct and normalize changelog
…d across all routes
…revent transcript duplication
… transcript duplication
…and prevent legacy delta duplication
…rser, and map 415 error
…l on retry, and clean reconnect listeners
…vation, and lifecycle hardening
… poll count assertion
…g whitespace, and harden socket closure
Summary
This PR addresses all known gateway issues identified during deployed container testing and deep-dive investigations, adapting best practices from
/home/samson1357924/projects/magpie.1. Phase 1 — Tool-Call Streaming Assembly & Authoritative Snapshots
src/stream/collector.ts): Replaces lossy delta-only stream accumulation with sequence-based per-part reconciliation between incremental deltas and full snapshots (message.part.updatedpart.text,message.updatedinfo.parts).src/tool-runtime/stream-source.ts): Resolves a single authoritative source group across Chat, Messages, and Responses, eliminating falsemalformed_external_tool_callerrors caused by validating raw deltas and snapshots independently.shouldBufferExternalStream, visible text is stripped once from the authoritative source, completely preventing unclosed XML tags or raw tool-call JSON from leaking into client text deltas.{...}with valid JSON{}inbuildExternalToolsReminder.2. Phase 2 — SSE Framing, Block Closing & Stream Closure
: comment\n(instead of\n\n), eliminating phantom empty SSE events across all routes.X-Accel-Buffering: noto all streaming route responses.messages.ts): Opentextandthinkingblocks are cleanly closed viacontent_block_stopbefore emittingevent: error.res.headersSentand invokeres.end()with wire-compliant error framing.cut: true), and clears all timers.[DONE]markers on Chat, Messages, or Interactions error streams.3. Phase 3 — Body-Parser Error Handler, Auth-Before-Parse, & Hardening
src/errors/http.ts,system.ts): Added pure classifier and 4-argument Express error handler mappingentity.parse.failed(400),entity.too.large(413),charset/encoding.unsupported(415), andentity.verify.failed(400) to protocol-shaped JSON errors without leaking stack traces or internal paths.src/proxy.ts): Mounted auth verification before body parsing so unauthenticated requests are rejected with 401 immediately without consuming or parsing large/malformed payloads.X-Powered-Byheader and addedENV NODE_ENV=productionin the runtime Dockerfile stage.resolveRequestedModelexplicitly throws withstatusCode = 404andcode = 'model_not_found'.4. Phase 4 — 503 Streaming Retries, Concurrency & Caching
previous_response_idpreserve the parent multi-turn session on retry and delete only ephemeral attempt sessions.src/errors/upstream.ts): Addeddata['statusCode']and/endpoint is unavailable/itoisTransientUpstreamError.chat.ts,messages.ts): Moved model resolution outside the process-globallock(), allowing unknown model requests to fail fast with 404 in milliseconds without waiting on concurrent streaming requests.src/proxy.ts): Added 30s TTL cache forgetProvidersListwith error and backend reconnect invalidation.5. Phase 5 — CI Alignment & Documentation
.github/workflows/ai-fix.yml,ai-review.yml, andai-scan.ymltoopencode-ai@1.18.35matchingDockerfileARG OPENCODE_VERSION=1.18.35.tests/dockerfile-pin.test.jsto validate workflow pins againstPINNED_VERSION.CHANGELOG.mdwith all changes.Verification
npm run typecheck: Passed (0 errors)npm run build: Clean build todist/npm test -- --runInBand: 36 test suites passed, 869 tests passed, 0 failures.gitignorepreserved untouched, temporary test artifacts omitted.