import { CLOUDFLARE_AI_GATEWAY_ANTHROPIC_BASE_URL, CLOUDFLARE_AI_GATEWAY_COMPAT_BASE_URL, CLOUDFLARE_AI_GATEWAY_OPENAI_BASE_URL, CLOUDFLARE_WORKERS_AI_BASE_URL, } from "./providers/cloudflare.js"; import type { AnthropicMessagesCompat, Api, Model, ModelThinkingLevel, OpenAICompletionsCompat } from "./types.js"; export const MODELS_DEV_API_URL = "https://models.dev/api.json"; export interface ModelsDevModel { id?: string; name?: string; tool_call?: boolean; reasoning?: boolean; status?: string; limit?: { context?: number; output?: number; }; cost?: { input?: number; output?: number; cache_read?: number; cache_write?: number; }; modalities?: { input?: string[]; output?: string[]; }; provider?: { npm?: string; }; } export interface ModelsDevProviderData { models?: Record; } const COPILOT_STATIC_HEADERS = { "User-Agent": "GitHubCopilotChat/0.35.0", "Editor-Version": "vscode/1.107.0", "Editor-Plugin-Version": "copilot-chat/0.35.0", "Copilot-Integration-Id": "vscode-chat", } as const; const KIMI_STATIC_HEADERS = { "User-Agent": "KimiCLI/1.5", } as const; const ZAI_TOOL_STREAM_UNSUPPORTED_MODELS = new Set(["glm-4.5", "glm-4.5-air", "glm-4.5-flash", "glm-4.5v"]); const EAGER_TOOL_INPUT_STREAMING_UNSUPPORTED_ANTHROPIC_MODELS = new Set([ "github-copilot:claude-haiku-4.5", "github-copilot:claude-sonnet-4", "github-copilot:claude-sonnet-4.5", ]); const DEEPSEEK_V4_THINKING_LEVEL_MAP: Partial> = { minimal: null, low: null, medium: null, high: "high", xhigh: "max", max: null, }; const KIMI_K3_THINKING_LEVEL_MAP: Partial> = { off: null, minimal: null, low: null, medium: null, high: null, xhigh: null, max: "max", }; const DEEPSEEK_V4_COMPAT: OpenAICompletionsCompat = { requiresReasoningContentOnAssistantMessages: true, thinkingFormat: "deepseek", }; const ZAI_THINKING_COMPAT: OpenAICompletionsCompat = { supportsReasoningEffort: false, thinkingFormat: "zai", }; const OPENAI_RESPONSES_NONE_REASONING_MODELS = new Set([ "gpt-5.1", "gpt-5.2", "gpt-5.3-codex", "gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano", "gpt-5.5", "gpt-5.6", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", ]); function getAnthropicMessagesCompat(provider: string, modelId: string): AnthropicMessagesCompat | undefined { return EAGER_TOOL_INPUT_STREAMING_UNSUPPORTED_ANTHROPIC_MODELS.has(`${provider}:${modelId}`) ? { supportsEagerToolInputStreaming: false } : undefined; } function getBedrockBaseUrl(modelId: string): string { if (modelId.startsWith("eu.")) return "https://bedrock-runtime.eu-central-1.amazonaws.com"; if (modelId.startsWith("au.")) return "https://bedrock-runtime.ap-southeast-2.amazonaws.com"; if (modelId.startsWith("jp.")) return "https://bedrock-runtime.ap-northeast-1.amazonaws.com"; return "https://bedrock-runtime.us-east-1.amazonaws.com"; } function mergeThinkingLevelMap(model: Model, map: NonNullable["thinkingLevelMap"]>): void { model.thinkingLevelMap = { ...model.thinkingLevelMap, ...map }; } function supportsOpenAiXhigh(modelId: string): boolean { return ( modelId.includes("gpt-5.2") || modelId.includes("gpt-5.3") || modelId.includes("gpt-5.4") || modelId.includes("gpt-5.5") || modelId.includes("gpt-5.6") || modelId.includes("gpt-6") ); } function isGoogleThinkingApi(model: Model): boolean { return model.api === "google-generative-ai" || model.api === "google-vertex"; } function isGemini3ProModel(modelId: string): boolean { return /gemini-3(?:\.\d+)?-pro/.test(modelId.toLowerCase()); } function isGemini3FlashModel(modelId: string): boolean { return /gemini-3(?:\.\d+)?-flash/.test(modelId.toLowerCase()); } function isGemma4Model(modelId: string): boolean { return /gemma-?4/.test(modelId.toLowerCase()); } export function applyThinkingLevelMetadata(model: Model): void { if ( (model.api === "openai-responses" || model.api === "azure-openai-responses") && (model.id.startsWith("gpt-5") || model.id.startsWith("gpt-6")) ) { mergeThinkingLevelMap(model, { off: null }); } if ( model.api === "openai-responses" && model.provider === "openai" && OPENAI_RESPONSES_NONE_REASONING_MODELS.has(model.id) ) { mergeThinkingLevelMap(model, { off: "none" }); } if (supportsOpenAiXhigh(model.id)) { mergeThinkingLevelMap(model, { xhigh: "xhigh" }); } if (model.id.includes("gpt-5.6") || model.id.includes("gpt-6")) { mergeThinkingLevelMap(model, { minimal: null, max: "max" }); } if ( model.id.includes("opus-4-6") || model.id.includes("opus-4.6") || model.id.includes("sonnet-4-6") || model.id.includes("sonnet-4.6") ) { mergeThinkingLevelMap(model, { max: "max" }); } if ( model.id.includes("opus-4-7") || model.id.includes("opus-4.7") || model.id.includes("opus-4-8") || model.id.includes("opus-4.8") || model.id.includes("opus-5") || model.id.includes("sonnet-5") ) { mergeThinkingLevelMap(model, { xhigh: "xhigh", max: "max" }); } if (model.id.includes("fable-5") || model.id.includes("mythos-5")) { mergeThinkingLevelMap(model, { off: null, xhigh: "xhigh", max: "max" }); } if (model.id.includes("mythos-preview")) { mergeThinkingLevelMap(model, { off: null, max: "max" }); } if ( model.api === "openai-completions" && (model.id.includes("deepseek-v4") || model.id === "deepseek-flash" || model.id.endsWith("/deepseek-flash")) ) { mergeThinkingLevelMap(model, DEEPSEEK_V4_THINKING_LEVEL_MAP); } const kimiK3Id = model.id.toLowerCase(); if (!model.thinkingLevelMap && (/^k3(-|$)/.test(kimiK3Id) || /(^|\/)kimi-k3(-|$)/.test(kimiK3Id))) { mergeThinkingLevelMap(model, KIMI_K3_THINKING_LEVEL_MAP); } if (isGoogleThinkingApi(model) && isGemini3ProModel(model.id)) { mergeThinkingLevelMap(model, { off: null, minimal: null, low: "LOW", medium: null, high: "HIGH" }); } if (isGoogleThinkingApi(model) && isGemini3FlashModel(model.id)) { mergeThinkingLevelMap(model, { off: null }); } if (isGoogleThinkingApi(model) && isGemma4Model(model.id)) { mergeThinkingLevelMap(model, { off: null, minimal: "MINIMAL", low: null, medium: null, high: "HIGH" }); } } export function parseModelsDevData(data: Record): Model[] { const models: Model[] = []; const dataObj = data as Record; // Amazon Bedrock if (dataObj["amazon-bedrock"]?.models) { for (const [modelId, model] of Object.entries(dataObj["amazon-bedrock"].models)) { if (model.tool_call !== true) continue; if (modelId.startsWith("ai21.jamba") || modelId.startsWith("mistral.mistral-7b-instruct-v0")) continue; models.push({ id: modelId, name: model.name || modelId, api: "bedrock-converse-stream", provider: "amazon-bedrock", baseUrl: getBedrockBaseUrl(modelId), reasoning: model.reasoning === true, input: (model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"]) as ("text" | "image")[], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // Anthropic if (dataObj.anthropic?.models) { for (const [modelId, model] of Object.entries(dataObj.anthropic.models)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "anthropic-messages", provider: "anthropic", baseUrl: "https://api.anthropic.com", reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // Google const googleUnsupportedApiModelPattern = /(^|[-_.])(live|deep-research|computer-use)($|[-_.])/i; if (dataObj.google?.models) { for (const [modelId, model] of Object.entries(dataObj.google.models)) { if (model.tool_call !== true) continue; if (googleUnsupportedApiModelPattern.test(modelId)) continue; if (model.modalities?.output?.includes("image")) continue; models.push({ id: modelId, name: model.name || modelId, api: "google-generative-ai", provider: "google", baseUrl: "https://generativelanguage.googleapis.com/v1beta", reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // OpenAI if (dataObj.openai?.models) { for (const [modelId, model] of Object.entries(dataObj.openai.models)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "openai-responses", provider: "openai", baseUrl: "https://api.openai.com/v1", reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // Groq if (dataObj.groq?.models) { for (const [modelId, model] of Object.entries(dataObj.groq.models)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "openai-completions", provider: "groq", baseUrl: "https://api.groq.com/openai/v1", reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // Cerebras if (dataObj.cerebras?.models) { for (const [modelId, model] of Object.entries(dataObj.cerebras.models)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "openai-completions", provider: "cerebras", baseUrl: "https://api.cerebras.ai/v1", reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // Cloudflare Workers AI if (dataObj["cloudflare-workers-ai"]?.models) { for (const [modelId, model] of Object.entries(dataObj["cloudflare-workers-ai"].models)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "openai-completions", provider: "cloudflare-workers-ai", baseUrl: CLOUDFLARE_WORKERS_AI_BASE_URL, reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, compat: { sendSessionAffinityHeaders: true }, }); } } // Cloudflare AI Gateway if (dataObj["cloudflare-ai-gateway"]?.models) { for (const [prefixedId, model] of Object.entries(dataObj["cloudflare-ai-gateway"].models)) { if (model.tool_call !== true) continue; const slashIdx = prefixedId.indexOf("/"); if (slashIdx === -1) continue; const upstream = prefixedId.slice(0, slashIdx); const nativeId = prefixedId.slice(slashIdx + 1); let api: "anthropic-messages" | "openai-completions" | "openai-responses"; let baseUrl: string; let id: string; if (upstream === "openai") { api = "openai-responses"; baseUrl = CLOUDFLARE_AI_GATEWAY_OPENAI_BASE_URL; id = nativeId; } else if (upstream === "anthropic") { api = "anthropic-messages"; baseUrl = CLOUDFLARE_AI_GATEWAY_ANTHROPIC_BASE_URL; id = nativeId; } else if (upstream === "workers-ai") { api = "openai-completions"; baseUrl = CLOUDFLARE_AI_GATEWAY_COMPAT_BASE_URL; id = prefixedId; } else { continue; } const compat = upstream === "workers-ai" ? { sendSessionAffinityHeaders: true } : undefined; models.push({ id, name: model.name || id, api, provider: "cloudflare-ai-gateway", baseUrl, reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, ...(compat ? { compat } : {}), }); } } // xAI if (dataObj.xai?.models) { for (const [modelId, model] of Object.entries(dataObj.xai.models)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "openai-completions", provider: "xai", baseUrl: "https://api.x.ai/v1", reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // zAI if (dataObj["zai-coding-plan"]?.models) { for (const [modelId, model] of Object.entries(dataObj["zai-coding-plan"].models)) { if (model.tool_call !== true) continue; const supportsImage = model.modalities?.input?.includes("image"); models.push({ id: modelId, name: model.name || modelId, api: "openai-completions", provider: "zai", baseUrl: "https://api.z.ai/api/coding/paas/v4", reasoning: model.reasoning === true, input: supportsImage ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, compat: { supportsDeveloperRole: false, thinkingFormat: ZAI_THINKING_COMPAT.thinkingFormat, ...(!ZAI_TOOL_STREAM_UNSUPPORTED_MODELS.has(modelId) ? { zaiToolStream: true } : {}), }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // Mistral if (dataObj.mistral?.models) { for (const [modelId, model] of Object.entries(dataObj.mistral.models)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "mistral-conversations", provider: "mistral", baseUrl: "https://api.mistral.ai", reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // Hugging Face if (dataObj.huggingface?.models) { for (const [modelId, model] of Object.entries(dataObj.huggingface.models)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "openai-completions", provider: "huggingface", baseUrl: "https://router.huggingface.co/v1", reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, compat: { supportsDeveloperRole: false, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // Fireworks if (dataObj["fireworks-ai"]?.models) { for (const [modelId, model] of Object.entries(dataObj["fireworks-ai"].models)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "anthropic-messages", provider: "fireworks", baseUrl: "https://api.fireworks.ai/inference", reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // OpenCode and OpenCode Go const opencodeVariants = [ { key: "opencode", provider: "opencode" as const, basePath: "https://opencode.ai/zen" }, { key: "opencode-go", provider: "opencode-go" as const, basePath: "https://opencode.ai/zen/go" }, ]; for (const variant of opencodeVariants) { if (!dataObj[variant.key]?.models) continue; for (const [modelId, model] of Object.entries(dataObj[variant.key]!.models!)) { if (model.tool_call !== true) continue; if (model.status === "deprecated") continue; if (modelId === "gpt-5.3-codex-spark") continue; const npm = model.provider?.npm; let api: Api; let baseUrl: string; let compat: OpenAICompletionsCompat | undefined; if (npm === "@ai-sdk/openai") { api = "openai-responses"; baseUrl = `${variant.basePath}/v1`; } else if (npm === "@ai-sdk/anthropic") { api = "anthropic-messages"; baseUrl = variant.basePath; } else if (npm === "@ai-sdk/google") { api = "google-generative-ai"; baseUrl = `${variant.basePath}/v1`; } else if (npm === "@ai-sdk/alibaba") { api = "openai-completions"; baseUrl = `${variant.basePath}/v1`; compat = { cacheControlFormat: "anthropic" }; } else { api = "openai-completions"; baseUrl = `${variant.basePath}/v1`; } if (variant.provider === "opencode-go") { if (modelId === "minimax-m2.7" || (npm === "@ai-sdk/anthropic" && modelId.startsWith("qwen"))) { api = "openai-completions"; baseUrl = `${variant.basePath}/v1`; } if (modelId === "qwen3.5-plus" || modelId === "qwen3.6-plus") { api = "openai-completions"; baseUrl = `${variant.basePath}/v1`; compat = { ...(compat ?? {}), thinkingFormat: "qwen" }; } } models.push({ id: modelId, name: model.name || modelId, api, provider: variant.provider, baseUrl, reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, ...(compat ? { compat } : {}), contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // GitHub Copilot if (dataObj["github-copilot"]?.models) { for (const [modelId, model] of Object.entries(dataObj["github-copilot"].models)) { if (model.tool_call !== true) continue; if (model.status === "deprecated") continue; const isCopilotClaude = modelId.startsWith("claude-"); const needsResponsesApi = modelId.startsWith("gpt-5") || modelId.startsWith("oswe"); const api: Api = isCopilotClaude ? "anthropic-messages" : needsResponsesApi ? "openai-responses" : "openai-completions"; const anthropicCompat = api === "anthropic-messages" ? getAnthropicMessagesCompat("github-copilot", modelId) : undefined; const copilotModel: Model = { id: modelId, name: model.name || modelId, api, provider: "github-copilot", baseUrl: "https://api.individual.githubcopilot.com", reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 128000, maxTokens: model.limit?.output || 8192, headers: { ...COPILOT_STATIC_HEADERS }, ...(anthropicCompat ? { compat: anthropicCompat } : {}), ...(api === "openai-completions" ? { compat: { supportsStore: false, supportsDeveloperRole: false, supportsReasoningEffort: false, }, } : {}), }; models.push(copilotModel); } } // MiniMax const minimaxVariants = [ { key: "minimax", provider: "minimax" as const, baseUrl: "https://api.minimax.io/anthropic" }, { key: "minimax-cn", provider: "minimax-cn" as const, baseUrl: "https://api.minimaxi.com/anthropic" }, ]; for (const { key, provider, baseUrl } of minimaxVariants) { if (dataObj[key]?.models) { for (const [modelId, model] of Object.entries(dataObj[key]!.models!)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "anthropic-messages", provider, baseUrl, reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } } // Kimi for Coding if (dataObj["kimi-for-coding"]?.models) { const kimiModels = dataObj["kimi-for-coding"].models; const hasCanonicalModel = Object.hasOwn(kimiModels, "kimi-for-coding"); const kimiAliases = new Set(["k2p5", "k2p6"]); for (const [modelId, model] of Object.entries(kimiModels)) { if (model.tool_call !== true) continue; if (kimiAliases.has(modelId) && hasCanonicalModel) continue; const normalizedId = kimiAliases.has(modelId) ? "kimi-for-coding" : modelId; const normalizedName = kimiAliases.has(modelId) ? "Kimi For Coding" : model.name || normalizedId; models.push({ id: normalizedId, name: normalizedName, api: "anthropic-messages", provider: "kimi-coding", baseUrl: "https://api.kimi.com/coding", headers: { ...KIMI_STATIC_HEADERS }, reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } // Moonshot AI const moonshotVariants = [ { key: "moonshotai", provider: "moonshotai" as const, baseUrl: "https://api.moonshot.ai/v1" }, { key: "moonshotai-cn", provider: "moonshotai-cn" as const, baseUrl: "https://api.moonshot.cn/v1" }, ]; const moonshotCompat: OpenAICompletionsCompat = { supportsStore: false, supportsDeveloperRole: false, supportsReasoningEffort: false, maxTokensField: "max_tokens", supportsStrictMode: false, }; for (const { key, provider, baseUrl } of moonshotVariants) { if (!dataObj[key]?.models) continue; for (const [modelId, model] of Object.entries(dataObj[key]!.models!)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "openai-completions", provider, baseUrl, reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, compat: moonshotCompat, }); } } // Xiaomi MiMo const xiaomiVariants = [ { provider: "xiaomi" as const, baseUrl: "https://api.xiaomimimo.com/anthropic" }, { provider: "xiaomi-token-plan-cn" as const, baseUrl: "https://token-plan-cn.xiaomimimo.com/anthropic" }, { provider: "xiaomi-token-plan-ams" as const, baseUrl: "https://token-plan-ams.xiaomimimo.com/anthropic" }, { provider: "xiaomi-token-plan-sgp" as const, baseUrl: "https://token-plan-sgp.xiaomimimo.com/anthropic" }, ]; if (dataObj.xiaomi?.models) { for (const { provider, baseUrl } of xiaomiVariants) { for (const [modelId, model] of Object.entries(dataObj.xiaomi.models)) { if (model.tool_call !== true) continue; models.push({ id: modelId, name: model.name || modelId, api: "anthropic-messages", provider, baseUrl, reasoning: model.reasoning === true, input: model.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { input: model.cost?.input || 0, output: model.cost?.output || 0, cacheRead: model.cost?.cache_read || 0, cacheWrite: model.cost?.cache_write || 0, }, contextWindow: model.limit?.context || 4096, maxTokens: model.limit?.output || 4096, }); } } } // Apply post-processing and known fixes applyModelsDevOverrides(models); // Mirror OpenAI responses models to Azure OpenAI const azureOpenAiModels: Model[] = models .filter((model) => model.provider === "openai" && model.api === "openai-responses") .map((model) => ({ ...model, api: "azure-openai-responses", provider: "azure-openai-responses", baseUrl: "", })); models.push(...azureOpenAiModels); // Apply thinking level metadata for (const model of models) { applyThinkingLevelMetadata(model); } return models; } function applyModelsDevOverrides(models: Model[]): void { // models.dev prices the Z.AI coding plan at zero, because that plan is billed by // subscription. A zero price is true for the wallet and useless for comparing two // models, so the published per-token prices stand in. Source: artificialanalysis.ai, // retrieved 2026-09-11. const zaiListPrices: Record = { "glm-5.3-flash": { input: 0.15, output: 0.5 }, "glm-5.3": { input: 1.4, output: 4.4 }, }; for (const candidate of models) { if (candidate.provider !== "zai") continue; const listPrice = zaiListPrices[candidate.id]; if (!listPrice) continue; candidate.cost.input = listPrice.input; candidate.cost.output = listPrice.output; } // Fix Claude Opus 4.5 cache pricing from models.dev const opus45 = models.find((m) => m.provider === "anthropic" && m.id === "claude-opus-4-5"); if (opus45) { opus45.cost.cacheRead = 0.5; opus45.cost.cacheWrite = 6.25; } for (const candidate of models) { if (candidate.provider === "amazon-bedrock" && candidate.id.includes("anthropic.claude-opus-4-6-v1")) { candidate.cost.cacheRead = 0.5; candidate.cost.cacheWrite = 6.25; } if ( (candidate.provider === "anthropic" || candidate.provider === "opencode" || candidate.provider === "opencode-go" || candidate.provider === "github-copilot") && (candidate.id === "claude-opus-4-6" || candidate.id === "claude-sonnet-4-6" || candidate.id === "claude-opus-4.6" || candidate.id === "claude-sonnet-4.6") ) { candidate.contextWindow = 1000000; } if ( (candidate.provider === "opencode" || candidate.provider === "opencode-go") && (candidate.id === "claude-sonnet-4-5" || candidate.id === "claude-sonnet-4") ) { candidate.contextWindow = 200000; } if ((candidate.provider === "opencode" || candidate.provider === "opencode-go") && candidate.id === "gpt-5.4") { candidate.contextWindow = 272000; candidate.maxTokens = 128000; } if (candidate.provider === "openai" && (candidate.id === "gpt-5.4" || candidate.id === "gpt-5.5")) { candidate.contextWindow = 272000; candidate.maxTokens = 128000; } if ( candidate.api === "openai-completions" && (candidate.id.includes("deepseek-v4") || candidate.id === "deepseek-flash" || candidate.id.endsWith("/deepseek-flash")) ) { candidate.compat = { ...candidate.compat, ...DEEPSEEK_V4_COMPAT, }; mergeThinkingLevelMap(candidate, DEEPSEEK_V4_THINKING_LEVEL_MAP); } } const minimaxDirectSupportedIds = new Set(["MiniMax-M2.7", "MiniMax-M2.7-highspeed"]); for (const candidate of models) { if ( (candidate.provider === "minimax" || candidate.provider === "minimax-cn") && minimaxDirectSupportedIds.has(candidate.id) ) { candidate.contextWindow = 204800; candidate.maxTokens = 131072; } } for (let i = models.length - 1; i >= 0; i--) { const candidate = models[i]; if ( (candidate.provider === "minimax" || candidate.provider === "minimax-cn") && !minimaxDirectSupportedIds.has(candidate.id) ) { models.splice(i, 1); } } } export async function fetchModelsDevModels( fetchFn: typeof fetch = fetch, url: string = MODELS_DEV_API_URL, ): Promise[]> { const response = await fetchFn(url); if (!response.ok) { throw new Error(`Failed to fetch models from ${url}: ${response.status} ${response.statusText}`); } const data = (await response.json()) as Record; return parseModelsDevData(data); }