Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
30 changes: 30 additions & 0 deletions apps/web/src/lib/ai-gateway/providers/direct-byok/crofai.ts
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
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 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';

export default [
byteplusCoding,
chutesByok,
crofai,
kimiCoding,
martian,
neuralwatt,
ollamaCloud,
orcarouter,
synthetic,
xiaomiTokenPlanAms,
xiaomiTokenPlanSgp,
zaiCoding,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
10 changes: 6 additions & 4 deletions apps/web/src/lib/ai-gateway/providers/direct-byok/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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,
};
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) ||
Expand Down
24 changes: 24 additions & 0 deletions apps/web/src/lib/ai-gateway/providers/direct-byok/martian.ts
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
30 changes: 30 additions & 0 deletions apps/web/src/lib/ai-gateway/providers/direct-byok/orcarouter.ts
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -134,6 +135,26 @@ const FETCHERS: ReadonlyArray<ProviderFetcher> = [
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'),
Expand All @@ -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
Expand Down
30 changes: 30 additions & 0 deletions apps/web/src/lib/ai-gateway/providers/direct-byok/synthetic.ts
Original file line number Diff line number Diff line change
@@ -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;
5 changes: 3 additions & 2 deletions apps/web/src/lib/ai-gateway/providers/direct-byok/types.ts
Original file line number Diff line number Diff line change
@@ -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']);
Expand All @@ -28,7 +28,8 @@ export type DirectByokProvider = {
id: DirectByokProviderMetaId;
base_url: string;
models: () => Promise<ReadonlyArray<DirectByokModel>>;
ai_sdk_provider: CustomLlmProvider;
supported_chat_apis: ReadonlyArray<GatewayChatApiKind>;
default_ai_sdk_provider: CustomLlmProvider;
transformRequest(context: TransformRequestContext): void;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
7 changes: 2 additions & 5 deletions apps/web/src/lib/ai-gateway/providers/get-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -81,7 +78,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);
Expand Down
Loading