import { UIConfigField } from '@/lib/config/types'; import { getConfiguredModelProviderById } from '@/lib/config/serverRegistry'; import BaseModelProvider from '../../base/provider'; import { Model, ModelList, ProviderMetadata } from '../../types'; import BaseLLM from '../../base/llm'; import LemonadeLLM from './lemonadeLLM'; import BaseEmbedding from '../../base/embedding'; import LemonadeEmbedding from './lemonadeEmbedding'; interface LemonadeConfig { baseURL: string; apiKey?: string; } const providerConfigFields: UIConfigField[] = [ { type: 'string', name: 'Base URL', key: 'baseURL', description: 'The base URL for Lemonade API', required: true, placeholder: 'https://api.lemonade.ai/v1', env: 'LEMONADE_BASE_URL', scope: 'server', }, { type: 'password', name: 'API Key', key: 'apiKey', description: 'Your Lemonade API key (optional)', required: false, placeholder: 'Lemonade API Key', env: 'LEMONADE_API_KEY', scope: 'server', }, ]; class LemonadeProvider extends BaseModelProvider { constructor(id: string, name: string, config: LemonadeConfig) { super(id, name, config); } async getDefaultModels(): Promise { try { const res = await fetch(`${this.config.baseURL}/models`, { method: 'GET', headers: { 'Content-Type': 'application/json', ...(this.config.apiKey ? { Authorization: `Bearer ${this.config.apiKey}` } : {}), }, }); const data = await res.json(); const models: Model[] = data.data .filter((m: any) => m.recipe === 'llamacpp') .map((m: any) => { return { name: m.id, key: m.id, }; }); return { embedding: models, chat: models, }; } catch (err) { if (err instanceof TypeError) { throw new Error( 'Error connecting to Lemonade API. Please ensure the base URL is correct and the service is available.', ); } throw err; } } async getModelList(): Promise { const defaultModels = await this.getDefaultModels(); const configProvider = getConfiguredModelProviderById(this.id)!; return { embedding: [ ...defaultModels.embedding, ...configProvider.embeddingModels, ], chat: [...defaultModels.chat, ...configProvider.chatModels], }; } async loadChatModel(key: string): Promise> { const modelList = await this.getModelList(); const exists = modelList.chat.find((m) => m.key === key); if (!exists) { throw new Error( 'Error Loading Lemonade Chat Model. Invalid Model Selected', ); } return new LemonadeLLM({ apiKey: this.config.apiKey || 'not-needed', model: key, baseURL: this.config.baseURL, }); } async loadEmbeddingModel(key: string): Promise> { const modelList = await this.getModelList(); const exists = modelList.embedding.find((m) => m.key === key); if (!exists) { throw new Error( 'Error Loading Lemonade Embedding Model. Invalid Model Selected.', ); } return new LemonadeEmbedding({ apiKey: this.config.apiKey || 'not-needed', model: key, baseURL: this.config.baseURL, }); } static parseAndValidate(raw: any): LemonadeConfig { if (!raw || typeof raw !== 'object') throw new Error('Invalid config provided. Expected object'); if (!raw.baseURL) throw new Error('Invalid config provided. Base URL must be provided'); return { baseURL: String(raw.baseURL), apiKey: raw.apiKey ? String(raw.apiKey) : undefined, }; } static getProviderConfigFields(): UIConfigField[] { return providerConfigFields; } static getProviderMetadata(): ProviderMetadata { return { key: 'lemonade', name: 'Lemonade', }; } } export default LemonadeProvider;