UNPKG

@polybiouslabs/polybious

Version:

Polybius is a next-generation intelligent agent framework built for adaptability across diverse domains. It merges contextual awareness, multi-agent collaboration, and predictive reasoning to deliver dynamic, self-optimizing performance.

250 lines (249 loc) 10.3 kB
import { BaseTool } from '../base-tool'; import { SentimentAnalyzer, PorterStemmer } from 'natural'; import { logger } from '../../config/logger'; export class SentimentAnalysisTool extends BaseTool { analyzer; constructor() { super(); this.analyzer = new SentimentAnalyzer('English', PorterStemmer, 'afinn'); } getConfig() { return { name: 'sentiment_analysis', description: 'Analyze the sentiment and emotional tone of text content', parameters: { type: 'object', properties: { text: { type: 'string', description: 'The text to analyze for sentiment', maxLength: 5000 }, includeEmotions: { type: 'boolean', description: 'Include detailed emotion analysis', default: false }, language: { type: 'string', description: 'Language of the text', enum: ['en', 'es', 'fr', 'de', 'it'], default: 'en' } }, required: ['text'] }, handler: 'SentimentAnalysisTool', enabled: true, rateLimited: { maxCalls: 200, timeWindow: 60 } }; } validate(params) { if (!this.validateRequired(params, ['text'])) { return false; } if (typeof params.text !== 'string' || params.text.trim().length === 0) { return false; } return true; } async execute(params) { const { text, includeEmotions = false, language = 'en' } = this.sanitizeParams(params); try { logger.info('Analyzing sentiment', { textLength: text.length, includeEmotions }); const sentiment = this.analyzeSentiment(text); const polarity = this.calculatePolarity(text); const confidence = this.calculateConfidence(sentiment, text); const result = { text: text.substring(0, 200) + (text.length > 200 ? '...' : ''), sentiment: this.categorizeSentiment(sentiment), score: sentiment, polarity, confidence, wordCount: text.split(/\s+/).length, timestamp: new Date().toISOString() }; if (includeEmotions) { result.emotions = this.analyzeEmotions(text); result.toxicity = this.analyzeToxicity(text); result.subjectivity = this.analyzeSubjectivity(text); } return result; } catch (error) { logger.error('Sentiment analysis failed', { error, textLength: text.length }); throw new Error(`Sentiment analysis failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } analyzeSentiment(text) { // Tokenize and clean text const tokens = text.toLowerCase() .replace(/[^\w\s]/g, ' ') .split(/\s+/) .filter(token => token.length > 0); if (tokens.length === 0) return 0; // Simple sentiment scoring using word lists const sentimentWords = this.getSentimentWords(); let totalScore = 0; let scoredWords = 0; for (const token of tokens) { if (sentimentWords.positive.includes(token)) { totalScore += 1; scoredWords++; } else if (sentimentWords.negative.includes(token)) { totalScore -= 1; scoredWords++; } } // Normalize by number of sentiment-bearing words return scoredWords > 0 ? totalScore / scoredWords : 0; } calculatePolarity(text) { const score = this.analyzeSentiment(text); if (score > 0.1) return 'positive'; if (score < -0.1) return 'negative'; return 'neutral'; } calculateConfidence(score, text) { const absScore = Math.abs(score); const textLength = text.split(/\s+/).length; // Confidence increases with stronger sentiment and longer text (up to a point) const lengthFactor = Math.min(textLength / 50, 1); const sentimentFactor = Math.min(absScore * 2, 1); return Math.round((lengthFactor * sentimentFactor) * 100) / 100; } categorizeSentiment(score) { if (score > 0.5) return 'very positive'; if (score > 0.1) return 'positive'; if (score > -0.1) return 'neutral'; if (score > -0.5) return 'negative'; return 'very negative'; } analyzeEmotions(text) { const emotionWords = this.getEmotionWords(); const tokens = text.toLowerCase().split(/\s+/); const emotions = { joy: 0, anger: 0, fear: 0, sadness: 0, surprise: 0, disgust: 0, trust: 0, anticipation: 0 }; for (const token of tokens) { for (const [emotion, words] of Object.entries(emotionWords)) { if (words.includes(token)) { emotions[emotion]++; } } } // Normalize by total words const totalWords = tokens.length; for (const emotion in emotions) { emotions[emotion] = Math.round((emotions[emotion] / totalWords) * 1000) / 1000; } return emotions; } analyzeToxicity(text) { const toxicWords = [ 'hate', 'stupid', 'idiot', 'kill', 'die', 'worst', 'terrible', 'awful', 'disgusting', 'pathetic', 'loser', 'failure', 'worthless' ]; const tokens = text.toLowerCase().split(/\s+/); const toxicCount = tokens.filter(token => toxicWords.includes(token)).length; return Math.min(toxicCount / tokens.length, 1); } analyzeSubjectivity(text) { const subjectiveWords = [ 'think', 'feel', 'believe', 'opinion', 'probably', 'maybe', 'seems', 'appears', 'likely', 'possibly', 'apparently', 'supposedly' ]; const tokens = text.toLowerCase().split(/\s+/); const subjectiveCount = tokens.filter(token => subjectiveWords.includes(token)).length; return Math.min(subjectiveCount / tokens.length * 2, 1); } getSentimentWords() { return { positive: [ 'good', 'great', 'excellent', 'amazing', 'wonderful', 'fantastic', 'awesome', 'brilliant', 'perfect', 'outstanding', 'superb', 'magnificent', 'marvelous', 'love', 'like', 'enjoy', 'happy', 'pleased', 'delighted', 'thrilled', 'excited', 'grateful', 'thankful', 'appreciate', 'success', 'win', 'achieve' ], negative: [ 'bad', 'terrible', 'awful', 'horrible', 'disgusting', 'pathetic', 'worst', 'hate', 'dislike', 'loathe', 'despise', 'angry', 'furious', 'mad', 'upset', 'sad', 'depressed', 'miserable', 'disappointed', 'frustrated', 'annoyed', 'fail', 'failure', 'lose', 'problem', 'issue', 'trouble', 'difficult' ] }; } getEmotionWords() { return { joy: ['happy', 'joy', 'cheerful', 'delighted', 'pleased', 'glad', 'content', 'elated'], anger: ['angry', 'mad', 'furious', 'rage', 'irritated', 'annoyed', 'frustrated', 'hostile'], fear: ['afraid', 'scared', 'terrified', 'anxious', 'worried', 'nervous', 'panic', 'dread'], sadness: ['sad', 'depressed', 'miserable', 'gloomy', 'melancholy', 'sorrowful', 'dejected'], surprise: ['surprised', 'amazed', 'astonished', 'shocked', 'stunned', 'bewildered'], disgust: ['disgusted', 'revolted', 'repulsed', 'sickened', 'nauseated', 'appalled'], trust: ['trust', 'confident', 'secure', 'reliable', 'dependable', 'faithful', 'loyal'], anticipation: ['excited', 'eager', 'hopeful', 'optimistic', 'expectant', 'anticipating'] }; } transform(result) { return { ...result, recommendation: this.generateRecommendation(result), insights: this.generateInsights(result) }; } generateRecommendation(result) { const { sentiment, confidence, polarity } = result; if (confidence < 0.3) { return 'The sentiment analysis has low confidence. Consider providing more context or emotional cues.'; } if (polarity === 'negative' && result.toxicity > 0.1) { return 'Content contains negative sentiment with some toxic elements. Consider revising for more positive engagement.'; } if (polarity === 'positive') { return 'Content has positive sentiment. This is likely to generate good engagement.'; } if (polarity === 'neutral') { return 'Content is emotionally neutral. Consider adding more emotional elements to increase engagement.'; } return 'Review content sentiment and adjust tone as needed for your audience.'; } generateInsights(result) { const insights = []; if (result.confidence > 0.8) { insights.push('High confidence in sentiment analysis'); } if (result.emotions) { const dominantEmotion = Object.entries(result.emotions) .sort(([, a], [, b]) => b - a)[0]; if (dominantEmotion[1] > 0.05) { insights.push(`Dominant emotion detected: ${dominantEmotion[0]}`); } } if (result.subjectivity > 0.3) { insights.push('Content is highly subjective/opinion-based'); } if (result.wordCount < 10) { insights.push('Short text may limit sentiment analysis accuracy'); } return insights; } }