diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index ffa0a7967..e00b638d5 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -1512,7 +1512,7 @@ function ProviderFormFull({ const auth = preset.auth ?? {}; const config = preset.config ?? ""; - resetCodexConfig(auth, config); + resetCodexConfig(auth, config, preset.modelCatalog ?? []); setLocalCodexApiFormat( preset.apiFormat ?? codexApiFormatFromWireApi(extractCodexWireApi(config)) ?? diff --git a/src/components/providers/forms/hooks/useCodexConfigState.ts b/src/components/providers/forms/hooks/useCodexConfigState.ts index f729b2841..79fcab22c 100644 --- a/src/components/providers/forms/hooks/useCodexConfigState.ts +++ b/src/components/providers/forms/hooks/useCodexConfigState.ts @@ -224,7 +224,7 @@ export function useCodexConfigState({ initialData }: UseCodexConfigStateProps) { setCodexApiKey(""); } }, - [setCodexAuth, setCodexConfig], + [setCodexAuth, setCodexConfig, setCodexCatalogModels], ); return { diff --git a/src/config/codexProviderPresets.ts b/src/config/codexProviderPresets.ts index 471abdad0..23c300f90 100644 --- a/src/config/codexProviderPresets.ts +++ b/src/config/codexProviderPresets.ts @@ -2,7 +2,7 @@ * Codex 预设供应商配置模板 */ import { ProviderCategory } from "../types"; -import type { CodexApiFormat } from "../types"; +import type { CodexApiFormat, CodexCatalogModel } from "../types"; import type { PresetTheme } from "./claudeProviderPresets"; export interface CodexProviderPreset { @@ -27,6 +27,8 @@ export interface CodexProviderPreset { iconColor?: string; // 图标颜色 // Codex API 格式 apiFormat?: CodexApiFormat; + // Codex Chat 本地路由模式下的模型目录 + modelCatalog?: CodexCatalogModel[]; } /** @@ -60,6 +62,22 @@ wire_api = "responses" requires_openai_auth = true`; } +function modelCatalog( + models: Array< + string | { model: string; displayName?: string; contextWindow?: number } + >, +): CodexCatalogModel[] { + return models.map((entry) => + typeof entry === "string" + ? { model: entry } + : { + model: entry.model, + displayName: entry.displayName, + contextWindow: entry.contextWindow, + }, + ); +} + export const codexProviderPresets: CodexProviderPreset[] = [ { name: "OpenAI Official", @@ -108,6 +126,89 @@ export const codexProviderPresets: CodexProviderPreset[] = [ partnerPromotionKey: "patewayai", icon: "pateway", }, + { + name: "火山Agentplan", + websiteUrl: + "https://www.volcengine.com/activity/agentplan?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", + apiKeyUrl: + "https://www.volcengine.com/activity/agentplan?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "ark_agentplan", + "https://ark.cn-beijing.volces.com/api/coding/v3", + "ark-code-latest", + ), + endpointCandidates: ["https://ark.cn-beijing.volces.com/api/coding/v3"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "ark-code-latest", + displayName: "Ark Code Latest", + contextWindow: 256000, + }, + ]), + category: "cn_official", + isPartner: true, + partnerPromotionKey: "volcengine_agentplan", + icon: "huoshan", + iconColor: "#3370FF", + }, + { + name: "BytePlus", + websiteUrl: + "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", + apiKeyUrl: + "https://www.byteplus.com/en/product/modelark?utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "byteplus", + "https://ark.ap-southeast.bytepluses.com/api/coding/v3", + "ark-code-latest", + ), + endpointCandidates: [ + "https://ark.ap-southeast.bytepluses.com/api/coding/v3", + ], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "ark-code-latest", + displayName: "Ark Code Latest", + contextWindow: 256000, + }, + ]), + category: "cn_official", + isPartner: true, + partnerPromotionKey: "byteplus", + icon: "byteplus", + iconColor: "#3370FF", + }, + { + name: "DouBaoSeed", + websiteUrl: + "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", + apiKeyUrl: + "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D&utm_campaign=hw&utm_content=ccswitch&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ccswitch", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "doubaoseed", + "https://ark.cn-beijing.volces.com/api/v3", + "doubao-seed-2-0-code-preview-latest", + ), + endpointCandidates: ["https://ark.cn-beijing.volces.com/api/v3"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "doubao-seed-2-0-code-preview-latest", + displayName: "Doubao Seed Code Preview", + contextWindow: 256000, + }, + ]), + category: "cn_official", + isPartner: true, + partnerPromotionKey: "doubaoseed", + icon: "doubao", + iconColor: "#3370FF", + }, { name: "Azure OpenAI", websiteUrl: @@ -136,6 +237,481 @@ requires_openai_auth = true`, icon: "azure", iconColor: "#0078D4", }, + { + name: "DeepSeek", + websiteUrl: "https://platform.deepseek.com", + apiKeyUrl: "https://platform.deepseek.com/api_keys", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "deepseek", + "https://api.deepseek.com", + "deepseek-v4-flash", + ), + endpointCandidates: ["https://api.deepseek.com"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "deepseek-v4-flash", + displayName: "DeepSeek V4 Flash", + contextWindow: 1000000, + }, + { + model: "deepseek-v4-pro", + displayName: "DeepSeek V4 Pro", + contextWindow: 1000000, + }, + ]), + category: "cn_official", + icon: "deepseek", + iconColor: "#1E88E5", + }, + { + name: "Zhipu GLM", + websiteUrl: "https://open.bigmodel.cn", + apiKeyUrl: "https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "zhipu_glm", + "https://open.bigmodel.cn/api/paas/v4", + "glm-5", + ), + endpointCandidates: ["https://open.bigmodel.cn/api/paas/v4"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { model: "glm-5", displayName: "GLM-5", contextWindow: 200000 }, + ]), + category: "cn_official", + icon: "zhipu", + iconColor: "#0F62FE", + }, + { + name: "Zhipu GLM en", + websiteUrl: "https://z.ai", + apiKeyUrl: "https://z.ai/subscribe?ic=8JVLJQFSKB", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "zhipu_glm_en", + "https://api.z.ai/api/paas/v4", + "glm-5", + ), + endpointCandidates: ["https://api.z.ai/api/paas/v4"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { model: "glm-5", displayName: "GLM-5", contextWindow: 200000 }, + ]), + category: "cn_official", + icon: "zhipu", + iconColor: "#0F62FE", + }, + { + name: "Baidu Qianfan Coding Plan", + websiteUrl: "https://cloud.baidu.com/product/qianfan_modelbuilder", + apiKeyUrl: + "https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "qianfan_coding", + "https://qianfan.baidubce.com/v2/coding", + "qianfan-code-latest", + ), + endpointCandidates: ["https://qianfan.baidubce.com/v2/coding"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "qianfan-code-latest", + displayName: "Qianfan Code Latest", + contextWindow: 131072, + }, + ]), + category: "cn_official", + icon: "baidu", + iconColor: "#2932E1", + }, + { + name: "Bailian", + websiteUrl: "https://bailian.console.aliyun.com", + apiKeyUrl: "https://bailian.console.aliyun.com/#/api-key", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "bailian", + "https://dashscope.aliyuncs.com/compatible-mode/v1", + "qwen3-coder-plus", + ), + endpointCandidates: ["https://dashscope.aliyuncs.com/compatible-mode/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "qwen3-coder-plus", + displayName: "Qwen3 Coder Plus", + contextWindow: 1000000, + }, + { model: "qwen3-max", displayName: "Qwen3 Max", contextWindow: 262144 }, + ]), + category: "cn_official", + icon: "bailian", + iconColor: "#624AFF", + }, + { + name: "Kimi", + websiteUrl: "https://platform.moonshot.cn/console", + apiKeyUrl: "https://platform.moonshot.cn/console/api-keys", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "kimi", + "https://api.moonshot.cn/v1", + "kimi-k2.6", + ), + endpointCandidates: ["https://api.moonshot.cn/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { model: "kimi-k2.6", displayName: "Kimi K2.6", contextWindow: 262144 }, + ]), + category: "cn_official", + icon: "kimi", + iconColor: "#6366F1", + }, + { + name: "Kimi For Coding", + websiteUrl: "https://www.kimi.com/code/docs/", + apiKeyUrl: "https://www.kimi.com/code/", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "kimi_coding", + "https://api.kimi.com/coding/v1", + "kimi-for-coding", + ), + endpointCandidates: ["https://api.kimi.com/coding/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "kimi-for-coding", + displayName: "Kimi For Coding", + contextWindow: 262144, + }, + ]), + category: "cn_official", + icon: "kimi", + iconColor: "#6366F1", + }, + { + name: "StepFun", + websiteUrl: "https://platform.stepfun.com/step-plan", + apiKeyUrl: "https://platform.stepfun.com/interface-key", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "stepfun", + "https://api.stepfun.com/step_plan/v1", + "step-3.5-flash-2603", + ), + endpointCandidates: ["https://api.stepfun.com/step_plan/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "step-3.5-flash-2603", + displayName: "Step 3.5 Flash 2603", + contextWindow: 262144, + }, + { + model: "step-3.5-flash", + displayName: "Step 3.5 Flash", + contextWindow: 262144, + }, + ]), + category: "cn_official", + icon: "stepfun", + iconColor: "#16D6D2", + }, + { + name: "StepFun en", + websiteUrl: "https://platform.stepfun.ai/step-plan", + apiKeyUrl: "https://platform.stepfun.ai/interface-key", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "stepfun_en", + "https://api.stepfun.ai/step_plan/v1", + "step-3.5-flash-2603", + ), + endpointCandidates: ["https://api.stepfun.ai/step_plan/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "step-3.5-flash-2603", + displayName: "Step 3.5 Flash 2603", + contextWindow: 262144, + }, + { + model: "step-3.5-flash", + displayName: "Step 3.5 Flash", + contextWindow: 262144, + }, + ]), + category: "cn_official", + icon: "stepfun", + iconColor: "#16D6D2", + }, + { + name: "ModelScope", + websiteUrl: "https://modelscope.cn", + apiKeyUrl: "https://modelscope.cn/my/myaccesstoken", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "modelscope", + "https://api-inference.modelscope.cn/v1", + "ZhipuAI/GLM-5", + ), + endpointCandidates: ["https://api-inference.modelscope.cn/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "ZhipuAI/GLM-5", + displayName: "ZhipuAI / GLM-5", + contextWindow: 200000, + }, + ]), + category: "aggregator", + icon: "modelscope", + iconColor: "#624AFF", + }, + { + name: "Longcat", + websiteUrl: "https://longcat.chat/platform", + apiKeyUrl: "https://longcat.chat/platform/api_keys", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "longcat", + "https://api.longcat.chat/openai/v1", + "LongCat-Flash-Chat", + ), + endpointCandidates: ["https://api.longcat.chat/openai/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "LongCat-Flash-Chat", + displayName: "LongCat Flash Chat", + contextWindow: 262144, + }, + ]), + category: "cn_official", + icon: "longcat", + iconColor: "#29E154", + }, + { + name: "MiniMax", + websiteUrl: "https://platform.minimaxi.com", + apiKeyUrl: "https://platform.minimaxi.com/subscribe/coding-plan", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "minimax", + "https://api.minimaxi.com/v1", + "MiniMax-M2.7", + ), + endpointCandidates: ["https://api.minimaxi.com/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "MiniMax-M2.7", + displayName: "MiniMax M2.7", + contextWindow: 200000, + }, + ]), + category: "cn_official", + isPartner: true, + partnerPromotionKey: "minimax_cn", + theme: { + backgroundColor: "#f64551", + textColor: "#FFFFFF", + }, + icon: "minimax", + iconColor: "#FF6B6B", + }, + { + name: "MiniMax en", + websiteUrl: "https://platform.minimax.io", + apiKeyUrl: "https://platform.minimax.io/subscribe/coding-plan", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "minimax_en", + "https://api.minimax.io/v1", + "MiniMax-M2.7", + ), + endpointCandidates: ["https://api.minimax.io/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "MiniMax-M2.7", + displayName: "MiniMax M2.7", + contextWindow: 200000, + }, + ]), + category: "cn_official", + isPartner: true, + partnerPromotionKey: "minimax_en", + theme: { + backgroundColor: "#f64551", + textColor: "#FFFFFF", + }, + icon: "minimax", + iconColor: "#FF6B6B", + }, + { + name: "BaiLing", + websiteUrl: "https://alipaytbox.yuque.com/sxs0ba/ling/get_started", + apiKeyUrl: "https://ling.tbox.cn/open", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "bailing", + "https://api.tbox.cn/api/llm/v1", + "Ling-2.5-1T", + ), + endpointCandidates: ["https://api.tbox.cn/api/llm/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "Ling-2.5-1T", + displayName: "Ling-2.5-1T", + contextWindow: 131072, + }, + ]), + category: "cn_official", + }, + { + name: "Xiaomi MiMo", + websiteUrl: "https://platform.xiaomimimo.com", + apiKeyUrl: "https://platform.xiaomimimo.com/#/console/api-keys", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "xiaomi_mimo", + "https://api.xiaomimimo.com/v1", + "mimo-v2.5-pro", + ), + endpointCandidates: ["https://api.xiaomimimo.com/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "mimo-v2.5-pro", + displayName: "MiMo V2.5 Pro", + contextWindow: 1048576, + }, + ]), + category: "cn_official", + icon: "xiaomimimo", + iconColor: "#000000", + }, + { + name: "Xiaomi MiMo Token Plan (China)", + websiteUrl: "https://platform.xiaomimimo.com/#/token-plan", + apiKeyUrl: "https://platform.xiaomimimo.com/#/console/plan-manage", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "xiaomi_mimo_token_plan", + "https://token-plan-cn.xiaomimimo.com/v1", + "mimo-v2.5-pro", + ), + endpointCandidates: ["https://token-plan-cn.xiaomimimo.com/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "mimo-v2.5-pro", + displayName: "MiMo V2.5 Pro", + contextWindow: 1048576, + }, + ]), + category: "cn_official", + icon: "xiaomimimo", + iconColor: "#000000", + }, + { + name: "SiliconFlow", + websiteUrl: "https://siliconflow.cn", + apiKeyUrl: "https://cloud.siliconflow.cn/i/drGuwc9k", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "siliconflow", + "https://api.siliconflow.cn/v1", + "Pro/MiniMaxAI/MiniMax-M2.7", + ), + endpointCandidates: ["https://api.siliconflow.cn/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "Pro/MiniMaxAI/MiniMax-M2.7", + displayName: "Pro / MiniMax M2.7", + contextWindow: 200000, + }, + ]), + category: "aggregator", + isPartner: true, + partnerPromotionKey: "siliconflow", + icon: "siliconflow", + iconColor: "#6E29F6", + }, + { + name: "SiliconFlow en", + websiteUrl: "https://siliconflow.com", + apiKeyUrl: "https://cloud.siliconflow.cn/i/drGuwc9k", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "siliconflow_en", + "https://api.siliconflow.com/v1", + "MiniMaxAI/MiniMax-M2.7", + ), + endpointCandidates: ["https://api.siliconflow.com/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "MiniMaxAI/MiniMax-M2.7", + displayName: "MiniMax M2.7", + contextWindow: 200000, + }, + ]), + category: "aggregator", + isPartner: true, + partnerPromotionKey: "siliconflow", + icon: "siliconflow", + iconColor: "#000000", + }, + { + name: "Novita AI", + websiteUrl: "https://novita.ai", + apiKeyUrl: "https://novita.ai", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "novita", + "https://api.novita.ai/openai/v1", + "zai-org/glm-5", + ), + endpointCandidates: ["https://api.novita.ai/openai/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { model: "zai-org/glm-5", displayName: "GLM-5", contextWindow: 202800 }, + ]), + category: "aggregator", + icon: "novita", + iconColor: "#000000", + }, + { + name: "Nvidia", + websiteUrl: "https://build.nvidia.com", + apiKeyUrl: "https://build.nvidia.com/settings/api-keys", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "nvidia", + "https://integrate.api.nvidia.com/v1", + "moonshotai/kimi-k2.5", + ), + endpointCandidates: ["https://integrate.api.nvidia.com/v1"], + apiFormat: "openai_chat", + modelCatalog: modelCatalog([ + { + model: "moonshotai/kimi-k2.5", + displayName: "Kimi K2.5", + contextWindow: 262144, + }, + ]), + category: "aggregator", + icon: "nvidia", + iconColor: "#000000", + }, { name: "AiHubMix", websiteUrl: "https://aihubmix.com", diff --git a/tests/config/codexChatProviderPresets.test.ts b/tests/config/codexChatProviderPresets.test.ts new file mode 100644 index 000000000..74ed5306a --- /dev/null +++ b/tests/config/codexChatProviderPresets.test.ts @@ -0,0 +1,212 @@ +import { describe, expect, it } from "vitest"; +import { codexProviderPresets } from "@/config/codexProviderPresets"; +import { + extractCodexBaseUrl, + extractCodexModelName, + extractCodexWireApi, +} from "@/utils/providerConfigUtils"; + +const expectedChatPresets = new Map< + string, + { baseUrl: string; contextWindows: Record } +>([ + [ + "火山Agentplan", + { + baseUrl: "https://ark.cn-beijing.volces.com/api/coding/v3", + contextWindows: { "ark-code-latest": 256000 }, + }, + ], + [ + "BytePlus", + { + baseUrl: "https://ark.ap-southeast.bytepluses.com/api/coding/v3", + contextWindows: { "ark-code-latest": 256000 }, + }, + ], + [ + "DouBaoSeed", + { + baseUrl: "https://ark.cn-beijing.volces.com/api/v3", + contextWindows: { "doubao-seed-2-0-code-preview-latest": 256000 }, + }, + ], + [ + "DeepSeek", + { + baseUrl: "https://api.deepseek.com", + contextWindows: { + "deepseek-v4-flash": 1000000, + "deepseek-v4-pro": 1000000, + }, + }, + ], + [ + "Zhipu GLM", + { + baseUrl: "https://open.bigmodel.cn/api/paas/v4", + contextWindows: { "glm-5": 200000 }, + }, + ], + [ + "Zhipu GLM en", + { + baseUrl: "https://api.z.ai/api/paas/v4", + contextWindows: { "glm-5": 200000 }, + }, + ], + [ + "Baidu Qianfan Coding Plan", + { + baseUrl: "https://qianfan.baidubce.com/v2/coding", + contextWindows: { "qianfan-code-latest": 131072 }, + }, + ], + [ + "Bailian", + { + baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1", + contextWindows: { + "qwen3-coder-plus": 1000000, + "qwen3-max": 262144, + }, + }, + ], + [ + "Kimi", + { + baseUrl: "https://api.moonshot.cn/v1", + contextWindows: { "kimi-k2.6": 262144 }, + }, + ], + [ + "Kimi For Coding", + { + baseUrl: "https://api.kimi.com/coding/v1", + contextWindows: { "kimi-for-coding": 262144 }, + }, + ], + [ + "StepFun", + { + baseUrl: "https://api.stepfun.com/step_plan/v1", + contextWindows: { + "step-3.5-flash-2603": 262144, + "step-3.5-flash": 262144, + }, + }, + ], + [ + "StepFun en", + { + baseUrl: "https://api.stepfun.ai/step_plan/v1", + contextWindows: { + "step-3.5-flash-2603": 262144, + "step-3.5-flash": 262144, + }, + }, + ], + [ + "ModelScope", + { + baseUrl: "https://api-inference.modelscope.cn/v1", + contextWindows: { "ZhipuAI/GLM-5": 200000 }, + }, + ], + [ + "Longcat", + { + baseUrl: "https://api.longcat.chat/openai/v1", + contextWindows: { "LongCat-Flash-Chat": 262144 }, + }, + ], + [ + "MiniMax", + { + baseUrl: "https://api.minimaxi.com/v1", + contextWindows: { "MiniMax-M2.7": 200000 }, + }, + ], + [ + "MiniMax en", + { + baseUrl: "https://api.minimax.io/v1", + contextWindows: { "MiniMax-M2.7": 200000 }, + }, + ], + [ + "BaiLing", + { + baseUrl: "https://api.tbox.cn/api/llm/v1", + contextWindows: { "Ling-2.5-1T": 131072 }, + }, + ], + [ + "Xiaomi MiMo", + { + baseUrl: "https://api.xiaomimimo.com/v1", + contextWindows: { "mimo-v2.5-pro": 1048576 }, + }, + ], + [ + "Xiaomi MiMo Token Plan (China)", + { + baseUrl: "https://token-plan-cn.xiaomimimo.com/v1", + contextWindows: { "mimo-v2.5-pro": 1048576 }, + }, + ], + [ + "SiliconFlow", + { + baseUrl: "https://api.siliconflow.cn/v1", + contextWindows: { "Pro/MiniMaxAI/MiniMax-M2.7": 200000 }, + }, + ], + [ + "SiliconFlow en", + { + baseUrl: "https://api.siliconflow.com/v1", + contextWindows: { "MiniMaxAI/MiniMax-M2.7": 200000 }, + }, + ], + [ + "Novita AI", + { + baseUrl: "https://api.novita.ai/openai/v1", + contextWindows: { "zai-org/glm-5": 202800 }, + }, + ], + [ + "Nvidia", + { + baseUrl: "https://integrate.api.nvidia.com/v1", + contextWindows: { "moonshotai/kimi-k2.5": 262144 }, + }, + ], +]); + +describe("Codex Chat provider presets", () => { + it("marks migrated Chat Completions presets for local routing", () => { + for (const [name, expected] of expectedChatPresets) { + const preset = codexProviderPresets.find((item) => item.name === name); + + expect(preset, `${name} preset`).toBeDefined(); + expect(preset?.apiFormat).toBe("openai_chat"); + expect(extractCodexBaseUrl(preset?.config)).toBe(expected.baseUrl); + expect(extractCodexWireApi(preset?.config)).toBe("responses"); + expect(preset?.endpointCandidates).toContain(expected.baseUrl); + expect(preset?.modelCatalog?.length).toBeGreaterThan(0); + expect(extractCodexModelName(preset?.config)).toBe( + preset?.modelCatalog?.[0]?.model, + ); + expect( + Object.fromEntries( + (preset?.modelCatalog ?? []).map((model) => [ + model.model, + model.contextWindow, + ]), + ), + ).toEqual(expected.contextWindows); + } + }); +}); diff --git a/tests/config/providerPresetOrder.test.ts b/tests/config/providerPresetOrder.test.ts index 6f15918ea..4f42c8f09 100644 --- a/tests/config/providerPresetOrder.test.ts +++ b/tests/config/providerPresetOrder.test.ts @@ -46,8 +46,14 @@ describe("provider preset order", () => { }); }); - it("Codex 预设把 PatewayAI 放在胜算云后面", () => { - expectInOrder(namesOf(codexProviderPresets), ["Shengsuanyun", "PatewayAI"]); + it("Codex 预设按合作伙伴优先顺序排列", () => { + expectInOrder(namesOf(codexProviderPresets), [ + "Shengsuanyun", + "PatewayAI", + "火山Agentplan", + "BytePlus", + "DouBaoSeed", + ]); }); it("OpenCode 预设把火山、BytePlus、DouBaoSeed 放在胜算云后面", () => { diff --git a/tests/config/therouterProviderPresets.test.ts b/tests/config/therouterProviderPresets.test.ts index 77a180e13..aec49bc66 100644 --- a/tests/config/therouterProviderPresets.test.ts +++ b/tests/config/therouterProviderPresets.test.ts @@ -40,7 +40,9 @@ describe("TheRouter provider presets", () => { "https://api.therouter.ai/v1", ]); expect(preset?.auth).toEqual({ OPENAI_API_KEY: "" }); - expect(preset?.config).toContain('model_provider = "therouter"'); + expect(preset?.config).toContain('model_provider = "custom"'); + expect(preset?.config).toContain("[model_providers.custom]"); + expect(preset?.config).toContain('name = "therouter"'); expect(preset?.config).toContain('model = "openai/gpt-5.3-codex"'); expect(preset?.config).toContain( 'base_url = "https://api.therouter.ai/v1"',