import test from "node:test"; import assert from "node:assert/strict"; const { extractThinkingFromContent, sanitizeOpenAIResponse, sanitizeResponsesApiResponse, sanitizeStreamingChunk, } = await import("../../open-sse/handlers/responseSanitizer.ts"); test("extractThinkingFromContent separates think blocks from visible content", () => { const parsed = extractThinkingFromContent( "Beforereasoning 1middlereasoning 2after" ); assert.equal(parsed.content, "Beforemiddleafter"); assert.equal(parsed.thinking, "reasoning 1\n\nreasoning 2"); }); test("sanitizeOpenAIResponse strips non-standard fields and preserves required top-level fields", () => { const sanitized = sanitizeOpenAIResponse({ id: "chatcmpl_existing", object: "chat.completion", created: 123, model: "gpt-4.1", choices: [], x_groq: { ignored: true }, service_tier: "premium", }); assert.deepEqual(sanitized, { id: "chatcmpl_existing", object: "chat.completion", created: 123, model: "gpt-4.1", choices: [], }); }); test("sanitizeOpenAIResponse extracts thinking, collapses newlines, preserves reasoning_content with tool_calls, and preserves tool calls", () => { const sanitized = sanitizeOpenAIResponse({ id: "chatcmpl_test", model: "gpt-4.1", choices: [ { index: 2, finish_reason: "tool_calls", message: { role: "assistant", content: "Hello\n\n\ninternal chain\n\nworld", tool_calls: [{ id: "call_1" }], function_call: { name: "legacy" }, }, }, ], }); assert.equal((sanitized as any).choices[0].index, 2); assert.equal((sanitized as any).choices[0].finish_reason, "tool_calls"); (assert as any).equal((sanitized as any).choices[0].message.content, "Hello\n\nworld"); assert.equal((sanitized as any).choices[0].message.reasoning_content, "internal chain"); (assert as any).deepEqual((sanitized as any).choices[0].message.tool_calls, [{ id: "call_1" }]); assert.deepEqual((sanitized as any).choices[0].message.function_call, { name: "legacy" }); }); test("sanitizeOpenAIResponse preserves native reasoning_content when no visible content remains", () => { const sanitized = sanitizeOpenAIResponse({ model: "gpt-4.1", choices: [ { message: { role: "assistant", content: "discard me", reasoning_content: "provider reasoning", }, }, ], }); assert.equal(((sanitized as any).choices[0].message as any).content, ""); assert.equal((sanitized as any).choices[0].message.reasoning_content, "provider reasoning"); }); test("sanitizeOpenAIResponse maps Claude-style usage fields and strips extras", () => { const sanitized = sanitizeOpenAIResponse({ model: "claude-3-7-sonnet", choices: [], usage: { input_tokens: 11, output_tokens: 7, service_tier: "ignored", usage_breakdown: { ignored: true }, }, }); assert.deepEqual((sanitized as any).usage, { prompt_tokens: 11, completion_tokens: 7, total_tokens: 18, }); }); test("sanitizeOpenAIResponse strips reasoning_details-derived reasoning_content when visible text exists", () => { const sanitized = sanitizeOpenAIResponse({ model: "openrouter/model", choices: [ { message: { role: "assistant", content: "Visible", reasoning_details: [ { type: "reasoning.text", text: "first " }, { type: "thinking", content: "second" }, { type: "other", text: "ignored" }, ], }, }, ], }); assert.equal((sanitized as any).choices[0].message.reasoning_content, undefined); }); test("sanitizeOpenAIResponse preserves DeepSeek V4 reasoning_content with visible text", () => { const sanitized = sanitizeOpenAIResponse({ model: "deepseek-v4-pro", choices: [ { message: { role: "assistant", content: "Visible answer", reasoning_content: "DeepSeek reasoning", }, }, ], }); assert.equal((sanitized as any).choices[0].message.content, "Visible answer"); assert.equal((sanitized as any).choices[0].message.reasoning_content, "DeepSeek reasoning"); }); test("sanitizeOpenAIResponse preserves DeepSeek V4 reasoning_details with visible text", () => { const sanitized = sanitizeOpenAIResponse({ model: "deepseek-v4/reasoner", choices: [ { message: { role: "assistant", content: "Visible answer", reasoning_details: [ { type: "reasoning.text", text: "first " }, { type: "thinking", content: "second" }, ], }, }, ], }); assert.equal((sanitized as any).choices[0].message.reasoning_content, "first second"); }); test("sanitizeOpenAIResponse still strips non-DeepSeek reasoning_content with visible text", () => { const sanitized = sanitizeOpenAIResponse({ model: "o3-mini", choices: [ { message: { role: "assistant", content: "Visible answer", reasoning_content: "OpenAI reasoning", }, }, ], }); assert.equal((sanitized as any).choices[0].message.reasoning_content, undefined); }); test("sanitizeOpenAIResponse keeps reasoning_details-derived reasoning_content for reasoning-only messages", () => { const sanitized = sanitizeOpenAIResponse({ model: "openrouter/model", choices: [ { message: { role: "assistant", content: "", reasoning_details: [ { type: "reasoning.text", text: "first " }, { type: "thinking", content: "second" }, ], }, }, ], }); assert.equal((sanitized as any).choices[0].message.reasoning_content, "first second"); }); test("sanitizeResponsesApiResponse converts chat completions tool calls into Responses output items", () => { const sanitized = sanitizeResponsesApiResponse({ id: "chatcmpl_tool", object: "chat.completion", created: 123, model: "gpt-4.1", choices: [ { index: 0, finish_reason: "tool_calls", message: { role: "assistant", content: "", reasoning_content: "Check web results first.", tool_calls: [ { id: "call_web_search", type: "function", function: { name: "omniroute_web_search", arguments: '{"query":"omniroute"}', }, }, ], }, }, ], usage: { prompt_tokens: 12, completion_tokens: 5, prompt_tokens_details: { cached_tokens: 3 }, completion_tokens_details: { reasoning_tokens: 2 }, }, }); assert.equal((sanitized as any).object, "response"); assert.equal((sanitized as any).id, "resp_chatcmpl_tool"); assert.equal((sanitized as any).output[0].type, "reasoning"); (assert as any).equal((sanitized as any).output[1].type, "function_call"); (assert as any).equal((sanitized as any).output[1].call_id, "call_web_search"); (assert as any).equal((sanitized as any).output[1].name, "omniroute_web_search"); assert.equal((sanitized as any).usage.input_tokens, 12); assert.equal(((sanitized as any).usage as any).output_tokens, 5); assert.equal((sanitized as any).usage.input_tokens_details.cached_tokens, 3); assert.equal((sanitized as any).usage.output_tokens_details.reasoning_tokens, 2); }); test("sanitizeResponsesApiResponse preserves native Responses payloads and usage details", () => { const sanitized = sanitizeResponsesApiResponse({ id: "resp_native", object: "response", created_at: 456, model: "gpt-5.1-codex", status: "completed", output: [ { id: "msg_1", type: "message", role: "assistant", content: [{ type: "output_text", text: "Hello\n\n\nworld", annotations: [] }], }, { id: "fc_1", type: "function_call", call_id: "call_1", name: "lookup", arguments: { path: "/tmp/a" }, }, ], usage: { input_tokens: 20, output_tokens: 7, prompt_tokens_details: { cached_tokens: 4 }, cache_creation_input_tokens: 1, completion_tokens_details: { reasoning_tokens: 3 }, }, }); assert.equal((sanitized as any).object, "response"); assert.equal(((sanitized as any).output[0] as any).content[0].text, "Hello\n\nworld"); assert.equal((sanitized as any).output[1].arguments, '{"path":"/tmp/a"}'); assert.equal((sanitized as any).output_text, "Hello\n\nworld"); assert.equal((sanitized as any).usage.input_tokens, 20); (assert as any).equal((sanitized as any).usage.output_tokens, 7); assert.equal((sanitized as any).usage.input_tokens_details.cached_tokens, 4); assert.equal((sanitized as any).usage.input_tokens_details.cache_creation_tokens, 1); assert.equal((sanitized as any).usage.output_tokens_details.reasoning_tokens, 3); }); test("sanitizeStreamingChunk keeps only safe chunk fields and maps reasoning aliases", () => { const sanitized = sanitizeStreamingChunk({ id: "chunk_1", object: "chat.completion.chunk", created: 456, model: "gpt-4.1", choices: [ { index: 3, delta: { role: "assistant", content: "Line 1\n\n\nLine 2", reasoning: "stream reasoning", tool_calls: [{ id: "call_1" }], }, finish_reason: "stop", logprobs: { mock: true }, }, ], usage: { input_tokens: 2, output_tokens: 1, secret: true }, system_fingerprint: "fp_123", provider_debug: "drop-me", }); assert.deepEqual(sanitized, { id: "chunk_1", object: "chat.completion.chunk", created: 456, model: "gpt-4.1", choices: [ { index: 3, delta: { role: "assistant", content: "Line 1\n\nLine 2", reasoning_content: "stream reasoning", tool_calls: [{ id: "call_1" }], }, finish_reason: "stop", logprobs: { mock: true }, }, ], usage: { prompt_tokens: 2, completion_tokens: 1, total_tokens: 3, }, system_fingerprint: "fp_123", }); }); test("sanitizeStreamingChunk converts reasoning_details arrays in deltas", () => { const sanitized = sanitizeStreamingChunk({ choices: [ { delta: { reasoning_details: [{ type: "reasoning.text", text: "alpha" }, { content: "beta" }], }, }, ], }); assert.equal((sanitized as any).choices[0].delta.reasoning_content, "alphabeta"); }); test("sanitizeStreamingChunk preserves Copilot reasoning_text deltas", () => { const sanitized = sanitizeStreamingChunk({ choices: [ { delta: { reasoning_text: "copilot reasoning", }, }, ], }); assert.equal((sanitized as any).choices[0].delta.reasoning_text, "copilot reasoning"); }); test("sanitizeOpenAIResponse preserves reasoning_content when tool_calls are present", () => { // Bug fix: Kimi and other thinking-enabled providers require reasoning_content // on assistant messages that contain tool_calls. The sanitizer was stripping // reasoning_content whenever visible content existed, breaking subsequent // requests with "thinking is enabled but reasoning_content is missing". const sanitized = sanitizeOpenAIResponse({ model: "kimi-k2.6-thinking", choices: [ { message: { role: "assistant", content: "Let me search for that.", reasoning_content: "I need to use the web search tool to find current information.", tool_calls: [ { id: "call_search_1", type: "function", function: { name: "web_search", arguments: '{"query":"latest news"}', }, }, ], }, }, ], }); const message = (sanitized as any).choices[0].message; assert.equal(message.content, "Let me search for that."); assert.equal( message.reasoning_content, "I need to use the web search tool to find current information.", "reasoning_content must be preserved when tool_calls are present" ); assert.equal(message.tool_calls.length, 1); assert.equal(message.tool_calls[0].id, "call_search_1"); }); test("sanitizeOpenAIResponse still strips reasoning_content when no tool_calls exist", () => { // When there are no tool_calls, the original behavior should remain: // reasoning_content is stripped to avoid client rendering issues. const sanitized = sanitizeOpenAIResponse({ model: "gpt-4.1", choices: [ { message: { role: "assistant", content: "Hello world", reasoning_content: "Some internal reasoning", }, }, ], }); const message = (sanitized as any).choices[0].message; assert.equal(message.content, "Hello world"); assert.equal(message.reasoning_content, undefined); }); test("sanitizeOpenAIResponse preserves reasoning_content when legacy function_call is present", () => { const sanitized = sanitizeOpenAIResponse({ model: "kimi-k2.6-thinking", choices: [ { message: { role: "assistant", content: "Let me calculate that.", reasoning_content: "I need to use the calculator function.", function_call: { name: "calculate", arguments: '{"expr":"1+1"}' }, }, }, ], }); const message = (sanitized as any).choices[0].message; assert.equal(message.content, "Let me calculate that."); assert.equal( message.reasoning_content, "I need to use the calculator function.", "reasoning_content must be preserved when legacy function_call is present" ); assert.deepEqual(message.function_call, { name: "calculate", arguments: '{"expr":"1+1"}' }); }); test("sanitize functions return non-object inputs unchanged", () => { assert.equal(sanitizeOpenAIResponse(null), null); assert.equal(sanitizeStreamingChunk("raw text"), "raw text"); }); test("sanitizeOpenAIResponse converts textual pseudo tool-call content into structured tool_calls", () => { const sanitized = sanitizeOpenAIResponse({ id: "chatcmpl_textual_tool_call", object: "chat.completion", created: 1, model: "MainAgent", choices: [ { index: 0, finish_reason: "stop", message: { role: "assistant", content: 'Проверю.\n[Tool call: terminal]\nArguments: {"command":"echo hermes_textual_toolcall_guard","timeout":10}', }, }, ], }) as any; const choice = sanitized.choices[0]; assert.equal(choice.finish_reason, "tool_calls"); assert.equal(choice.message.content, null); assert.equal(choice.message.tool_calls[0].type, "function"); assert.equal(choice.message.tool_calls[0].function.name, "terminal"); assert.deepEqual(JSON.parse(choice.message.tool_calls[0].function.arguments), { command: "echo hermes_textual_toolcall_guard", timeout: 10, }); assert.equal(JSON.stringify(sanitized).includes("[Tool call:"), false); assert.equal(JSON.stringify(sanitized).includes("Arguments:"), false); }); test("sanitizeOpenAIResponse suppresses malformed textual pseudo tool-call content", () => { const sanitized = sanitizeOpenAIResponse({ id: "chatcmpl_malformed_textual_tool_call", object: "chat.completion", created: 1, model: "MainAgent", choices: [ { index: 0, finish_reason: "stop", message: { role: "assistant", content: "[Tool call: terminal]\nArguments: {not json", }, }, ], }) as any; const choice = sanitized.choices[0]; assert.equal(choice.finish_reason, "stop"); assert.equal(choice.message.content, null); assert.equal(choice.message.tool_calls, undefined); assert.equal(JSON.stringify(sanitized).includes("[Tool call:"), false); assert.equal(JSON.stringify(sanitized).includes("Arguments:"), false); });