Files
OmniRoute/tests/unit/responses-additional-tools.test.ts
Jan Leon 6b59e814da Restore Responses API custom tool calls (#7905)
* 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
2026-07-20 22:22:24 -03:00

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";
}
);
});