chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const {
|
||||
coerceSchemaNumericFields,
|
||||
sanitizeToolDescription,
|
||||
coerceToolSchemas,
|
||||
sanitizeToolDescriptions,
|
||||
injectEmptyReasoningContentForToolCalls,
|
||||
} = await import("../../open-sse/translator/helpers/schemaCoercion.ts");
|
||||
const { translateRequest } = await import("../../open-sse/translator/index.ts");
|
||||
const { NON_ANTHROPIC_THINKING_PLACEHOLDER } = await import(
|
||||
"../../open-sse/translator/helpers/claudeHelper.ts"
|
||||
);
|
||||
const { FORMATS } = await import("../../open-sse/translator/formats.ts");
|
||||
const { clearModelsDevCapabilities, saveModelsDevCapabilities } =
|
||||
await import("../../src/lib/modelsDevSync.ts");
|
||||
|
||||
function buildCapability(overrides = {}) {
|
||||
return {
|
||||
tool_call: null,
|
||||
reasoning: null,
|
||||
attachment: null,
|
||||
structured_output: null,
|
||||
temperature: null,
|
||||
modalities_input: "[]",
|
||||
modalities_output: "[]",
|
||||
knowledge_cutoff: null,
|
||||
release_date: null,
|
||||
last_updated: null,
|
||||
status: null,
|
||||
family: null,
|
||||
open_weights: null,
|
||||
limit_context: null,
|
||||
limit_input: null,
|
||||
limit_output: null,
|
||||
interleaved_field: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test("tool sanitization: coerces numeric JSON Schema fields recursively", () => {
|
||||
const schema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
count: { type: "integer", minimum: "1", maximum: "10" },
|
||||
items: {
|
||||
type: "array",
|
||||
minItems: "2",
|
||||
items: { type: "string", minLength: "3" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = coerceSchemaNumericFields(schema);
|
||||
assert.equal((result as any).properties.count.minimum, 1);
|
||||
(assert as any).equal((result as any).properties.count.maximum, 10);
|
||||
(assert as any).equal((result as any).properties.items.minItems, 2);
|
||||
assert.equal((result as any).properties.items.items.minLength, 3);
|
||||
});
|
||||
|
||||
test("tool sanitization: preserves non-numeric JSON Schema strings", () => {
|
||||
const schema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
value: { type: "string", minimum: "abc" },
|
||||
},
|
||||
};
|
||||
|
||||
const result = coerceSchemaNumericFields(schema);
|
||||
assert.equal((result as any).properties.value.minimum, "abc");
|
||||
});
|
||||
|
||||
test("tool sanitization: normalizes descriptions across OpenAI, Claude, and Gemini shapes", () => {
|
||||
const openAITool = sanitizeToolDescription({
|
||||
type: "function",
|
||||
function: { name: "sum", description: null, parameters: {} },
|
||||
});
|
||||
const claudeTool = sanitizeToolDescription({
|
||||
name: "sum",
|
||||
description: 42,
|
||||
input_schema: { type: "object" },
|
||||
});
|
||||
const geminiTool = sanitizeToolDescription({
|
||||
functionDeclarations: [{ name: "sum", description: false, parameters: {} }],
|
||||
});
|
||||
|
||||
(assert as any).equal(((openAITool as any).function as any).description, "");
|
||||
assert.equal((claudeTool as any).description, "42");
|
||||
assert.equal((geminiTool as any).functionDeclarations[0].description, "false");
|
||||
});
|
||||
|
||||
test("tool sanitization: coerces schemas and descriptions in tool arrays", () => {
|
||||
const tools = sanitizeToolDescriptions(
|
||||
coerceToolSchemas([
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "sum",
|
||||
description: 5,
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
count: { type: "integer", minimum: "1" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
assert.equal(tools[0].function.description, "5");
|
||||
assert.equal(tools[0].function.parameters.properties.count.minimum, 1);
|
||||
});
|
||||
|
||||
test("translateRequest sanitizes tools before Claude output", () => {
|
||||
const translated = translateRequest(
|
||||
FORMATS.OPENAI,
|
||||
FORMATS.CLAUDE,
|
||||
"claude-sonnet-4-6",
|
||||
{
|
||||
messages: [{ role: "user", content: "hello" }],
|
||||
tools: [
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "sum",
|
||||
description: null,
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
count: { type: "integer", minimum: "1", maximum: "9" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
false,
|
||||
null,
|
||||
"claude"
|
||||
);
|
||||
|
||||
assert.equal(translated.tools[0].description, "");
|
||||
assert.equal(translated.tools[0].input_schema.properties.count.minimum, 1);
|
||||
assert.equal(translated.tools[0].input_schema.properties.count.maximum, 9);
|
||||
});
|
||||
|
||||
test("translateRequest sanitizes OpenAI tool payloads on passthrough", () => {
|
||||
const translated = translateRequest(
|
||||
FORMATS.OPENAI,
|
||||
FORMATS.OPENAI,
|
||||
"gpt-5.2",
|
||||
{
|
||||
messages: [{ role: "user", content: "hello" }],
|
||||
tools: [
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "sum",
|
||||
description: 7,
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
count: { type: "integer", minimum: "2" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
false,
|
||||
null,
|
||||
"openai"
|
||||
);
|
||||
|
||||
assert.equal(translated.tools[0].function.description, "7");
|
||||
assert.equal(translated.tools[0].function.parameters.properties.count.minimum, 2);
|
||||
});
|
||||
|
||||
test("tool sanitization: injects empty reasoning_content only for DeepSeek tool-call history", () => {
|
||||
const messages = [
|
||||
{ role: "user", content: "hello" },
|
||||
{
|
||||
role: "assistant",
|
||||
tool_calls: [{ id: "call_1", type: "function", function: { name: "sum", arguments: "{}" } }],
|
||||
},
|
||||
];
|
||||
|
||||
const deepseekMessages = injectEmptyReasoningContentForToolCalls(
|
||||
messages,
|
||||
"deepseek",
|
||||
"deepseek-v4-flash"
|
||||
);
|
||||
const openaiMessages = injectEmptyReasoningContentForToolCalls(messages, "openai", "gpt-4o");
|
||||
|
||||
assert.equal(deepseekMessages[1].reasoning_content, "");
|
||||
assert.equal(openaiMessages[1].reasoning_content, undefined);
|
||||
});
|
||||
|
||||
test("translateRequest injects reasoning_content for DeepSeek assistant tool calls", () => {
|
||||
clearModelsDevCapabilities();
|
||||
saveModelsDevCapabilities({
|
||||
deepseek: {
|
||||
"deepseek-v4-flash": buildCapability({
|
||||
interleaved_field: "reasoning_content",
|
||||
reasoning: true,
|
||||
tool_call: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
const translated = translateRequest(
|
||||
FORMATS.OPENAI,
|
||||
FORMATS.OPENAI,
|
||||
"deepseek-v4-flash",
|
||||
{
|
||||
messages: [
|
||||
{ role: "user", content: "hello" },
|
||||
{
|
||||
role: "assistant",
|
||||
tool_calls: [
|
||||
{ id: "call_1", type: "function", function: { name: "sum", arguments: "{}" } },
|
||||
],
|
||||
},
|
||||
{ role: "tool", tool_call_id: "call_1", content: "3" },
|
||||
],
|
||||
},
|
||||
false,
|
||||
null,
|
||||
"deepseek"
|
||||
);
|
||||
|
||||
assert.equal(translated.messages[1].reasoning_content, NON_ANTHROPIC_THINKING_PLACEHOLDER);
|
||||
clearModelsDevCapabilities();
|
||||
});
|
||||
Reference in New Issue
Block a user