9router/tests/unit/fusion-strip-stream-options-3024.test.js
Clayton Tavares 6d30ce6de5 fix: Fusion strip stream_options + reasoning model test probe
- combos: strip stream_options from Fusion panel fan-out to avoid DeepSeek 400 (#3024)
- dashboard: raise model-test probe budget to 1024 + soft-pass reasoning-only responses (#3010)
2026-08-13 11:56:43 +07:00

83 lines
3 KiB
JavaScript

// Issue #3024 — Fusion combo must strip `stream_options` from panel requests
// when running non-streaming, or DeepSeek rejects with
// "stream_options should be set along with stream = true".
import { describe, it, expect, vi } from "vitest";
import { handleFusionChat } from "../../open-sse/services/combo.js";
// Minimal logger stub (combo.js calls log.info/warn).
const log = { info: () => {}, warn: () => {}, error: () => {} };
function makeBody(extra = {}) {
return {
model: "combo/gemseek",
stream: true,
stream_options: { include_usage: true },
messages: [{ role: "user", content: "hi" }],
...extra,
};
}
describe("Fusion strips stream_options (#3024)", () => {
it("removes stream_options before fanning out to panel models", async () => {
let capturedPanelBody = null;
const handleSingleModel = vi.fn(async (panelBody, model, isPanel) => {
if (isPanel) capturedPanelBody = panelBody;
// Simulate a successful non-stream JSON answer for the panel.
if (isPanel) {
return new Response(JSON.stringify({ choices: [{ message: { content: `ans-${model}` } }] }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
// Judge leg: return a final answer.
return new Response(JSON.stringify({ choices: [{ message: { content: "final" } }] }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
});
const res = await handleFusionChat({
body: makeBody(),
models: ["ds/deepseek-v4-flash", "gemini/gemini-3.5-flash-lite"],
handleSingleModel,
log,
comboName: "GemSeek",
judgeModel: "gemini/gemini-3.5-flash-lite",
});
expect(res).toBeInstanceOf(Response);
expect(capturedPanelBody).not.toBeNull();
// Critical assertion: stream_options must NOT leak into panel requests.
expect(capturedPanelBody.stream_options).toBeUndefined();
expect(capturedPanelBody.stream).toBe(false);
// Ensure the original client body still had it (proves we stripped deliberately).
expect(makeBody().stream_options).toBeDefined();
});
it("does not throw for a 2-model fusion with stream_options present", async () => {
const handleSingleModel = vi.fn(async (panelBody, model, isPanel) => {
if (isPanel) {
return new Response(JSON.stringify({ choices: [{ message: { content: "ok" } }] }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
return new Response(JSON.stringify({ choices: [{ message: { content: "final" } }] }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
});
const res = await handleFusionChat({
body: makeBody({ stream_options: { include_usage: true } }),
models: ["ds/deepseek-v4-pro", "ds/deepseek-v4-flash"],
handleSingleModel,
log,
comboName: "GemSeek",
judgeModel: "ds/deepseek-v4-pro",
});
expect(res.status).toBe(200);
});
});