mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-13 18:32:12 +03:00
Signed-off-by: Minxi Hou <houminxi@gmail.com>
This commit is contained in:
@@ -768,6 +768,15 @@ export function openaiResponsesToOpenAIRequest(
|
||||
}
|
||||
}
|
||||
|
||||
// #12141: When translated Chat tools is empty/absent, strip neutral tool_choice
|
||||
// ("auto" / "none") so strict Chat endpoints (e.g. vLLM) do not reject with 400
|
||||
// ("When using tool_choice, tools must be set"). Contradictory choices like "required"
|
||||
// or forced functions are preserved so the upstream error remains visible.
|
||||
const finalChatTools = Array.isArray(result.tools) ? result.tools : [];
|
||||
if (finalChatTools.length === 0 && (result.tool_choice === "auto" || result.tool_choice === "none")) {
|
||||
delete result.tool_choice;
|
||||
}
|
||||
|
||||
// Cleanup Responses API specific fields
|
||||
// Note: prompt_cache_key is intentionally preserved for OpenAI destinations — it is
|
||||
// used by Codex as a cache-affinity signal and stripping it unconditionally broke
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { openaiResponsesToOpenAIRequest } = await import(
|
||||
"../../open-sse/translator/request/openai-responses.ts"
|
||||
);
|
||||
|
||||
test("#12141: Responses-to-Chat strips tool_choice='auto' when tools is empty array", () => {
|
||||
const body = {
|
||||
model: "vllm/qwen-2.5-72b",
|
||||
input: "hello",
|
||||
tools: [],
|
||||
tool_choice: "auto",
|
||||
stream: false,
|
||||
};
|
||||
|
||||
const result = openaiResponsesToOpenAIRequest(null, body, null, null) as Record<string, unknown>;
|
||||
|
||||
assert.equal(
|
||||
result.tool_choice,
|
||||
undefined,
|
||||
"Neutral tool_choice 'auto' must be omitted when no tools exist"
|
||||
);
|
||||
});
|
||||
|
||||
test("#12141: Responses-to-Chat strips tool_choice='none' when tools is absent or empty", () => {
|
||||
const body = {
|
||||
model: "vllm/qwen-2.5-72b",
|
||||
input: "hello",
|
||||
tool_choice: "none",
|
||||
stream: false,
|
||||
};
|
||||
|
||||
const result = openaiResponsesToOpenAIRequest(null, body, null, null) as Record<string, unknown>;
|
||||
|
||||
assert.equal(
|
||||
result.tool_choice,
|
||||
undefined,
|
||||
"Neutral tool_choice 'none' must be omitted when no tools exist"
|
||||
);
|
||||
});
|
||||
|
||||
test("#12141: Responses-to-Chat preserves tools and tool_choice='auto' when valid tools are present", () => {
|
||||
const body = {
|
||||
model: "vllm/qwen-2.5-72b",
|
||||
input: "what is the weather?",
|
||||
tools: [
|
||||
{
|
||||
type: "function",
|
||||
name: "get_weather",
|
||||
description: "Get weather",
|
||||
parameters: { type: "object", properties: { location: { type: "string" } } },
|
||||
},
|
||||
],
|
||||
tool_choice: "auto",
|
||||
stream: false,
|
||||
};
|
||||
|
||||
const result = openaiResponsesToOpenAIRequest(null, body, null, null) as Record<string, unknown>;
|
||||
|
||||
assert.ok(Array.isArray(result.tools), "tools must be preserved as an array");
|
||||
assert.equal((result.tools as unknown[]).length, 1);
|
||||
assert.equal(result.tool_choice, "auto", "tool_choice 'auto' must be preserved when tools exist");
|
||||
});
|
||||
|
||||
test("#12141: Responses-to-Chat preserves tool_choice='required' when tools is empty (explicit contradiction)", () => {
|
||||
const body = {
|
||||
model: "vllm/qwen-2.5-72b",
|
||||
input: "hello",
|
||||
tools: [],
|
||||
tool_choice: "required",
|
||||
stream: false,
|
||||
};
|
||||
|
||||
const result = openaiResponsesToOpenAIRequest(null, body, null, null) as Record<string, unknown>;
|
||||
|
||||
assert.equal(
|
||||
result.tool_choice,
|
||||
"required",
|
||||
"Contradictory tool_choice 'required' must be preserved so upstream surfaces the error"
|
||||
);
|
||||
});
|
||||
@@ -405,10 +405,19 @@ test("Chat→Responses: tool_choice {type:'function', function:{name}} unwrapped
|
||||
});
|
||||
});
|
||||
|
||||
test("Responses→Chat: string tool_choice passes through unchanged", () => {
|
||||
const body = { model: "gpt-4", input: "hello", tool_choice: "auto" };
|
||||
const result = openaiResponsesToOpenAIRequest(null, body, null, null);
|
||||
assert.equal((result as any).tool_choice, "auto");
|
||||
test("Responses->Chat: string tool_choice passes through when tools present, stripped when absent (#12141)", () => {
|
||||
const withTools = {
|
||||
model: "gpt-4",
|
||||
input: "hello",
|
||||
tools: [{ type: "function", name: "f", parameters: {} }],
|
||||
tool_choice: "auto",
|
||||
};
|
||||
const resWith = openaiResponsesToOpenAIRequest(null, withTools, null, null) as Record<string, unknown>;
|
||||
assert.equal(resWith.tool_choice, "auto");
|
||||
|
||||
const noTools = { model: "gpt-4", input: "hello", tool_choice: "auto" };
|
||||
const resWithout = openaiResponsesToOpenAIRequest(null, noTools, null, null) as Record<string, unknown>;
|
||||
assert.equal(resWithout.tool_choice, undefined);
|
||||
});
|
||||
|
||||
test("Chat→Responses: string tool_choice passes through unchanged", () => {
|
||||
|
||||
Reference in New Issue
Block a user