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 new file mode 100644 index 0000000000..5a72b584ba --- /dev/null +++ b/apps/web/src/lib/ai-gateway/providers/direct-byok/alibaba-token-plan.ts @@ -0,0 +1,22 @@ +import { cachedEnhancedDirectByokModelList } from '@/lib/ai-gateway/providers/direct-byok/model-list'; +import type { DirectByokProvider } from '@/lib/ai-gateway/providers/direct-byok/types'; + +export default { + id: 'alibaba-token-plan', + base_url: 'https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1', + supported_chat_apis: ['chat_completions'], + default_ai_sdk_provider: 'openai-compatible', + transformRequest() {}, + models: cachedEnhancedDirectByokModelList({ + providerId: 'alibaba-token-plan', + recommendedModels: [ + { + id: 'qwen3.7-plus', + name: 'Qwen3.7 Plus', + flags: ['vision'], + context_length: 1_000_000, + max_completion_tokens: 64_000, + }, + ], + }), +} 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 5a2065e2a5..0209d8ed67 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,5 @@ import type { DirectByokProvider } from './types'; +import alibabaTokenPlan from './alibaba-token-plan'; import byteplusCoding from './byteplus-coding'; import chutesByok from './chutes-byok'; import crofai from './crofai'; @@ -15,6 +16,7 @@ import xiaomiTokenPlanSgp from './xiaomi-token-plan-sgp'; import zaiCoding from './zai-coding'; export default [ + alibabaTokenPlan, byteplusCoding, chutesByok, crofai, 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 37f2800fd5..06b494f8c4 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 @@ -2,6 +2,7 @@ import type { DirectUserByokInferenceProviderId } from '@/lib/ai-gateway/provide // Client-safe display names for direct BYOK providers. export const DIRECT_BYOK_PROVIDERS_META = { + 'alibaba-token-plan': 'Alibaba Token Plan (Singapore)', 'byteplus-coding': 'BytePlus Coding Plan', 'chutes-byok': 'Chutes BYOK', crofai: 'CrofAI', 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 7bfb247d83..8429b344f6 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 @@ -1,7 +1,7 @@ import { parseModelsDevProviderModels } from './sync-direct-byok'; describe('parseModelsDevProviderModels', () => { - test('excludes deprecated models while retaining other statuses', () => { + test('excludes deprecated and non-text-output models while retaining other statuses', () => { const models = parseModelsDevProviderModels({ models: { stable: { @@ -27,6 +27,11 @@ describe('parseModelsDevProviderModels', () => { name: 'MiMo V2 Omni', status: 'deprecated', }, + imageOnly: { + id: 'wan2.7-image', + name: 'Wan2.7 Image', + modalities: { input: ['text'], output: ['image'] }, + }, }, }); 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 8f619eed1a..9ac31140b9 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 @@ -104,7 +104,11 @@ function openAICompatibleFetcher(options: { export function parseModelsDevProviderModels(entry: unknown): RawModel[] { const provider = ModelsDevProviderSchema.parse(entry); return Object.values(provider.models) - .filter(model => model.status !== 'deprecated') + .filter( + model => + model.status !== 'deprecated' && + (!model.modalities?.output || model.modalities.output.includes('text')) + ) .map(model => ({ id: model.id, name: shortenDisplayName(model.name), @@ -177,6 +181,7 @@ const FETCHERS: ReadonlyArray = [ label: 'Synthetic', url: 'https://api.synthetic.new/v1/models', }), + modelsDevFetcher('alibaba-token-plan', 'alibaba-token-plan'), modelsDevFetcher('zai-coding', 'zai-coding-plan'), modelsDevFetcher('ollama-cloud', 'ollama-cloud'), modelsDevFetcher('opencode-go', 'opencode-go'), 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 ba1954643f..69a6b565f6 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 @@ -95,6 +95,7 @@ export type VercelUserByokInferenceProviderId = z.infer< >; export const DirectUserByokInferenceProviderIdSchema = z.enum([ + 'alibaba-token-plan', 'byteplus-coding', 'chutes-byok', 'codestral', @@ -138,6 +139,7 @@ export const UserByokTestModels = { [VercelUserByokInferenceProviderIdSchema.enum.xai]: 'xai/grok-4.1-fast-non-reasoning', [VercelUserByokInferenceProviderIdSchema.enum.xiaomi]: 'xiaomi/mimo-v2-flash', [VercelUserByokInferenceProviderIdSchema.enum.zai]: 'zai/glm-4.7-flash', + [DirectUserByokInferenceProviderIdSchema.enum['alibaba-token-plan']]: 'deepseek-v3.2', [DirectUserByokInferenceProviderIdSchema.enum['byteplus-coding']]: 'bytedance-seed-code', [DirectUserByokInferenceProviderIdSchema.enum['chutes-byok']]: 'Qwen/Qwen3-30B-A3B', [DirectUserByokInferenceProviderIdSchema.enum.codestral]: 'mistral/codestral',