diff --git a/README.md b/README.md index dc4485402..bbe931a8c 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,7 @@ npx bumpp --no-commit --no-tag ## Support of LLM API Providers (powered by [xsai](https://github.com/moeru-ai/xsai)) +- [x] [302.AI (sponsored)](https://share.302.ai/514k2v) - [x] [OpenRouter](https://openrouter.ai/) - [x] [vLLM](https://github.com/vllm-project/vllm) - [x] [SGLang](https://github.com/sgl-project/sglang) diff --git a/apps/stage-tamagotchi-electron/src/renderer/pages/settings/providers/302-ai.vue b/apps/stage-tamagotchi-electron/src/renderer/pages/settings/providers/302-ai.vue new file mode 100644 index 000000000..fc3dcc696 --- /dev/null +++ b/apps/stage-tamagotchi-electron/src/renderer/pages/settings/providers/302-ai.vue @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + {{ t('settings.dialogs.onboarding.validationFailed') }} + + + + {{ validationMessage }} + + + + + + {{ t('settings.dialogs.onboarding.validationSuccess') }} + + + + + + + +meta: + layout: settings + stageTransition: + name: slide + diff --git a/apps/stage-tamagotchi/src/pages/settings/providers/302-ai.vue b/apps/stage-tamagotchi/src/pages/settings/providers/302-ai.vue new file mode 100644 index 000000000..fc3dcc696 --- /dev/null +++ b/apps/stage-tamagotchi/src/pages/settings/providers/302-ai.vue @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + {{ t('settings.dialogs.onboarding.validationFailed') }} + + + + {{ validationMessage }} + + + + + + {{ t('settings.dialogs.onboarding.validationSuccess') }} + + + + + + + +meta: + layout: settings + stageTransition: + name: slide + diff --git a/apps/stage-web/src/pages/settings/providers/302-ai.vue b/apps/stage-web/src/pages/settings/providers/302-ai.vue new file mode 100644 index 000000000..fc3dcc696 --- /dev/null +++ b/apps/stage-web/src/pages/settings/providers/302-ai.vue @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + {{ t('settings.dialogs.onboarding.validationFailed') }} + + + + {{ validationMessage }} + + + + + + {{ t('settings.dialogs.onboarding.validationSuccess') }} + + + + + + + +meta: + layout: settings + stageTransition: + name: slide + diff --git a/docs/README.fr.md b/docs/README.fr.md index 034b49284..206cb6099 100644 --- a/docs/README.fr.md +++ b/docs/README.fr.md @@ -181,6 +181,7 @@ npx bumpp --no-commit --no-tag ## Support des fournisseurs d'API LLM suivants (propulsé par [xsai](https://github.com/moeru-ai/xsai)) +- [x] [302.AI](https://share.302.ai/514k2v) - [x] [OpenRouter](https://openrouter.ai/) - [x] [vLLM](https://github.com/vllm-project/vllm) - [x] [SGLang](https://github.com/sgl-project/sglang) diff --git a/docs/README.ja-JP.md b/docs/README.ja-JP.md index 26b4a240f..c84e276ee 100644 --- a/docs/README.ja-JP.md +++ b/docs/README.ja-JP.md @@ -158,6 +158,7 @@ pnpm dev:docs ## サポートされているLLM APIプロバイダー([xsai](https://github.com/moeru-ai/xsai)によって提供) +- [x] [302.AI](https://share.302.ai/514k2v) - [x] [OpenRouter](https://openrouter.ai/) - [x] [vLLM](https://github.com/vllm-project/vllm) - [x] [SGLang](https://github.com/sgl-project/sglang) diff --git a/docs/README.ru-RU.md b/docs/README.ru-RU.md index 593dce83c..b714bce4e 100644 --- a/docs/README.ru-RU.md +++ b/docs/README.ru-RU.md @@ -182,6 +182,7 @@ npx bumpp --no-commit --no-tag ## Поддержка провайдеров LLM API (на базе [xsai](https://github.com/moeru-ai/xsai)) +- [x] [302.AI](https://share.302.ai/514k2v) - [x] [OpenRouter](https://openrouter.ai/) - [x] [vLLM](https://github.com/vllm-project/vllm) - [x] [SGLang](https://github.com/sgl-project/sglang) diff --git a/docs/README.vi.md b/docs/README.vi.md index 222770b5e..3804df035 100644 --- a/docs/README.vi.md +++ b/docs/README.vi.md @@ -180,6 +180,7 @@ npx bumpp --no-commit --no-tag ## Các LLM API hỗ trợ (cung cấp bởi [xsai](https://github.com/moeru-ai/xsai)) +- [x] [302.AI](https://share.302.ai/514k2v) - [x] [OpenRouter](https://openrouter.ai/) - [x] [vLLM](https://github.com/vllm-project/vllm) - [x] [SGLang](https://github.com/sgl-project/sglang) diff --git a/docs/README.zh-CN.md b/docs/README.zh-CN.md index e5cd90940..2ee732fb3 100644 --- a/docs/README.zh-CN.md +++ b/docs/README.zh-CN.md @@ -150,6 +150,7 @@ pnpm -F @proj-airi/docs dev ## 原生支持的 LLM API 服务来源列表(由 [xsai](https://github.com/moeru-ai/xsai) 驱动) +- [x] [302.AI](https://share.302.ai/514k2v) - [x] [OpenRouter](https://openrouter.ai/) - [x] [vLLM](https://github.com/vllm-project/vllm) - [x] [SGLang](https://github.com/sgl-project/sglang) diff --git a/packages/stage-ui/src/stores/providers.ts b/packages/stage-ui/src/stores/providers.ts index 35a4326f6..3706eb72b 100644 --- a/packages/stage-ui/src/stores/providers.ts +++ b/packages/stage-ui/src/stores/providers.ts @@ -36,6 +36,7 @@ import { import { createOllama, createPlayer2 } from '@xsai-ext/providers-local' import { createChatProvider, + createEmbedProvider, createMetadataProvider, createModelProvider, merge, @@ -555,102 +556,6 @@ export const useProvidersStore = defineStore('providers', () => { }, }, }, - 'vllm': { - id: 'vllm', - category: 'chat', - tasks: ['text-generation'], - nameKey: 'settings.pages.providers.provider.vllm.title', - name: 'vLLM', - descriptionKey: 'settings.pages.providers.provider.vllm.description', - description: 'vllm.ai', - iconColor: 'i-lobe-icons:vllm', - createProvider: async config => createOllama((config.baseUrl as string).trim()), - capabilities: { - listModels: async () => { - return [ - { - id: 'llama-2-7b', - name: 'Llama 2 (7B)', - provider: 'vllm', - description: 'Meta\'s Llama 2 7B parameter model', - contextLength: 4096, - }, - { - id: 'llama-2-13b', - name: 'Llama 2 (13B)', - provider: 'vllm', - description: 'Meta\'s Llama 2 13B parameter model', - contextLength: 4096, - }, - { - id: 'llama-2-70b', - name: 'Llama 2 (70B)', - provider: 'vllm', - description: 'Meta\'s Llama 2 70B parameter model', - contextLength: 4096, - }, - { - id: 'mistral-7b', - name: 'Mistral (7B)', - provider: 'vllm', - description: 'Mistral AI\'s 7B parameter model', - contextLength: 8192, - }, - { - id: 'mixtral-8x7b', - name: 'Mixtral (8x7B)', - provider: 'vllm', - description: 'Mistral AI\'s Mixtral 8x7B MoE model', - contextLength: 32768, - }, - { - id: 'custom', - name: 'Custom Model', - provider: 'vllm', - description: 'Specify a custom model name', - contextLength: 0, - }, - ] - }, - }, - validators: { - validateProviderConfig: (config) => { - if (!config.baseUrl) { - return { - errors: [new Error('Base URL is required.')], - reason: 'Base URL is required. Default to http://localhost:8000/v1/ for vLLM.', - valid: false, - } - } - - const res = baseUrlValidator.value(config.baseUrl) - if (res) { - return res - } - - // Check if the vLLM is reachable - return fetch(`${(config.baseUrl as string).trim()}models`, { headers: (config.headers as HeadersInit) || undefined }) - .then((response) => { - const errors = [ - !response.ok && new Error(`vLLM returned non-ok status code: ${response.statusText}`), - ].filter(Boolean) - - return { - errors, - reason: errors.filter(e => e).map(e => String(e)).join(', ') || '', - valid: response.ok, - } - }) - .catch((err) => { - return { - errors: [err], - reason: `Failed to reach vLLM, error: ${String(err)} occurred.`, - valid: false, - } - }) - }, - }, - }, 'lm-studio': { id: 'lm-studio', category: 'chat', @@ -942,55 +847,6 @@ export const useProvidersStore = defineStore('providers', () => { tasks: ['speech-to-text', 'automatic-speech-recognition', 'asr', 'stt'], creator: createOpenAI, }), - 'azure-ai-foundry': { - id: 'azure-ai-foundry', - category: 'chat', - tasks: ['text-generation'], - nameKey: 'settings.pages.providers.provider.azure-ai-foundry.title', - name: 'Azure AI Foundry', - descriptionKey: 'settings.pages.providers.provider.azure-ai-foundry.description', - description: 'azure.com', - icon: 'i-lobe-icons:microsoft', - defaultOptions: () => ({}), - createProvider: async (config) => { - return await createAzure({ - apiKey: async () => (config.apiKey as string).trim(), - resourceName: config.resourceName as string, - apiVersion: config.apiVersion as string, - }) - }, - capabilities: { - listModels: async (config) => { - return [{ id: config.modelId }].map((model) => { - return { - id: model.id as string, - name: model.id as string, - provider: 'azure-ai-foundry', - description: '', - contextLength: 0, - deprecated: false, - } satisfies ModelInfo - }) - }, - }, - validators: { - validateProviderConfig: (config) => { - // return !!config.apiKey && !!config.resourceName && !!config.modelId - - const errors = [ - !config.apiKey && new Error('API key is required'), - !config.resourceName && new Error('Resource name is required'), - !config.modelId && new Error('Model ID is required'), - ] - - return { - errors, - reason: errors.filter(e => e).map(e => String(e)).join(', ') || '', - valid: !!config.apiKey && !!config.resourceName && !!config.modelId, - } - }, - }, - }, 'anthropic': buildOpenAICompatibleProvider({ id: 'anthropic', name: 'Anthropic', @@ -1016,17 +872,6 @@ export const useProvidersStore = defineStore('providers', () => { creator: createGoogleGenerativeAI, validation: ['health', 'model_list'], }), - 'xai': buildOpenAICompatibleProvider({ - id: 'xai', - name: 'xAI', - nameKey: 'settings.pages.providers.provider.xai.title', - descriptionKey: 'settings.pages.providers.provider.xai.description', - icon: 'i-lobe-icons:xai', - description: 'x.ai', - defaultBaseUrl: 'https://api.x.ai/v1/', - creator: createXAI, - validation: ['health', 'model_list'], - }), 'deepseek': buildOpenAICompatibleProvider({ id: 'deepseek', name: 'DeepSeek', @@ -1038,6 +883,21 @@ export const useProvidersStore = defineStore('providers', () => { creator: createDeepSeek, validation: ['health', 'model_list'], }), + '302-ai': buildOpenAICompatibleProvider({ + id: '302-ai', + name: '302.AI', + nameKey: 'settings.pages.providers.provider.302-ai.title', + descriptionKey: 'settings.pages.providers.provider.302-ai.description', + icon: 'i-lobe-icons:ai302', + description: '302.ai', + defaultBaseUrl: 'https://api.302.ai/v1/', + creator: (apiKey, baseURL = 'https://api.302.ai/v1/') => merge( + createChatProvider({ apiKey, baseURL }), + createEmbedProvider({ apiKey, baseURL }), + createModelProvider({ apiKey, baseURL }), + ), + validation: ['model_list'], + }), 'elevenlabs': { id: 'elevenlabs', category: 'speech', @@ -1402,6 +1262,162 @@ export const useProvidersStore = defineStore('providers', () => { validation: ['health', 'model_list'], iconColor: 'i-lobe-icons:together', }), + 'azure-ai-foundry': { + id: 'azure-ai-foundry', + category: 'chat', + tasks: ['text-generation'], + nameKey: 'settings.pages.providers.provider.azure-ai-foundry.title', + name: 'Azure AI Foundry', + descriptionKey: 'settings.pages.providers.provider.azure-ai-foundry.description', + description: 'azure.com', + icon: 'i-lobe-icons:microsoft', + defaultOptions: () => ({}), + createProvider: async (config) => { + return await createAzure({ + apiKey: async () => (config.apiKey as string).trim(), + resourceName: config.resourceName as string, + apiVersion: config.apiVersion as string, + }) + }, + capabilities: { + listModels: async (config) => { + return [{ id: config.modelId }].map((model) => { + return { + id: model.id as string, + name: model.id as string, + provider: 'azure-ai-foundry', + description: '', + contextLength: 0, + deprecated: false, + } satisfies ModelInfo + }) + }, + }, + validators: { + validateProviderConfig: (config) => { + // return !!config.apiKey && !!config.resourceName && !!config.modelId + + const errors = [ + !config.apiKey && new Error('API key is required'), + !config.resourceName && new Error('Resource name is required'), + !config.modelId && new Error('Model ID is required'), + ] + + return { + errors, + reason: errors.filter(e => e).map(e => String(e)).join(', ') || '', + valid: !!config.apiKey && !!config.resourceName && !!config.modelId, + } + }, + }, + }, + 'xai': buildOpenAICompatibleProvider({ + id: 'xai', + name: 'xAI', + nameKey: 'settings.pages.providers.provider.xai.title', + descriptionKey: 'settings.pages.providers.provider.xai.description', + icon: 'i-lobe-icons:xai', + description: 'x.ai', + defaultBaseUrl: 'https://api.x.ai/v1/', + creator: createXAI, + validation: ['health', 'model_list'], + }), + 'vllm': { + id: 'vllm', + category: 'chat', + tasks: ['text-generation'], + nameKey: 'settings.pages.providers.provider.vllm.title', + name: 'vLLM', + descriptionKey: 'settings.pages.providers.provider.vllm.description', + description: 'vllm.ai', + iconColor: 'i-lobe-icons:vllm', + createProvider: async config => createOllama((config.baseUrl as string).trim()), + capabilities: { + listModels: async () => { + return [ + { + id: 'llama-2-7b', + name: 'Llama 2 (7B)', + provider: 'vllm', + description: 'Meta\'s Llama 2 7B parameter model', + contextLength: 4096, + }, + { + id: 'llama-2-13b', + name: 'Llama 2 (13B)', + provider: 'vllm', + description: 'Meta\'s Llama 2 13B parameter model', + contextLength: 4096, + }, + { + id: 'llama-2-70b', + name: 'Llama 2 (70B)', + provider: 'vllm', + description: 'Meta\'s Llama 2 70B parameter model', + contextLength: 4096, + }, + { + id: 'mistral-7b', + name: 'Mistral (7B)', + provider: 'vllm', + description: 'Mistral AI\'s 7B parameter model', + contextLength: 8192, + }, + { + id: 'mixtral-8x7b', + name: 'Mixtral (8x7B)', + provider: 'vllm', + description: 'Mistral AI\'s Mixtral 8x7B MoE model', + contextLength: 32768, + }, + { + id: 'custom', + name: 'Custom Model', + provider: 'vllm', + description: 'Specify a custom model name', + contextLength: 0, + }, + ] + }, + }, + validators: { + validateProviderConfig: (config) => { + if (!config.baseUrl) { + return { + errors: [new Error('Base URL is required.')], + reason: 'Base URL is required. Default to http://localhost:8000/v1/ for vLLM.', + valid: false, + } + } + + const res = baseUrlValidator.value(config.baseUrl) + if (res) { + return res + } + + // Check if the vLLM is reachable + return fetch(`${(config.baseUrl as string).trim()}models`, { headers: (config.headers as HeadersInit) || undefined }) + .then((response) => { + const errors = [ + !response.ok && new Error(`vLLM returned non-ok status code: ${response.statusText}`), + ].filter(Boolean) + + return { + errors, + reason: errors.filter(e => e).map(e => String(e)).join(', ') || '', + valid: response.ok, + } + }) + .catch((err) => { + return { + errors: [err], + reason: `Failed to reach vLLM, error: ${String(err)} occurred.`, + valid: false, + } + }) + }, + }, + }, 'novita-ai': buildOpenAICompatibleProvider({ id: 'novita-ai', name: 'Novita', diff --git a/packages/stage-ui/src/stores/providers/openai-compatible-builder.ts b/packages/stage-ui/src/stores/providers/openai-compatible-builder.ts index ca6e4b600..8377fbf10 100644 --- a/packages/stage-ui/src/stores/providers/openai-compatible-builder.ts +++ b/packages/stage-ui/src/stores/providers/openai-compatible-builder.ts @@ -28,25 +28,27 @@ export function buildOpenAICompatibleProvider( const finalCapabilities = capabilities || { listModels: async (config: Record) => { - const provider = creator( + const provider = await creator( (config.apiKey as string || '').trim(), (config.baseUrl as string || '').trim(), ) - if (provider.model) { - return (await listModels({ - ...provider.model(), - })).map((model: any) => { - return { - id: model.id, - name: model.name || model.display_name || model.id, - provider: id, - description: model.description || '', - contextLength: model.context_length || 0, - deprecated: false, - } satisfies ModelInfo - }) + + if (!provider.model) { + return [] } - return [] + + return (await listModels({ + ...provider.model(), + })).map((model: any) => { + return { + id: model.id, + name: model.name || model.display_name || model.id, + provider: id, + description: model.description || '', + contextLength: model.context_length || 0, + deprecated: false, + } satisfies ModelInfo + }) }, }