mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 22:02:08 +03:00
fix(translator): coerce tool descriptions to strings in OpenAI normalization (#4675)
Integrated into release/v3.8.37 — coerce tool descriptions to strings in OpenAI normalization. Cherry-picked onto release tip; tests 3/3 green.
This commit is contained in:
committed by
GitHub
parent
fd8a52ed64
commit
33ac3f6b7d
@@ -178,7 +178,9 @@ export function filterToOpenAIFormat(body) {
|
||||
type: "function",
|
||||
function: {
|
||||
name: tool.name,
|
||||
description: tool.description || "",
|
||||
// Coerce: strict upstream validators (NVIDIA NIM, Codex) reject
|
||||
// non-string descriptions. Ports decolua/9router#397.
|
||||
description: String(tool.description ?? ""),
|
||||
parameters: tool.input_schema || { type: "object", properties: {} },
|
||||
},
|
||||
};
|
||||
@@ -190,7 +192,7 @@ export function filterToOpenAIFormat(body) {
|
||||
type: "function",
|
||||
function: {
|
||||
name: fn.name,
|
||||
description: fn.description || "",
|
||||
description: String(fn.description ?? ""),
|
||||
parameters: fn.parameters || { type: "object", properties: {} },
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Regression: tool descriptions must always be strings for strict upstream
|
||||
// validation (e.g., NVIDIA NIM, Codex). Ports upstream PR decolua/9router#397
|
||||
// (Ibrahim Ryan), restricted to the gap that still exists in OmniRoute —
|
||||
// the two non-string-tolerant paths in openaiHelper.filterToOpenAIFormat
|
||||
// (Claude-style and Gemini-style tool normalization). The other locations
|
||||
// upstream-patched (claude-to-openai.ts, openai-responses.ts) already coerce
|
||||
// via String/toString in OmniRoute.
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const openaiHelper = await import(
|
||||
"../../open-sse/translator/helpers/openaiHelper.ts"
|
||||
);
|
||||
|
||||
test("filterToOpenAIFormat coerces non-string Claude-tool description to string", () => {
|
||||
const result = openaiHelper.filterToOpenAIFormat({
|
||||
messages: [{ role: "user", content: "hi" }],
|
||||
tools: [
|
||||
{
|
||||
name: "claude-tool",
|
||||
// Non-string truthy value — must NOT leak through to upstream.
|
||||
description: { invalid: "object" } as unknown as string,
|
||||
input_schema: { type: "object" },
|
||||
},
|
||||
],
|
||||
});
|
||||
assert.equal(typeof result.tools[0].function.description, "string");
|
||||
});
|
||||
|
||||
test("filterToOpenAIFormat coerces null/undefined Claude-tool description to empty string", () => {
|
||||
const result = openaiHelper.filterToOpenAIFormat({
|
||||
messages: [{ role: "user", content: "hi" }],
|
||||
tools: [
|
||||
{ name: "t1", description: null as unknown as string, input_schema: {} },
|
||||
{ name: "t2", description: undefined, input_schema: {} },
|
||||
],
|
||||
});
|
||||
assert.equal(result.tools[0].function.description, "");
|
||||
assert.equal(result.tools[1].function.description, "");
|
||||
});
|
||||
|
||||
test("filterToOpenAIFormat coerces non-string Gemini functionDeclarations description", () => {
|
||||
const result = openaiHelper.filterToOpenAIFormat({
|
||||
messages: [{ role: "user", content: "hi" }],
|
||||
tools: [
|
||||
{
|
||||
functionDeclarations: [
|
||||
{
|
||||
name: "gemini-tool",
|
||||
description: 12345 as unknown as string,
|
||||
parameters: { type: "object" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
assert.equal(typeof result.tools[0].function.description, "string");
|
||||
});
|
||||
Reference in New Issue
Block a user