fix(streaming): fail loudly when a [DONE]-less stream cuts tool-call arguments (#184) - #188
Conversation
|
The direction here is right and the tests are solid @Fahad090NP. traced it through the code though and found a gap that stops the main claim from landing. the when your new so the row in your behavior matrix that says "truncation error, user resends ✅" doesn't hold. both things happen: the corrupted tool call gets emitted, and the error popup shows on top of it. the tool still executes with the fix is small: guard the flush. either check second thing, smaller: one housekeeping note: repo convention is an entry under happy to merge once the flush guard is in. |
… as complete (review ltmoerdani#188) - flushRemainingToolCalls skips flushing when pending calls are incomplete (empty/truncated JSON) — previously the finally-block emitted a corrupted LanguageModelToolCallPart before the truncation error surfaced - hasCompletePendingCalls: empty arguments string now counts as COMPLETE — gateways legitimately send no args delta for no-param tools; Copilot's own loop normalizes arguments === '' to '{}' (toolCallingLoop.ts:1909). Only non-empty invalid JSON counts as truncated - docs/issues/80 entry per repo convention
|
All three points addressed: 1. Flush guard — fixed. 2. Empty-args assumption — you were right to push on it, and I verified against a real reference. Copilot's own tool-calling loop ( 3. Housekeeping — done. Full gate green (all 7 checks + 6 accumulator tests). Fi Amanillah |
…arguments (ltmoerdani#184) ltmoerdani#187 made content-delivered streams without [DONE]/finish_reason return successfully (Muse Spark). But for tool-calling streams that means a stream cut mid-arguments flushes a tool call whose truncated JSON silently becomes {} input via parseToolInput — the tool executes with corrupted input. - ToolCallAccumulator.hasCompletePendingCalls(): every named pending call must carry non-empty, parseable JSON-object arguments - OpenAiResponseExtractor.hasCompletePendingToolCalls() exposes it - engine: hasCompletePendingWork option — when it returns false on an unterminated stream, throw the truncation error instead of returning success (responses adapter wired; other transports unaffected) - 5 new unit tests
… as complete (review ltmoerdani#188) - flushRemainingToolCalls skips flushing when pending calls are incomplete (empty/truncated JSON) — previously the finally-block emitted a corrupted LanguageModelToolCallPart before the truncation error surfaced - hasCompletePendingCalls: empty arguments string now counts as COMPLETE — gateways legitimately send no args delta for no-param tools; Copilot's own loop normalizes arguments === '' to '{}' (toolCallingLoop.ts:1909). Only non-empty invalid JSON counts as truncated - docs/issues/80 entry per repo convention
b4d9ae3 to
49a3d22
Compare
|
Rebased onto current |
Fixes #184
PEACE BE UPON YOU
Summary
Follow-up to #187 (merged), reported in #184: GPT 5.6 luna with tool calling ends the Responses stream without
[DONE]/finish_reasonafter 257 KB / 93 events. #187 made such streams return successfully — but for a tool-calling stream that was cut mid-arguments, that success flushes a tool call whose truncated JSONparseToolInputsilently coerces to{}. The tool then executes with corrupted/empty input, and nothing tells the user.Fix
src/toolCallAccumulator.ts— newhasCompletePendingCalls(): every named pending call must carry non-empty arguments that parse as a JSON object. Empty args = the stream was cut before any delta arrived (OpenAI-style streams always send at least one args delta).src/transports/extractors.ts—OpenAiResponseExtractor.hasCompletePendingToolCalls()exposes it.src/transports/streamParts.ts+engine.ts— new optionalhasCompletePendingWorkoption. In the content-delivered success path (fix(streaming): don't throw after content delivered when [DONE] missing (Muse Spark) #187), when the callback returnsfalse, the engine throws the truncation error instead of returning success — failing loudly so the user resends, instead of executing a broken tool call.Behavior matrix
[DONE], text-only content delivered[DONE], tool calls with complete JSON args[DONE], tool-call args truncated mid-JSON{}input ❌Verification
npm run lintall 7 checks green; 5 new unit tests forhasCompletePendingCalls.Fi Amanillah