From 53cafa4e38a0be279842cbbb473c432045989fd3 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 2 Nov 2025 22:37:45 -0500 Subject: [PATCH 1/2] Don't output newline-only reasoning --- src/api/providers/base-openai-compatible-provider.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/api/providers/base-openai-compatible-provider.ts b/src/api/providers/base-openai-compatible-provider.ts index 9ac00b0293c..86ddb25fdfd 100644 --- a/src/api/providers/base-openai-compatible-provider.ts +++ b/src/api/providers/base-openai-compatible-provider.ts @@ -124,8 +124,11 @@ export abstract class BaseOpenAiCompatibleProvider } } - if (delta && "reasoning_content" in delta && delta.reasoning_content) { - yield { type: "reasoning", text: (delta.reasoning_content as string | undefined) || "" } + if (delta && "reasoning_content" in delta) { + const reasoning_content = (delta.reasoning_content as string | undefined) || "" + if (reasoning_content?.trim()) { + yield { type: "reasoning", text: reasoning_content } + } } if (chunk.usage) { From 71f060eeff025b13a20df96d5ab8e72b5f739126 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Mon, 3 Nov 2025 04:09:30 +0000 Subject: [PATCH 2/2] test: add coverage for whitespace-only reasoning_content filtering --- .../base-openai-compatible-provider.spec.ts | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/api/providers/__tests__/base-openai-compatible-provider.spec.ts b/src/api/providers/__tests__/base-openai-compatible-provider.spec.ts index 548019020db..667083ee5f6 100644 --- a/src/api/providers/__tests__/base-openai-compatible-provider.spec.ts +++ b/src/api/providers/__tests__/base-openai-compatible-provider.spec.ts @@ -228,6 +228,106 @@ describe("BaseOpenAiCompatibleProvider", () => { }) }) + describe("reasoning_content field", () => { + it("should filter out whitespace-only reasoning_content", async () => { + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + next: vi + .fn() + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { reasoning_content: "\n" } }] }, + }) + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { reasoning_content: " " } }] }, + }) + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { reasoning_content: "\t\n " } }] }, + }) + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { content: "Regular content" } }] }, + }) + .mockResolvedValueOnce({ done: true }), + }), + } + }) + + const stream = handler.createMessage("system prompt", []) + const chunks = [] + for await (const chunk of stream) { + chunks.push(chunk) + } + + // Should only have the regular content, not the whitespace-only reasoning + expect(chunks).toEqual([{ type: "text", text: "Regular content" }]) + }) + + it("should yield non-empty reasoning_content", async () => { + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + next: vi + .fn() + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { reasoning_content: "Thinking step 1" } }] }, + }) + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { reasoning_content: "\n" } }] }, + }) + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { reasoning_content: "Thinking step 2" } }] }, + }) + .mockResolvedValueOnce({ done: true }), + }), + } + }) + + const stream = handler.createMessage("system prompt", []) + const chunks = [] + for await (const chunk of stream) { + chunks.push(chunk) + } + + // Should only yield the non-empty reasoning content + expect(chunks).toEqual([ + { type: "reasoning", text: "Thinking step 1" }, + { type: "reasoning", text: "Thinking step 2" }, + ]) + }) + + it("should handle reasoning_content with leading/trailing whitespace", async () => { + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + next: vi + .fn() + .mockResolvedValueOnce({ + done: false, + value: { choices: [{ delta: { reasoning_content: " content with spaces " } }] }, + }) + .mockResolvedValueOnce({ done: true }), + }), + } + }) + + const stream = handler.createMessage("system prompt", []) + const chunks = [] + for await (const chunk of stream) { + chunks.push(chunk) + } + + // Should yield reasoning with spaces (only pure whitespace is filtered) + expect(chunks).toEqual([{ type: "reasoning", text: " content with spaces " }]) + }) + }) + describe("Basic functionality", () => { it("should create stream with correct parameters", async () => { mockCreate.mockImplementationOnce(() => {