From 63e42047e3394afa424aee341229bca9e6ca7ea7 Mon Sep 17 00:00:00 2001 From: Paijo <14921983+oyi77@users.noreply.github.com> Date: Sat, 28 Mar 2026 01:12:51 +0700 Subject: [PATCH] fix: hasValuableContent explicit boolean returns for SSE streaming (#676) The hasValuableContent() function in streamHelpers.ts returned undefined instead of explicit false when checking empty delta chunks. This caused JavaScript type coercion issues where undefined !== '' evaluated to true, passing empty chunks through to clients. Fix: Replace implicit returns with explicit boolean returns using typeof checks and length comparisons for all content fields (content, reasoning_content, tool_calls, text, thinking, partial_json). Test: Added unit tests covering OpenAI, Claude, and Gemini format edge cases. Co-authored-by: oyi77 --- open-sse/utils/streamHelpers.ts | 32 +++++++------- tests/unit/streamHelpers.test.mjs | 72 +++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 tests/unit/streamHelpers.test.mjs diff --git a/open-sse/utils/streamHelpers.ts b/open-sse/utils/streamHelpers.ts index 12f8e00aa1..260cc93e66 100644 --- a/open-sse/utils/streamHelpers.ts +++ b/open-sse/utils/streamHelpers.ts @@ -39,26 +39,28 @@ export function parseSSELine(line) { // Check if chunk has valuable content (not empty) export function hasValuableContent(chunk, format) { // OpenAI format - if (format === FORMATS.OPENAI && chunk.choices?.[0]?.delta) { + if (format === FORMATS.OPENAI) { + if (!chunk.choices?.[0]?.delta) return false; const delta = chunk.choices[0].delta; - return ( - (delta.content && delta.content !== "") || - (delta.reasoning_content && delta.reasoning_content !== "") || - (delta.tool_calls && delta.tool_calls.length > 0) || - chunk.choices[0].finish_reason || - delta.role - ); + if (typeof delta.content === "string" && delta.content.length > 0) return true; + if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) + return true; + if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) return true; + if (chunk.choices[0].finish_reason) return true; + if (typeof delta.role === "string" && delta.role.length > 0) return true; + return false; } // Claude format if (format === FORMATS.CLAUDE) { const isContentBlockDelta = chunk.type === "content_block_delta"; - const hasText = chunk.delta?.text && chunk.delta.text !== ""; - const hasThinking = chunk.delta?.thinking && chunk.delta.thinking !== ""; - const hasInputJson = chunk.delta?.partial_json && chunk.delta.partial_json !== ""; - - if (isContentBlockDelta && !hasText && !hasThinking && !hasInputJson) { - return false; + if (isContentBlockDelta) { + const hasText = typeof chunk.delta?.text === "string" && chunk.delta.text.length > 0; + const hasThinking = + typeof chunk.delta?.thinking === "string" && chunk.delta.thinking.length > 0; + const hasInputJson = + typeof chunk.delta?.partial_json === "string" && chunk.delta.partial_json.length > 0; + if (!hasText && !hasThinking && !hasInputJson) return false; } return true; } @@ -73,7 +75,7 @@ export function hasValuableContent(chunk, format) { if (!parts || parts.length === 0) return false; // Filter out chunks where all parts have empty text const hasContent = parts.some( - (p) => (p.text && p.text !== "") || p.functionCall || p.executableCode + (p) => (typeof p.text === "string" && p.text.length > 0) || p.functionCall || p.executableCode ); return hasContent; } diff --git a/tests/unit/streamHelpers.test.mjs b/tests/unit/streamHelpers.test.mjs new file mode 100644 index 0000000000..8b7dcd1a50 --- /dev/null +++ b/tests/unit/streamHelpers.test.mjs @@ -0,0 +1,72 @@ +import { describe, it } from "node:test"; +import assert from "node:assert"; +import { hasValuableContent } from "../../open-sse/utils/streamHelpers.ts"; +import { FORMATS } from "../../open-sse/translator/formats.ts"; + +describe("hasValuableContent", () => { + describe("OpenAI format", () => { + it("returns true for content with text", () => { + const chunk = { choices: [{ delta: { content: "Hello" } }] }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.OPENAI), true); + }); + + it("returns false for empty delta", () => { + const chunk = { choices: [{ delta: {} }] }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.OPENAI), false); + }); + + it("returns false for delta with empty string content", () => { + const chunk = { choices: [{ delta: { content: "" } }] }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.OPENAI), false); + }); + + it("returns true for reasoning_content", () => { + const chunk = { choices: [{ delta: { reasoning_content: "thinking" } }] }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.OPENAI), true); + }); + + it("returns true for finish_reason", () => { + const chunk = { choices: [{ delta: {}, finish_reason: "stop" }] }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.OPENAI), true); + }); + + it("returns true for role delta", () => { + const chunk = { choices: [{ delta: { role: "assistant" } }] }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.OPENAI), true); + }); + }); + + describe("Claude format", () => { + it("returns true for content_block_delta with text", () => { + const chunk = { type: "content_block_delta", delta: { text: "Hello" } }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.CLAUDE), true); + }); + + it("returns false for empty content_block_delta", () => { + const chunk = { type: "content_block_delta", delta: {} }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.CLAUDE), false); + }); + + it("returns true for thinking blocks", () => { + const chunk = { type: "content_block_delta", delta: { thinking: "reasoning" } }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.CLAUDE), true); + }); + }); + + describe("Gemini format", () => { + it("returns true for content with text", () => { + const chunk = { candidates: [{ content: { parts: [{ text: "Hello" }] } }] }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.GEMINI), true); + }); + + it("returns false for empty parts", () => { + const chunk = { candidates: [{ content: { parts: [] } }] }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.GEMINI), false); + }); + + it("returns true for finishReason", () => { + const chunk = { candidates: [{ finishReason: "STOP" }] }; + assert.strictEqual(hasValuableContent(chunk, FORMATS.GEMINI), true); + }); + }); +});