import { describe, expect, it } from
"vitest" ;
import {
applyXaiModelCompat,
buildProviderToolCompatFamilyHooks,
findOpenAIStrictSchemaViolations,
inspectGeminiToolSchemas,
inspectOpenAIToolSchemas,
normalizeGeminiToolSchemas,
normalizeOpenAIToolSchemas,
resolveXaiModelCompatPatch,
} from
"./provider-tools.js" ;
describe(
"buildProviderToolCompatFamilyHooks" , () => {
function normalizeOpenAIParameters(parameters: unknown): unknown {
const hooks = buildProviderToolCompatFamilyHooks(
"openai" );
const tools = [{ name:
"demo" , description:
"" , parameters }] as never;
const normalized = hooks.normalizeToolSchemas({
provider:
"openai" ,
modelId:
"gpt-5.4" ,
modelApi:
"openai-responses" ,
model: {
provider:
"openai" ,
api:
"openai-responses" ,
baseUrl:
"https://api.openai.com/v1 ",
id:
"gpt-5.4" ,
} as never,
tools,
});
return normalized[
0 ]?.parameters;
}
it(
"covers the tool compat family matrix" , () => {
const cases = [
{
family:
"gemini" as
const ,
normalizeToolSchemas: normalizeGeminiToolSchemas,
inspectToolSchemas: inspectGeminiToolSchemas,
},
{
family:
"openai" as
const ,
normalizeToolSchemas: normalizeOpenAIToolSchemas,
inspectToolSchemas: inspectOpenAIToolSchemas,
},
];
for (
const testCase of cases) {
const hooks = buildProviderToolCompatFamilyHooks(testCase.family);
expect(hooks.normalizeToolSchemas).toBe(testCase.normalizeToolSchemas);
expect(hooks.inspectToolSchemas).toBe(testCase.inspectToolSchemas);
}
});
it(
"normalizes parameter-free and typed-object schemas for the openai family" , () => {
const hooks = buildProviderToolCompatFamilyHooks(
"openai" );
const tools = [
{ name:
"ping" , description:
"" , parameters: {} },
{ name:
"exec" , description:
"" , parameters: { type:
"object" } },
] as never;
const normalized = hooks.normalizeToolSchemas({
provider:
"openai" ,
modelId:
"gpt-5.4" ,
modelApi:
"openai-responses" ,
model: {
provider:
"openai" ,
api:
"openai-responses" ,
baseUrl:
"https://api.openai.com/v1 ",
id:
"gpt-5.4" ,
} as never,
tools,
});
expect(normalized.map((tool) => tool.parameters)).toEqual([
{ type:
"object" , properties: {}, required: [], additionalProperties:
false },
{ type:
"object" , properties: {}, required: [], additionalProperties:
false },
]);
expect(
hooks.inspectToolSchemas({
provider:
"openai" ,
modelId:
"gpt-5.4" ,
modelApi:
"openai-responses" ,
model: {
provider:
"openai" ,
api:
"openai-responses" ,
baseUrl:
"https://api.openai.com/v1 ",
id:
"gpt-5.4" ,
} as never,
tools,
}),
).toEqual([]);
});
it(
"preserves explicit empty properties maps when normalizing strict openai schemas" , () => {
const hooks = buildProviderToolCompatFamilyHooks(
"openai" );
const parameters = {
type:
"object" ,
properties: {},
};
const tools = [{ name:
"ping" , description:
"" , parameters }] as never;
const normalized = hooks.normalizeToolSchemas({
provider:
"openai" ,
modelId:
"gpt-5.4" ,
modelApi:
"openai-responses" ,
model: {
provider:
"openai" ,
api:
"openai-responses" ,
baseUrl:
"https://api.openai.com/v1 ",
id:
"gpt-5.4" ,
} as never,
tools,
});
expect(normalized[
0 ]?.parameters).toEqual({
type:
"object" ,
properties: {},
required: [],
additionalProperties:
false ,
});
});
it(
"preserves nested schemas and annotation objects while normalizing strict openai schemas" , () => {
const cases = [
{
name:
"property schema" ,
parameters: {
type:
"object" ,
properties: { payload: {} },
required: [
"payload" ],
additionalProperties:
false ,
},
},
{
name:
"schema maps" ,
parameters: {
type:
"object" ,
properties: { mode: { $defs: { nested: {} }, dependentSchemas: { flag: {} } } },
required: [
"mode" ],
additionalProperties:
false ,
},
},
{
name:
"nested schema arrays" ,
parameters: {
type:
"object" ,
properties: { mode: { anyOf: [{}], prefixItems: [{}] } },
required: [
"mode" ],
additionalProperties:
false ,
},
},
{
name:
"annotation objects" ,
parameters: {
type:
"object" ,
properties: { mode: { type:
"string" ,
default : {},
const : {}, examples: [{}] } },
required: [
"mode" ],
additionalProperties:
false ,
},
},
];
for (
const testCase of cases) {
expect(normalizeOpenAIParameters(testCase.parameters), testCase.name).toEqual(
testCase.parameters,
);
}
});
it(
"does not tighten or warn for permissive object schemas that use strict:false" , () => {
const hooks = buildProviderToolCompatFamilyHooks(
"openai" );
const permissiveParameters = {
type:
"object" ,
properties: {
action: { type:
"string" },
schedule: { type:
"string" },
},
required: [
"action" ],
additionalProperties:
true ,
};
const permissiveTool = {
name:
"cron" ,
description:
"" ,
parameters: permissiveParameters,
} as never;
const normalized = hooks.normalizeToolSchemas({
provider:
"openai" ,
modelId:
"gpt-5.4" ,
modelApi:
"openai-responses" ,
model: {
provider:
"openai" ,
api:
"openai-responses" ,
baseUrl:
"https://api.openai.com/v1 ",
id:
"gpt-5.4" ,
} as never,
tools: [permissiveTool],
});
expect(normalized[
0 ]?.parameters).toEqual(permissiveParameters);
expect(findOpenAIStrictSchemaViolations(permissiveParameters,
"cron.parameters" ))
.toEqual(
expect.arrayContaining([
"cron.parameters.required.schedule" ,
"cron.parameters.additionalProperties" ,
]),
);
expect(
hooks.inspectToolSchemas({
provider: "openai" ,
modelId: "gpt-5.4" ,
modelApi: "openai-responses" ,
model: {
provider: "openai" ,
api: "openai-responses" ,
baseUrl: "https://api.openai.com/v1 ",
id: "gpt-5.4" ,
} as never,
tools: [permissiveTool],
}),
).toEqual([]);
});
it("skips openai strict-tool normalization on non-native routes" , () => {
const hooks = buildProviderToolCompatFamilyHooks("openai" );
const tools = [{ name: "ping" , description: "" , parameters: {} }] as never;
expect(
hooks.normalizeToolSchemas({
provider: "openai" ,
modelId: "gpt-5.4" ,
modelApi: "openai-completions" ,
model: {
provider: "openai" ,
api: "openai-completions" ,
baseUrl: "https://example.com/v1 ",
id: "gpt-5.4" ,
} as never,
tools,
}),
).toBe(tools);
expect(
hooks.inspectToolSchemas({
provider: "openai" ,
modelId: "gpt-5.4" ,
modelApi: "openai-completions" ,
model: {
provider: "openai" ,
api: "openai-completions" ,
baseUrl: "https://example.com/v1 ",
id: "gpt-5.4" ,
} as never,
tools,
}),
).toEqual([]);
});
it("suppresses openai strict-schema diagnostics because transport falls back to strict false" , () => {
const hooks = buildProviderToolCompatFamilyHooks("openai" );
const diagnostics = hooks.inspectToolSchemas({
provider: "openai-codex" ,
modelId: "gpt-5.4" ,
modelApi: "openai-codex-responses" ,
model: {
provider: "openai-codex" ,
api: "openai-codex-responses" ,
baseUrl: "https://chatgpt.com/backend-api ",
id: "gpt-5.4" ,
} as never,
tools: [
{
name: "exec" ,
description: "" ,
parameters: {
type: "object" ,
properties: {
mode: {
anyOf: [{ type: "string" }, { type: "number" }],
},
cwd: { type: "string" },
},
required: ["mode" ],
additionalProperties: true ,
},
} as never,
],
});
expect(diagnostics).toEqual([]);
});
it("covers the shared xAI tool compat patch" , () => {
const patch = resolveXaiModelCompatPatch();
expect(patch).toMatchObject({
toolSchemaProfile: "xai" ,
nativeWebSearchTool: true ,
toolCallArgumentsEncoding: "html-entities" ,
});
expect(patch.unsupportedToolSchemaKeywords).toEqual(
expect.arrayContaining(["minLength" , "maxLength" , "minItems" , "maxItems" ]),
);
expect(
applyXaiModelCompat({
id: "grok-4" ,
compat: {
supportsUsageInStreaming: true ,
},
}),
).toMatchObject({
compat: {
supportsUsageInStreaming: true ,
toolSchemaProfile: "xai" ,
nativeWebSearchTool: true ,
toolCallArgumentsEncoding: "html-entities" ,
},
});
});
});
Messung V0.5 in Prozent C=98 H=92 G=94
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland