diff --git a/.env.example b/.env.example index b724838845..2c8e3c1bfa 100644 --- a/.env.example +++ b/.env.example @@ -17,6 +17,10 @@ ANTHROPIC_API_KEY=your_anthropic_api_key_here CEREBRAS_API_KEY=your_cerebras_api_key_here # Fireworks AI (Fast inference with FireAttention engine) +# EUrouter (EU-hosted, GDPR-friendly AI gateway) +# Get your API key from: https://www.eurouter.ai +EUROUTER_API_KEY=your_eurouter_api_key_here + # Get your API key from: https://fireworks.ai/api-keys FIREWORKS_API_KEY=your_fireworks_api_key_here diff --git a/app/lib/modules/llm/providers/eurouter.ts b/app/lib/modules/llm/providers/eurouter.ts new file mode 100644 index 0000000000..9887a0c0df --- /dev/null +++ b/app/lib/modules/llm/providers/eurouter.ts @@ -0,0 +1,108 @@ +import { BaseProvider } from '~/lib/modules/llm/base-provider'; +import type { ModelInfo } from '~/lib/modules/llm/types'; +import type { IProviderSetting } from '~/types/model'; +import type { LanguageModelV1 } from 'ai'; +import { createOpenAI } from '@ai-sdk/openai'; + +export default class EUrouterProvider extends BaseProvider { + name = 'EUrouter'; + getApiKeyLink = 'https://www.eurouter.ai'; + + config = { + apiTokenKey: 'EUROUTER_API_KEY', + }; + + staticModels: ModelInfo[] = [ + { name: 'deepseek-r1', label: 'DeepSeek R1', provider: 'EUrouter', maxTokenAllowed: 64000 }, + { name: 'kimi-k2.5', label: 'Kimi K2.5', provider: 'EUrouter', maxTokenAllowed: 128000 }, + { + name: 'mistral-large-latest', + label: 'Mistral Large 3', + provider: 'EUrouter', + maxTokenAllowed: 128000, + }, + { name: 'minimax-m2.5', label: 'MiniMax M2.5', provider: 'EUrouter', maxTokenAllowed: 128000 }, + ]; + + async getDynamicModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv?: Record, + ): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: settings, + serverEnv: serverEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'EUROUTER_API_KEY', + }); + + if (!apiKey) { + return []; + } + + try { + const response = await fetch('https://api.eurouter.ai/api/v1/models', { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + signal: this.createTimeoutSignal(5000), + }); + + if (!response.ok) { + console.error(`EUrouter API error: ${response.statusText}`); + return []; + } + + const data = (await response.json()) as any; + const staticModelIds = this.staticModels.map((m) => m.name); + + const dynamicModels = + data.data + ?.filter((model: any) => !staticModelIds.includes(model.id)) + .map((m: any) => ({ + name: m.id, + label: m.id, + provider: this.name, + maxTokenAllowed: 128000, + })) || []; + + return dynamicModels; + } catch (error) { + console.error('Failed to fetch EUrouter models:', error); + return []; + } + } + + getModelInstance(options: { + model: string; + serverEnv: Env; + apiKeys?: Record; + providerSettings?: Record; + }): LanguageModelV1 { + const { model, serverEnv, apiKeys, providerSettings } = options; + + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: providerSettings?.[this.name], + serverEnv: serverEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'EUROUTER_API_KEY', + }); + + if (!apiKey) { + throw new Error(`Missing API key for ${this.name} provider`); + } + + const openai = createOpenAI({ + baseURL: 'https://api.eurouter.ai/api/v1', + apiKey, + headers: { + 'HTTP-Referer': 'https://bolt.diy', + 'X-EUrouter-Title': 'bolt.diy', + }, + }); + + return openai(model); + } +} diff --git a/app/lib/modules/llm/registry.ts b/app/lib/modules/llm/registry.ts index 01bbe81140..40bc5eb78e 100644 --- a/app/lib/modules/llm/registry.ts +++ b/app/lib/modules/llm/registry.ts @@ -2,6 +2,7 @@ import AnthropicProvider from './providers/anthropic'; import CerebrasProvider from './providers/cerebras'; import CohereProvider from './providers/cohere'; import DeepseekProvider from './providers/deepseek'; +import EUrouterProvider from './providers/eurouter'; import FireworksProvider from './providers/fireworks'; import GoogleProvider from './providers/google'; import GroqProvider from './providers/groq'; @@ -26,6 +27,7 @@ export { CerebrasProvider, CohereProvider, DeepseekProvider, + EUrouterProvider, FireworksProvider, GoogleProvider, GroqProvider,