Skip to content

Commit 53ab795

Browse files
mrubensroomote
authored andcommitted
Don't output newline-only reasoning (RooCodeInc#8990)
Co-authored-by: Roo Code <[email protected]>
1 parent 361532e commit 53ab795

File tree

2 files changed

+105
-2
lines changed

2 files changed

+105
-2
lines changed

src/api/providers/__tests__/base-openai-compatible-provider.spec.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,106 @@ describe("BaseOpenAiCompatibleProvider", () => {
228228
})
229229
})
230230

231+
describe("reasoning_content field", () => {
232+
it("should filter out whitespace-only reasoning_content", async () => {
233+
mockCreate.mockImplementationOnce(() => {
234+
return {
235+
[Symbol.asyncIterator]: () => ({
236+
next: vi
237+
.fn()
238+
.mockResolvedValueOnce({
239+
done: false,
240+
value: { choices: [{ delta: { reasoning_content: "\n" } }] },
241+
})
242+
.mockResolvedValueOnce({
243+
done: false,
244+
value: { choices: [{ delta: { reasoning_content: " " } }] },
245+
})
246+
.mockResolvedValueOnce({
247+
done: false,
248+
value: { choices: [{ delta: { reasoning_content: "\t\n " } }] },
249+
})
250+
.mockResolvedValueOnce({
251+
done: false,
252+
value: { choices: [{ delta: { content: "Regular content" } }] },
253+
})
254+
.mockResolvedValueOnce({ done: true }),
255+
}),
256+
}
257+
})
258+
259+
const stream = handler.createMessage("system prompt", [])
260+
const chunks = []
261+
for await (const chunk of stream) {
262+
chunks.push(chunk)
263+
}
264+
265+
// Should only have the regular content, not the whitespace-only reasoning
266+
expect(chunks).toEqual([{ type: "text", text: "Regular content" }])
267+
})
268+
269+
it("should yield non-empty reasoning_content", async () => {
270+
mockCreate.mockImplementationOnce(() => {
271+
return {
272+
[Symbol.asyncIterator]: () => ({
273+
next: vi
274+
.fn()
275+
.mockResolvedValueOnce({
276+
done: false,
277+
value: { choices: [{ delta: { reasoning_content: "Thinking step 1" } }] },
278+
})
279+
.mockResolvedValueOnce({
280+
done: false,
281+
value: { choices: [{ delta: { reasoning_content: "\n" } }] },
282+
})
283+
.mockResolvedValueOnce({
284+
done: false,
285+
value: { choices: [{ delta: { reasoning_content: "Thinking step 2" } }] },
286+
})
287+
.mockResolvedValueOnce({ done: true }),
288+
}),
289+
}
290+
})
291+
292+
const stream = handler.createMessage("system prompt", [])
293+
const chunks = []
294+
for await (const chunk of stream) {
295+
chunks.push(chunk)
296+
}
297+
298+
// Should only yield the non-empty reasoning content
299+
expect(chunks).toEqual([
300+
{ type: "reasoning", text: "Thinking step 1" },
301+
{ type: "reasoning", text: "Thinking step 2" },
302+
])
303+
})
304+
305+
it("should handle reasoning_content with leading/trailing whitespace", async () => {
306+
mockCreate.mockImplementationOnce(() => {
307+
return {
308+
[Symbol.asyncIterator]: () => ({
309+
next: vi
310+
.fn()
311+
.mockResolvedValueOnce({
312+
done: false,
313+
value: { choices: [{ delta: { reasoning_content: " content with spaces " } }] },
314+
})
315+
.mockResolvedValueOnce({ done: true }),
316+
}),
317+
}
318+
})
319+
320+
const stream = handler.createMessage("system prompt", [])
321+
const chunks = []
322+
for await (const chunk of stream) {
323+
chunks.push(chunk)
324+
}
325+
326+
// Should yield reasoning with spaces (only pure whitespace is filtered)
327+
expect(chunks).toEqual([{ type: "reasoning", text: " content with spaces " }])
328+
})
329+
})
330+
231331
describe("Basic functionality", () => {
232332
it("should create stream with correct parameters", async () => {
233333
mockCreate.mockImplementationOnce(() => {

src/api/providers/base-openai-compatible-provider.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,11 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
124124
}
125125
}
126126

127-
if (delta && "reasoning_content" in delta && delta.reasoning_content) {
128-
yield { type: "reasoning", text: (delta.reasoning_content as string | undefined) || "" }
127+
if (delta && "reasoning_content" in delta) {
128+
const reasoning_content = (delta.reasoning_content as string | undefined) || ""
129+
if (reasoning_content?.trim()) {
130+
yield { type: "reasoning", text: reasoning_content }
131+
}
129132
}
130133

131134
if (chunk.usage) {

0 commit comments

Comments
 (0)