diff --git a/src/integrations/compatibility.test.ts b/src/integrations/compatibility.test.ts index ec82c2a694..ee18ae7113 100644 --- a/src/integrations/compatibility.test.ts +++ b/src/integrations/compatibility.test.ts @@ -3,6 +3,7 @@ import { describe, expect, test } from 'bun:test' import './index.js' import { getGateway, + getModelsForGateway, getVendor, } from './index.js' import { @@ -76,6 +77,28 @@ describe('compatibility mappings', () => { } }) + test('Atlas Cloud is modeled as a gateway while preserving the atlas-cloud preset', () => { + expect(getVendor('atlas-cloud')).toBeUndefined() + expect(getGateway('atlas-cloud')?.id).toBe('atlas-cloud') + expect(routeForPreset('atlas-cloud')).toEqual({ + vendorId: 'openai', + gatewayId: 'atlas-cloud', + routeId: 'atlas-cloud', + }) + expect(resolveProfileRoute('atlas-cloud')).toEqual({ + vendorId: 'openai', + gatewayId: 'atlas-cloud', + routeId: 'atlas-cloud', + }) + }) + + test('Atlas Cloud gateway models do not resolve to NearAI-scoped descriptors', () => { + const atlasModels = getModelsForGateway('atlas-cloud') + expect(atlasModels.length).toBeGreaterThan(0) + expect( + atlasModels.filter(model => model.vendorId === 'nearai'), + ).toEqual([]) + }) test('native gateway profile routes use their descriptor vendor', () => { expect(resolveProfileRoute('bedrock')).toEqual({ vendorId: 'anthropic', diff --git a/src/integrations/descriptors.ts b/src/integrations/descriptors.ts index c441612a0b..63030d63c1 100644 --- a/src/integrations/descriptors.ts +++ b/src/integrations/descriptors.ts @@ -103,6 +103,7 @@ export type ReadinessProbeKind = 'ollama-generation' | 'openai-compatible-models export interface ModelCatalogEntry { id: string apiName: string + aliases?: string[] label?: string default?: boolean hidden?: boolean diff --git a/src/integrations/gateways/atlas-cloud.ts b/src/integrations/gateways/atlas-cloud.ts new file mode 100644 index 0000000000..d63b456a7a --- /dev/null +++ b/src/integrations/gateways/atlas-cloud.ts @@ -0,0 +1,94 @@ +import { defineGateway } from '../define.js' + +export default defineGateway({ + id: 'atlas-cloud', + label: 'Atlas Cloud', + vendorId: 'openai', + category: 'hosted', + defaultBaseUrl: 'https://api.atlascloud.ai/v1', + defaultModel: 'deepseek-ai/deepseek-v4-pro', + setup: { + requiresAuth: true, + authMode: 'api-key', + credentialEnvVars: ['ATLAS_CLOUD_API_KEY'], + dedicatedCredentialsOnly: true, + }, + transportConfig: { + kind: 'openai-compatible', + }, + preset: { + id: 'atlas-cloud', + vendorId: 'openai', + description: 'Atlas Cloud AI (OpenAI-compatible)', + apiKeyEnvVars: ['ATLAS_CLOUD_API_KEY'], + modelEnvVars: ['ATLAS_CLOUD_MODEL', 'OPENAI_MODEL'], + }, + validation: { + kind: 'credential-env', + routing: { + matchDefaultBaseUrl: true, + }, + credentialEnvVars: ['ATLAS_CLOUD_API_KEY'], + missingCredentialMessage: + 'Atlas Cloud auth is required. Set ATLAS_CLOUD_API_KEY.', + }, + catalog: { + source: 'static', + // Mirrors https://www.atlascloud.ai/models/list/llm + models: [ + { id: 'deepseek-ai/deepseek-v4-pro', apiName: 'deepseek-ai/deepseek-v4-pro', aliases: ['deepseek-v4-pro'], modelDescriptorId: 'deepseek-v4-pro', label: 'DeepSeek V4 Pro', contextWindow: 1_048_576, maxOutputTokens: 393_216, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'deepseek-ai/deepseek-v4-flash', apiName: 'deepseek-ai/deepseek-v4-flash', aliases: ['deepseek-v4-flash'], modelDescriptorId: 'deepseek-v4-flash', label: 'DeepSeek V4 Flash', contextWindow: 1_048_576, maxOutputTokens: 393_216, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'deepseek-ai/deepseek-v3.2', apiName: 'deepseek-ai/deepseek-v3.2', modelDescriptorId: 'deepseek-ai/deepseek-v3.2', label: 'DeepSeek V3.2', contextWindow: 163_840, maxOutputTokens: 163_840, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'deepseek-ai/DeepSeek-V3.2-Exp', apiName: 'deepseek-ai/DeepSeek-V3.2-Exp', modelDescriptorId: 'deepseek-ai/DeepSeek-V3.2-Exp', label: 'DeepSeek V3.2 Exp', contextWindow: 163_840, maxOutputTokens: 163_840, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'anthropic/claude-opus-4.8', apiName: 'anthropic/claude-opus-4.8', aliases: ['claude-opus-4-8'], modelDescriptorId: 'claude-opus-4-8', label: 'Claude Opus 4.8', contextWindow: 1_000_000, maxOutputTokens: 128_000, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'anthropic/claude-sonnet-4.6', apiName: 'anthropic/claude-sonnet-4.6', aliases: ['claude-sonnet-4-6'], modelDescriptorId: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', contextWindow: 200_000, maxOutputTokens: 64_000, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'anthropic/claude-sonnet-4.6-coding', apiName: 'anthropic/claude-sonnet-4.6-coding', aliases: ['claude-sonnet-4-6-coding'], modelDescriptorId: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6 Coding', contextWindow: 200_000, maxOutputTokens: 64_000, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'anthropic/claude-haiku-4.5-20251001', apiName: 'anthropic/claude-haiku-4.5-20251001', aliases: ['claude-haiku-4-5'], modelDescriptorId: 'claude-haiku-4-5', label: 'Claude Haiku 4.5', contextWindow: 200_000, maxOutputTokens: 64_000, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'anthropic/claude-haiku-4.5-20251001-coding', apiName: 'anthropic/claude-haiku-4.5-20251001-coding', aliases: ['claude-haiku-4-5-coding'], modelDescriptorId: 'claude-haiku-4-5', label: 'Claude Haiku 4.5 Coding', contextWindow: 200_000, maxOutputTokens: 64_000, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'openai/gpt-5.5', apiName: 'openai/gpt-5.5', aliases: ['gpt-5.5'], modelDescriptorId: 'gpt-5.5', label: 'GPT 5.5', contextWindow: 1_050_000, maxOutputTokens: 128_000, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'openai/gpt-5.4', apiName: 'openai/gpt-5.4', aliases: ['gpt-5.4'], modelDescriptorId: 'gpt-5.4', label: 'GPT 5.4', contextWindow: 400_000, maxOutputTokens: 128_000, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'google/gemini-3.5-flash', apiName: 'google/gemini-3.5-flash', aliases: ['gemini-3.5-flash'], modelDescriptorId: 'gemini-3.5-flash', label: 'Gemini 3.5 Flash', contextWindow: 1_048_576, maxOutputTokens: 65_536, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'google/gemini-3.1-pro-preview', apiName: 'google/gemini-3.1-pro-preview', modelDescriptorId: 'google/gemini-3.1-pro-preview', label: 'Gemini 3.1 Pro Preview', contextWindow: 1_000_000, maxOutputTokens: 64_000, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'google/gemini-3.1-flash-lite', apiName: 'google/gemini-3.1-flash-lite', modelDescriptorId: 'google/gemini-3.1-flash-lite', label: 'Gemini 3.1 Flash Lite', contextWindow: 1_048_576, maxOutputTokens: 65_536 }, + { id: 'xai/grok-4.3', apiName: 'xai/grok-4.3', aliases: ['grok-4.3'], modelDescriptorId: 'grok-4.3', label: 'Grok 4.3', contextWindow: 1_000_000, maxOutputTokens: 1_000_000, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'xai/grok-build-0.1', apiName: 'xai/grok-build-0.1', modelDescriptorId: 'xai/grok-build-0.1', label: 'Grok Build 0.1', contextWindow: 262_144, maxOutputTokens: 262_144, reasoning: { mode: 'always-on', wireFormat: 'none' }, transportOverrides: { openaiShim: { removeBodyFields: ['reasoning_effort'] } } }, + { id: 'moonshotai/kimi-k2.7-code', apiName: 'moonshotai/kimi-k2.7-code', aliases: ['kimi-k2.7-code'], modelDescriptorId: 'kimi-k2.7-code', label: 'Kimi K2.7 Code', contextWindow: 262_144, maxOutputTokens: 262_144, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'moonshotai/kimi-k2.6', apiName: 'moonshotai/kimi-k2.6', aliases: ['kimi-k2.6'], modelDescriptorId: 'kimi-k2.6', label: 'Kimi K2.6', contextWindow: 262_144, maxOutputTokens: 262_144, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'moonshotai/kimi-k2.5', apiName: 'moonshotai/kimi-k2.5', aliases: ['kimi-k2.5'], modelDescriptorId: 'kimi-k2.5', label: 'Kimi K2.5', contextWindow: 262_144, maxOutputTokens: 262_144, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'zai-org/glm-5.2', apiName: 'zai-org/glm-5.2', aliases: ['glm-5.2'], modelDescriptorId: 'glm-5.2', label: 'GLM 5.2', contextWindow: 202_752, maxOutputTokens: 202_752, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'zai-org/glm-5.1', apiName: 'zai-org/glm-5.1', aliases: ['glm-5.1'], modelDescriptorId: 'glm-5.1', label: 'GLM 5.1', contextWindow: 202_752, maxOutputTokens: 202_752, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'zai-org/glm-5', apiName: 'zai-org/glm-5', aliases: ['glm-5'], modelDescriptorId: 'glm-5', label: 'GLM 5', contextWindow: 202_752, maxOutputTokens: 202_752, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'zai-org/glm-5-turbo', apiName: 'zai-org/glm-5-turbo', aliases: ['glm-5-turbo'], modelDescriptorId: 'glm-5-turbo', label: 'GLM 5 Turbo', contextWindow: 262_144, maxOutputTokens: 131_072, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'zai-org/glm-5v-turbo', apiName: 'zai-org/glm-5v-turbo', aliases: ['glm-5v-turbo'], modelDescriptorId: 'glm-5v-turbo', label: 'GLM 5V Turbo', contextWindow: 202_752, maxOutputTokens: 131_072, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'zai-org/glm-4.7', apiName: 'zai-org/glm-4.7', aliases: ['glm-4.7'], modelDescriptorId: 'glm-4.7', label: 'GLM 4.7', contextWindow: 202_752, maxOutputTokens: 202_752, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'zai-org/GLM-4.6', apiName: 'zai-org/GLM-4.6', aliases: ['glm-4.6'], modelDescriptorId: 'glm-4.6', label: 'GLM 4.6', contextWindow: 202_752, maxOutputTokens: 202_752, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'minimaxai/minimax-m3', apiName: 'minimaxai/minimax-m3', aliases: ['minimax-m3'], modelDescriptorId: 'minimax-m3', label: 'MiniMax M3', contextWindow: 524_288, maxOutputTokens: 524_288, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'minimaxai/minimax-m2.7', apiName: 'minimaxai/minimax-m2.7', aliases: ['minimax-m2.7'], modelDescriptorId: 'minimax-m2.7', label: 'MiniMax M2.7', contextWindow: 196_608, maxOutputTokens: 196_608, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'minimaxai/minimax-m2.5', apiName: 'minimaxai/minimax-m2.5', aliases: ['minimax-m2.5'], modelDescriptorId: 'minimax-m2.5', label: 'MiniMax M2.5', contextWindow: 196_608, maxOutputTokens: 196_608, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'qwen/qwen3.7-max', apiName: 'qwen/qwen3.7-max', aliases: ['qwen3.7-max'], modelDescriptorId: 'qwen3.7-max', label: 'Qwen3.7 Max', contextWindow: 1_000_000, maxOutputTokens: 67_072, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'qwen/qwen3.7-plus', apiName: 'qwen/qwen3.7-plus', aliases: ['qwen3.7-plus'], modelDescriptorId: 'qwen3.7-plus', label: 'Qwen3.7 Plus', contextWindow: 1_000_000, maxOutputTokens: 67_072, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'qwen/qwen3.6-plus', apiName: 'qwen/qwen3.6-plus', aliases: ['qwen3.6-plus'], modelDescriptorId: 'qwen3.6-plus', label: 'Qwen3.6 Plus', contextWindow: 1_000_000, maxOutputTokens: 65_536, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'qwen/qwen3.6-35b-a3b', apiName: 'qwen/qwen3.6-35b-a3b', modelDescriptorId: 'qwen/qwen3.6-35b-a3b', label: 'Qwen3.6 35B A3B', contextWindow: 262_144, maxOutputTokens: 65_536, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'qwen/qwen3.5-397b-a17b', apiName: 'qwen/qwen3.5-397b-a17b', modelDescriptorId: 'qwen/qwen3.5-397b-a17b', label: 'Qwen3.5 397B A17B', contextWindow: 262_144, maxOutputTokens: 65_536, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'qwen/qwen3.5-122b-a10b', apiName: 'qwen/qwen3.5-122b-a10b', modelDescriptorId: 'qwen/qwen3.5-122b-a10b', label: 'Qwen3.5 122B A10B', contextWindow: 262_144, maxOutputTokens: 65_536, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'qwen/qwen3.5-35b-a3b', apiName: 'qwen/qwen3.5-35b-a3b', modelDescriptorId: 'qwen/qwen3.5-35b-a3b', label: 'Qwen3.5 35B A3B', contextWindow: 262_144, maxOutputTokens: 65_536, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'qwen/qwen3.5-27b', apiName: 'qwen/qwen3.5-27b', modelDescriptorId: 'qwen/qwen3.5-27b', label: 'Qwen3.5 27B', contextWindow: 262_144, maxOutputTokens: 65_536, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'qwen/qwen3-coder-next', apiName: 'qwen/qwen3-coder-next', aliases: ['qwen3-coder-next'], modelDescriptorId: 'qwen3-coder-next', label: 'Qwen3 Coder Next', contextWindow: 262_144, maxOutputTokens: 262_144, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, + { id: 'qwen/qwen3-max-2026-01-23', apiName: 'qwen/qwen3-max-2026-01-23', aliases: ['qwen3-max-2026-01-23'], modelDescriptorId: 'qwen3-max-2026-01-23', label: 'Qwen3 Max 2026-01-23', contextWindow: 252_000, maxOutputTokens: 32_000, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, + { id: 'qwen/qwen3-vl-30b-a3b-thinking', apiName: 'qwen/qwen3-vl-30b-a3b-thinking', modelDescriptorId: 'qwen/qwen3-vl-30b-a3b-thinking', label: 'Qwen3 VL 30B A3B Thinking', contextWindow: 128_000, maxOutputTokens: 32_000, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'qwen/qwen3-vl-30b-a3b-instruct', apiName: 'qwen/qwen3-vl-30b-a3b-instruct', modelDescriptorId: 'qwen/qwen3-vl-30b-a3b-instruct', label: 'Qwen3 VL 30B A3B Instruct', contextWindow: 128_000, maxOutputTokens: 32_000, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true } }, + { id: 'qwen/qwen3-vl-8b-instruct', apiName: 'qwen/qwen3-vl-8b-instruct', modelDescriptorId: 'qwen/qwen3-vl-8b-instruct', label: 'Qwen3 VL 8B Instruct', contextWindow: 128_000, maxOutputTokens: 32_000, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true } }, + { id: 'Qwen/Qwen3-VL-235B-A22B-Instruct', apiName: 'Qwen/Qwen3-VL-235B-A22B-Instruct', modelDescriptorId: 'Qwen/Qwen3-VL-235B-A22B-Instruct', label: 'Qwen3 VL 235B A22B Instruct', contextWindow: 131_072, maxOutputTokens: 32_768, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true } }, + { id: 'Qwen/Qwen3-Next-80B-A3B-Thinking', apiName: 'Qwen/Qwen3-Next-80B-A3B-Thinking', modelDescriptorId: 'Qwen/Qwen3-Next-80B-A3B-Thinking', label: 'Qwen3 Next 80B A3B Thinking', contextWindow: 262_144, maxOutputTokens: 32_768, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high', 'xhigh'], wireFormat: 'reasoning_effort' } }, + { id: 'Qwen/Qwen3-Next-80B-A3B-Instruct', apiName: 'Qwen/Qwen3-Next-80B-A3B-Instruct', modelDescriptorId: 'Qwen/Qwen3-Next-80B-A3B-Instruct', label: 'Qwen3 Next 80B A3B Instruct', contextWindow: 262_144, maxOutputTokens: 131_072, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, + { id: 'Qwen/Qwen3-30B-A3B-Instruct-2507', apiName: 'Qwen/Qwen3-30B-A3B-Instruct-2507', aliases: ['qwen3-30b-a3b-instruct-2507'], modelDescriptorId: 'qwen3-30b-a3b-instruct-2507', label: 'Qwen3 30B A3B Instruct 2507', contextWindow: 131_072, maxOutputTokens: 131_072, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, + { id: 'bytedance/doubao-seed-2.0-pro-260215', apiName: 'bytedance/doubao-seed-2.0-pro-260215', modelDescriptorId: 'bytedance/doubao-seed-2.0-pro-260215', label: 'Doubao Seed 2.0 Pro', contextWindow: 262_144, maxOutputTokens: 131_072, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high'], wireFormat: 'reasoning_effort' } }, + { id: 'bytedance/doubao-seed-2.0-code-preview-260215', apiName: 'bytedance/doubao-seed-2.0-code-preview-260215', modelDescriptorId: 'bytedance/doubao-seed-2.0-code-preview-260215', label: 'Doubao Seed 2.0 Code Preview', contextWindow: 262_144, maxOutputTokens: 131_072, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high'], wireFormat: 'reasoning_effort' } }, + { id: 'bytedance/doubao-seed-2.0-lite-260428', apiName: 'bytedance/doubao-seed-2.0-lite-260428', modelDescriptorId: 'bytedance/doubao-seed-2.0-lite-260428', label: 'Doubao Seed 2.0 Lite', contextWindow: 262_144, maxOutputTokens: 131_072, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high'], wireFormat: 'reasoning_effort' } }, + { id: 'bytedance/doubao-seed-2.0-mini-260428', apiName: 'bytedance/doubao-seed-2.0-mini-260428', modelDescriptorId: 'bytedance/doubao-seed-2.0-mini-260428', label: 'Doubao Seed 2.0 Mini', contextWindow: 262_144, maxOutputTokens: 131_072, capabilities: { supportsReasoning: true }, reasoning: { mode: 'levels', levels: ['low', 'medium', 'high'], wireFormat: 'reasoning_effort' } }, + { id: 'kwaipilot/kat-coder-pro-v2', apiName: 'kwaipilot/kat-coder-pro-v2', modelDescriptorId: 'kwaipilot/kat-coder-pro-v2', label: 'KAT Coder Pro V2', contextWindow: 262_144, maxOutputTokens: 144_000, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, + { id: 'owl', apiName: 'owl', modelDescriptorId: 'owl', label: 'OWL', contextWindow: 1_048_576, maxOutputTokens: 262_144, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true }, notes: 'Free' }, + ], + }, + usage: { supported: false }, +}) diff --git a/src/integrations/generated/integrationArtifacts.generated.ts b/src/integrations/generated/integrationArtifacts.generated.ts index fa0af222db..41052e79af 100644 --- a/src/integrations/generated/integrationArtifacts.generated.ts +++ b/src/integrations/generated/integrationArtifacts.generated.ts @@ -6,7 +6,6 @@ import type { AnthropicProxyDescriptor, BrandDescriptor, GatewayDescriptor, ModelDescriptor, VendorDescriptor } from '../descriptors.js' import vendorAnthropic from '../vendors/anthropic.js' -import vendorAtlasCloud from '../vendors/atlas-cloud.js' import vendorBankr from '../vendors/bankr.js' import vendorDeepseek from '../vendors/deepseek.js' import vendorFireworks from '../vendors/fireworks.js' @@ -19,6 +18,7 @@ import vendorVenice from '../vendors/venice.js' import vendorXai from '../vendors/xai.js' import vendorXiaomiMimo from '../vendors/xiaomi-mimo.js' import vendorZai from '../vendors/zai.js' +import gatewayAtlasCloud from '../gateways/atlas-cloud.js' import gatewayAtomicChat from '../gateways/atomic-chat.js' import gatewayAzureOpenai from '../gateways/azure-openai.js' import gatewayBedrock from '../gateways/bedrock.js' @@ -75,8 +75,8 @@ import modelQwen from '../models/qwen.js' import modelXai from '../models/xai.js' import modelXiaomiMimo from '../models/xiaomi-mimo.js' -export const VENDOR_DESCRIPTORS = [vendorAnthropic, vendorAtlasCloud, vendorBankr, vendorDeepseek, vendorFireworks, vendorGemini, vendorMinimax, vendorMoonshot, vendorNearai, vendorOpenai, vendorVenice, vendorXai, vendorXiaomiMimo, vendorZai] as const satisfies readonly VendorDescriptor[] -export const GATEWAY_DESCRIPTORS = [gatewayAtomicChat, gatewayAzureOpenai, gatewayBedrock, gatewayCustom, gatewayDashscopeCn, gatewayDashscopeIntl, gatewayGithubEnterprise, gatewayGithub, gatewayGitlawbOpengateway, gatewayGroq, gatewayHicap, gatewayKimiCode, gatewayLmstudio, gatewayMistral, gatewayNvidiaNim, gatewayOllama, gatewayOpencodeGo, gatewayOpencode, gatewayOpenrouter, gatewayTogether, gatewayVertex, gatewayXiaomiMimoToken] as const satisfies readonly GatewayDescriptor[] +export const VENDOR_DESCRIPTORS = [vendorAnthropic, vendorBankr, vendorDeepseek, vendorFireworks, vendorGemini, vendorMinimax, vendorMoonshot, vendorNearai, vendorOpenai, vendorVenice, vendorXai, vendorXiaomiMimo, vendorZai] as const satisfies readonly VendorDescriptor[] +export const GATEWAY_DESCRIPTORS = [gatewayAtlasCloud, gatewayAtomicChat, gatewayAzureOpenai, gatewayBedrock, gatewayCustom, gatewayDashscopeCn, gatewayDashscopeIntl, gatewayGithubEnterprise, gatewayGithub, gatewayGitlawbOpengateway, gatewayGroq, gatewayHicap, gatewayKimiCode, gatewayLmstudio, gatewayMistral, gatewayNvidiaNim, gatewayOllama, gatewayOpencodeGo, gatewayOpencode, gatewayOpenrouter, gatewayTogether, gatewayVertex, gatewayXiaomiMimoToken] as const satisfies readonly GatewayDescriptor[] export const ANTHROPIC_PROXY_DESCRIPTORS = [] as const satisfies readonly AnthropicProxyDescriptor[] export const BRAND_DESCRIPTORS = [brandClaude, brandDeepseek, brandFireworks, brandGemini, brandGlm, brandGpt, brandKimi, brandLlama, brandMinimax, brandMistral, brandNearai, brandNemotron, brandOpenaiCompatibleAlias, brandQwen, brandXai, brandXiaomiMimo] as const satisfies readonly BrandDescriptor[] export const MODEL_DESCRIPTOR_GROUPS = [modelClaude, modelDeepseek, modelFireworksMerged, modelGemini, modelGlm, modelGpt, modelKimi, modelLlama, modelMinimax, modelMistral, modelNearai, modelNemotron, modelOpenaiCompatibleAlias, modelOpencode, modelQwen, modelXai, modelXiaomiMimo] as const satisfies readonly (readonly ModelDescriptor[])[] diff --git a/src/integrations/generated/integrationManifest.generated.ts b/src/integrations/generated/integrationManifest.generated.ts index fe1c5d598b..9544977ec5 100644 --- a/src/integrations/generated/integrationManifest.generated.ts +++ b/src/integrations/generated/integrationManifest.generated.ts @@ -74,9 +74,10 @@ export const PROVIDER_PRESET_MANIFEST = [ }, { "preset": "atlas-cloud", - "routeKind": "vendor", + "routeKind": "gateway", "routeId": "atlas-cloud", - "vendorId": "atlas-cloud", + "vendorId": "openai", + "gatewayId": "atlas-cloud", "description": "Atlas Cloud AI (OpenAI-compatible)", "apiKeyEnvVars": [ "ATLAS_CLOUD_API_KEY" diff --git a/src/integrations/models/claude.ts b/src/integrations/models/claude.ts index 7e3e422bb8..eb2d6a9581 100644 --- a/src/integrations/models/claude.ts +++ b/src/integrations/models/claude.ts @@ -1,6 +1,24 @@ import { defineModel } from '../define.js' export default [ + defineModel({ + id: 'claude-opus-4-8', + label: 'Claude Opus 4.8', + brandId: 'claude', + vendorId: 'anthropic', + classification: ['chat', 'reasoning', 'vision', 'coding'], + defaultModel: 'claude-opus-4-8', + capabilities: { + supportsVision: true, + supportsStreaming: true, + supportsFunctionCalling: true, + supportsJsonMode: true, + supportsReasoning: true, + supportsPreciseTokenCount: false, + }, + contextWindow: 1_000_000, + maxOutputTokens: 128_000, + }), defineModel({ id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', diff --git a/src/integrations/models/gemini.ts b/src/integrations/models/gemini.ts index c4fd564c3e..cb8b5f701d 100644 --- a/src/integrations/models/gemini.ts +++ b/src/integrations/models/gemini.ts @@ -43,6 +43,8 @@ export default [ geminiModel('gemini-2.5-flash', 'Gemini 2.5 Flash', 65_536), geminiModel('gemini-2.5-pro', 'Gemini 2.5 Pro', 65_536), geminiModel('gemini-2.0-flash', 'Gemini 2.0 Flash', 8_192), + geminiModel('gemini-3.5-flash', 'Gemini 3.5 Flash', 65_536), + geminiModel('google/gemini-3.1-pro-preview', 'Google Gemini 3.1 Pro Preview', 64_000), geminiModel('google/gemini-3.1-flash-lite', 'Google Gemini 3.1 Flash Lite', 65_536), geminiModel('google/gemini-2.5-pro', 'Google Gemini 2.5 Pro', 65_536), geminiModel('google/gemini-2.0-flash', 'Google Gemini 2.0 Flash', 8_192), diff --git a/src/integrations/models/glm.ts b/src/integrations/models/glm.ts index 8f9b41b5e6..64d6549c9f 100644 --- a/src/integrations/models/glm.ts +++ b/src/integrations/models/glm.ts @@ -29,6 +29,20 @@ function glmModel( } export default [ + defineModel({ + id: 'glm-5v-turbo', + label: 'GLM 5V Turbo', + brandId: 'glm', + vendorId: 'zai', + classification: ['chat', 'reasoning', 'vision', 'coding'], + defaultModel: 'glm-5v-turbo', + capabilities: { + ...glmCapabilities, + supportsVision: true, + }, + contextWindow: 202_752, + maxOutputTokens: 131_072, + }), glmModel('glm-5.2', 'GLM 5.2', 1_000_000, 131_072), glmModel('GLM-5.1', 'GLM-5.1', 202_752, 131_072), glmModel('GLM-5-Turbo', 'GLM-5-Turbo', 202_752, 131_072), @@ -39,5 +53,6 @@ export default [ glmModel('glm-5-turbo', 'GLM 5 Turbo', 202_752, 16_384), glmModel('glm-5', 'GLM 5', 202_752, 16_384), glmModel('glm-4.7', 'GLM 4.7', 202_752, 16_384), + glmModel('glm-4.6', 'GLM 4.6', 202_752, 202_752), glmModel('glm-4.5-air', 'GLM 4.5 Air', 128_000, 16_384), ] diff --git a/src/integrations/models/kimi.ts b/src/integrations/models/kimi.ts index fb31df8f18..e7260d9c57 100644 --- a/src/integrations/models/kimi.ts +++ b/src/integrations/models/kimi.ts @@ -30,6 +30,7 @@ function kimiModel( export default [ kimiModel('kimi-for-coding', 'Kimi for Coding', 262_144, 32_768), + kimiModel('kimi-k2.7-code', 'Kimi K2.7 Code', 262_144, 262_144), kimiModel('kimi-k2.6', 'Kimi K2.6', 262_144, 32_768), kimiModel('kimi-k2.5', 'Kimi K2.5', 262_144, 32_768), kimiModel('kimi-k2-thinking', 'Kimi K2 Thinking', 262_144, 32_768), diff --git a/src/integrations/models/openai-compatible-alias.ts b/src/integrations/models/openai-compatible-alias.ts index c8c04da086..b2ba8be3b9 100644 --- a/src/integrations/models/openai-compatible-alias.ts +++ b/src/integrations/models/openai-compatible-alias.ts @@ -36,6 +36,13 @@ function aliasModel( } const aliasModels: readonly AliasModel[] = [ + ['deepseek-ai/DeepSeek-V3.2-Exp', 'DeepSeek V3.2 Exp', 163_840, 163_840], + ['bytedance/doubao-seed-2.0-pro-260215', 'Doubao Seed 2.0 Pro', 262_144, 131_072], + ['bytedance/doubao-seed-2.0-code-preview-260215', 'Doubao Seed 2.0 Code Preview', 262_144, 131_072], + ['bytedance/doubao-seed-2.0-lite-260428', 'Doubao Seed 2.0 Lite', 262_144, 131_072], + ['bytedance/doubao-seed-2.0-mini-260428', 'Doubao Seed 2.0 Mini', 262_144, 131_072], + ['kwaipilot/kat-coder-pro-v2', 'KAT Coder Pro V2', 262_144, 144_000], + ['owl', 'OWL', 1_048_576, 262_144], ['github:copilot', 'GitHub Copilot', 128_000, 16_384], ['github:copilot:claude-sonnet-4', 'GitHub Copilot Claude Sonnet 4', 216_000, 16_000], ['github:copilot:claude-haiku-4', 'GitHub Copilot Claude Haiku 4', 200_000, 64_000], diff --git a/src/integrations/models/qwen.ts b/src/integrations/models/qwen.ts index 477f5e7b3c..395fe95341 100644 --- a/src/integrations/models/qwen.ts +++ b/src/integrations/models/qwen.ts @@ -28,7 +28,65 @@ function qwenModel( }) } +function qwenTextModel( + id: string, + label: string, + contextWindow: number, + maxOutputTokens: number, +) { + return defineModel({ + id, + label, + brandId: 'qwen', + vendorId: 'openai', + classification: ['chat', 'reasoning', 'coding'], + defaultModel: id, + capabilities: { + ...qwenCapabilities, + supportsVision: false, + }, + contextWindow, + maxOutputTokens, + }) +} + +function qwenInstructModel( + id: string, + label: string, + contextWindow: number, + maxOutputTokens: number, + supportsVision = true, +) { + return defineModel({ + id, + label, + brandId: 'qwen', + vendorId: 'openai', + classification: supportsVision ? ['chat', 'vision', 'coding'] : ['chat', 'coding'], + defaultModel: id, + capabilities: { + ...qwenCapabilities, + supportsVision, + supportsReasoning: false, + }, + contextWindow, + maxOutputTokens, + }) +} export default [ + qwenTextModel('qwen3.7-plus', 'Qwen 3.7 Plus', 1_000_000, 67_072), + qwenModel('qwen/qwen3.6-35b-a3b', 'Qwen 3.6 35B A3B', 262_144, 65_536), + qwenModel('qwen/qwen3.5-397b-a17b', 'Qwen 3.5 397B A17B', 262_144, 65_536), + qwenModel('qwen/qwen3.5-122b-a10b', 'Qwen 3.5 122B A10B', 262_144, 65_536), + qwenModel('qwen/qwen3.5-35b-a3b', 'Qwen 3.5 35B A3B', 262_144, 65_536), + qwenModel('qwen/qwen3.5-27b', 'Qwen 3.5 27B', 262_144, 65_536), + qwenModel('qwen/qwen3-vl-30b-a3b-thinking', 'Qwen 3 VL 30B A3B Thinking', 128_000, 32_000), + qwenInstructModel('qwen/qwen3-vl-30b-a3b-instruct', 'Qwen 3 VL 30B A3B Instruct', 128_000, 32_000), + qwenInstructModel('qwen/qwen3-vl-8b-instruct', 'Qwen 3 VL 8B Instruct', 128_000, 32_000), + qwenInstructModel('Qwen/Qwen3-VL-235B-A22B-Instruct', 'Qwen 3 VL 235B A22B Instruct', 131_072, 32_768), + qwenTextModel('Qwen/Qwen3-Next-80B-A3B-Thinking', 'Qwen 3 Next 80B A3B Thinking', 262_144, 32_768), + qwenInstructModel('Qwen/Qwen3-Next-80B-A3B-Instruct', 'Qwen 3 Next 80B A3B Instruct', 262_144, 131_072, false), + qwenInstructModel('qwen3-30b-a3b-instruct-2507', 'Qwen 3 30B A3B Instruct 2507', 131_072, 131_072, false), qwenModel('qwen3.6-plus', 'Qwen 3.6 Plus', 1_000_000, 65_536), qwenModel('qwen3.5-plus', 'Qwen 3.5 Plus', 1_000_000, 65_536), qwenModel('qwen3-coder-plus', 'Qwen 3 Coder Plus', 1_000_000, 65_536), diff --git a/src/integrations/models/xai.ts b/src/integrations/models/xai.ts index 45c2f112cb..76130c7d0a 100644 --- a/src/integrations/models/xai.ts +++ b/src/integrations/models/xai.ts @@ -10,6 +10,20 @@ const grokCapabilities = { } export default [ + defineModel({ + id: 'xai/grok-build-0.1', + label: 'Grok Build 0.1', + brandId: 'xai', + vendorId: 'xai', + classification: ['chat', 'reasoning', 'coding'], + defaultModel: 'xai/grok-build-0.1', + capabilities: { + ...grokCapabilities, + supportsVision: false, + }, + contextWindow: 262_144, + maxOutputTokens: 262_144, + }), defineModel({ id: 'grok-4.3', label: 'Grok 4.3', diff --git a/src/integrations/vendors/atlas-cloud.ts b/src/integrations/vendors/atlas-cloud.ts deleted file mode 100644 index 022e2f006f..0000000000 --- a/src/integrations/vendors/atlas-cloud.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { defineVendor } from '../define.js' - -export default defineVendor({ - id: 'atlas-cloud', - label: 'Atlas Cloud', - classification: 'openai-compatible', - defaultBaseUrl: 'https://api.atlascloud.ai/v1', - defaultModel: 'deepseek-ai/deepseek-v4-pro', - requiredEnvVars: ['ATLAS_CLOUD_API_KEY'], - setup: { - requiresAuth: true, - authMode: 'api-key', - credentialEnvVars: ['ATLAS_CLOUD_API_KEY'], - dedicatedCredentialsOnly: true, - }, - transportConfig: { - kind: 'openai-compatible', - }, - preset: { - id: 'atlas-cloud', - description: 'Atlas Cloud AI (OpenAI-compatible)', - apiKeyEnvVars: ['ATLAS_CLOUD_API_KEY'], - modelEnvVars: ['ATLAS_CLOUD_MODEL', 'OPENAI_MODEL'], - }, - validation: { - kind: 'credential-env', - routing: { - matchDefaultBaseUrl: true, - }, - credentialEnvVars: ['ATLAS_CLOUD_API_KEY'], - missingCredentialMessage: - 'Atlas Cloud auth is required. Set ATLAS_CLOUD_API_KEY.', - }, - catalog: { - source: 'static', - // Mirrors https://www.atlascloud.ai/models/list/llm - models: [ - { id: 'deepseek-ai/deepseek-v4-pro', apiName: 'deepseek-ai/deepseek-v4-pro', label: 'DeepSeek V4 Pro', contextWindow: 1_048_576, maxOutputTokens: 393_216, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, - { id: 'deepseek-ai/deepseek-v4-flash', apiName: 'deepseek-ai/deepseek-v4-flash', label: 'DeepSeek V4 Flash', contextWindow: 1_048_576, maxOutputTokens: 393_216, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, - { id: 'deepseek-ai/deepseek-v3.2', apiName: 'deepseek-ai/deepseek-v3.2', label: 'DeepSeek V3.2', contextWindow: 163_840, maxOutputTokens: 163_840, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'deepseek-ai/DeepSeek-V3.2-Exp', apiName: 'deepseek-ai/DeepSeek-V3.2-Exp', label: 'DeepSeek V3.2 Exp', contextWindow: 163_840, maxOutputTokens: 163_840, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'anthropic/claude-opus-4.8', apiName: 'anthropic/claude-opus-4.8', label: 'Claude Opus 4.8', contextWindow: 1_000_000, maxOutputTokens: 128_000 }, - { id: 'anthropic/claude-sonnet-4.6', apiName: 'anthropic/claude-sonnet-4.6', label: 'Claude Sonnet 4.6', contextWindow: 200_000, maxOutputTokens: 64_000 }, - { id: 'anthropic/claude-sonnet-4.6-coding', apiName: 'anthropic/claude-sonnet-4.6-coding', label: 'Claude Sonnet 4.6 Coding', contextWindow: 200_000, maxOutputTokens: 64_000 }, - { id: 'anthropic/claude-haiku-4.5-20251001', apiName: 'anthropic/claude-haiku-4.5-20251001', label: 'Claude Haiku 4.5', contextWindow: 200_000, maxOutputTokens: 64_000 }, - { id: 'anthropic/claude-haiku-4.5-20251001-coding', apiName: 'anthropic/claude-haiku-4.5-20251001-coding', label: 'Claude Haiku 4.5 Coding', contextWindow: 200_000, maxOutputTokens: 64_000 }, - { id: 'openai/gpt-5.5', apiName: 'openai/gpt-5.5', label: 'GPT 5.5', contextWindow: 1_050_000, maxOutputTokens: 128_000 }, - { id: 'openai/gpt-5.4', apiName: 'openai/gpt-5.4', label: 'GPT 5.4', contextWindow: 400_000, maxOutputTokens: 128_000 }, - { id: 'google/gemini-3.5-flash', apiName: 'google/gemini-3.5-flash', label: 'Gemini 3.5 Flash', contextWindow: 1_048_576, maxOutputTokens: 65_536 }, - { id: 'google/gemini-3.1-pro-preview', apiName: 'google/gemini-3.1-pro-preview', label: 'Gemini 3.1 Pro Preview', contextWindow: 1_000_000, maxOutputTokens: 64_000 }, - { id: 'google/gemini-3.1-flash-lite', apiName: 'google/gemini-3.1-flash-lite', label: 'Gemini 3.1 Flash Lite', contextWindow: 1_048_576, maxOutputTokens: 65_536 }, - { id: 'xai/grok-4.3', apiName: 'xai/grok-4.3', label: 'Grok 4.3', contextWindow: 1_000_000, maxOutputTokens: 1_000_000 }, - { id: 'xai/grok-build-0.1', apiName: 'xai/grok-build-0.1', label: 'Grok Build 0.1', contextWindow: 262_144, maxOutputTokens: 262_144, transportOverrides: { openaiShim: { removeBodyFields: ['reasoning_effort'] } } }, - { id: 'moonshotai/kimi-k2.7-code', apiName: 'moonshotai/kimi-k2.7-code', label: 'Kimi K2.7 Code', contextWindow: 262_144, maxOutputTokens: 262_144, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'moonshotai/kimi-k2.6', apiName: 'moonshotai/kimi-k2.6', label: 'Kimi K2.6', contextWindow: 262_144, maxOutputTokens: 262_144, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'moonshotai/kimi-k2.5', apiName: 'moonshotai/kimi-k2.5', label: 'Kimi K2.5', contextWindow: 262_144, maxOutputTokens: 262_144, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'zai-org/glm-5.2', apiName: 'zai-org/glm-5.2', label: 'GLM 5.2', contextWindow: 202_752, maxOutputTokens: 202_752, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'zai-org/glm-5.1', apiName: 'zai-org/glm-5.1', label: 'GLM 5.1', contextWindow: 202_752, maxOutputTokens: 202_752, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'zai-org/glm-5', apiName: 'zai-org/glm-5', label: 'GLM 5', contextWindow: 202_752, maxOutputTokens: 202_752, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'zai-org/glm-5-turbo', apiName: 'zai-org/glm-5-turbo', label: 'GLM 5 Turbo', contextWindow: 262_144, maxOutputTokens: 131_072, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'zai-org/glm-5v-turbo', apiName: 'zai-org/glm-5v-turbo', label: 'GLM 5V Turbo', contextWindow: 202_752, maxOutputTokens: 131_072, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'zai-org/glm-4.7', apiName: 'zai-org/glm-4.7', label: 'GLM 4.7', contextWindow: 202_752, maxOutputTokens: 202_752, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'zai-org/GLM-4.6', apiName: 'zai-org/GLM-4.6', label: 'GLM 4.6', contextWindow: 202_752, maxOutputTokens: 202_752, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'minimaxai/minimax-m3', apiName: 'minimaxai/minimax-m3', label: 'MiniMax M3', contextWindow: 524_288, maxOutputTokens: 524_288 }, - { id: 'minimaxai/minimax-m2.7', apiName: 'minimaxai/minimax-m2.7', label: 'MiniMax M2.7', contextWindow: 196_608, maxOutputTokens: 196_608, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'minimaxai/minimax-m2.5', apiName: 'minimaxai/minimax-m2.5', label: 'MiniMax M2.5', contextWindow: 196_608, maxOutputTokens: 196_608, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'qwen/qwen3.7-max', apiName: 'qwen/qwen3.7-max', label: 'Qwen3.7 Max', contextWindow: 1_000_000, maxOutputTokens: 67_072 }, - { id: 'qwen/qwen3.7-plus', apiName: 'qwen/qwen3.7-plus', label: 'Qwen3.7 Plus', contextWindow: 1_000_000, maxOutputTokens: 67_072 }, - { id: 'qwen/qwen3.6-plus', apiName: 'qwen/qwen3.6-plus', label: 'Qwen3.6 Plus', contextWindow: 1_000_000, maxOutputTokens: 65_536, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'qwen/qwen3.6-35b-a3b', apiName: 'qwen/qwen3.6-35b-a3b', label: 'Qwen3.6 35B A3B', contextWindow: 262_144, maxOutputTokens: 65_536, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'qwen/qwen3.5-397b-a17b', apiName: 'qwen/qwen3.5-397b-a17b', label: 'Qwen3.5 397B A17B', contextWindow: 262_144, maxOutputTokens: 65_536, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'qwen/qwen3.5-122b-a10b', apiName: 'qwen/qwen3.5-122b-a10b', label: 'Qwen3.5 122B A10B', contextWindow: 262_144, maxOutputTokens: 65_536, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'qwen/qwen3.5-35b-a3b', apiName: 'qwen/qwen3.5-35b-a3b', label: 'Qwen3.5 35B A3B', contextWindow: 262_144, maxOutputTokens: 65_536, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'qwen/qwen3.5-27b', apiName: 'qwen/qwen3.5-27b', label: 'Qwen3.5 27B', contextWindow: 262_144, maxOutputTokens: 65_536, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'qwen/qwen3-coder-next', apiName: 'qwen/qwen3-coder-next', label: 'Qwen3 Coder Next', contextWindow: 262_144, maxOutputTokens: 262_144, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, - { id: 'qwen/qwen3-max-2026-01-23', apiName: 'qwen/qwen3-max-2026-01-23', label: 'Qwen3 Max 2026-01-23', contextWindow: 252_000, maxOutputTokens: 32_000, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, - { id: 'qwen/qwen3-vl-30b-a3b-thinking', apiName: 'qwen/qwen3-vl-30b-a3b-thinking', label: 'Qwen3 VL 30B A3B Thinking', contextWindow: 128_000, maxOutputTokens: 32_000, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'qwen/qwen3-vl-30b-a3b-instruct', apiName: 'qwen/qwen3-vl-30b-a3b-instruct', label: 'Qwen3 VL 30B A3B Instruct', contextWindow: 128_000, maxOutputTokens: 32_000, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true } }, - { id: 'qwen/qwen3-vl-8b-instruct', apiName: 'qwen/qwen3-vl-8b-instruct', label: 'Qwen3 VL 8B Instruct', contextWindow: 128_000, maxOutputTokens: 32_000, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true } }, - { id: 'Qwen/Qwen3-VL-235B-A22B-Instruct', apiName: 'Qwen/Qwen3-VL-235B-A22B-Instruct', label: 'Qwen3 VL 235B A22B Instruct', contextWindow: 131_072, maxOutputTokens: 32_768, capabilities: { supportsVision: true, supportsFunctionCalling: true, supportsJsonMode: true } }, - { id: 'Qwen/Qwen3-Next-80B-A3B-Thinking', apiName: 'Qwen/Qwen3-Next-80B-A3B-Thinking', label: 'Qwen3 Next 80B A3B Thinking', contextWindow: 262_144, maxOutputTokens: 32_768, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true, supportsReasoning: true } }, - { id: 'Qwen/Qwen3-Next-80B-A3B-Instruct', apiName: 'Qwen/Qwen3-Next-80B-A3B-Instruct', label: 'Qwen3 Next 80B A3B Instruct', contextWindow: 262_144, maxOutputTokens: 131_072, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, - { id: 'Qwen/Qwen3-30B-A3B-Instruct-2507', apiName: 'Qwen/Qwen3-30B-A3B-Instruct-2507', label: 'Qwen3 30B A3B Instruct 2507', contextWindow: 131_072, maxOutputTokens: 131_072, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, - { id: 'bytedance/doubao-seed-2.0-pro-260215', apiName: 'bytedance/doubao-seed-2.0-pro-260215', label: 'Doubao Seed 2.0 Pro', contextWindow: 262_144, maxOutputTokens: 131_072 }, - { id: 'bytedance/doubao-seed-2.0-code-preview-260215', apiName: 'bytedance/doubao-seed-2.0-code-preview-260215', label: 'Doubao Seed 2.0 Code Preview', contextWindow: 262_144, maxOutputTokens: 131_072 }, - { id: 'bytedance/doubao-seed-2.0-lite-260428', apiName: 'bytedance/doubao-seed-2.0-lite-260428', label: 'Doubao Seed 2.0 Lite', contextWindow: 262_144, maxOutputTokens: 131_072 }, - { id: 'bytedance/doubao-seed-2.0-mini-260428', apiName: 'bytedance/doubao-seed-2.0-mini-260428', label: 'Doubao Seed 2.0 Mini', contextWindow: 262_144, maxOutputTokens: 131_072 }, - { id: 'kwaipilot/kat-coder-pro-v2', apiName: 'kwaipilot/kat-coder-pro-v2', label: 'KAT Coder Pro V2', contextWindow: 262_144, maxOutputTokens: 144_000, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true } }, - { id: 'owl', apiName: 'owl', label: 'OWL', contextWindow: 1_048_576, maxOutputTokens: 262_144, capabilities: { supportsFunctionCalling: true, supportsJsonMode: true }, notes: 'Free' }, - ], - }, - usage: { supported: false }, -}) diff --git a/src/services/api/client.test.ts b/src/services/api/client.test.ts index ac4a1bce22..ebad88ecbc 100644 --- a/src/services/api/client.test.ts +++ b/src/services/api/client.test.ts @@ -1525,6 +1525,113 @@ test('providerOverride clamps stale effort against metadata levels', async () => expect(requestBody?.reasoning_effort).toBe('high') }) +test('providerOverride Atlas Kimi metadata emits top-level reasoning_effort and clamps unsupported levels', async () => { + let requestBody: Record | undefined + + globalThis.fetch = (async (_input, init) => { + requestBody = JSON.parse(String(init?.body)) + + return new Response( + JSON.stringify({ + id: 'chatcmpl-provider-override-atlas-kimi', + model: 'moonshotai/kimi-k2.6', + choices: [ + { + message: { + role: 'assistant', + content: 'ok', + }, + finish_reason: 'stop', + }, + ], + usage: { + prompt_tokens: 8, + completion_tokens: 3, + total_tokens: 11, + }, + }), + { + headers: { + 'Content-Type': 'application/json', + }, + }, + ) + }) as FetchType + + const client = (await getAnthropicClient({ + maxRetries: 0, + effortValue: 'xhigh', + providerOverride: { + model: 'moonshotai/kimi-k2.6', + baseURL: 'https://api.atlascloud.ai/v1', + apiKey: 'atlas-test-key', + }, + })) as unknown as ShimClient + + await client.beta.messages.create({ + model: 'unused', + system: 'test system', + messages: [{ role: 'user', content: 'hello' }], + max_tokens: 64, + stream: false, + }) + + expect(requestBody?.reasoning_effort).toBe('xhigh') +}) + +test('providerOverride Atlas Grok Build uses always-on no-wire reasoning metadata', async () => { + let requestBody: Record | undefined + + globalThis.fetch = (async (_input, init) => { + requestBody = JSON.parse(String(init?.body)) + + return new Response( + JSON.stringify({ + id: 'chatcmpl-provider-override-atlas-grok-build', + model: 'xai/grok-build-0.1', + choices: [ + { + message: { + role: 'assistant', + content: 'ok', + }, + finish_reason: 'stop', + }, + ], + usage: { + prompt_tokens: 8, + completion_tokens: 3, + total_tokens: 11, + }, + }), + { + headers: { + 'Content-Type': 'application/json', + }, + }, + ) + }) as FetchType + + const client = (await getAnthropicClient({ + maxRetries: 0, + effortValue: 'high', + providerOverride: { + model: 'xai/grok-build-0.1', + baseURL: 'https://api.atlascloud.ai/v1', + apiKey: 'atlas-test-key', + }, + })) as unknown as ShimClient + + await client.beta.messages.create({ + model: 'unused', + system: 'test system', + messages: [{ role: 'user', content: 'hello' }], + max_tokens: 64, + stream: false, + }) + + expect(requestBody?.reasoning_effort).toBeUndefined() +}) test('providerOverride Groq DeepSeek does not receive stripped effort override', async () => { let requestBody: Record | undefined diff --git a/src/services/api/providerConfig.test.ts b/src/services/api/providerConfig.test.ts index a3ce55abc8..0bc4ad6531 100644 --- a/src/services/api/providerConfig.test.ts +++ b/src/services/api/providerConfig.test.ts @@ -24,3 +24,57 @@ test('resolveProviderRequest exposes model-query thinking defaults', () => { expect(request.resolvedModel).toBe('glm-5.2') expect(request.thinking).toEqual({ type: 'disabled' }) }) + +test('resolveProviderRequest maps explicit route catalog aliases to API model ids', () => { + const request = resolveProviderRequest({ + model: 'glm-5.2?reasoning=high', + baseUrl: 'https://api.atlascloud.ai/v1', + processEnv: {}, + }) + + expect(request.requestedModel).toBe('glm-5.2?reasoning=high') + expect(request.resolvedModel).toBe('zai-org/glm-5.2') + expect(request.reasoning).toEqual({ effort: 'high' }) +}) + +test('resolveProviderRequest maps explicit Atlas coding aliases without ambiguity', () => { + expect(resolveProviderRequest({ + model: 'claude-sonnet-4-6', + baseUrl: 'https://api.atlascloud.ai/v1', + processEnv: {}, + }).resolvedModel).toBe('anthropic/claude-sonnet-4.6') + + expect(resolveProviderRequest({ + model: 'claude-sonnet-4-6-coding', + baseUrl: 'https://api.atlascloud.ai/v1', + processEnv: {}, + }).resolvedModel).toBe('anthropic/claude-sonnet-4.6-coding') + + expect(resolveProviderRequest({ + model: 'deepseek-ai/deepseek-v3.2', + baseUrl: 'https://api.atlascloud.ai/v1', + processEnv: {}, + }).resolvedModel).toBe('deepseek-ai/deepseek-v3.2') +}) + +test('resolveProviderRequest leaves OpenRouter routing untouched without explicit aliases', () => { + const request = resolveProviderRequest({ + model: 'gpt-5-mini', + baseUrl: 'https://openrouter.ai/api/v1', + processEnv: {}, + }) + + expect(request.resolvedModel).toBe('gpt-5-mini') + expect(request.baseUrl).toBe('https://openrouter.ai/api/v1') +}) + +test('resolveProviderRequest leaves Hicap routing untouched without explicit aliases', () => { + const request = resolveProviderRequest({ + model: 'claude-opus-4-7', + baseUrl: 'https://api.hicap.ai/v1', + processEnv: {}, + }) + + expect(request.resolvedModel).toBe('claude-opus-4-7') + expect(request.baseUrl).toBe('https://api.hicap.ai/v1') +}) diff --git a/src/services/api/providerConfig.ts b/src/services/api/providerConfig.ts index d424357c81..8331df3172 100644 --- a/src/services/api/providerConfig.ts +++ b/src/services/api/providerConfig.ts @@ -19,6 +19,7 @@ import { DEFAULT_GEMINI_BASE_URL, DEFAULT_GEMINI_MODEL, } from 'src/utils/providerProfile.js' +import { getCatalogEntriesForRoute } from '../../integrations/registry.js' import { openAIShimSupportsApiFormatForModel, resolveOpenAIShimRuntimeContext, @@ -249,6 +250,35 @@ function readNestedString( return undefined } +function normalizeModelLookupKey(model: string): string { + return model.trim().split('?', 1)[0]?.trim().toLowerCase() ?? '' +} + +function resolveRouteCatalogAliasApiName(options: { + model: string + baseUrl: string | undefined + processEnv: NodeJS.ProcessEnv +}): string { + const normalizedModel = normalizeModelLookupKey(options.model) + if (!normalizedModel) return options.model + + const runtimeShimContext = resolveOpenAIShimRuntimeContext({ + processEnv: options.processEnv, + baseUrl: options.baseUrl, + model: options.model, + treatAsLocal: options.baseUrl ? isLocalProviderUrl(options.baseUrl) : false, + }) + const routeId = runtimeShimContext.routeId + if (!routeId || routeId === 'anthropic' || routeId === 'openai') { + return options.model + } + + const entry = getCatalogEntriesForRoute(routeId).find(catalogEntry => + (catalogEntry.aliases ?? []).some(alias => normalizeModelLookupKey(alias) === normalizedModel), + ) + return entry?.apiName ?? options.model +} + function parseReasoningEffort(value: string | undefined): ReasoningEffort | undefined { if (!value) return undefined const normalized = value.trim().toLowerCase() @@ -879,7 +909,11 @@ export function resolveProviderRequest(options?: { ? normalizeGithubCopilotModel(descriptor.baseModel) : (isGithubModels || isGithubCustom || isGithubGhe ? normalizeGithubModelsApiModel(descriptor.baseModel) - : descriptor.baseModel) + : resolveRouteCatalogAliasApiName({ + model: descriptor.baseModel, + baseUrl: finalBaseUrl, + processEnv, + })) const reasoning = options?.reasoningEffortOverride ? { effort: options.reasoningEffortOverride } diff --git a/src/utils/effort.codex.test.ts b/src/utils/effort.codex.test.ts index e15758345a..960c6c3999 100644 --- a/src/utils/effort.codex.test.ts +++ b/src/utils/effort.codex.test.ts @@ -414,6 +414,154 @@ test('explicit reasoning metadata enables model-level effort without provider-wi expect(resolveAppliedEffort('xai/grok-build-0.1', 'high')).toBeUndefined() }) +test('Atlas Cloud catalog exposes only verified reasoning controls for exact models', async () => { + const atlasGateway = (await import('../integrations/gateways/atlas-cloud.js')).default + const { + getAvailableEffortLevels, + modelSupportsEffort, + modelSupportsWireEffort, + resolveAppliedEffort, + resolveModelReasoningControl, + } = await importFreshEffortModule({ + provider: 'openai', + supportsCodexReasoningEffort: false, + routeId: 'atlas-cloud', + catalogEntries: atlasGateway.catalog?.models ?? [], + }) + + expect(resolveModelReasoningControl('moonshotai/kimi-k2.5')).toMatchObject({ + supportsReasoning: true, + controllable: true, + source: 'metadata', + levels: ['low', 'medium', 'high', 'xhigh'], + wireFormat: 'reasoning_effort', + }) + expect(getAvailableEffortLevels('moonshotai/kimi-k2.5')).toEqual([ + 'low', + 'medium', + 'high', + 'xhigh', + ]) + expect(resolveAppliedEffort('moonshotai/kimi-k2.5', 'max')).toBe('high') + + expect(resolveModelReasoningControl('moonshotai/kimi-k2.6')).toMatchObject({ + supportsReasoning: true, + controllable: true, + source: 'metadata', + levels: ['low', 'medium', 'high', 'xhigh'], + wireFormat: 'reasoning_effort', + }) + expect(getAvailableEffortLevels('moonshotai/kimi-k2.6')).toEqual([ + 'low', + 'medium', + 'high', + 'xhigh', + ]) + expect(resolveAppliedEffort('moonshotai/kimi-k2.6', 'xhigh')).toBe('xhigh') + expect(resolveAppliedEffort('moonshotai/kimi-k2.6', 'max')).toBe('high') + + expect(resolveModelReasoningControl('glm-5.2')).toMatchObject({ + supportsReasoning: true, + controllable: true, + source: 'metadata', + levels: ['low', 'medium', 'high', 'xhigh'], + wireFormat: 'reasoning_effort', + }) + expect(getAvailableEffortLevels('glm-5.2')).toEqual(['low', 'medium', 'high', 'xhigh']) + expect(resolveAppliedEffort('glm-5.2', 'xhigh')).toBe('xhigh') + + const verifiedAtlasReasoningModels = [ + 'deepseek-ai/deepseek-v4-pro', + 'deepseek-ai/deepseek-v4-flash', + 'deepseek-ai/deepseek-v3.2', + 'deepseek-ai/DeepSeek-V3.2-Exp', + 'anthropic/claude-opus-4.8', + 'anthropic/claude-sonnet-4.6', + 'anthropic/claude-sonnet-4.6-coding', + 'anthropic/claude-haiku-4.5-20251001', + 'anthropic/claude-haiku-4.5-20251001-coding', + 'openai/gpt-5.5', + 'openai/gpt-5.4', + 'google/gemini-3.5-flash', + 'google/gemini-3.1-pro-preview', + 'xai/grok-4.3', + 'zai-org/glm-5.2', + 'zai-org/glm-5.1', + 'zai-org/glm-5', + 'zai-org/glm-5-turbo', + 'zai-org/glm-5v-turbo', + 'zai-org/glm-4.7', + 'zai-org/GLM-4.6', + 'minimaxai/minimax-m3', + 'minimaxai/minimax-m2.7', + 'minimaxai/minimax-m2.5', + 'qwen/qwen3.7-max', + 'qwen/qwen3.7-plus', + 'qwen/qwen3.6-plus', + 'qwen/qwen3.6-35b-a3b', + 'qwen/qwen3.5-397b-a17b', + 'qwen/qwen3.5-122b-a10b', + 'qwen/qwen3.5-35b-a3b', + 'qwen/qwen3.5-27b', + 'qwen/qwen3-vl-30b-a3b-thinking', + 'Qwen/Qwen3-Next-80B-A3B-Thinking', + ] + for (const model of verifiedAtlasReasoningModels) { + expect(resolveModelReasoningControl(model)).toMatchObject({ + supportsReasoning: true, + controllable: true, + source: 'metadata', + levels: ['low', 'medium', 'high', 'xhigh'], + wireFormat: 'reasoning_effort', + }) + expect(getAvailableEffortLevels(model)).toEqual(['low', 'medium', 'high', 'xhigh']) + expect(resolveAppliedEffort(model, 'xhigh')).toBe('xhigh') + expect(resolveAppliedEffort(model, 'max')).toBe('high') + } + + const verifiedAtlasHighOnlyReasoningModels = [ + 'bytedance/doubao-seed-2.0-pro-260215', + 'bytedance/doubao-seed-2.0-code-preview-260215', + 'bytedance/doubao-seed-2.0-lite-260428', + 'bytedance/doubao-seed-2.0-mini-260428', + ] + for (const model of verifiedAtlasHighOnlyReasoningModels) { + expect(resolveModelReasoningControl(model)).toMatchObject({ + supportsReasoning: true, + controllable: true, + source: 'metadata', + levels: ['low', 'medium', 'high'], + wireFormat: 'reasoning_effort', + }) + expect(getAvailableEffortLevels(model)).toEqual(['low', 'medium', 'high']) + expect(resolveAppliedEffort(model, 'xhigh')).toBe('high') + } + + expect(resolveModelReasoningControl('owl')).toMatchObject({ + supportsReasoning: false, + controllable: false, + }) + + expect(resolveModelReasoningControl('moonshotai/kimi-k2.7-code')).toMatchObject({ + supportsReasoning: true, + controllable: true, + source: 'metadata', + levels: ['low', 'medium', 'high', 'xhigh'], + wireFormat: 'reasoning_effort', + }) + expect(modelSupportsEffort('moonshotai/kimi-k2.7-code')).toBe(true) + expect(modelSupportsWireEffort('moonshotai/kimi-k2.7-code')).toBe(true) + + expect(resolveModelReasoningControl('xai/grok-build-0.1')).toMatchObject({ + supportsReasoning: true, + controllable: false, + source: 'metadata', + wireFormat: 'none', + }) + expect(modelSupportsEffort('xai/grok-build-0.1')).toBe(false) + expect(modelSupportsWireEffort('xai/grok-build-0.1')).toBe(false) + expect(resolveAppliedEffort('xai/grok-build-0.1', 'high')).toBeUndefined() +}) test('explicit non-controllable metadata opts out even when the model matches legacy rules', async () => { const { getAvailableEffortLevels, diff --git a/src/utils/effort.ts b/src/utils/effort.ts index 9786abfecd..f03f18c0a1 100644 --- a/src/utils/effort.ts +++ b/src/utils/effort.ts @@ -317,7 +317,10 @@ function resolveCatalogReasoningMetadata( const entries = context?.catalogEntries ?? getCatalogEntriesForRoute(routeId) const entry = entries.find(catalogEntry => catalogEntry.apiName.trim().toLowerCase() === normalizedModel || - catalogEntry.id.trim().toLowerCase() === normalizedModel, + catalogEntry.id.trim().toLowerCase() === normalizedModel || + (catalogEntry.aliases ?? []).some(alias => + alias.trim().split('?', 1)[0]?.trim().toLowerCase() === normalizedModel, + ), ) if (!entry) { diff --git a/src/utils/providerProfiles.ts b/src/utils/providerProfiles.ts index dbe7c55149..03da18b710 100644 --- a/src/utils/providerProfiles.ts +++ b/src/utils/providerProfiles.ts @@ -1260,7 +1260,7 @@ function buildStartupProfileFromActiveProfile( : null } - if (route.vendorId === 'atlas-cloud') { + if (route.routeId === 'atlas-cloud') { const env = buildAtlasCloudProfileEnv({ model: getPrimaryModel(activeProfile.model),