skailan-ai
Version:
Servicio de IA y procesamiento de lenguaje natural para Skailan
195 lines (189 loc) • 7 kB
JavaScript
import { AIService } from './AIService';
export class ConversationContextService {
aiService;
contexts = new Map();
constructor(llmConfig) {
this.aiService = new AIService(llmConfig);
}
async createContext(organizationId, conversationId, initialMessage) {
const contextId = `${organizationId}:${conversationId}`;
const context = {
id: contextId,
organizationId,
conversationId,
messages: [],
createdAt: new Date(),
updatedAt: new Date()
};
if (initialMessage) {
context.messages.push({
role: 'user',
content: initialMessage,
timestamp: new Date()
});
}
this.contexts.set(contextId, context);
return context;
}
async addMessage(organizationId, conversationId, role, content, metadata) {
const contextId = `${organizationId}:${conversationId}`;
const context = this.contexts.get(contextId);
if (!context) {
throw new Error(`Context not found for conversation ${conversationId}`);
}
const message = {
role,
content,
timestamp: new Date(),
metadata
};
context.messages.push(message);
context.updatedAt = new Date();
// Mantener solo los últimos N mensajes para evitar contextos muy largos
if (context.messages.length > 50) {
context.messages = context.messages.slice(-50);
}
this.contexts.set(contextId, context);
return context;
}
async getContext(organizationId, conversationId) {
const contextId = `${organizationId}:${conversationId}`;
return this.contexts.get(contextId) || null;
}
async updateContext(organizationId, conversationId, updates) {
const contextId = `${organizationId}:${conversationId}`;
const context = this.contexts.get(contextId);
if (!context) {
throw new Error(`Context not found for conversation ${conversationId}`);
}
const updatedContext = {
...context,
...updates,
updatedAt: new Date()
};
this.contexts.set(contextId, updatedContext);
return updatedContext;
}
async generateSummary(organizationId, conversationId, options) {
const context = await this.getContext(organizationId, conversationId);
if (!context || context.messages.length === 0) {
return 'No hay mensajes para resumir.';
}
const messagesText = context.messages
.map(msg => `${msg.role}: ${msg.content}`)
.join('\n');
const prompt = `
Genera un resumen conciso de la siguiente conversación:
Conversación:
${messagesText}
Genera un resumen que incluya:
1. Los puntos principales discutidos
2. Las decisiones tomadas (si las hay)
3. Las acciones pendientes (si las hay)
4. El estado general de la conversación
Responde solo con el resumen, sin formato adicional.
`;
const summary = await this.aiService.generateText(prompt);
// Actualizar el contexto con el resumen
await this.updateContext(organizationId, conversationId, { summary });
return summary;
}
async analyzeContext(organizationId, conversationId, options) {
const context = await this.getContext(organizationId, conversationId);
if (!context || context.messages.length === 0) {
return {};
}
const messagesText = context.messages
.map(msg => `${msg.role}: ${msg.content}`)
.join('\n');
const prompt = `
Analiza la siguiente conversación y responde en formato JSON:
Conversación:
${messagesText}
Responde con un JSON que contenga:
{
"intent": "intención_principal",
"sentiment": "positive|negative|neutral",
"entities": [
{
"type": "tipo_entidad",
"value": "valor_extraido",
"confidence": 0.9
}
],
"slots": {
"slot_name": "valor_extraido"
}
}
Solo responde con el JSON, sin texto adicional.
`;
try {
const response = await this.aiService.generateText(prompt);
const analysis = JSON.parse(response);
// Actualizar el contexto con el análisis
await this.updateContext(organizationId, conversationId, {
intent: analysis.intent,
sentiment: analysis.sentiment,
entities: analysis.entities,
slots: analysis.slots
});
return analysis;
}
catch (error) {
console.error('Error analyzing context:', error);
return {};
}
}
async getRelevantContext(organizationId, conversationId, currentMessage, maxMessages = 10) {
const context = await this.getContext(organizationId, conversationId);
if (!context || context.messages.length === 0) {
return [];
}
// Si hay pocos mensajes, devolver todos
if (context.messages.length <= maxMessages) {
return context.messages;
}
// Para conversaciones largas, usar el resumen + últimos mensajes
const recentMessages = context.messages.slice(-maxMessages);
if (context.summary) {
// Agregar el resumen como mensaje del sistema
const summaryMessage = {
role: 'system',
content: `Resumen de la conversación anterior: ${context.summary}`,
timestamp: new Date()
};
return [summaryMessage, ...recentMessages];
}
return recentMessages;
}
async clearContext(organizationId, conversationId) {
const contextId = `${organizationId}:${conversationId}`;
this.contexts.delete(contextId);
}
async getAllContexts(organizationId) {
return Array.from(this.contexts.values())
.filter(context => context.organizationId === organizationId);
}
async cleanupOldContexts(maxAgeHours = 24) {
const cutoffTime = new Date(Date.now() - maxAgeHours * 60 * 60 * 1000);
for (const [contextId, context] of this.contexts.entries()) {
if (context.updatedAt < cutoffTime) {
this.contexts.delete(contextId);
}
}
}
async exportContext(organizationId, conversationId) {
const context = await this.getContext(organizationId, conversationId);
if (!context) {
throw new Error(`Context not found for conversation ${conversationId}`);
}
return JSON.stringify(context, null, 2);
}
async importContext(contextData) {
const context = JSON.parse(contextData);
const contextId = context.id;
this.contexts.set(contextId, context);
return context;
}
}
//# sourceMappingURL=ConversationContextService.js.map