Jelajahi Sumber

fix: remove duplicate tool_call emission from Responses API providers (#11008)

Daniel 2 minggu lalu
induk
melakukan
17d3456e96
2 mengubah file dengan 12 tambahan dan 34 penghapusan
  1. 6 16
      src/api/providers/openai-codex.ts
  2. 6 18
      src/api/providers/openai-native.ts

+ 6 - 16
src/api/providers/openai-codex.ts

@@ -925,22 +925,12 @@ export class OpenAiCodexHandler extends BaseProvider implements SingleCompletion
 					}
 				}
 
-				// Only handle tool/function calls from done events (to ensure arguments are complete)
-				if (
-					(item.type === "function_call" || item.type === "tool_call") &&
-					event.type === "response.output_item.done"
-				) {
-					const callId = item.call_id || item.tool_call_id || item.id
-					if (callId) {
-						const args = item.arguments || item.function?.arguments || item.function_arguments
-						yield {
-							type: "tool_call",
-							id: callId,
-							name: item.name || item.function?.name || item.function_name || "",
-							arguments: typeof args === "string" ? args : "{}",
-						}
-					}
-				}
+				// Note: We intentionally do NOT emit tool_call from response.output_item.done
+				// for function_call/tool_call items. The streaming path handles tool calls via:
+				// 1. tool_call_partial events during argument deltas
+				// 2. NativeToolCallParser.finalizeRawChunks() at stream end emitting tool_call_end
+				// 3. NativeToolCallParser.finalizeStreamingToolCall() creating the final ToolUse
+				// Emitting tool_call here would cause duplicate tool rendering.
 			}
 			return
 		}

+ 6 - 18
src/api/providers/openai-native.ts

@@ -1241,24 +1241,12 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
 					}
 				}
 
-				// Only handle tool/function calls from done events (to ensure arguments are complete)
-				if (
-					(item.type === "function_call" || item.type === "tool_call") &&
-					event.type === "response.output_item.done"
-				) {
-					// Handle complete tool/function call item
-					// Emit as tool_call for backward compatibility with non-streaming tool handling
-					const callId = item.call_id || item.tool_call_id || item.id
-					if (callId) {
-						const args = item.arguments || item.function?.arguments || item.function_arguments
-						yield {
-							type: "tool_call",
-							id: callId,
-							name: item.name || item.function?.name || item.function_name || "",
-							arguments: typeof args === "string" ? args : "{}",
-						}
-					}
-				}
+				// Note: We intentionally do NOT emit tool_call from response.output_item.done
+				// for function_call/tool_call items. The streaming path handles tool calls via:
+				// 1. tool_call_partial events during argument deltas
+				// 2. NativeToolCallParser.finalizeRawChunks() at stream end emitting tool_call_end
+				// 3. NativeToolCallParser.finalizeStreamingToolCall() creating the final ToolUse
+				// Emitting tool_call here would cause duplicate tool rendering.
 			}
 			return
 		}