mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-01 12:52:11 +03:00
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
This commit is contained in:
committed by
GitHub
parent
db358991f2
commit
ec9c93fe29
@@ -12,6 +12,7 @@ _In development — bullets added per PR; finalized at release._
|
||||
|
||||
- **db-backups**: make the database-import size cap configurable via `OMNIROUTE_DB_IMPORT_MAX_MB` (default 100 MB, 4 GB ceiling) so backups larger than 100 MB can be restored; error message now points to the env var and to VACUUM (#4719).
|
||||
- **Onboarding**: add the missing `onboarding.tiers` step-title translation so the setup wizard no longer crashes with `MISSING_MESSAGE: onboarding.tiers` (#4698).
|
||||
- **deepseek-web**: fold `role:"tool"` results into the single-prompt transcript (`messagesToPrompt`) so tool outputs reach the model instead of being silently dropped when a follow-up turn omits the `tools[]` array (#4712).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -114,8 +114,9 @@
|
||||
"open-sse/executors/claude-web.ts": 1057,
|
||||
"open-sse/executors/codex.ts": 1449,
|
||||
"open-sse/executors/cursor.ts": 1453,
|
||||
"open-sse/executors/deepseek-web.ts": 1125,
|
||||
"open-sse/executors/deepseek-web.ts": 1148,
|
||||
"_rebaseline_2026_06_22_4644_deepseek_web_tools": "PR #4644 (BugsBag/robust deepseek-web tool-call parsing): open-sse/executors/deepseek-web.ts 1117->1125 (+8). The new agentic tool-call path emits surrounding text + reasoning before tool_calls and swaps to the dedicated deepseekWebTools.ts parser; the +8 lines are cohesive wiring at the existing transformSSE chokepoint (the parser itself lives in the new deepseekWebTools.ts file, already under cap). The PR's own fast-gate (PR->release) does not run check:file-size, so this surfaced only at release reconcile. Covered by tests/unit/deepseek-web-tools-variants.test.ts + deepseek-web-tools-execute.test.ts.",
|
||||
"_rebaseline_2026_06_23_4712_deepseek_web_tool_results": "PR for #4712 (deepseek-web drops role:tool): open-sse/executors/deepseek-web.ts 1125->1148 (+23). messagesToPrompt() now folds role:\"tool\" results into the single-prompt transcript (recovering the tool name from the preceding assistant tool_calls by tool_call_id) instead of silently dropping them; the lines are cohesive wiring inside the existing function. Covered by tests/unit/deepseek-web-tool-result-prompt-4712.test.ts.",
|
||||
"open-sse/executors/duckduckgo-web.ts": 925,
|
||||
"open-sse/executors/grok-web.ts": 1871,
|
||||
"open-sse/executors/muse-spark-web.ts": 1284,
|
||||
|
||||
@@ -511,13 +511,14 @@ function extractMessageText(content: unknown): string {
|
||||
* is the last line of the transcript.
|
||||
*/
|
||||
export function messagesToPrompt(
|
||||
messages: Array<{ role: string; content: string }>,
|
||||
messages: Array<{ role: string; content: string; tool_call_id?: string; name?: string }>,
|
||||
historyWindow = 0
|
||||
): string {
|
||||
if (messages.length === 0) return "";
|
||||
|
||||
const systemParts: string[] = [];
|
||||
const conversation: Array<{ role: string; text: string }> = [];
|
||||
const callNameById = new Map<string, string>();
|
||||
let lastUserContent = "";
|
||||
for (const m of messages) {
|
||||
const text = extractMessageText(m.content).trim();
|
||||
@@ -526,6 +527,22 @@ export function messagesToPrompt(
|
||||
} else if (m.role === "user" || m.role === "assistant") {
|
||||
if (text) conversation.push({ role: m.role, text });
|
||||
if (m.role === "user") lastUserContent = text;
|
||||
const calls = Array.isArray((m as { tool_calls?: unknown }).tool_calls)
|
||||
? (m as { tool_calls: Array<{ id?: string; function?: { name?: string } }> }).tool_calls
|
||||
: [];
|
||||
for (const c of calls) {
|
||||
if (c?.id && typeof c.function?.name === "string") callNameById.set(c.id, c.function.name);
|
||||
}
|
||||
} else if (m.role === "tool") {
|
||||
// Tool results have no native slot in deepseek-web's single-prompt format. Without
|
||||
// this branch they were silently dropped (#4712) — the model never saw the tool
|
||||
// output and either re-called the tool endlessly or answered "I don't have that
|
||||
// information". Fold them into the transcript as plain text, mirroring the agentic
|
||||
// buildToolConversationPrompt() path.
|
||||
if (text) {
|
||||
const name = (m.tool_call_id && callNameById.get(m.tool_call_id)) || m.name || "tool";
|
||||
conversation.push({ role: "tool", text: `(${name}) ${text}` });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,7 +555,13 @@ export function messagesToPrompt(
|
||||
// Rolling-window transcript of the most recent turns (#2942).
|
||||
const recent = conversation.slice(-historyWindow);
|
||||
const transcript = recent
|
||||
.map((turn) => `${turn.role === "assistant" ? "Assistant" : "User"}: ${turn.text}`)
|
||||
.map((turn) =>
|
||||
turn.role === "assistant"
|
||||
? `Assistant: ${turn.text}`
|
||||
: turn.role === "tool"
|
||||
? `Tool result ${turn.text}`
|
||||
: `User: ${turn.text}`
|
||||
)
|
||||
.join("\n\n");
|
||||
parts.push(transcript);
|
||||
} else if (lastUserContent) {
|
||||
|
||||
66
tests/unit/deepseek-web-tool-result-prompt-4712.test.ts
Normal file
66
tests/unit/deepseek-web-tool-result-prompt-4712.test.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
// #4712 — deepseek-web drops role:"tool" messages. The web API takes a single `prompt`
|
||||
// string, so tool results must be folded into the transcript. The agentic path
|
||||
// (buildToolConversationPrompt) already does this, but messagesToPrompt — used whenever
|
||||
// the follow-up request no longer carries a `tools[]` array (hasTools=false) — silently
|
||||
// discarded role:"tool" messages, so the model never saw the tool output and either
|
||||
// re-called the tool endlessly or answered "I don't have that information".
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { messagesToPrompt } = await import("../../open-sse/executors/deepseek-web.ts");
|
||||
|
||||
const CONVO = [
|
||||
{ role: "system", content: "You are helpful." },
|
||||
{ role: "user", content: "what is the weather in Tokyo?" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: "",
|
||||
tool_calls: [
|
||||
{ id: "call_1", function: { name: "get_weather", arguments: '{"city":"Tokyo"}' } },
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "tool",
|
||||
tool_call_id: "call_1",
|
||||
name: "get_weather",
|
||||
content: '{"temp":22,"conditions":"Sunny"}',
|
||||
},
|
||||
{ role: "user", content: "should I bring an umbrella?" },
|
||||
];
|
||||
|
||||
test("messagesToPrompt includes role:tool results in the rolling-window transcript (#4712)", () => {
|
||||
const prompt = messagesToPrompt(CONVO, 50);
|
||||
// The tool output must reach the model — this is the regression guard.
|
||||
assert.match(prompt, /22/);
|
||||
assert.match(prompt, /Sunny/);
|
||||
// It should be labelled as a tool result, not silently merged into a user turn.
|
||||
assert.match(prompt, /Tool result/i);
|
||||
// Tool name is preserved for context when available.
|
||||
assert.match(prompt, /get_weather/);
|
||||
});
|
||||
|
||||
test("messagesToPrompt tags the tool name from tool_call_id when no explicit name (#4712)", () => {
|
||||
const convo = [
|
||||
{ role: "user", content: "q" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: "",
|
||||
tool_calls: [{ id: "c9", function: { name: "lookup", arguments: "{}" } }],
|
||||
},
|
||||
{ role: "tool", tool_call_id: "c9", content: "RESULT_PAYLOAD" },
|
||||
];
|
||||
const prompt = messagesToPrompt(convo, 50);
|
||||
assert.match(prompt, /RESULT_PAYLOAD/);
|
||||
assert.match(prompt, /lookup/);
|
||||
});
|
||||
|
||||
test("messagesToPrompt still drops empty tool results without crashing (#4712)", () => {
|
||||
const convo = [
|
||||
{ role: "user", content: "hello" },
|
||||
{ role: "tool", tool_call_id: "x", content: "" },
|
||||
{ role: "user", content: "world" },
|
||||
];
|
||||
const prompt = messagesToPrompt(convo, 50);
|
||||
assert.match(prompt, /hello/);
|
||||
assert.match(prompt, /world/);
|
||||
});
|
||||
Reference in New Issue
Block a user