From b4a4e1b10dfd59ae52d348fe6d46b69bab474ed6 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 3 Jun 2026 16:35:02 +0200 Subject: [PATCH 1/3] Add a bunch of BYOK provider --- .../providers/direct-byok/byteplus-coding.ts | 3 +- .../providers/direct-byok/chutes-byok.ts | 3 +- .../providers/direct-byok/crofai.ts | 30 +++++++++++++++++++ .../direct-byok/direct-byok-definitions.ts | 10 ++++++- .../providers/direct-byok/direct-byok-meta.ts | 4 +++ .../ai-gateway/providers/direct-byok/index.ts | 10 ++++--- .../providers/direct-byok/kimi-coding.ts | 3 +- .../providers/direct-byok/martian.ts | 24 +++++++++++++++ .../providers/direct-byok/neurowatt.ts | 3 +- .../providers/direct-byok/ollama-cloud.ts | 3 +- .../providers/direct-byok/orcarouter.ts | 30 +++++++++++++++++++ .../providers/direct-byok/sync-direct-byok.ts | 23 +++++++++++++- .../providers/direct-byok/synthetic.ts | 30 +++++++++++++++++++ .../ai-gateway/providers/direct-byok/types.ts | 5 ++-- .../direct-byok/xiaomi-token-plan-ams.ts | 3 +- .../direct-byok/xiaomi-token-plan-sgp.ts | 3 +- .../providers/direct-byok/zai-coding.ts | 3 +- .../lib/ai-gateway/providers/get-provider.ts | 2 +- .../ai-gateway/providers/model-settings.ts | 26 ++++++++++------ .../openrouter/inference-provider-id.ts | 8 +++++ 20 files changed, 200 insertions(+), 26 deletions(-) create mode 100644 apps/web/src/lib/ai-gateway/providers/direct-byok/crofai.ts create mode 100644 apps/web/src/lib/ai-gateway/providers/direct-byok/martian.ts create mode 100644 apps/web/src/lib/ai-gateway/providers/direct-byok/orcarouter.ts create mode 100644 apps/web/src/lib/ai-gateway/providers/direct-byok/synthetic.ts 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 1267610978..efb08e5b69 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 @@ -8,7 +8,8 @@ import type { DirectByokProvider } from '@/lib/ai-gateway/providers/direct-byok/ export default { id: 'byteplus-coding', base_url: 'https://ark.ap-southeast.bytepluses.com/api/coding/v3', - ai_sdk_provider: 'openai-compatible', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', transformRequest(context) { context.request.body.thinking = { type: isReasoningExplicitlyDisabled(context.request) ? 'disabled' : 'enabled', 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 050b9123ec..25853bcc90 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 @@ -5,7 +5,8 @@ import { REASONING_VARIANTS_LOW_MEDIUM_HIGH } from '@/lib/ai-gateway/providers/m export default { id: 'chutes-byok', base_url: 'https://llm.chutes.ai/v1', - ai_sdk_provider: 'openai-compatible', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', transformRequest(context) { const { request } = context; if (request.kind !== 'chat_completions') { 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 new file mode 100644 index 0000000000..568c1f485c --- /dev/null +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/crofai.ts @@ -0,0 +1,30 @@ +import { cachedEnhancedDirectByokModelList } from '@/lib/ai-gateway/providers/direct-byok/model-list'; +import type { DirectByokProvider } from '@/lib/ai-gateway/providers/direct-byok/types'; +import { REASONING_VARIANTS_LOW_MEDIUM_HIGH } from '@/lib/ai-gateway/providers/model-settings'; + +export default { + id: 'crofai', + base_url: 'https://crof.ai/v1', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', + transformRequest(context) { + const { request } = context; + if (request.kind !== 'chat_completions') { + return; + } + request.body.reasoning_effort ??= request.body.reasoning?.effort ?? undefined; + }, + models: cachedEnhancedDirectByokModelList({ + providerId: 'crofai', + recommendedModels: [ + { + id: 'kimi-k2.6', + name: 'Kimi K2.6', + flags: ['vision'], + context_length: 262144, + max_completion_tokens: 65535, + }, + ], + variants: REASONING_VARIANTS_LOW_MEDIUM_HIGH, + }), +} satisfies DirectByokProvider; diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-definitions.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-definitions.ts index 9adad335d3..fc5d565cbb 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-definitions.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-definitions.ts @@ -1,4 +1,4 @@ -import type { DirectByokProvider } from '@/lib/ai-gateway/providers/direct-byok/types'; +import type { DirectByokProvider } from './types'; import byteplusCoding from './byteplus-coding'; import chutesByok from './chutes-byok'; import kimiCoding from './kimi-coding'; @@ -7,13 +7,21 @@ import ollamaCloud from './ollama-cloud'; import xiaomiTokenPlanAms from './xiaomi-token-plan-ams'; import xiaomiTokenPlanSgp from './xiaomi-token-plan-sgp'; import zaiCoding from './zai-coding'; +import crofai from './crofai'; +import martian from './martian'; +import orcarouter from './orcarouter'; +import synthetic from './synthetic'; export default [ byteplusCoding, chutesByok, + crofai, kimiCoding, + martian, neuralwatt, ollamaCloud, + orcarouter, + synthetic, xiaomiTokenPlanAms, xiaomiTokenPlanSgp, zaiCoding, diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-meta.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-meta.ts index fede86d4e9..f5fc69a7a6 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-meta.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-meta.ts @@ -4,9 +4,13 @@ import type { DirectUserByokInferenceProviderId } from '@/lib/ai-gateway/provide export const DIRECT_BYOK_PROVIDERS_META = { 'byteplus-coding': 'BytePlus Coding Plan', 'chutes-byok': 'Chutes BYOK', + crofai: 'CrofAI', 'kimi-coding': 'Kimi Code', + martian: 'Martian', neuralwatt: 'Neuralwatt', 'ollama-cloud': 'Ollama Cloud', + orcarouter: 'OrcaRouter', + synthetic: 'Synthetic', 'xiaomi-token-plan-ams': 'Xiaomi Token Plan (Europe)', 'xiaomi-token-plan-sgp': 'Xiaomi Token Plan (Singapore)', 'zai-coding': 'Z.ai Coding Plan', 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 73ef92f203..e7ca8fda9e 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 @@ -11,6 +11,7 @@ import { readDb } from '@/lib/drizzle'; import { preferredModels } from '@/lib/ai-gateway/models'; import { createOpenAICompatible } from '@ai-sdk/openai-compatible'; import type { OpenCodeSettings } from '@kilocode/db'; +import { getCreatorRecommendedAiSdkProvider } from '@/lib/ai-gateway/providers/model-settings'; export function formatDirectByokModelId(provider: DirectByokProvider, model: DirectByokModel) { return (provider.id + '/' + model.id).toLowerCase(); @@ -57,7 +58,7 @@ function convertModel( default_parameters: {}, preferredIndex: model.flags?.includes('recommended') ? preferredIndex : undefined, opencode: { - ai_sdk_provider: provider.ai_sdk_provider, + ai_sdk_provider: getCreatorRecommendedAiSdkProvider(id) ?? provider.default_ai_sdk_provider, variants: model.variants, } satisfies OpenCodeSettings, }; @@ -110,8 +111,9 @@ export async function getDirectByokModelsForUser(userId: string) { export function createAiSdkProvider(directByokProvider: DirectByokProvider, apiKey: string) { if ( - directByokProvider.ai_sdk_provider === 'openai-compatible' || - directByokProvider.ai_sdk_provider === 'alibaba' + directByokProvider.default_ai_sdk_provider === 'openai-compatible' || + directByokProvider.default_ai_sdk_provider === 'openrouter' || + directByokProvider.default_ai_sdk_provider === 'alibaba' ) { return createOpenAICompatible({ baseURL: directByokProvider.base_url, @@ -124,6 +126,6 @@ export function createAiSdkProvider(directByokProvider: DirectByokProvider, apiK }, }); } else { - throw new Error('Unrecognized AI SDK provider: ' + directByokProvider.ai_sdk_provider); + throw new Error('Unrecognized AI SDK provider: ' + directByokProvider.default_ai_sdk_provider); } } 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 ea5f022b02..5f92f795d6 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 @@ -9,7 +9,8 @@ import { isRooCodeBasedClient } from '@/lib/utils'; export default { id: 'kimi-coding', base_url: 'https://api.kimi.com/coding/v1', - ai_sdk_provider: 'openai-compatible', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', transformRequest(context) { const reasoningDisabled = isRooCodeBasedClient(context.originalHeaders) || 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 new file mode 100644 index 0000000000..883687d0d1 --- /dev/null +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/martian.ts @@ -0,0 +1,24 @@ +import { cachedEnhancedDirectByokModelList } from '@/lib/ai-gateway/providers/direct-byok/model-list'; +import type { DirectByokProvider } from '@/lib/ai-gateway/providers/direct-byok/types'; +import { REASONING_VARIANTS_LOW_MEDIUM_HIGH } from '@/lib/ai-gateway/providers/model-settings'; + +export default { + id: 'martian', + base_url: 'https://api.withmartian.com/v1', + supported_chat_apis: ['chat_completions', 'messages', 'responses'], + default_ai_sdk_provider: 'openrouter', + transformRequest() {}, + models: cachedEnhancedDirectByokModelList({ + providerId: 'martian', + recommendedModels: [ + { + id: 'moonshotai/Kimi-K2.6', + name: 'Kimi-K2.6', + flags: ['vision'], + context_length: 262144, + max_completion_tokens: 65535, + }, + ], + variants: REASONING_VARIANTS_LOW_MEDIUM_HIGH, + }), +} satisfies DirectByokProvider; 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 639e3c204a..0e23c95c85 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 @@ -4,7 +4,8 @@ import type { DirectByokProvider } from '@/lib/ai-gateway/providers/direct-byok/ export default { id: 'neuralwatt', base_url: 'https://api.neuralwatt.com/v1', - ai_sdk_provider: 'openai-compatible', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', transformRequest(_context) {}, models: cachedEnhancedDirectByokModelList({ providerId: 'neuralwatt', 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 79c28073fd..62e49f320a 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 @@ -5,7 +5,8 @@ import { REASONING_VARIANTS_NONE_LOW_MEDIUM_HIGH } from '@/lib/ai-gateway/provid export default { id: 'ollama-cloud', base_url: 'https://ollama.com/v1', - ai_sdk_provider: 'openai-compatible', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', transformRequest(context) { const { request } = context; if (request.kind !== 'chat_completions') { 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 new file mode 100644 index 0000000000..43925e3861 --- /dev/null +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/orcarouter.ts @@ -0,0 +1,30 @@ +import { cachedEnhancedDirectByokModelList } from '@/lib/ai-gateway/providers/direct-byok/model-list'; +import type { DirectByokProvider } from '@/lib/ai-gateway/providers/direct-byok/types'; +import { REASONING_VARIANTS_LOW_MEDIUM_HIGH } from '@/lib/ai-gateway/providers/model-settings'; + +export default { + id: 'orcarouter', + base_url: 'https://api.orcarouter.ai/v1', + supported_chat_apis: ['chat_completions', 'messages', 'responses'], + default_ai_sdk_provider: 'openai-compatible', + transformRequest(context) { + const { request } = context; + if (request.kind !== 'chat_completions') { + return; + } + request.body.reasoning_effort ??= request.body.reasoning?.effort ?? undefined; + }, + models: cachedEnhancedDirectByokModelList({ + providerId: 'orcarouter', + recommendedModels: [ + { + id: 'kimi/kimi-k2.6', + name: 'kimi-k2.6', + flags: ['vision'], + context_length: 262144, + max_completion_tokens: 65535, + }, + ], + variants: REASONING_VARIANTS_LOW_MEDIUM_HIGH, + }), +} satisfies DirectByokProvider; 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 a3fa354817..fc56a1b29a 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 @@ -4,6 +4,7 @@ import type { DirectUserByokInferenceProviderId } from '@/lib/ai-gateway/provide import { redisSet } from '@/lib/redis'; import { directByokModelsRedisKey } from '@/lib/redis-keys'; +const DEFAULT_CONTENT_LENGTH = 200_000; const DEFAULT_MAX_COMPLETION_TOKENS = 32_000; const ModalitySchema = z @@ -134,6 +135,26 @@ const FETCHERS: ReadonlyArray = [ label: 'Chutes', url: 'https://llm.chutes.ai/v1/models', }), + openAICompatibleFetcher({ + providerId: 'crofai', + label: 'CrofAI', + url: 'https://crof.ai/v1/models', + }), + openAICompatibleFetcher({ + providerId: 'orcarouter', + label: 'OrcaRouter', + url: 'https://api.orcarouter.ai/v1/models', + }), + openAICompatibleFetcher({ + providerId: 'martian', + label: 'Martian', + url: 'https://api.withmartian.com/v1/models', + }), + openAICompatibleFetcher({ + providerId: 'synthetic', + label: 'Synthetic', + url: 'https://api.synthetic.new/v1/models', + }), modelsDevFetcher('zai-coding', 'zai-coding-plan'), modelsDevFetcher('ollama-cloud', 'ollama-cloud'), modelsDevFetcher('xiaomi-token-plan-ams', 'xiaomi-token-plan-ams'), @@ -151,7 +172,7 @@ async function syncProvider(fetcher: ProviderFetcher, ctx: SyncContext): Promise for (const raw of fetched) { const name = raw.name ?? modelIdToDisplayName(raw.id); - const context_length = raw.context_length ?? DEFAULT_MAX_COMPLETION_TOKENS; + const context_length = raw.context_length ?? DEFAULT_CONTENT_LENGTH; const max_completion_tokens = Math.min( raw.max_completion_tokens ?? DEFAULT_MAX_COMPLETION_TOKENS, context_length 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 new file mode 100644 index 0000000000..42bbeec631 --- /dev/null +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/synthetic.ts @@ -0,0 +1,30 @@ +import { cachedEnhancedDirectByokModelList } from '@/lib/ai-gateway/providers/direct-byok/model-list'; +import type { DirectByokProvider } from '@/lib/ai-gateway/providers/direct-byok/types'; +import { REASONING_VARIANTS_LOW_MEDIUM_HIGH } from '@/lib/ai-gateway/providers/model-settings'; + +export default { + id: 'synthetic', + base_url: 'https://api.synthetic.new/v1', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', + transformRequest(context) { + const { request } = context; + if (request.kind !== 'chat_completions') { + return; + } + request.body.reasoning_effort ??= request.body.reasoning?.effort ?? undefined; + }, + models: cachedEnhancedDirectByokModelList({ + providerId: 'synthetic', + recommendedModels: [ + { + id: 'moonshotai/Kimi-K2.6', + name: 'Kimi-K2.6', + flags: ['vision'], + context_length: 262144, + max_completion_tokens: 65535, + }, + ], + variants: REASONING_VARIANTS_LOW_MEDIUM_HIGH, + }), +} satisfies DirectByokProvider; 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 84e2309c0b..194294a5b1 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 @@ -1,7 +1,7 @@ import * as z from 'zod'; import { OpenCodeVariantSchema } from '@kilocode/db/schema-types'; import type { DirectByokProviderMetaId } from '@/lib/ai-gateway/providers/direct-byok/direct-byok-meta'; -import type { TransformRequestContext } from '@/lib/ai-gateway/providers/types'; +import type { GatewayChatApiKind, TransformRequestContext } from '@/lib/ai-gateway/providers/types'; import type { CustomLlmProvider } from '@kilocode/db'; export const DirectByokModelFlagSchema = z.enum(['recommended', 'vision']); @@ -28,7 +28,8 @@ export type DirectByokProvider = { id: DirectByokProviderMetaId; base_url: string; models: () => Promise>; - ai_sdk_provider: CustomLlmProvider; + supported_chat_apis: ReadonlyArray; + default_ai_sdk_provider: CustomLlmProvider; transformRequest(context: TransformRequestContext): void; }; 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 52b41e644b..3d17b23c3c 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 @@ -5,7 +5,8 @@ import { cachedEnhancedDirectByokModelList } from '@/lib/ai-gateway/providers/di export default { id: 'xiaomi-token-plan-ams', base_url: 'https://token-plan-ams.xiaomimimo.com/v1', - ai_sdk_provider: 'openai-compatible', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', transformRequest() {}, models: cachedEnhancedDirectByokModelList({ providerId: 'xiaomi-token-plan-ams', 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 149489d342..c4842a583b 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 @@ -5,7 +5,8 @@ import { cachedEnhancedDirectByokModelList } from '@/lib/ai-gateway/providers/di export default { id: 'xiaomi-token-plan-sgp', base_url: 'https://token-plan-sgp.xiaomimimo.com/v1', - ai_sdk_provider: 'openai-compatible', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', transformRequest() {}, models: cachedEnhancedDirectByokModelList({ providerId: 'xiaomi-token-plan-sgp', 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 ca308aaccc..352a3154b0 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 @@ -6,7 +6,8 @@ import { cachedEnhancedDirectByokModelList } from '@/lib/ai-gateway/providers/di export default { id: 'zai-coding', base_url: 'https://api.z.ai/api/coding/paas/v4', - ai_sdk_provider: 'openai-compatible', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', transformRequest(context) { context.request.body.thinking = { type: isReasoningExplicitlyDisabled(context.request) ? 'disabled' : 'enabled', diff --git a/apps/web/src/lib/ai-gateway/providers/get-provider.ts b/apps/web/src/lib/ai-gateway/providers/get-provider.ts index d00e76072f..8aca0ed7ee 100644 --- a/apps/web/src/lib/ai-gateway/providers/get-provider.ts +++ b/apps/web/src/lib/ai-gateway/providers/get-provider.ts @@ -81,7 +81,7 @@ async function checkDirectBYOK( id: 'direct-byok', apiUrl: directByok.base_url, apiKey: userByok[0].decryptedAPIKey, - supportedChatApis: inferSupportedChatApis(directByok.ai_sdk_provider), + supportedChatApis: directByok.supported_chat_apis, transformRequest(context) { context.request.body.model = directByokModel.id; directByok.transformRequest(context); diff --git a/apps/web/src/lib/ai-gateway/providers/model-settings.ts b/apps/web/src/lib/ai-gateway/providers/model-settings.ts index 0ca265be50..923031f686 100644 --- a/apps/web/src/lib/ai-gateway/providers/model-settings.ts +++ b/apps/web/src/lib/ai-gateway/providers/model-settings.ts @@ -109,7 +109,24 @@ export function getModelVariants(model: string): OpenCodeSettings['variants'] { return undefined; } +export function getCreatorRecommendedAiSdkProvider(model: string): CustomLlmProvider | undefined { + if (isClaudeModel(model)) { + // on Vercel AI Gateway, this is necessary to support document attachments + return 'anthropic'; + } + if (isOpenAiModel(model) || isGrokModel(model)) { + // OpenAI: "While Chat Completions remains supported, Responses is recommended for all new projects."" + // xAI: "The Responses API is the recommended way to interact with xAI models." + return 'openai'; + } + return undefined; +} + function getAiSdkProvider(model: string): CustomLlmProvider | undefined { + const creatorRecommendedAiSdkProvider = getCreatorRecommendedAiSdkProvider(model); + if (creatorRecommendedAiSdkProvider) { + return creatorRecommendedAiSdkProvider; + } if (isAlibabaDirectModel(model)) { // with 'openai' (Responses) prompt caching doesn't work // with 'openai-compatible' (Chat Completions) cost is wrong (cache writes are not counted) @@ -122,15 +139,6 @@ function getAiSdkProvider(model: string): CustomLlmProvider | undefined { // with 'openai' (Responses API) prompt caching doesn't work return 'openai-compatible'; } - if (isClaudeModel(model)) { - // on Vercel AI Gateway, this is necessary to support document attachments - return 'anthropic'; - } - if (isOpenAiModel(model) || isGrokModel(model)) { - // OpenAI: "While Chat Completions remains supported, Responses is recommended for all new projects."" - // xAI: "The Responses API is the recommended way to interact with xAI models." - return 'openai'; - } return undefined; } diff --git a/apps/web/src/lib/ai-gateway/providers/openrouter/inference-provider-id.ts b/apps/web/src/lib/ai-gateway/providers/openrouter/inference-provider-id.ts index 624f57eb45..c8e3b2be7d 100644 --- a/apps/web/src/lib/ai-gateway/providers/openrouter/inference-provider-id.ts +++ b/apps/web/src/lib/ai-gateway/providers/openrouter/inference-provider-id.ts @@ -97,10 +97,14 @@ export type VercelUserByokInferenceProviderId = z.infer< export const DirectUserByokInferenceProviderIdSchema = z.enum([ 'byteplus-coding', 'chutes-byok', + 'crofai', 'codestral', 'kimi-coding', + 'martian', 'neuralwatt', 'ollama-cloud', + 'orcarouter', + 'synthetic', 'xiaomi-token-plan-ams', 'xiaomi-token-plan-sgp', 'zai-coding', @@ -136,7 +140,11 @@ export const UserByokTestModels = { [DirectUserByokInferenceProviderIdSchema.enum['chutes-byok']]: 'Qwen/Qwen3-30B-A3B', [DirectUserByokInferenceProviderIdSchema.enum.codestral]: 'mistral/codestral', [DirectUserByokInferenceProviderIdSchema.enum['kimi-coding']]: 'kimi-for-coding', + [DirectUserByokInferenceProviderIdSchema.enum.martian]: 'google/gemini-3.5-flash', [DirectUserByokInferenceProviderIdSchema.enum.neuralwatt]: 'Qwen/Qwen3.5-35B-A3B', + [DirectUserByokInferenceProviderIdSchema.enum['orcarouter']]: 'google/gemini-3.5-flash', + [DirectUserByokInferenceProviderIdSchema.enum['crofai']]: 'deepseek-v4-flash', + [DirectUserByokInferenceProviderIdSchema.enum['synthetic']]: 'zai-org/GLM-5.1', [DirectUserByokInferenceProviderIdSchema.enum['ollama-cloud']]: 'kimi-k2.6:cloud', [DirectUserByokInferenceProviderIdSchema.enum['xiaomi-token-plan-ams']]: 'mimo-v2-flash', [DirectUserByokInferenceProviderIdSchema.enum['xiaomi-token-plan-sgp']]: 'mimo-v2-flash', From d8ed19941aba3b6ac799960845e34035a1dac9b0 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 3 Jun 2026 16:54:42 +0200 Subject: [PATCH 2/3] Fix --- apps/web/src/lib/ai-gateway/providers/get-provider.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/web/src/lib/ai-gateway/providers/get-provider.ts b/apps/web/src/lib/ai-gateway/providers/get-provider.ts index 8aca0ed7ee..ea800c457d 100644 --- a/apps/web/src/lib/ai-gateway/providers/get-provider.ts +++ b/apps/web/src/lib/ai-gateway/providers/get-provider.ts @@ -16,10 +16,7 @@ import type { BYOKResult, Provider } from '@/lib/ai-gateway/providers/types'; import PROVIDERS from '@/lib/ai-gateway/providers/provider-definitions'; import { getDirectByokModel } from '@/lib/ai-gateway/providers/direct-byok'; import { CustomLlmDefinitionSchema } from '@kilocode/db'; -import { - buildDirectProvider, - inferSupportedChatApis, -} from '@/lib/ai-gateway/experiments/build-direct-provider'; +import { buildDirectProvider } from '@/lib/ai-gateway/experiments/build-direct-provider'; import { isPublicIdExperimented } from '@/lib/ai-gateway/experiments/membership'; import { pickModelExperimentVariant, From bc0f0dc583a6698cb1da1bab72493db4cfce4d1f Mon Sep 17 00:00:00 2001 From: chrarnoldus <12196001+chrarnoldus@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:21:03 +0000 Subject: [PATCH 3/3] fix(ai-gateway): keep direct BYOK registries ordered --- .../providers/direct-byok/direct-byok-definitions.ts | 8 ++++---- .../providers/openrouter/inference-provider-id.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-definitions.ts b/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-definitions.ts index fc5d565cbb..5ac3e9eec5 100644 --- a/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-definitions.ts +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/direct-byok-definitions.ts @@ -1,16 +1,16 @@ import type { DirectByokProvider } from './types'; import byteplusCoding from './byteplus-coding'; import chutesByok from './chutes-byok'; +import crofai from './crofai'; import kimiCoding from './kimi-coding'; +import martian from './martian'; import neuralwatt from './neurowatt'; import ollamaCloud from './ollama-cloud'; +import orcarouter from './orcarouter'; +import synthetic from './synthetic'; import xiaomiTokenPlanAms from './xiaomi-token-plan-ams'; import xiaomiTokenPlanSgp from './xiaomi-token-plan-sgp'; import zaiCoding from './zai-coding'; -import crofai from './crofai'; -import martian from './martian'; -import orcarouter from './orcarouter'; -import synthetic from './synthetic'; export default [ byteplusCoding, diff --git a/apps/web/src/lib/ai-gateway/providers/openrouter/inference-provider-id.ts b/apps/web/src/lib/ai-gateway/providers/openrouter/inference-provider-id.ts index c8e3b2be7d..75ba6aa015 100644 --- a/apps/web/src/lib/ai-gateway/providers/openrouter/inference-provider-id.ts +++ b/apps/web/src/lib/ai-gateway/providers/openrouter/inference-provider-id.ts @@ -97,8 +97,8 @@ export type VercelUserByokInferenceProviderId = z.infer< export const DirectUserByokInferenceProviderIdSchema = z.enum([ 'byteplus-coding', 'chutes-byok', - 'crofai', 'codestral', + 'crofai', 'kimi-coding', 'martian', 'neuralwatt',