"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 __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var AIService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.AIService = void 0; const common_1 = require("@nestjs/common"); const typeorm_1 = require("@nestjs/typeorm"); const typeorm_2 = require("typeorm"); const entities_1 = require("../entities"); const clients_1 = require("../clients"); let AIService = AIService_1 = class AIService { constructor(configRepository, usageRepository, openRouterClient) { this.configRepository = configRepository; this.usageRepository = usageRepository; this.openRouterClient = openRouterClient; this.logger = new common_1.Logger(AIService_1.name); } async getConfig(tenantId) { let config = await this.configRepository.findOne({ where: { tenant_id: tenantId }, }); if (!config) { config = this.configRepository.create({ tenant_id: tenantId, provider: entities_1.AIProvider.OPENROUTER, default_model: 'anthropic/claude-3-haiku', temperature: 0.7, max_tokens: 2048, is_enabled: true, }); await this.configRepository.save(config); } return config; } async updateConfig(tenantId, dto) { let config = await this.getConfig(tenantId); Object.assign(config, dto); config.updated_at = new Date(); return this.configRepository.save(config); } async chat(tenantId, userId, dto) { const config = await this.getConfig(tenantId); if (!config.is_enabled) { throw new common_1.BadRequestException('AI features are disabled for this tenant'); } if (!this.openRouterClient.isReady()) { throw new common_1.BadRequestException('AI service is not configured'); } const usage = this.usageRepository.create({ tenant_id: tenantId, user_id: userId, provider: config.provider, model: dto.model || config.default_model, status: entities_1.UsageStatus.PENDING, started_at: new Date(), }); await this.usageRepository.save(usage); try { let messages = [...dto.messages]; if (config.system_prompt && !messages.some((m) => m.role === 'system')) { messages = [{ role: 'system', content: config.system_prompt }, ...messages]; } const startTime = Date.now(); const response = await this.openRouterClient.chatCompletion({ ...dto, messages }, config.default_model, config.temperature, config.max_tokens); const latencyMs = Date.now() - startTime; const costs = this.openRouterClient.calculateCost(response.model, response.usage.prompt_tokens, response.usage.completion_tokens); usage.status = entities_1.UsageStatus.COMPLETED; usage.model = response.model; usage.input_tokens = response.usage.prompt_tokens; usage.output_tokens = response.usage.completion_tokens; usage.cost_input = costs.input; usage.cost_output = costs.output; usage.latency_ms = latencyMs; usage.completed_at = new Date(); usage.request_id = response.id; usage.endpoint = 'chat'; await this.usageRepository.save(usage); return response; } catch (error) { usage.status = entities_1.UsageStatus.FAILED; usage.error_message = error.message; usage.completed_at = new Date(); await this.usageRepository.save(usage); throw error; } } async getModels() { return this.openRouterClient.getModels(); } async getCurrentMonthUsage(tenantId) { const startOfMonth = new Date(); startOfMonth.setDate(1); startOfMonth.setHours(0, 0, 0, 0); const result = await this.usageRepository .createQueryBuilder('usage') .select('COUNT(*)', 'request_count') .addSelect('COALESCE(SUM(usage.input_tokens), 0)', 'total_input_tokens') .addSelect('COALESCE(SUM(usage.output_tokens), 0)', 'total_output_tokens') .addSelect('COALESCE(SUM(usage.input_tokens + usage.output_tokens), 0)', 'total_tokens') .addSelect('COALESCE(SUM(usage.cost_input + usage.cost_output), 0)', 'total_cost') .addSelect('COALESCE(AVG(usage.latency_ms), 0)', 'avg_latency_ms') .where('usage.tenant_id = :tenantId', { tenantId }) .andWhere('usage.status = :status', { status: entities_1.UsageStatus.COMPLETED }) .andWhere('usage.created_at >= :startOfMonth', { startOfMonth }) .getRawOne(); return { request_count: parseInt(result.request_count, 10), total_input_tokens: parseInt(result.total_input_tokens, 10), total_output_tokens: parseInt(result.total_output_tokens, 10), total_tokens: parseInt(result.total_tokens, 10), total_cost: parseFloat(result.total_cost), avg_latency_ms: parseFloat(result.avg_latency_ms), }; } async getUsageHistory(tenantId, page = 1, limit = 20) { const [data, total] = await this.usageRepository.findAndCount({ where: { tenant_id: tenantId }, order: { created_at: 'DESC' }, skip: (page - 1) * limit, take: limit, }); return { data, total }; } isServiceReady() { return this.openRouterClient.isReady(); } }; exports.AIService = AIService; exports.AIService = AIService = AIService_1 = __decorate([ (0, common_1.Injectable)(), __param(0, (0, typeorm_1.InjectRepository)(entities_1.AIConfig)), __param(1, (0, typeorm_1.InjectRepository)(entities_1.AIUsage)), __metadata("design:paramtypes", [typeorm_2.Repository, typeorm_2.Repository, clients_1.OpenRouterClient]) ], AIService); //# sourceMappingURL=ai.service.js.map