UNPKG

@agentdao/core

Version:

Core functionality, skills, and ready-made UI components for AgentDAO - Web3 subscriptions, content generation, social media, help support, live chat, RSS fetching, web search, and agent pricing integration

423 lines (421 loc) 17.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HelpSupportSkill = void 0; const axios_1 = __importDefault(require("axios")); class HelpSupportSkill { constructor(config) { this.conversations = new Map(); if (!config.ai?.apiKey) { throw new Error('No OpenAI API key provided. Please add your key in Settings > API Keys.'); } this.config = config; } async handleMessage(message, context) { const conversationId = context.userId || this.generateConversationId(); // Get conversation history const conversation = this.conversations.get(conversationId) || []; conversation.push({ role: 'user', content: message }); // Check if we should escalate to human const shouldEscalate = this.shouldEscalateToHuman(conversation); if (shouldEscalate) { return { response: this.getEscalationMessage(), confidence: 0, escalateToHuman: true, conversationId }; } // Generate AI response const aiResponse = await this.generateAIResponse(message, conversation, context); // Add AI response to conversation conversation.push({ role: 'assistant', content: aiResponse.response }); this.conversations.set(conversationId, conversation); return { ...aiResponse, conversationId }; } async startConversation(userId) { const conversationId = this.generateConversationId(); this.conversations.set(conversationId, []); console.log(`Started conversation ${conversationId} for user ${userId}`); return conversationId; } async endConversation(conversationId) { const conversation = this.conversations.get(conversationId) || []; const startTime = new Date(); // This should be stored when conversation starts const endTime = new Date(); const summary = { conversationId, userId: '', // This should be stored with the conversation startTime, endTime, messageCount: conversation.length, resolution: 'resolved', satisfaction: undefined }; // Clean up conversation this.conversations.delete(conversationId); return summary; } async addToKnowledgeBase(content, category) { if (!this.config.database || !this.config.database.endpoint) { throw new Error('No database endpoint configured for knowledge base. Please configure a database endpoint to use this feature.'); } const knowledgeId = this.generateKnowledgeId(); try { await fetch(this.config.database.endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {}) }, body: JSON.stringify({ type: 'add_knowledge', data: { id: knowledgeId, content, category, agentId: this.config.agentId, agentName: this.config.agentName, domain: this.config.domain, timestamp: new Date().toISOString() } }) }); } catch (error) { throw new Error(`Failed to add to knowledge base: ${error instanceof Error ? error.message : 'Unknown error'}`); } return knowledgeId; } async updateKnowledgeBase(itemId, content) { if (!this.config.database || !this.config.database.endpoint) { throw new Error('No database endpoint configured for knowledge base. Please configure a database endpoint to use this feature.'); } try { const response = await fetch(this.config.database.endpoint, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {}) }, body: JSON.stringify({ type: 'update_knowledge', itemId, content, agentId: this.config.agentId, agentName: this.config.agentName, domain: this.config.domain, timestamp: new Date().toISOString() }) }); return response.ok; } catch (error) { throw new Error(`Failed to update knowledge base: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async createTicket(userId, issue, priority) { if (!this.config.database || !this.config.database.endpoint) { throw new Error('No database endpoint configured for ticket management. Please configure a database endpoint to use this feature.'); } const ticket = { id: this.generateTicketId(), userId, issue, priority, status: 'open', createdAt: new Date(), updatedAt: new Date(), notes: [] }; try { await fetch(this.config.database.endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {}) }, body: JSON.stringify({ type: 'create_ticket', data: ticket, agentId: this.config.agentId, agentName: this.config.agentName, domain: this.config.domain, timestamp: new Date().toISOString() }) }); } catch (error) { throw new Error(`Failed to create ticket: ${error instanceof Error ? error.message : 'Unknown error'}`); } return ticket; } async updateTicket(ticketId, status, notes) { if (!this.config.database || !this.config.database.endpoint) { throw new Error('No database endpoint configured for ticket management. Please configure a database endpoint to use this feature.'); } try { const response = await fetch(this.config.database.endpoint, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {}) }, body: JSON.stringify({ type: 'update_ticket', ticketId, status, notes, agentId: this.config.agentId, agentName: this.config.agentName, domain: this.config.domain, timestamp: new Date().toISOString() }) }); if (!response.ok) { throw new Error(`Failed to update ticket: ${response.statusText}`); } const data = await response.json(); return data.ticket; } catch (error) { throw new Error(`Failed to update ticket: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async getTicketHistory(userId) { if (!this.config.database || !this.config.database.endpoint) { throw new Error('No database endpoint configured for ticket management. Please configure a database endpoint to use this feature.'); } try { const response = await fetch(`${this.config.database.endpoint}?type=tickets&userId=${userId}`, { headers: { ...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {}) } }); if (!response.ok) { throw new Error(`Failed to fetch ticket history: ${response.statusText}`); } const data = await response.json(); return data.tickets || []; } catch (error) { throw new Error(`Failed to get ticket history: ${error instanceof Error ? error.message : 'Unknown error'}`); } } // Private helper methods async generateAIResponse(message, conversation, context) { const systemPrompt = this.buildSystemPrompt(context); const userPrompt = this.buildUserPrompt(message, conversation); try { if (this.config.ai.provider === 'openai') { return await this.callOpenAI(systemPrompt, userPrompt); } else { throw new Error(`AI provider ${this.config.ai.provider} not implemented`); } } catch (error) { console.error('AI response generation failed:', error); return { response: 'I apologize, but I\'m having trouble processing your request. Please try again or contact human support.', confidence: 0, escalateToHuman: true, conversationId: '' }; } } async callOpenAI(systemPrompt, userPrompt) { const response = await axios_1.default.post('https://api.openai.com/v1/chat/completions', { model: this.config.ai.model, messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: userPrompt } ], max_tokens: this.config.ai.maxTokens, temperature: this.config.ai.temperature }, { headers: { 'Authorization': `Bearer ${this.config.ai.apiKey}`, 'Content-Type': 'application/json' } }); const aiResponse = response.data.choices[0]?.message?.content || ''; // Analyze response for confidence and escalation const confidence = this.calculateConfidence(aiResponse); const escalateToHuman = confidence < 0.7 || this.shouldEscalateToHuman([{ role: 'assistant', content: aiResponse }]); return { response: aiResponse, confidence, sources: await this.findRelevantSources(aiResponse), suggestedActions: this.extractSuggestedActions(aiResponse), escalateToHuman, conversationId: '' }; } buildSystemPrompt(context) { let prompt = `You are a helpful customer support agent for ${this.config.agentName}. Your goal is to help users with their questions and issues. Guidelines: - Be polite, professional, and helpful - Provide accurate information based on your knowledge - If you're not sure about something, say so and suggest contacting human support - Keep responses concise but informative - Use a friendly and approachable tone`; if (context.subscription) { prompt += `\n\nUser subscription: ${context.subscription}`; } if (this.config.knowledgeBase.sources.length > 0) { prompt += `\n\nKnowledge base sources: ${this.config.knowledgeBase.sources.join(', ')}`; } return prompt; } buildUserPrompt(message, conversation) { let prompt = `User message: ${message}`; if (conversation.length > 1) { prompt += '\n\nConversation history:\n'; conversation.slice(-5).forEach(msg => { prompt += `${msg.role}: ${msg.content}\n`; }); } return prompt; } shouldEscalateToHuman(conversation) { // Check conversation length if (conversation.length > this.config.escalationThreshold) { return true; } // Check for escalation keywords const escalationKeywords = [ 'speak to human', 'talk to someone', 'real person', 'agent', 'escalate', 'supervisor', 'manager', 'complaint' ]; const lastMessage = conversation[conversation.length - 1]?.content?.toLowerCase() || ''; return escalationKeywords.some(keyword => lastMessage.includes(keyword)); } getEscalationMessage() { if (this.config.humanSupport.enabled) { return `I understand you'd like to speak with a human. You can reach our support team at ${this.config.humanSupport.email} or call ${this.config.humanSupport.phone}. They typically respond within ${this.config.humanSupport.responseTime}.`; } else { return 'I apologize, but I\'m unable to connect you with a human agent at this time. Please try rephrasing your question or check our knowledge base for more information.'; } } calculateConfidence(response) { // Simple confidence calculation based on response characteristics let confidence = 0.8; // Base confidence // Reduce confidence for uncertain language const uncertainPhrases = ['i think', 'maybe', 'possibly', 'not sure', 'might']; uncertainPhrases.forEach(phrase => { if (response.toLowerCase().includes(phrase)) { confidence -= 0.1; } }); // Reduce confidence for short responses if (response.length < 50) { confidence -= 0.2; } // Increase confidence for specific, detailed responses if (response.includes('step') || response.includes('click') || response.includes('go to')) { confidence += 0.1; } return Math.max(0, Math.min(1, confidence)); } extractSuggestedActions(response) { const actions = []; // Extract action items from response const actionPatterns = [ /click on (.+)/gi, /go to (.+)/gi, /try (.+)/gi, /check (.+)/gi ]; actionPatterns.forEach(pattern => { const matches = response.match(pattern); if (matches) { actions.push(...matches); } }); return actions; } generateConversationId() { return `conv_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } generateKnowledgeId() { return `kb_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } generateTicketId() { return `ticket_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } async findRelevantSources(response) { try { const sources = []; // Search through configured knowledge base sources for (const source of this.config.knowledgeBase.sources) { try { if (source.startsWith('http')) { // For web-based knowledge bases, search for relevant content const relevantContent = await this.searchWebKnowledgeBase(source, response); if (relevantContent) { sources.push(relevantContent); } } else { // For local knowledge bases, search local content const relevantContent = await this.searchLocalKnowledgeBase(source, response); if (relevantContent) { sources.push(relevantContent); } } } catch (error) { console.warn(`Failed to search knowledge base source: ${source}`, error); } } return sources; } catch (error) { console.error('Error finding relevant sources:', error); return []; } } async searchWebKnowledgeBase(sourceUrl, query) { try { // This would integrate with a search service or API // For now, we'll use a simple keyword matching approach const response = await fetch(`${sourceUrl}/search?q=${encodeURIComponent(query)}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); if (response.ok) { const data = await response.json(); return data.url || sourceUrl; } } catch (error) { console.warn('Web knowledge base search failed:', error); } return null; } async searchLocalKnowledgeBase(sourcePath, query) { try { // This would search through local documentation or files // For now, return the source path if it contains relevant keywords const keywords = query.toLowerCase().split(' '); const sourceLower = sourcePath.toLowerCase(); const hasRelevantKeywords = keywords.some(keyword => sourceLower.includes(keyword) && keyword.length > 3); return hasRelevantKeywords ? sourcePath : null; } catch (error) { console.warn('Local knowledge base search failed:', error); } return null; } } exports.HelpSupportSkill = HelpSupportSkill;