Repository navigation
Conversation
…njection
DuckDuckGo AI Chat has no native function calling. Serialize OpenAI tools[] into system prompt instructions (webTools.ts), parse <tool>{...}</tool> blocks from response text back into OpenAI tool_calls.
Pattern: same as deepseek-web (diegosouzapw#2820) and qwen-web (diegosouzapw#3252). When tools requested, buffers full response for tool-call parsing. When no tools, standard streaming passthrough.
There was a problem hiding this comment.
Code Review
This pull request introduces tool-calling support to the DuckDuckGo web executor by serializing requested tools into a system prompt and parsing tool calls from the response. Feedback on these changes highlights a critical issue where returning a standard JSON response when streaming is enabled with tools will break clients expecting an SSE stream; a code suggestion was provided to format the tool calls as SSE chunks. Additionally, the changes violate the repository style guide as no tests were added for the new production code under open-sse/.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (hasTools) { | ||
| const { content: cleanedContent, toolCalls } = parseToolCallsFromText( | ||
| fullContent, `call-${Date.now()}`, requestedTools | ||
| ); | ||
| const choice: Record<string, unknown> = { | ||
| index: 0, | ||
| message: { role: "assistant", content: cleanedContent }, | ||
| finish_reason: toolCalls ? "tool_calls" : "stop", | ||
| }; | ||
| if (toolCalls) { | ||
| (choice.message as Record<string, unknown>).tool_calls = toolCalls; | ||
| (choice.message as Record<string, unknown>).content = null; | ||
| } | ||
| return new Response(JSON.stringify({ choices: [choice] }), { | ||
| headers: { "Content-Type": "application/json" }, | ||
| }); | ||
| } |
There was a problem hiding this comment.
When streaming is true and hasTools is true, returning a standard JSON response with Content-Type: application/json will break clients that expect an SSE stream (text/event-stream). Instead, if streaming is true, we should format the tool calls (or plain text) as SSE chunks and return a text/event-stream response.
if (hasTools) {
const { content: cleanedContent, toolCalls } = parseToolCallsFromText(
fullContent,
"call-" + Date.now(),
requestedTools
);
if (streaming) {
const chunks: string[] = [];
if (toolCalls) {
chunks.push(JSON.stringify({
choices: [{
index: 0,
delta: { role: "assistant", content: null, tool_calls: toolCalls },
finish_reason: null
}]
}));
chunks.push(JSON.stringify({
choices: [{
index: 0,
delta: {},
finish_reason: "tool_calls"
}]
}));
} else {
if (cleanedContent) {
chunks.push(JSON.stringify({
choices: [{
index: 0,
delta: { role: "assistant", content: cleanedContent },
finish_reason: null
}]
}));
}
chunks.push(JSON.stringify({
choices: [{
index: 0,
delta: {},
finish_reason: "stop"
}]
}));
}
const sseData = chunks.map(function(c) { return "data: " + c + "\n\n"; }).join("") + "data: [DONE]\n\n";
return new Response(sseData, {
headers: { "Content-Type": "text/event-stream" },
});
} else {
const choice: Record<string, unknown> = {
index: 0,
message: { role: "assistant", content: cleanedContent },
finish_reason: toolCalls ? "tool_calls" : "stop",
};
if (toolCalls) {
(choice.message as Record<string, unknown>).tool_calls = toolCalls;
(choice.message as Record<string, unknown>).content = null;
}
return new Response(JSON.stringify({ choices: [choice] }), {
headers: { "Content-Type": "application/json" },
});
}
}| @@ -1,5 +1,6 @@ | |||
| import { BaseExecutor, type ExecuteInput } from "./base.ts"; | |||
| import { FETCH_TIMEOUT_MS } from "../config/constants.ts"; | |||
| import { serializeToolsToPrompt, parseToolCallsFromText } from "../translator/webTools.ts"; | |||
There was a problem hiding this comment.
According to the Repository Style Guide (Rule 9), you must always include tests when changing production code under open-sse/. Please add unit/integration tests for the new tool-call translation logic in the tests/ directory.
References
- Always include tests when changing production code (src/, open-sse/, electron/, bin/). (link)
| const requestedTools = bodyObj.tools; | ||
| const hasTools = Array.isArray(requestedTools) && requestedTools.length > 0; | ||
| const toolSystemPrompt = hasTools ? serializeToolsToPrompt(requestedTools) : ""; | ||
| const effectiveMessages = toolSystemPrompt |
There was a problem hiding this comment.
WARNING: Unconditionally prepending a system message can collide with an existing system message in the user's messages array. If messages[0].role === "system", the tool instructions will precede the user's intended system prompt, potentially causing the upstream model to process them in an unintended order or ignore the user's system message entirely. Consider merging tool instructions into the existing system message if one is present, or appending after any existing system message.
| } | ||
|
|
||
| if (streaming) { | ||
| if (streaming && !hasTools) { |
There was a problem hiding this comment.
WARNING: When stream !== false and tools are present, this condition bypasses the SSE streaming path entirely and returns a buffered non-streaming JSON response. The client's stream=true request is silently downgraded to a single JSON payload, breaking the streaming contract for clients that always expect SSE chunks. Tool calls should either be streamed as SSE delta events or the executor should reject stream=true with tools rather than silently changing the response format.
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Other Observations (not in diff)
Files Reviewed (1 file)
Fix these issues in Kilo Cloud Reviewed by step-3.7-flash-20260528 · 290,000 tokens |
|
Superseded by #3259 (consolidated PR with shared helpers) |
Description
DuckDuckGo AI Chat has no native function calling support. This PR adds tool-call translation using the same pattern as deepseek-web (#2820) and qwen-web (#3252).
How it works
Request side: Serialize OpenAI
tools[]into a system prompt instruction viaserializeToolsToPrompt()fromwebTools.ts. Prepends tool instructions to messages array.Response side: Parse
<tool>{...}</tool>blocks from model text response viaparseToolCallsFromText(). Returns proper OpenAItool_callsformat.When tools are requested, buffers full response for tool-call parsing. When no tools are requested, standard streaming passthrough is used (no behavior change).
Files Changed
open-sse/executors/duckduckgo-web.ts— +34 lines (tool injection + parsing)Tests
All 15 existing tests pass.