import type { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { ChatAnthropic } from '@langchain/anthropic'; import type { FastifyBaseLogger } from 'fastify'; /** * Supported LLM providers */ export enum LLMProvider { ANTHROPIC = 'anthropic', } /** * Model configuration */ export interface ModelConfig { provider: LLMProvider; model: string; temperature?: number; maxTokens?: number; } /** * License tier model configuration */ export interface LicenseTierModels { default: string; cost_optimized: string; complex: string; allowed_models?: string[]; blocked_models?: string[]; } /** * License models configuration */ export interface LicenseModelsConfig { free: LicenseTierModels; pro: LicenseTierModels; enterprise: LicenseTierModels; } /** * Provider configuration with API keys */ export interface ProviderConfig { anthropicApiKey?: string; defaultModel?: ModelConfig; licenseModels?: LicenseModelsConfig; } /** * LLM Provider factory * Creates model instances with unified interface across providers */ export class LLMProviderFactory { private config: ProviderConfig; private logger: FastifyBaseLogger; constructor(config: ProviderConfig, logger: FastifyBaseLogger) { this.config = config; this.logger = logger; } /** * Create a chat model instance */ createModel(modelConfig: ModelConfig): BaseChatModel { this.logger.debug( { provider: modelConfig.provider, model: modelConfig.model }, 'Creating LLM model' ); switch (modelConfig.provider) { case LLMProvider.ANTHROPIC: return this.createAnthropicModel(modelConfig); default: throw new Error(`Unsupported provider: ${modelConfig.provider}`); } } /** * Create Anthropic Claude model */ private createAnthropicModel(config: ModelConfig): ChatAnthropic { if (!this.config.anthropicApiKey) { throw new Error('Anthropic API key not configured'); } return new ChatAnthropic({ model: config.model, temperature: config.temperature ?? 0.7, maxTokens: config.maxTokens ?? 4096, anthropicApiKey: this.config.anthropicApiKey, }); } /** * Get default model based on environment */ getDefaultModel(): ModelConfig { if (this.config.defaultModel) { return this.config.defaultModel; } if (!this.config.anthropicApiKey) { throw new Error('Anthropic API key not configured'); } return { provider: LLMProvider.ANTHROPIC, model: 'claude-sonnet-4-6', }; } /** * Get license models configuration */ getLicenseModelsConfig(): LicenseModelsConfig | undefined { return this.config.licenseModels; } } /** * Predefined model configurations */ export const MODELS = { CLAUDE_SONNET: { provider: LLMProvider.ANTHROPIC, model: 'claude-sonnet-4-6', }, CLAUDE_HAIKU: { provider: LLMProvider.ANTHROPIC, model: 'claude-haiku-4-5-20251001', }, CLAUDE_OPUS: { provider: LLMProvider.ANTHROPIC, model: 'claude-opus-4-6', }, } as const satisfies Record;