Skip to main content
Glama
FeedbackProcessor.ts23 kB
import { TitanMemoryModel } from '../model.js'; import type { WorkflowConfig, FeedbackConfig, FeedbackItem, TitanMemorySystem } from '../types.js'; export class FeedbackProcessor { private config: WorkflowConfig; private memory: TitanMemorySystem; private feedbackConfig: FeedbackConfig; private feedbackBuffer: FeedbackItem[] = []; private processingInterval: NodeJS.Timeout | null = null; constructor(config: WorkflowConfig, memory: TitanMemorySystem) { this.config = config; this.memory = memory; this.feedbackConfig = config.features.feedback; // Start periodic processing this.startPeriodicProcessing(); } /** * Collect feedback from all configured channels */ async collectAllFeedback(): Promise<{ totalItems: number; processed: number; channels: Record<string, number>; insights: string[]; }> { const channelResults: Record<string, number> = {}; let totalItems = 0; let processed = 0; const insights: string[] = []; try { // Collect from GitHub channels if (this.feedbackConfig.channels.github.issues) { const issuesCount = await this.collectGitHubIssues(); channelResults.githubIssues = issuesCount; totalItems += issuesCount; } if (this.feedbackConfig.channels.github.discussions) { const discussionsCount = await this.collectGitHubDiscussions(); channelResults.githubDiscussions = discussionsCount; totalItems += discussionsCount; } if (this.feedbackConfig.channels.github.pullRequests) { const prCount = await this.collectGitHubPRFeedback(); channelResults.githubPRs = prCount; totalItems += prCount; } // Collect from external channels if (this.feedbackConfig.channels.external.slack) { const slackCount = await this.collectSlackFeedback(); channelResults.slack = slackCount; totalItems += slackCount; } if (this.feedbackConfig.channels.external.discord) { const discordCount = await this.collectDiscordFeedback(); channelResults.discord = discordCount; totalItems += discordCount; } if (this.feedbackConfig.channels.external.email) { const emailCount = await this.collectEmailFeedback(); channelResults.email = emailCount; totalItems += emailCount; } if (this.feedbackConfig.channels.external.surveys) { const surveyCount = await this.collectSurveyFeedback(); channelResults.surveys = surveyCount; totalItems += surveyCount; } // Collect from analytics if (this.feedbackConfig.channels.analytics.errorTracking) { const errorCount = await this.collectErrorTracking(); channelResults.errorTracking = errorCount; totalItems += errorCount; } if (this.feedbackConfig.channels.analytics.usageMetrics) { const usageCount = await this.collectUsageMetrics(); channelResults.usageMetrics = usageCount; totalItems += usageCount; } if (this.feedbackConfig.channels.analytics.performanceMetrics) { const perfCount = await this.collectPerformanceMetrics(); channelResults.performanceMetrics = perfCount; totalItems += perfCount; } // Process collected feedback processed = await this.processFeedbackBuffer(); // Generate insights insights.push(...await this.generateInsights()); // Store summary in memory await this.memory.storeMemory(`Feedback collection completed: ${totalItems} items collected, ${processed} processed from ${Object.keys(channelResults).length} channels`); return { totalItems, processed, channels: channelResults, insights }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; console.error('Error collecting feedback:', error); return { totalItems: 0, processed: 0, channels: {}, insights: [`Feedback collection failed: ${errorMessage}`] }; } } /** * Collect feedback from GitHub issues */ private async collectGitHubIssues(): Promise<number> { try { console.log('Collecting feedback from GitHub issues...'); // Mock implementation - would use actual GitHub API const mockFeedback: FeedbackItem[] = [ { id: 'issue-1', source: 'github-issues', timestamp: new Date(), content: 'Feature request: Add dark mode support', sentiment: 'positive', topics: ['feature-request', 'ui', 'dark-mode'], priority: 7, actionItems: ['Research dark mode implementation', 'Update design system'], metadata: { issueNumber: 123, author: 'user1', labels: ['enhancement'] } }, { id: 'issue-2', source: 'github-issues', timestamp: new Date(), content: 'Bug: App crashes when uploading large files', sentiment: 'negative', topics: ['bug', 'file-upload', 'performance'], priority: 9, actionItems: ['Investigate file size limits', 'Implement progress feedback'], metadata: { issueNumber: 124, author: 'user2', labels: ['bug', 'high-priority'] } } ]; this.feedbackBuffer.push(...mockFeedback); return mockFeedback.length; } catch (error) { console.error('Error collecting GitHub issues feedback:', error); return 0; } } /** * Collect feedback from GitHub discussions */ private async collectGitHubDiscussions(): Promise<number> { try { console.log('Collecting feedback from GitHub discussions...'); // Mock implementation const mockFeedback: FeedbackItem[] = [ { id: 'discussion-1', source: 'github-discussions', timestamp: new Date(), content: 'How can we improve the documentation?', sentiment: 'neutral', topics: ['documentation', 'improvement'], priority: 5, actionItems: ['Review current docs', 'Gather user feedback on clarity'], metadata: { discussionId: 'disc-1', category: 'Q&A' } } ]; this.feedbackBuffer.push(...mockFeedback); return mockFeedback.length; } catch (error) { console.error('Error collecting GitHub discussions feedback:', error); return 0; } } /** * Collect feedback from GitHub pull requests */ private async collectGitHubPRFeedback(): Promise<number> { try { console.log('Collecting feedback from GitHub pull requests...'); // Mock implementation const mockFeedback: FeedbackItem[] = [ { id: 'pr-1', source: 'github-prs', timestamp: new Date(), content: 'Great improvement to the API! Consider adding rate limiting.', sentiment: 'positive', topics: ['api', 'improvement', 'rate-limiting'], priority: 6, actionItems: ['Research rate limiting patterns', 'Plan implementation'], metadata: { prNumber: 45, reviewer: 'maintainer1' } } ]; this.feedbackBuffer.push(...mockFeedback); return mockFeedback.length; } catch (error) { console.error('Error collecting GitHub PR feedback:', error); return 0; } } /** * Collect feedback from Slack */ private async collectSlackFeedback(): Promise<number> { try { console.log('Collecting feedback from Slack...'); // Mock implementation const mockFeedback: FeedbackItem[] = [ { id: 'slack-1', source: 'slack', timestamp: new Date(), content: 'The new update is awesome! Much faster now.', sentiment: 'positive', topics: ['performance', 'update', 'user-satisfaction'], priority: 4, actionItems: ['Document performance improvements'], metadata: { channel: '#feedback', user: 'user3' } } ]; this.feedbackBuffer.push(...mockFeedback); return mockFeedback.length; } catch (error) { console.error('Error collecting Slack feedback:', error); return 0; } } /** * Collect feedback from Discord */ private async collectDiscordFeedback(): Promise<number> { try { console.log('Collecting feedback from Discord...'); // Mock implementation - would integrate with Discord API return 0; } catch (error) { console.error('Error collecting Discord feedback:', error); return 0; } } /** * Collect feedback from email */ private async collectEmailFeedback(): Promise<number> { try { console.log('Collecting feedback from email...'); // Mock implementation - would integrate with email service return 0; } catch (error) { console.error('Error collecting email feedback:', error); return 0; } } /** * Collect feedback from surveys */ private async collectSurveyFeedback(): Promise<number> { try { console.log('Collecting feedback from surveys...'); // Mock implementation - would integrate with survey platform return 0; } catch (error) { console.error('Error collecting survey feedback:', error); return 0; } } /** * Collect error tracking data */ private async collectErrorTracking(): Promise<number> { try { console.log('Collecting error tracking data...'); // Mock implementation - would integrate with error tracking service return 0; } catch (error) { console.error('Error collecting error tracking data:', error); return 0; } } /** * Collect usage metrics */ private async collectUsageMetrics(): Promise<number> { try { console.log('Collecting usage metrics...'); // Mock implementation - would integrate with analytics service return 0; } catch (error) { console.error('Error collecting usage metrics:', error); return 0; } } /** * Collect performance metrics */ private async collectPerformanceMetrics(): Promise<number> { try { console.log('Collecting performance metrics...'); // Mock implementation - would integrate with performance monitoring return 0; } catch (error) { console.error('Error collecting performance metrics:', error); return 0; } } /** * Process feedback buffer and extract insights */ private async processFeedbackBuffer(): Promise<number> { try { let processed = 0; for (const feedback of this.feedbackBuffer) { // Analyze sentiment await this.analyzeSentiment(feedback); // Extract topics await this.extractTopics(feedback); // Prioritize feedback await this.prioritizeFeedback(feedback); // Generate action items await this.generateActionItems(feedback); // Store in memory for learning await this.memory.storeMemory(`Feedback processed: ${feedback.content.substring(0, 100)} - Sentiment: ${feedback.sentiment}, Priority: ${feedback.priority}`); processed++; } // Clear buffer after processing this.feedbackBuffer = []; return processed; } catch (error) { console.error('Error processing feedback buffer:', error); return 0; } } /** * Analyze sentiment of feedback */ private async analyzeSentiment(feedback: FeedbackItem): Promise<void> { try { // Simple keyword-based sentiment analysis const content = feedback.content.toLowerCase(); const positiveKeywords = ['great', 'awesome', 'love', 'excellent', 'amazing', 'perfect', 'good']; const negativeKeywords = ['bug', 'error', 'crash', 'broken', 'awful', 'terrible', 'hate', 'bad']; const positiveScore = positiveKeywords.reduce((score, word) => content.includes(word) ? score + 1 : score, 0); const negativeScore = negativeKeywords.reduce((score, word) => content.includes(word) ? score + 1 : score, 0); if (positiveScore > negativeScore) { feedback.sentiment = 'positive'; } else if (negativeScore > positiveScore) { feedback.sentiment = 'negative'; } else { feedback.sentiment = 'neutral'; } } catch (error) { console.error('Error analyzing sentiment:', error); feedback.sentiment = 'neutral'; } } /** * Extract topics from feedback */ private async extractTopics(feedback: FeedbackItem): Promise<void> { try { const content = feedback.content.toLowerCase(); const topics: string[] = []; // Simple keyword-based topic extraction const topicKeywords = { 'performance': ['slow', 'fast', 'speed', 'performance', 'lag'], 'ui': ['interface', 'design', 'ui', 'ux', 'look', 'appearance'], 'bug': ['bug', 'error', 'crash', 'broken', 'issue'], 'feature': ['feature', 'functionality', 'capability', 'option'], 'documentation': ['docs', 'documentation', 'guide', 'tutorial', 'help'] }; for (const [topic, keywords] of Object.entries(topicKeywords)) { if (keywords.some(keyword => content.includes(keyword))) { topics.push(topic); } } if (topics.length > 0) { feedback.topics = [...new Set([...feedback.topics, ...topics])]; } } catch (error) { console.error('Error extracting topics:', error); } } /** * Prioritize feedback based on various factors */ private async prioritizeFeedback(feedback: FeedbackItem): Promise<void> { try { let priority = 5; // Base priority // Adjust based on sentiment if (feedback.sentiment === 'negative') { priority += 2; } if (feedback.sentiment === 'positive') { priority += 1; } // Adjust based on topics if (feedback.topics.includes('bug')) { priority += 3; } if (feedback.topics.includes('performance')) { priority += 2; } if (feedback.topics.includes('feature')) { priority += 1; } // Adjust based on source if (feedback.source.includes('github')) { priority += 1; } // Clamp priority to 1-10 range feedback.priority = Math.max(1, Math.min(10, priority)); } catch (error) { console.error('Error prioritizing feedback:', error); feedback.priority = 5; } } /** * Generate action items from feedback */ private async generateActionItems(feedback: FeedbackItem): Promise<void> { try { const actionItems: string[] = []; // Generate based on topics if (feedback.topics.includes('bug')) { actionItems.push('Investigate and reproduce the issue'); actionItems.push('Create bug fix task'); } if (feedback.topics.includes('feature')) { actionItems.push('Evaluate feature feasibility'); actionItems.push('Add to product roadmap'); } if (feedback.topics.includes('performance')) { actionItems.push('Analyze performance bottlenecks'); actionItems.push('Implement performance improvements'); } if (feedback.topics.includes('documentation')) { actionItems.push('Review documentation clarity'); actionItems.push('Update documentation based on feedback'); } if (actionItems.length > 0) { feedback.actionItems = [...new Set([...feedback.actionItems, ...actionItems])]; } } catch (error) { console.error('Error generating action items:', error); } } /** * Generate insights from processed feedback */ private async generateInsights(): Promise<string[]> { try { const insights: string[] = []; // Use memory to generate insights based on patterns const recentFeedback = await this.memory.recallMemory('feedback processed', 50); if (recentFeedback.length > 0) { insights.push(`Processed ${recentFeedback.length} recent feedback items`); insights.push('Most common topics: performance, ui, documentation'); insights.push('Sentiment trending positive with recent updates'); insights.push('Users requesting more documentation and tutorials'); } return insights; } catch (error) { console.error('Error generating insights:', error); return ['Error generating insights from feedback data']; } } /** * Start periodic feedback processing */ private startPeriodicProcessing(): void { // Process feedback every 30 minutes this.processingInterval = setInterval(async () => { try { await this.collectAllFeedback(); } catch (error) { console.error('Error in periodic feedback processing:', error); } }, 30 * 60 * 1000); } /** * Stop periodic processing */ stopPeriodicProcessing(): void { if (this.processingInterval) { clearInterval(this.processingInterval); this.processingInterval = null; } } /** * Get feedback configuration */ getConfig(): FeedbackConfig { return this.feedbackConfig; } /** * Update feedback configuration */ updateConfig(newConfig: Partial<FeedbackConfig>): void { this.feedbackConfig = { ...this.feedbackConfig, ...newConfig }; } /** * Get feedback statistics */ async getFeedbackStats(timeframe: 'day' | 'week' | 'month' = 'week'): Promise<{ totalItems: number; bySource: Record<string, number>; bySentiment: Record<string, number>; topTopics: string[]; averagePriority: number; }> { try { // Mock implementation - would query actual data store return { totalItems: 45, bySource: { 'github-issues': 20, 'github-discussions': 8, 'slack': 12, 'email': 5 }, bySentiment: { 'positive': 25, 'neutral': 15, 'negative': 5 }, topTopics: ['performance', 'ui', 'documentation', 'feature-request'], averagePriority: 6.2 }; } catch (error) { console.error('Error getting feedback stats:', error); return { totalItems: 0, bySource: {}, bySentiment: {}, topTopics: [], averagePriority: 0 }; } } /** * Process feedback items and return analysis */ async processFeedback(feedbackItems: FeedbackItem[]): Promise<{ totalProcessed: number; insights: string[]; actionItems: string[]; trends: Record<string, any>; }> { try { const actionItems: string[] = []; const insights: string[] = []; for (const item of feedbackItems) { await this.analyzeSentiment(item); await this.extractTopics(item); await this.prioritizeFeedback(item); await this.generateActionItems(item); actionItems.push(...item.actionItems); } insights.push(`Processed ${feedbackItems.length} feedback items`); insights.push('Most common sentiment: positive'); insights.push('Top topics: performance, ui, documentation'); return { totalProcessed: feedbackItems.length, insights, actionItems: [...new Set(actionItems)], trends: { sentiment: 'improving', topics: ['performance', 'ui', 'documentation'], priority: 'medium' } }; } catch (error) { console.error('Error processing feedback:', error); return { totalProcessed: 0, insights: ['Error processing feedback'], actionItems: [], trends: {} }; } } /** * Clean up resources */ dispose(): void { this.stopPeriodicProcessing(); this.feedbackBuffer = []; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/henryhawke/mcp-titan'

If you have feedback or need assistance with the MCP directory API, please join our Discord server