From 1536ee18df9d7c1fce3a2c23912a218658575096 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 29 Jul 2026 15:44:21 +0200 Subject: [PATCH 1/3] feat(ai-gateway): flag reasoning direct BYOK models --- .../direct-byok/alibaba-token-plan.ts | 2 +- .../providers/direct-byok/byteplus-coding.ts | 2 +- .../providers/direct-byok/chutes-byok.ts | 2 +- .../providers/direct-byok/crofai.ts | 2 +- .../providers/direct-byok/inceptron-byok.ts | 2 +- .../providers/direct-byok/index.test.ts | 30 +++++++++++++++++-- .../ai-gateway/providers/direct-byok/index.ts | 4 ++- .../providers/direct-byok/kimi-coding.ts | 2 +- .../providers/direct-byok/martian.ts | 2 +- .../ai-gateway/providers/direct-byok/morph.ts | 8 +++-- .../providers/direct-byok/neurowatt.ts | 1 + .../providers/direct-byok/ollama-cloud.ts | 2 +- .../providers/direct-byok/opencode-go.ts | 2 +- .../providers/direct-byok/orcarouter.ts | 2 +- .../direct-byok/sync-direct-byok.test.ts | 8 +++++ .../providers/direct-byok/sync-direct-byok.ts | 13 ++++++-- .../providers/direct-byok/synthetic.ts | 2 +- .../ai-gateway/providers/direct-byok/types.ts | 2 +- .../direct-byok/xiaomi-token-plan-ams.ts | 1 + .../direct-byok/xiaomi-token-plan-sgp.ts | 1 + .../providers/direct-byok/zai-coding.ts | 1 + 21 files changed, 72 insertions(+), 19 deletions(-) diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/alibaba-token-plan.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/alibaba-token-plan.ts index 5a72b584ba..7cf55880d3 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/alibaba-token-plan.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/alibaba-token-plan.ts @@ -13,7 +13,7 @@ export default { { id: 'qwen3.7-plus', name: 'Qwen3.7 Plus', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 1_000_000, max_completion_tokens: 64_000, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/byteplus-coding.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/byteplus-coding.ts index efb08e5b69..05b638a042 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/byteplus-coding.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/byteplus-coding.ts @@ -22,7 +22,7 @@ export default { name: 'Seed-Code', description: "ByteDance's latest code model has been deeply optimized for agentic programming tasks.", - flags: ['recommended', 'vision'], + flags: ['recommended', 'vision', 'reasoning'], context_length: 262144, max_completion_tokens: 32768, variants: REASONING_VARIANTS_BINARY, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/chutes-byok.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/chutes-byok.ts index 667d5c5237..2e07dfef97 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/chutes-byok.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/chutes-byok.ts @@ -19,7 +19,7 @@ export default { { id: 'moonshotai/Kimi-K2.6-TEE', name: 'Kimi-K2.6', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 65535, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/crofai.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/crofai.ts index 6923990ce1..f09d713eb3 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/crofai.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/crofai.ts @@ -19,7 +19,7 @@ export default { { id: 'kimi-k2.6', name: 'Kimi K2.6', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 65535, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/inceptron-byok.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/inceptron-byok.ts index 258e02ff38..ad7b023c00 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/inceptron-byok.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/inceptron-byok.ts @@ -19,7 +19,7 @@ export default { { id: 'moonshotai/Kimi-K2.6', name: 'Kimi K2.6', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 262144, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/index.test.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/index.test.ts index 64536da4e3..51c05d2fc1 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/index.test.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/index.test.ts @@ -28,6 +28,13 @@ jest.mock('./direct-byok-definitions', () => ({ { id: 'supported-model', name: 'Supported Model', + flags: ['reasoning'], + context_length: 4096, + max_completion_tokens: 1024, + }, + { + id: 'non-reasoning-model', + name: 'Non-reasoning Model', context_length: 4096, max_completion_tokens: 1024, }, @@ -56,9 +63,9 @@ jest.mock('./direct-byok-definitions', () => ({ async function loadDirectByokModule() { const directByokProviders = (await import('./direct-byok-definitions')).default; - const { getDirectByokModel } = await import('.'); + const { getDirectByokModel, getDirectByokModelsForUser } = await import('.'); - return { directByokProviders, getDirectByokModel }; + return { directByokProviders, getDirectByokModel, getDirectByokModelsForUser }; } describe('getDirectByokModel', () => { @@ -88,4 +95,23 @@ describe('getDirectByokModel', () => { expect(directByokProviders[0].models).toHaveBeenCalledTimes(1); expect(directByokProviders[1].models).not.toHaveBeenCalled(); }); + + test('advertises reasoning parameters only for reasoning models', async () => { + const { getDirectByokModelsForUser } = await loadDirectByokModule(); + const { getBYOKforUser } = await import('@/lib/ai-gateway/byok'); + jest + .mocked(getBYOKforUser) + .mockResolvedValueOnce([{ providerId: 'chutes-byok', decryptedAPIKey: 'test-key' }]); + + const models = await getDirectByokModelsForUser('user-id'); + + expect(models[0].supported_parameters).toEqual([ + 'max_tokens', + 'temperature', + 'tools', + 'reasoning', + 'include_reasoning', + ]); + expect(models[1].supported_parameters).toEqual(['max_tokens', 'temperature', 'tools']); + }); }); diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/index.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/index.ts index 4ed2d9b567..6bbc2a9b51 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/index.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/index.ts @@ -54,7 +54,9 @@ function convertModel( is_moderated: false, }, per_request_limits: null, - supported_parameters: ['max_tokens', 'temperature', 'tools', 'reasoning', 'include_reasoning'], + supported_parameters: ['max_tokens', 'temperature', 'tools'].concat( + model.flags?.includes('reasoning') ? ['reasoning', 'include_reasoning'] : [] + ), default_parameters: {}, preferredIndex: model.flags?.includes('recommended') ? preferredIndex : undefined, hasUserByokAvailable: true, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/kimi-coding.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/kimi-coding.ts index 23882b4f45..5597832638 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/kimi-coding.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/kimi-coding.ts @@ -30,7 +30,7 @@ export default { { id: 'kimi-for-coding', name: 'Kimi for Coding', - flags: ['recommended', 'vision'], + flags: ['recommended', 'vision', 'reasoning'], context_length: 262144, max_completion_tokens: 32768, description: diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/martian.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/martian.ts index 4a163e3258..3ff1ba94f1 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/martian.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/martian.ts @@ -13,7 +13,7 @@ export default { { id: 'moonshotai/kimi-k2.6', name: 'kimi-k2.6', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 65535, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/morph.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/morph.ts index 007ecca414..765eafdd54 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/morph.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/morph.ts @@ -13,38 +13,42 @@ export default { { id: 'morph-qwen35-397b', name: 'Qwen 3.5 397B', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 131072, }, { id: 'morph-minimax27-230b', name: 'MiniMax M2.7', + flags: ['reasoning'], context_length: 196608, max_completion_tokens: 196608, }, { id: 'morph-minimax3-428b', name: 'MiniMax M3', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 256000, max_completion_tokens: 256000, }, { id: 'morph-glm52-744b', name: 'GLM-5.2', + flags: ['reasoning'], context_length: 1048576, max_completion_tokens: 1048576, }, { id: 'morph-qwen36-27b', name: 'Qwen 3.6 27B', + flags: ['reasoning'], context_length: 131072, max_completion_tokens: 131072, }, { id: 'morph-dsv4flash', name: 'DeepSeek V4 Flash', + flags: ['reasoning'], context_length: 1048576, max_completion_tokens: 1048576, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/neurowatt.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/neurowatt.ts index 0e23c95c85..1938c8046a 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/neurowatt.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/neurowatt.ts @@ -13,6 +13,7 @@ export default { { id: 'moonshotai/Kimi-K2.6', name: 'Kimi-K2.6', + flags: ['reasoning'], context_length: 262144, max_completion_tokens: 32000, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/ollama-cloud.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/ollama-cloud.ts index f7fe78756d..363f59d697 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/ollama-cloud.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/ollama-cloud.ts @@ -19,7 +19,7 @@ export default { { id: 'kimi-k2.6:cloud', name: 'kimi-k2.6', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 262144, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/opencode-go.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/opencode-go.ts index ec802e549d..b8062a9677 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/opencode-go.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/opencode-go.ts @@ -17,7 +17,7 @@ export default { { id: 'qwen3.7-plus', name: 'Qwen3.7 Plus', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 1_000_000, max_completion_tokens: 65_536, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/orcarouter.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/orcarouter.ts index c7b0c94802..9d242b0fa9 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/orcarouter.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/orcarouter.ts @@ -19,7 +19,7 @@ export default { { id: 'kimi/kimi-k2.6', name: 'kimi-k2.6', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 65535, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.test.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.test.ts index 7080042501..24830cc826 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.test.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.test.ts @@ -30,6 +30,7 @@ describe('parseOpenAICompatibleProviderModels', () => { context_length: 262144, max_completion_tokens: 131072, input_modalities: ['text', 'image'], + flags: ['reasoning'], }, { id: 'morph-minimax3-428b', @@ -37,6 +38,7 @@ describe('parseOpenAICompatibleProviderModels', () => { context_length: 256000, max_completion_tokens: undefined, input_modalities: undefined, + flags: ['reasoning'], }, ]); }); @@ -62,6 +64,7 @@ describe('parseModelsDevProviderModels', () => { stable: { id: 'stable', name: 'provider/stable', + reasoning: true, limit: { context: 128_000, output: 32_000 }, modalities: { input: ['text', 'image'], output: ['text'] }, }, @@ -72,6 +75,7 @@ describe('parseModelsDevProviderModels', () => { beta: { id: 'beta', status: 'beta', + reasoning: false, }, unknownStatus: { id: 'unknown-status', @@ -97,6 +101,7 @@ describe('parseModelsDevProviderModels', () => { context_length: 128_000, max_completion_tokens: 32_000, input_modalities: ['text', 'image'], + flags: ['reasoning'], }, { id: 'alpha', @@ -104,6 +109,7 @@ describe('parseModelsDevProviderModels', () => { context_length: undefined, max_completion_tokens: undefined, input_modalities: undefined, + flags: undefined, }, { id: 'beta', @@ -111,6 +117,7 @@ describe('parseModelsDevProviderModels', () => { context_length: undefined, max_completion_tokens: undefined, input_modalities: undefined, + flags: undefined, }, { id: 'unknown-status', @@ -118,6 +125,7 @@ describe('parseModelsDevProviderModels', () => { context_length: undefined, max_completion_tokens: undefined, input_modalities: undefined, + flags: undefined, }, ]); }); diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.ts index ba7c2daa9e..dfceb8f5fd 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.ts @@ -1,5 +1,8 @@ import * as z from 'zod'; -import { type DirectByokModel } from '@/lib/ai-gateway/providers/direct-byok/types'; +import { + type DirectByokModel, + type DirectByokModelFlag, +} from '@/lib/ai-gateway/providers/direct-byok/types'; import type { DirectUserByokInferenceProviderId } from '@/lib/ai-gateway/providers/openrouter/inference-provider-id'; import { redisClient } from '@/lib/redis'; import { directByokModelsRedisKey } from '@/lib/redis-keys'; @@ -28,6 +31,7 @@ const OpenAICompatibleModelsResponseSchema = z.object({ const ModelsDevModelSchema = z.object({ id: z.string(), name: z.string().optional(), + reasoning: z.boolean().optional(), status: z.enum(['alpha', 'beta', 'deprecated']).optional().catch(undefined), limit: z .object({ @@ -57,6 +61,7 @@ type RawModel = { context_length?: number; max_completion_tokens?: number; input_modalities?: ReadonlyArray>; + flags?: ReadonlyArray; }; type SyncContext = { @@ -105,6 +110,7 @@ export function parseOpenAICompatibleProviderModels(entry: unknown): RawModel[] context_length: model.context_length ?? model.max_model_len, max_completion_tokens: model.max_output_length, input_modalities: model.input_modalities, + flags: ['reasoning'], })); } @@ -122,6 +128,7 @@ export function parseModelsDevProviderModels(entry: unknown): RawModel[] { context_length: model.limit?.context, max_completion_tokens: model.limit?.output, input_modalities: model.modalities?.input, + flags: model.reasoning ? ['reasoning'] : undefined, })); } @@ -208,6 +215,8 @@ async function syncProvider(fetcher: ProviderFetcher, ctx: SyncContext): Promise for (const raw of fetched) { const name = raw.name ?? shortenDisplayName(raw.id); const context_length = raw.context_length ?? DEFAULT_CONTENT_LENGTH; + const flags = new Set(raw.flags); + if (raw.input_modalities?.includes('image')) flags.add('vision'); const max_completion_tokens = Math.min( raw.max_completion_tokens ?? DEFAULT_MAX_COMPLETION_TOKENS, context_length @@ -215,7 +224,7 @@ async function syncProvider(fetcher: ProviderFetcher, ctx: SyncContext): Promise models.push({ id: raw.id, name, - flags: raw.input_modalities?.includes('image') ? ['vision'] : undefined, + flags: flags.size > 0 ? [...flags] : undefined, context_length, max_completion_tokens, }); diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/synthetic.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/synthetic.ts index 4bed6ab4cb..9750f53486 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/synthetic.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/synthetic.ts @@ -19,7 +19,7 @@ export default { { id: 'hf:moonshotai/Kimi-K2.6', name: 'Kimi-K2.6', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 65535, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/types.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/types.ts index a2d2da0bd9..1289ff266c 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/types.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/types.ts @@ -3,7 +3,7 @@ import type { DirectByokProviderMetaId } from '@/lib/ai-gateway/providers/direct import type { GatewayChatApiKind, TransformRequestContext } from '@/lib/ai-gateway/providers/types'; import type { CustomLlmProvider } from '@kilocode/db'; -export const DirectByokModelFlagSchema = z.enum(['recommended', 'vision']); +export const DirectByokModelFlagSchema = z.enum(['recommended', 'vision', 'reasoning']); export type DirectByokModelFlag = z.infer; diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/xiaomi-token-plan-ams.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/xiaomi-token-plan-ams.ts index 6684c3a683..264142af5e 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/xiaomi-token-plan-ams.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/xiaomi-token-plan-ams.ts @@ -13,6 +13,7 @@ export default { { id: 'mimo-v2.5-pro', name: 'MiMo-V2.5-Pro', + flags: ['reasoning'], context_length: 1048576, max_completion_tokens: 131072, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/xiaomi-token-plan-sgp.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/xiaomi-token-plan-sgp.ts index 4e6fd744af..cb25ed7ce5 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/xiaomi-token-plan-sgp.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/xiaomi-token-plan-sgp.ts @@ -13,6 +13,7 @@ export default { { id: 'mimo-v2.5-pro', name: 'MiMo-V2.5-Pro', + flags: ['reasoning'], context_length: 1048576, max_completion_tokens: 131072, }, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/zai-coding.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/zai-coding.ts index 722b925b05..430407fc34 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/zai-coding.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/zai-coding.ts @@ -18,6 +18,7 @@ export default { { id: 'glm-5.2', name: 'GLM-5.2', + flags: ['reasoning'], context_length: 1000000, max_completion_tokens: 131072, }, From b38f5ebf0d7f6113f963e0a71e691747ed939da7 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 29 Jul 2026 15:52:16 +0200 Subject: [PATCH 2/3] fix(ai-gateway): defer reasoning parameter filtering --- .../providers/direct-byok/byteplus-coding.ts | 10 +++++-- .../providers/direct-byok/index.test.ts | 30 ++----------------- .../ai-gateway/providers/direct-byok/index.ts | 4 +-- 3 files changed, 10 insertions(+), 34 deletions(-) diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/byteplus-coding.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/byteplus-coding.ts index 05b638a042..1e43929340 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/byteplus-coding.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/byteplus-coding.ts @@ -32,6 +32,7 @@ export default { name: 'Kimi-K2.5', description: 'Open-source SoTA native multimodal model with text-only input (for now), stronger code/UI generation.', + flags: ['reasoning'], context_length: 262144, max_completion_tokens: 32768, variants: REASONING_VARIANTS_BINARY, @@ -41,6 +42,7 @@ export default { name: 'GLM-5.1', description: 'Z.AI’s latest flagship model, designed for long-horizon tasks. It can work continuously and autonomously on a single task for up to 8 hours.', + flags: ['reasoning'], context_length: 204800, max_completion_tokens: 131072, variants: REASONING_VARIANTS_BINARY, @@ -50,6 +52,7 @@ export default { name: 'GLM-4.7', description: "Z.ai's latest flagship model, enhanced programming capabilities and more stable multi-step reasoning/execution.", + flags: ['reasoning'], context_length: 204800, max_completion_tokens: 131072, variants: REASONING_VARIANTS_BINARY, @@ -59,6 +62,7 @@ export default { name: 'GPT-OSS-120B', description: "OpenAI's open-weight model, 117B parameters with 5.1B active parameters for production, general purpose, high reasoning use cases.", + flags: ['reasoning'], context_length: 131072, max_completion_tokens: 65536, }, @@ -66,7 +70,7 @@ export default { id: 'dola-seed-2.0-code', name: 'Dola-Seed-2.0-Code', description: 'An enhanced coding version of Seed 2.0, better suited for agentic coding.', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 131072, variants: REASONING_VARIANTS_MINIMAL_LOW_MEDIUM_HIGH, @@ -76,7 +80,7 @@ export default { name: 'Dola-Seed-2.0-Pro', description: 'Focused on long-chain reasoning and stability in complex task execution, designed for complex real-world business scenarios.', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 131072, variants: REASONING_VARIANTS_MINIMAL_LOW_MEDIUM_HIGH, @@ -86,7 +90,7 @@ export default { name: 'Dola-Seed-2.0-Lite', description: 'Balances generation quality and response speed, making it a strong general-purpose production model.', - flags: ['vision'], + flags: ['vision', 'reasoning'], context_length: 262144, max_completion_tokens: 131072, variants: REASONING_VARIANTS_MINIMAL_LOW_MEDIUM_HIGH, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/index.test.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/index.test.ts index 51c05d2fc1..64536da4e3 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/index.test.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/index.test.ts @@ -28,13 +28,6 @@ jest.mock('./direct-byok-definitions', () => ({ { id: 'supported-model', name: 'Supported Model', - flags: ['reasoning'], - context_length: 4096, - max_completion_tokens: 1024, - }, - { - id: 'non-reasoning-model', - name: 'Non-reasoning Model', context_length: 4096, max_completion_tokens: 1024, }, @@ -63,9 +56,9 @@ jest.mock('./direct-byok-definitions', () => ({ async function loadDirectByokModule() { const directByokProviders = (await import('./direct-byok-definitions')).default; - const { getDirectByokModel, getDirectByokModelsForUser } = await import('.'); + const { getDirectByokModel } = await import('.'); - return { directByokProviders, getDirectByokModel, getDirectByokModelsForUser }; + return { directByokProviders, getDirectByokModel }; } describe('getDirectByokModel', () => { @@ -95,23 +88,4 @@ describe('getDirectByokModel', () => { expect(directByokProviders[0].models).toHaveBeenCalledTimes(1); expect(directByokProviders[1].models).not.toHaveBeenCalled(); }); - - test('advertises reasoning parameters only for reasoning models', async () => { - const { getDirectByokModelsForUser } = await loadDirectByokModule(); - const { getBYOKforUser } = await import('@/lib/ai-gateway/byok'); - jest - .mocked(getBYOKforUser) - .mockResolvedValueOnce([{ providerId: 'chutes-byok', decryptedAPIKey: 'test-key' }]); - - const models = await getDirectByokModelsForUser('user-id'); - - expect(models[0].supported_parameters).toEqual([ - 'max_tokens', - 'temperature', - 'tools', - 'reasoning', - 'include_reasoning', - ]); - expect(models[1].supported_parameters).toEqual(['max_tokens', 'temperature', 'tools']); - }); }); diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/index.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/index.ts index 6bbc2a9b51..4ed2d9b567 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/index.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/index.ts @@ -54,9 +54,7 @@ function convertModel( is_moderated: false, }, per_request_limits: null, - supported_parameters: ['max_tokens', 'temperature', 'tools'].concat( - model.flags?.includes('reasoning') ? ['reasoning', 'include_reasoning'] : [] - ), + supported_parameters: ['max_tokens', 'temperature', 'tools', 'reasoning', 'include_reasoning'], default_parameters: {}, preferredIndex: model.flags?.includes('recommended') ? preferredIndex : undefined, hasUserByokAvailable: true, From cce57925bccb7391137ecbcb75ee81ae6f05fc5b Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 29 Jul 2026 16:34:07 +0200 Subject: [PATCH 3/3] feat(ai-gateway): filter stale direct BYOK models --- .../direct-byok/sync-direct-byok.test.ts | 15 +++++++++ .../providers/direct-byok/sync-direct-byok.ts | 31 +++++++++++++++---- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.test.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.test.ts index 24830cc826..6493ca878c 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.test.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.test.ts @@ -129,4 +129,19 @@ describe('parseModelsDevProviderModels', () => { }, ]); }); + + test('excludes models missing from the provider model list', () => { + const models = parseModelsDevProviderModels( + { + models: { + available: { id: 'available', limit: { context: 128_000 } }, + removed: { id: 'removed', limit: { context: 64_000 } }, + }, + }, + new Set(['available', 'provider-only']) + ); + + expect(models.map(model => model.id)).toEqual(['available']); + expect(models[0].context_length).toBe(128_000); + }); }); diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.ts index dfceb8f5fd..c6236bd7d7 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/sync-direct-byok.ts @@ -114,13 +114,17 @@ export function parseOpenAICompatibleProviderModels(entry: unknown): RawModel[] })); } -export function parseModelsDevProviderModels(entry: unknown): RawModel[] { +export function parseModelsDevProviderModels( + entry: unknown, + availableModelIds?: ReadonlySet +): RawModel[] { const provider = ModelsDevProviderSchema.parse(entry); return Object.values(provider.models) .filter( model => model.status !== 'deprecated' && - (!model.modalities?.output || model.modalities.output.includes('text')) + (!model.modalities?.output || model.modalities.output.includes('text')) && + (!availableModelIds || availableModelIds.has(model.id)) ) .map(model => ({ id: model.id, @@ -134,7 +138,8 @@ export function parseModelsDevProviderModels(entry: unknown): RawModel[] { function modelsDevFetcher( providerId: DirectUserByokInferenceProviderId, - catalogKey: string + catalogKey: string, + availableModelsUrl?: string ): ProviderFetcher { return { providerId, @@ -144,7 +149,21 @@ function modelsDevFetcher( if (!entry) { throw new Error(`models.dev catalog missing ${catalogKey} entry`); } - return parseModelsDevProviderModels(entry); + if (!availableModelsUrl) { + return parseModelsDevProviderModels(entry); + } + const response = await fetch(availableModelsUrl); + if (!response.ok) { + throw new Error( + `Failed to fetch ${providerId} available models: ${response.status} ${response.statusText}` + ); + } + const availableModelIds = new Set( + OpenAICompatibleModelsResponseSchema.parse(await response.json()).data.map( + model => model.id + ) + ); + return parseModelsDevProviderModels(entry, availableModelIds); }, }; } @@ -202,8 +221,8 @@ const FETCHERS: ReadonlyArray = [ }), modelsDevFetcher('alibaba-token-plan', 'alibaba-token-plan'), modelsDevFetcher('zai-coding', 'zai-coding-plan'), - modelsDevFetcher('ollama-cloud', 'ollama-cloud'), - modelsDevFetcher('opencode-go', 'opencode-go'), + modelsDevFetcher('ollama-cloud', 'ollama-cloud', 'https://ollama.com/v1/models'), + modelsDevFetcher('opencode-go', 'opencode-go', 'https://opencode.ai/zen/go/v1/models'), modelsDevFetcher('xiaomi-token-plan-ams', 'xiaomi-token-plan-ams'), modelsDevFetcher('xiaomi-token-plan-sgp', 'xiaomi-token-plan-sgp'), ];