mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
* fix: restore Responses custom tool calls * fix: preserve nested Responses custom tool calls * fix: reset superseded tool call state * fix: preserve tool precedence and buffered Responses tool arguments * test: verify top-level tool descriptions take precedence * fix: preserve custom Responses tool streaming semantics * test: cover declared custom tool streaming round trips * test: cover Responses custom tool metadata collection * test: cover active Responses custom tool stream * test: isolate Responses active stream regression * fix: complete Responses custom tool round trips
352 lines
9.5 KiB
TypeScript
352 lines
9.5 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
const { openaiResponsesToOpenAIRequest } =
|
|
await import("../../open-sse/translator/request/openai-responses.ts");
|
|
const { collectCustomToolNamesForSourceFormat, collectResponsesCustomToolNames } =
|
|
await import("../../open-sse/translator/request/openai-responses/additionalTools.ts");
|
|
|
|
interface ChatTool {
|
|
function: {
|
|
name: string;
|
|
description?: string;
|
|
parameters?: unknown;
|
|
};
|
|
}
|
|
|
|
interface ChatRequest {
|
|
messages: unknown[];
|
|
tools: ChatTool[];
|
|
}
|
|
|
|
test("Responses -> Chat merges additional_tools into the universal tool conversion path", () => {
|
|
const result = openaiResponsesToOpenAIRequest(
|
|
"any-model",
|
|
{
|
|
input: [
|
|
{
|
|
type: "additional_tools",
|
|
role: "developer",
|
|
tools: [
|
|
{
|
|
type: "custom",
|
|
name: "exec",
|
|
description: "Run orchestration code",
|
|
format: { type: "grammar", syntax: "lark", definition: "start: /.+/" },
|
|
},
|
|
{
|
|
type: "function",
|
|
name: "wait",
|
|
description: "Wait for a running operation",
|
|
parameters: {
|
|
type: "object",
|
|
properties: { cell_id: { type: "string" } },
|
|
required: ["cell_id"],
|
|
},
|
|
},
|
|
{
|
|
type: "namespace",
|
|
name: "collaboration",
|
|
tools: [
|
|
{
|
|
name: "spawn_agent",
|
|
description: "Spawn an agent",
|
|
parameters: { type: "object", properties: {} },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: "message",
|
|
role: "user",
|
|
content: [{ type: "input_text", text: "Use the tools" }],
|
|
},
|
|
],
|
|
tool_choice: "auto",
|
|
},
|
|
true,
|
|
{ provider: "any-openai-compatible-provider" }
|
|
) as ChatRequest;
|
|
|
|
assert.deepEqual(result.messages, [
|
|
{ role: "user", content: [{ type: "text", text: "Use the tools" }] },
|
|
]);
|
|
assert.deepEqual(
|
|
result.tools.map((tool) => tool.function?.name),
|
|
["exec", "wait", "spawn_agent"]
|
|
);
|
|
assert.deepEqual(result.tools[0].function.parameters, {
|
|
type: "object",
|
|
properties: { input: { type: "string" } },
|
|
required: ["input"],
|
|
additionalProperties: false,
|
|
});
|
|
});
|
|
|
|
test("Responses -> Chat merges multiple tool sources and keeps top-level declarations on conflict", () => {
|
|
const topLevel = {
|
|
type: "function",
|
|
name: "lookup",
|
|
description: "Authoritative top-level declaration",
|
|
parameters: { type: "object", properties: { id: { type: "string" } } },
|
|
};
|
|
const result = openaiResponsesToOpenAIRequest(
|
|
"any-model",
|
|
{
|
|
input: [
|
|
{
|
|
type: "additional_tools",
|
|
tools: [
|
|
{ ...topLevel, description: "Conflicting deferred declaration" },
|
|
{ type: "function", name: "first", parameters: { type: "object" } },
|
|
],
|
|
},
|
|
{
|
|
type: "additional_tools",
|
|
tools: [{ type: "function", name: "second", parameters: { type: "object" } }],
|
|
},
|
|
{ type: "message", role: "user", content: [{ type: "input_text", text: "go" }] },
|
|
],
|
|
tools: [topLevel],
|
|
},
|
|
false,
|
|
{ provider: "another-provider" }
|
|
) as ChatRequest;
|
|
|
|
assert.deepEqual(
|
|
result.tools.map((tool) => tool.function.name),
|
|
["lookup", "first", "second"]
|
|
);
|
|
assert.equal(result.tools[0].function.description, "Authoritative top-level declaration");
|
|
});
|
|
|
|
test("Responses -> Chat preserves a namespace that shares a name with a function", () => {
|
|
const result = openaiResponsesToOpenAIRequest(
|
|
"any-model",
|
|
{
|
|
input: [
|
|
{
|
|
type: "additional_tools",
|
|
tools: [
|
|
{
|
|
type: "namespace",
|
|
name: "server",
|
|
tools: [
|
|
{
|
|
name: "mcp__server__read",
|
|
parameters: { type: "object", properties: {} },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type: "message", role: "user", content: [{ type: "input_text", text: "go" }] },
|
|
],
|
|
tools: [{ type: "function", name: "server", parameters: { type: "object" } }],
|
|
},
|
|
false,
|
|
{ provider: "another-provider" }
|
|
) as ChatRequest;
|
|
|
|
assert.deepEqual(
|
|
result.tools.map((tool) => tool.function.name),
|
|
["server", "mcp__server__read"]
|
|
);
|
|
});
|
|
|
|
test("Responses -> Chat merges members from same-named namespaces", () => {
|
|
const result = openaiResponsesToOpenAIRequest(
|
|
"any-model",
|
|
{
|
|
input: [
|
|
{
|
|
type: "additional_tools",
|
|
tools: [
|
|
{
|
|
type: "namespace",
|
|
name: "server",
|
|
tools: [
|
|
{
|
|
name: "mcp__server__write",
|
|
parameters: { type: "object", properties: {} },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{ type: "message", role: "user", content: [{ type: "input_text", text: "go" }] },
|
|
],
|
|
tools: [
|
|
{
|
|
type: "namespace",
|
|
name: "server",
|
|
tools: [
|
|
{
|
|
name: "mcp__server__read",
|
|
parameters: { type: "object", properties: {} },
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
false,
|
|
{ provider: "another-provider" }
|
|
) as ChatRequest;
|
|
|
|
assert.deepEqual(
|
|
result.tools.map((tool) => tool.function.name),
|
|
["mcp__server__read", "mcp__server__write"]
|
|
);
|
|
});
|
|
|
|
test("Responses -> Chat gives top-level tools precedence over namespaced members", () => {
|
|
const rootTools = [
|
|
{
|
|
type: "function",
|
|
name: "exec",
|
|
description: "Explicit function tool",
|
|
parameters: { type: "object" },
|
|
},
|
|
{
|
|
type: "namespace",
|
|
name: "commands",
|
|
tools: [{ type: "custom", name: "exec", description: "Shadowed custom tool" }],
|
|
},
|
|
];
|
|
const result = openaiResponsesToOpenAIRequest(
|
|
"any-model",
|
|
{
|
|
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "go" }] }],
|
|
tools: rootTools,
|
|
},
|
|
false,
|
|
{ provider: "another-provider" }
|
|
) as ChatRequest;
|
|
|
|
assert.deepEqual(
|
|
result.tools.map((tool) => tool.function.name),
|
|
["exec"]
|
|
);
|
|
assert.equal(result.tools[0].function.description, "Explicit function tool");
|
|
assert.deepEqual([...collectResponsesCustomToolNames(rootTools, [])], []);
|
|
});
|
|
|
|
test("Responses custom metadata includes additional and namespaced custom tools", () => {
|
|
const input = [
|
|
{
|
|
type: "additional_tools",
|
|
tools: [
|
|
{ type: "custom", name: "exec" },
|
|
{
|
|
type: "namespace",
|
|
name: "server",
|
|
tools: [{ type: "custom", name: "apply_diff" }],
|
|
},
|
|
],
|
|
},
|
|
];
|
|
assert.deepEqual([...collectResponsesCustomToolNames([], input)].sort(), ["apply_diff", "exec"]);
|
|
});
|
|
|
|
test("Responses source format enables custom metadata independently of model apiFormat", () => {
|
|
assert.deepEqual(
|
|
[
|
|
...collectCustomToolNamesForSourceFormat(
|
|
"openai-responses",
|
|
"openai-responses",
|
|
[{ type: "custom", name: "exec" }],
|
|
[]
|
|
),
|
|
],
|
|
["exec"]
|
|
);
|
|
assert.deepEqual(
|
|
[
|
|
...collectCustomToolNamesForSourceFormat(
|
|
"openai",
|
|
"openai-responses",
|
|
[{ type: "custom", name: "exec" }],
|
|
[]
|
|
),
|
|
],
|
|
[]
|
|
);
|
|
});
|
|
|
|
test("Responses -> Chat normalizes custom tools nested in namespaces", () => {
|
|
const result = openaiResponsesToOpenAIRequest(
|
|
"any-model",
|
|
{
|
|
input: [{ type: "message", role: "user", content: "go" }],
|
|
tools: [
|
|
{
|
|
type: "namespace",
|
|
name: "commands",
|
|
tools: [{ type: "custom", name: "exec", description: "Run code" }],
|
|
},
|
|
],
|
|
},
|
|
false,
|
|
{ provider: "another-provider" }
|
|
) as ChatRequest;
|
|
|
|
assert.deepEqual(result.tools[0].function.parameters, {
|
|
type: "object",
|
|
properties: { input: { type: "string" } },
|
|
required: ["input"],
|
|
additionalProperties: false,
|
|
});
|
|
});
|
|
|
|
test("Responses -> Chat enforces explicit precedence within additional_tools", () => {
|
|
const input = [
|
|
{
|
|
type: "additional_tools",
|
|
tools: [
|
|
{ type: "function", name: "exec", description: "Explicit function" },
|
|
{
|
|
type: "namespace",
|
|
name: "commands",
|
|
tools: [{ type: "custom", name: "exec", description: "Shadowed custom" }],
|
|
},
|
|
],
|
|
},
|
|
{ type: "message", role: "user", content: "go" },
|
|
];
|
|
const result = openaiResponsesToOpenAIRequest("any-model", { input }, false, {
|
|
provider: "another-provider",
|
|
}) as ChatRequest;
|
|
|
|
assert.deepEqual(
|
|
result.tools.map((tool) => tool.function.name),
|
|
["exec"]
|
|
);
|
|
assert.equal(result.tools[0].function.description, "Explicit function");
|
|
assert.deepEqual([...collectResponsesCustomToolNames([], input)], []);
|
|
});
|
|
|
|
test("Responses -> Chat validates tools supplied through additional_tools", () => {
|
|
assert.throws(
|
|
() =>
|
|
openaiResponsesToOpenAIRequest(
|
|
"any-model",
|
|
{
|
|
input: [
|
|
{
|
|
type: "additional_tools",
|
|
tools: [{ type: "file_search", name: "search" }],
|
|
},
|
|
{ type: "message", role: "user", content: "hi" },
|
|
],
|
|
},
|
|
false,
|
|
{ provider: "any-provider" }
|
|
),
|
|
(error: unknown) => {
|
|
const typedError = error as { statusCode?: number; errorType?: string };
|
|
return typedError.statusCode === 400 && typedError.errorType === "unsupported_feature";
|
|
}
|
|
);
|
|
});
|