From d9b3ce266f5bd7d96727f645eb406c45e8bb55af Mon Sep 17 00:00:00 2001 From: Dizzle <112548150+maxmad64bis@users.noreply.github.com> Date: Sat, 22 Aug 2026 19:38:28 +0200 Subject: [PATCH] test(stream): direct coverage + comment for splitConcatenatedToolCallArguments (#11043 followup) (#11135) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green. Test-only followup to #11043 — 3 direct tests for splitConcatenatedToolCallArguments plus the index-normalization comment, exactly the two review nits. Thank you @maxmad64bis! --- open-sse/utils/streamPayloadCollector.ts | 1 + tests/unit/stream-payload-collector.test.ts | 31 +++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/open-sse/utils/streamPayloadCollector.ts b/open-sse/utils/streamPayloadCollector.ts index ea1b2e658e..31b9e818f4 100644 --- a/open-sse/utils/streamPayloadCollector.ts +++ b/open-sse/utils/streamPayloadCollector.ts @@ -337,6 +337,7 @@ function createOpenAIReducer(fallbackModel?: string | null): SummaryReducer { // same-name tool_calls) into its own separate tool_calls entries. const finalToolCalls: ToolCall[] = []; let nextIndex = 0; + // Normalize tool_call indexes to contiguous 0-based (OpenAI contract). for (const tc of mergedToolCalls) { const splitArgs = splitConcatenatedToolCallArguments(tc.function.arguments); if (!splitArgs) { diff --git a/tests/unit/stream-payload-collector.test.ts b/tests/unit/stream-payload-collector.test.ts index e8869bb8bf..63b96c5eaf 100644 --- a/tests/unit/stream-payload-collector.test.ts +++ b/tests/unit/stream-payload-collector.test.ts @@ -2,6 +2,7 @@ import test from "node:test"; import assert from "node:assert/strict"; const collector = await import("../../open-sse/utils/streamPayloadCollector.ts"); +import { splitConcatenatedToolCallArguments } from "../../open-sse/utils/streamPayloadCollector.ts"; test("compactStructuredStreamPayload returns null for null input", () => { assert.equal(collector.compactStructuredStreamPayload(null), null); @@ -413,3 +414,33 @@ test("#9315: getSummary() returns undefined when no format was configured (unaff c.push({ choices: [{ index: 0, delta: { content: "hi" } }] }); assert.equal(c.getSummary(), undefined); }); + +test("splitConcatenatedToolCallArguments — two back-to-back JSON objects", () => { + const a = JSON.stringify({ tool: "x", args: "1" }); + const b = JSON.stringify({ tool: "y", args: "2" }); + const out = splitConcatenatedToolCallArguments(a + b); + assert.deepEqual(out, [a, b]); // >=2 valid values -> split (array of parts) +}); + +test("splitConcatenatedToolCallArguments — nested object + escaped quotes stay single JSON", () => { + const a = JSON.stringify({ a: 'he said "hi"', b: { c: 1 } }); + const single = a; // a is ONE valid JSON object -> no split + const out = splitConcatenatedToolCallArguments(single); + assert.equal(out, null); // single valid JSON -> untouched (null) +}); + +test("splitConcatenatedToolCallArguments — braces/quotes inside strings exercise escaped scanner", () => { + // Two valid JSON values whose string bodies contain braces and escaped quotes. + // Concatenated they reach the inString/escaped state machine (not the JSON.parse + // fast path), so this covers the case the owner asked about. + const a = JSON.stringify({ cmd: 'echo "}{" ; x' }); + const b = JSON.stringify({ cmd: "{[not json]}" }); + const out = splitConcatenatedToolCallArguments(a + b); + assert.deepEqual(out, [a, b]); // >=2 valid values -> split into parts +}); + +test("splitConcatenatedToolCallArguments — top-level array is single value", () => { + const arr = JSON.stringify([{ tool: "x" }, { tool: "y" }]); + const out = splitConcatenatedToolCallArguments(arr); + assert.equal(out, null); // one value boundary (array) -> not split +});