From 11e39aa5cdb90396f09d647018c836baab23d21b Mon Sep 17 00:00:00 2001 From: Roo Code Date: Sat, 20 Dec 2025 19:40:47 +0000 Subject: [PATCH] fix: emit tool_call_end events for any finish_reason, not just tool_calls This fixes compatibility with OpenAI-compatible servers like ik_llama.cpp that may send finish_reason values like "stop" instead of "tool_calls" when tool calls are made. The change modifies NativeToolCallParser.processFinishReason() to emit tool_call_end events when ANY finish_reason is present and there are tracked tool calls that have started, ensuring tool calls are properly finalized regardless of the specific finish_reason value. Closes #9551 --- .../assistant-message/NativeToolCallParser.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/core/assistant-message/NativeToolCallParser.ts b/src/core/assistant-message/NativeToolCallParser.ts index 89bc1a9c340..6a784101822 100644 --- a/src/core/assistant-message/NativeToolCallParser.ts +++ b/src/core/assistant-message/NativeToolCallParser.ts @@ -146,17 +146,27 @@ export class NativeToolCallParser { /** * Process stream finish reason. - * Emits end events when finish_reason is 'tool_calls'. + * Emits end events when any finish_reason is present and there are tracked tool calls. + * + * This is important for compatibility with OpenAI-compatible servers like ik_llama.cpp + * that may not send finish_reason: "tool_calls" specifically. Some servers send + * "stop" or other finish reasons even when tool calls were made. */ public static processFinishReason(finishReason: string | null | undefined): ToolCallStreamEvent[] { const events: ToolCallStreamEvent[] = [] - if (finishReason === "tool_calls" && this.rawChunkTracker.size > 0) { + // Emit tool_call_end events when ANY finish_reason is present and we have tracked tool calls + // This ensures compatibility with various OpenAI-compatible servers including ik_llama.cpp + // that may not set finish_reason to "tool_calls" specifically + if (finishReason && this.rawChunkTracker.size > 0) { for (const [, tracked] of this.rawChunkTracker.entries()) { - events.push({ - type: "tool_call_end", - id: tracked.id, - }) + // Only emit end events for tool calls that have actually started + if (tracked.hasStarted) { + events.push({ + type: "tool_call_end", + id: tracked.id, + }) + } } }