diff --git a/.env.example b/.env.example index b724838845..49759be5e3 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,10 @@ # Get your API key from: https://console.anthropic.com/ ANTHROPIC_API_KEY=your_anthropic_api_key_here +# Avian (Fast inference for open-source models) +# Get your API key from: https://avian.io +AVIAN_API_KEY=your_avian_api_key_here + # Cerebras (High-performance inference) # Get your API key from: https://cloud.cerebras.ai/settings CEREBRAS_API_KEY=your_cerebras_api_key_here diff --git a/app/lib/modules/llm/providers/avian.ts b/app/lib/modules/llm/providers/avian.ts new file mode 100644 index 0000000000..aad44f7070 --- /dev/null +++ b/app/lib/modules/llm/providers/avian.ts @@ -0,0 +1,99 @@ +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 AvianProvider extends BaseProvider { + name = 'Avian'; + getApiKeyLink = 'https://avian.io'; + + config = { + apiTokenKey: 'AVIAN_API_KEY', + }; + + staticModels: ModelInfo[] = [ + { name: 'deepseek/deepseek-v3.2', label: 'DeepSeek V3.2', provider: 'Avian', maxTokenAllowed: 163840 }, + { name: 'moonshotai/kimi-k2.5', label: 'Kimi K2.5', provider: 'Avian', maxTokenAllowed: 131072 }, + { name: 'z-ai/glm-5', label: 'GLM-5', provider: 'Avian', maxTokenAllowed: 131072 }, + { name: 'minimax/minimax-m2.5', label: 'MiniMax M2.5', provider: 'Avian', maxTokenAllowed: 131072 }, + ]; + + async getDynamicModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv?: Record, + ): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: settings, + serverEnv: serverEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'AVIAN_API_KEY', + }); + + if (!apiKey) { + return []; + } + + try { + const response = await fetch('https://api.avian.io/v1/models', { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + signal: this.createTimeoutSignal(5000), + }); + + if (!response.ok) { + console.error(`Avian 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} (Dynamic)`, + provider: this.name, + maxTokenAllowed: 128000, + })) || []; + + return dynamicModels; + } catch (error) { + console.error(`Failed to fetch Avian 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: 'AVIAN_API_KEY', + }); + + if (!apiKey) { + throw new Error(`Missing API key for ${this.name} provider`); + } + + const openai = createOpenAI({ + baseURL: 'https://api.avian.io/v1', + apiKey, + }); + + return openai(model); + } +} diff --git a/app/lib/modules/llm/registry.ts b/app/lib/modules/llm/registry.ts index 01bbe81140..85d2996d1c 100644 --- a/app/lib/modules/llm/registry.ts +++ b/app/lib/modules/llm/registry.ts @@ -1,4 +1,5 @@ import AnthropicProvider from './providers/anthropic'; +import AvianProvider from './providers/avian'; import CerebrasProvider from './providers/cerebras'; import CohereProvider from './providers/cohere'; import DeepseekProvider from './providers/deepseek'; @@ -23,6 +24,7 @@ import ZaiProvider from './providers/z-ai'; export { AnthropicProvider, + AvianProvider, CerebrasProvider, CohereProvider, DeepseekProvider,