"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var OpenRouterClient_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenRouterClient = void 0; const common_1 = require("@nestjs/common"); const config_1 = require("@nestjs/config"); let OpenRouterClient = OpenRouterClient_1 = class OpenRouterClient { constructor(configService) { this.configService = configService; this.logger = new common_1.Logger(OpenRouterClient_1.name); this.baseUrl = 'https://openrouter.ai/api/v1'; this.isConfigured = false; this.timeout = this.configService.get('AI_TIMEOUT_MS', 30000); } onModuleInit() { this.apiKey = this.configService.get('OPENROUTER_API_KEY', ''); if (!this.apiKey) { this.logger.warn('OpenRouter API key not configured. AI features will be disabled.'); return; } this.isConfigured = true; this.logger.log('OpenRouter client initialized'); } isReady() { return this.isConfigured; } ensureConfigured() { if (!this.isConfigured) { throw new common_1.BadRequestException('AI service is not configured. Please set OPENROUTER_API_KEY.'); } } async chatCompletion(dto, defaultModel, defaultTemperature, defaultMaxTokens) { this.ensureConfigured(); const requestBody = { model: dto.model || defaultModel, messages: dto.messages, temperature: dto.temperature ?? defaultTemperature, max_tokens: dto.max_tokens ?? defaultMaxTokens, top_p: dto.top_p ?? 1.0, stream: false, }; const startTime = Date.now(); try { const response = await fetch(`${this.baseUrl}/chat/completions`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.apiKey}`, 'HTTP-Referer': this.configService.get('APP_URL', 'http://localhost:3001'), 'X-Title': 'Template SaaS', }, body: JSON.stringify(requestBody), signal: AbortSignal.timeout(this.timeout), }); if (!response.ok) { const errorBody = await response.text(); this.logger.error(`OpenRouter API error: ${response.status} - ${errorBody}`); throw new common_1.BadRequestException(`AI request failed: ${response.statusText}`); } const data = await response.json(); const latencyMs = Date.now() - startTime; this.logger.debug(`Chat completion completed in ${latencyMs}ms, tokens: ${data.usage?.total_tokens}`); return { id: data.id, model: data.model, choices: data.choices.map((c) => ({ index: c.index, message: { role: c.message.role, content: c.message.content, }, finish_reason: c.finish_reason, })), usage: { prompt_tokens: data.usage?.prompt_tokens || 0, completion_tokens: data.usage?.completion_tokens || 0, total_tokens: data.usage?.total_tokens || 0, }, created: data.created, }; } catch (error) { if (error.name === 'AbortError') { throw new common_1.BadRequestException('AI request timed out'); } throw error; } } async getModels() { this.ensureConfigured(); try { const response = await fetch(`${this.baseUrl}/models`, { headers: { Authorization: `Bearer ${this.apiKey}`, }, signal: AbortSignal.timeout(10000), }); if (!response.ok) { throw new common_1.BadRequestException('Failed to fetch models'); } const data = await response.json(); const models = data.data || []; const popularModels = [ 'anthropic/claude-3-haiku', 'anthropic/claude-3-sonnet', 'anthropic/claude-3-opus', 'openai/gpt-4-turbo', 'openai/gpt-4', 'openai/gpt-3.5-turbo', 'google/gemini-pro', 'meta-llama/llama-3-70b-instruct', ]; return models .filter((m) => popularModels.some((p) => m.id.includes(p.split('/')[1]))) .slice(0, 20) .map((m) => ({ id: m.id, name: m.name, provider: m.id.split('/')[0], context_length: m.context_length, pricing: { prompt: parseFloat(m.pricing.prompt) * 1000000, completion: parseFloat(m.pricing.completion) * 1000000, }, })); } catch (error) { this.logger.error('Failed to fetch models:', error); return [ { id: 'anthropic/claude-3-haiku', name: 'Claude 3 Haiku', provider: 'anthropic', context_length: 200000, pricing: { prompt: 0.25, completion: 1.25 }, }, { id: 'openai/gpt-3.5-turbo', name: 'GPT-3.5 Turbo', provider: 'openai', context_length: 16385, pricing: { prompt: 0.5, completion: 1.5 }, }, ]; } } calculateCost(model, inputTokens, outputTokens) { const pricing = { 'anthropic/claude-3-haiku': { input: 0.25, output: 1.25 }, 'anthropic/claude-3-sonnet': { input: 3.0, output: 15.0 }, 'anthropic/claude-3-opus': { input: 15.0, output: 75.0 }, 'openai/gpt-4-turbo': { input: 10.0, output: 30.0 }, 'openai/gpt-4': { input: 30.0, output: 60.0 }, 'openai/gpt-3.5-turbo': { input: 0.5, output: 1.5 }, default: { input: 1.0, output: 2.0 }, }; const modelPricing = pricing[model] || pricing.default; const inputCost = (inputTokens / 1_000_000) * modelPricing.input; const outputCost = (outputTokens / 1_000_000) * modelPricing.output; return { input: inputCost, output: outputCost, total: inputCost + outputCost, }; } }; exports.OpenRouterClient = OpenRouterClient; exports.OpenRouterClient = OpenRouterClient = OpenRouterClient_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [config_1.ConfigService]) ], OpenRouterClient); //# sourceMappingURL=openrouter.client.js.map