- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8 - Actualizaciones de configuracion Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
181 lines
7.6 KiB
JavaScript
181 lines
7.6 KiB
JavaScript
"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
|