claude-code-router/tests/main/codex-model-catalog.test.mjs

253 lines
9.1 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import { buildCodexModelCatalog } from "../../packages/core/src/agents/codex/model-catalog.ts";
function catalogModelFor(config, slug) {
const catalog = buildCodexModelCatalog(config, slug);
const model = catalog.models.find((item) => item.slug === slug);
assert.ok(model, `expected catalog model ${slug}`);
return model;
}
test("codex catalog treats unknown models as text-only without advanced tools", () => {
const model = catalogModelFor({
Providers: [
{ name: "Custom", type: "openai_chat_completions", models: ["unknown-model"] }
]
}, "Custom/unknown-model");
assert.deepEqual(model.input_modalities, ["text"]);
assert.equal(model.supports_search_tool, false);
assert.equal(model.supports_parallel_tool_calls, false);
assert.equal(model.supports_reasoning_summaries, false);
assert.equal(model.supports_image_detail_original, false);
assert.deepEqual(model.supported_reasoning_levels, []);
assert.equal(model.default_reasoning_level, null);
assert.equal(model.apply_patch_tool_type, null);
});
test("codex catalog uses model catalog capabilities for known text models", () => {
const model = catalogModelFor({
Providers: [
{ name: "deepseek", type: "openai_chat_completions", models: ["deepseek-chat"] }
]
}, "deepseek/deepseek-chat");
assert.deepEqual(model.input_modalities, ["text"]);
assert.equal(model.supports_parallel_tool_calls, true);
assert.equal(model.supports_search_tool, false);
assert.equal(model.supports_reasoning_summaries, false);
assert.equal(model.supports_image_detail_original, false);
assert.deepEqual(model.supported_reasoning_levels, []);
assert.equal(model.default_reasoning_level, null);
assert.equal(model.apply_patch_tool_type, null);
});
test("codex catalog enables multimodal reasoning and search when provider protocol supports it", () => {
const model = catalogModelFor({
Providers: [
{ name: "openrouter", type: "openai_responses", models: ["google/gemini-2.5-pro"] }
]
}, "openrouter/google/gemini-2.5-pro");
assert.deepEqual(model.input_modalities, ["text", "image"]);
assert.equal(model.supports_image_detail_original, true);
assert.equal(model.supports_parallel_tool_calls, true);
assert.equal(model.supports_reasoning_summaries, true);
assert.equal(model.supports_search_tool, true);
assert.equal(model.web_search_tool_type, "text_and_image");
assert.deepEqual(model.supported_reasoning_levels, []);
assert.equal(model.default_reasoning_level, null);
assert.equal(model.apply_patch_tool_type, "freeform");
});
test("codex catalog uses provider-specific reasoning effort levels when declared", () => {
const model = catalogModelFor({
Providers: [
{ name: "openrouter", type: "openai_responses", models: ["z-ai/glm-5.2"] }
]
}, "openrouter/z-ai/glm-5.2");
assert.equal(model.supports_reasoning_summaries, true);
assert.deepEqual(model.supported_reasoning_levels.map((level) => level.effort), ["xhigh", "high"]);
assert.equal(model.default_reasoning_level, "high");
});
test("codex catalog provides GPT reasoning levels for Codex API models", () => {
const model = catalogModelFor({
Providers: [
{ name: "Codex API", type: "openai_responses", models: ["gpt-5.5"] }
]
}, "Codex API/gpt-5.5");
assert.equal(model.supports_reasoning_summaries, true);
assert.deepEqual(model.supported_reasoning_levels.map((level) => level.effort), ["minimal", "low", "medium", "high"]);
assert.equal(model.default_reasoning_level, "medium");
});
test("codex catalog does not merge reasoning efforts from unrelated providers", () => {
const model = catalogModelFor({
Providers: [
{ name: "wrapped-zhipu", type: "openai_responses", models: ["glm-5.2"] }
]
}, "wrapped-zhipu/glm-5.2");
assert.equal(model.supports_reasoning_summaries, true);
assert.deepEqual(model.supported_reasoning_levels, []);
assert.equal(model.default_reasoning_level, null);
});
test("codex catalog enables native search for Gemini Interactions providers", () => {
const model = catalogModelFor({
Providers: [
{ name: "gemini", type: "gemini_interactions", models: ["gemini-2.5-pro"] }
]
}, "gemini/gemini-2.5-pro");
assert.equal(model.supports_search_tool, true);
});
test("codex catalog does not expose native search through chat-completions-only providers", () => {
const model = catalogModelFor({
Providers: [
{ name: "openrouter", type: "openai_chat_completions", models: ["google/gemini-2.5-pro"] }
]
}, "openrouter/google/gemini-2.5-pro");
assert.deepEqual(model.input_modalities, ["text", "image"]);
assert.equal(model.supports_parallel_tool_calls, true);
assert.equal(model.supports_reasoning_summaries, true);
assert.equal(model.supports_search_tool, false);
assert.equal(model.web_search_tool_type, "text");
assert.equal(model.apply_patch_tool_type, null);
});
test("codex catalog keeps freeform apply_patch for GPT-named chat-compatible models", () => {
const model = catalogModelFor({
Providers: [
{ name: "gateway", type: "openai_chat_completions", models: ["gpt-compatible-coder"] }
]
}, "gateway/gpt-compatible-coder");
assert.equal(model.apply_patch_tool_type, "freeform");
assert.deepEqual(model.supported_reasoning_levels, []);
assert.equal(model.default_reasoning_level, null);
});
test("codex catalog keeps freeform apply_patch when provider advertises Responses capability", () => {
const model = catalogModelFor({
Providers: [
{
capabilities: [
{ baseUrl: "https://openrouter.ai/api/v1", type: "openai_chat_completions" },
{ baseUrl: "https://openrouter.ai/api/v1", type: "openai_responses" }
],
name: "openrouter",
type: "openai_chat_completions",
models: ["google/gemini-2.5-pro"]
}
]
}, "openrouter/google/gemini-2.5-pro");
assert.equal(model.apply_patch_tool_type, "freeform");
});
test("codex catalog enables apply_patch bridge for non-GPT models when Codex built-in route enables it", () => {
const model = catalogModelFor({
Providers: [
{ name: "openrouter", type: "openai_chat_completions", models: ["google/gemini-2.5-pro"] }
],
Router: {
builtInRules: {
"claude-code": { enabled: true },
codex: { enabled: true }
},
fallback: { mode: "off", models: [], retryCount: 1 },
rules: []
}
}, "openrouter/google/gemini-2.5-pro");
assert.equal(model.apply_patch_tool_type, "freeform");
});
test("codex catalog disables apply_patch bridge for non-GPT models when the Codex built-in route is off", () => {
const model = catalogModelFor({
Providers: [
{ name: "openrouter", type: "openai_chat_completions", models: ["google/gemini-2.5-pro"] }
],
Router: {
builtInRules: {
"claude-code": { enabled: true },
codex: { enabled: false }
},
fallback: { mode: "off", models: [], retryCount: 1 },
rules: []
}
}, "openrouter/google/gemini-2.5-pro");
assert.equal(model.apply_patch_tool_type, null);
});
test("codex catalog marks Fusion aliases with builtin web search as searchable", () => {
const model = catalogModelFor({
Providers: [],
virtualModelProfiles: [
{
displayName: "Research Fusion",
enabled: true,
execution: {
clientToolsPolicy: "allow",
matchWebSearch: false,
maxToolCalls: 4,
maxTurns: 4,
mode: "tool_loop",
streamMode: "optimistic"
},
id: "research-fusion",
key: "research",
match: { exactAliases: ["research"], prefixes: [], suffixes: [] },
materialization: { enabled: true, includeInGatewayModels: true },
metadata: {
fusionWebSearch: { provider: "brave", toolName: "web_search_research" }
},
tools: [{ name: "web_search_research", visibility: "internal" }]
}
]
}, "Fusion/research");
assert.deepEqual(model.input_modalities, ["text"]);
assert.equal(model.supports_search_tool, true);
assert.equal(model.web_search_tool_type, "text");
assert.equal(model.apply_patch_tool_type, null);
});
test("codex catalog marks prefixed Fusion virtual models with legacy web search tools as searchable", () => {
const model = catalogModelFor({
Providers: [
{ name: "deepseek", type: "openai_chat_completions", models: ["deepseek-chat"] }
],
virtualModelProfiles: [
{
displayName: "Web Prefix",
enabled: true,
execution: {
clientToolsPolicy: "allow",
maxToolCalls: 4,
maxTurns: 4,
mode: "tool_loop",
streamMode: "optimistic"
},
id: "web-prefix",
key: "web-prefix",
match: { exactAliases: [], prefixes: ["web-"], suffixes: [] },
materialization: { enabled: true, includeInGatewayModels: true },
tools: [{ name: "web_prefix_web_search", visibility: "internal" }]
}
]
}, "deepseek/web-deepseek-chat");
assert.deepEqual(model.input_modalities, ["text"]);
assert.equal(model.supports_search_tool, true);
assert.equal(model.web_search_tool_type, "text");
assert.equal(model.apply_patch_tool_type, null);
});