CoolFace
Apppublic

CHKIM79/scalable-ai-agent-system

sourceHugging Facemitupdated 1y agoView on Hugging Face
0likes
nlp_processor.py458 linesDownload Raw Back to nlp
1"""2Natural Language Processing Module3Implements sentiment analysis, intent recognition, entity extraction, and text generation4"""5import asyncio6import logging7import re8from typing import Dict, List, Any, Optional, Tuple, Union9from dataclasses import dataclass, field10from enum import Enum11import openai12import numpy as np13from transformers import pipeline, AutoTokenizer, AutoModel14import torch15from sentence_transformers import SentenceTransformer16 17 18class SentimentLabel(Enum):19    POSITIVE = "positive"20    NEGATIVE = "negative"21    NEUTRAL = "neutral"22    MIXED = "mixed"23 24 25class IntentCategory(Enum):26    QUESTION = "question"27    REQUEST = "request"28    COMPLAINT = "complaint"29    COMPLIMENT = "compliment"30    INFORMATION_SEEKING = "information_seeking"31    TASK_EXECUTION = "task_execution"32    CONVERSATION = "conversation"33    UNKNOWN = "unknown"34 35 36@dataclass37class SentimentResult:38    label: SentimentLabel39    confidence: float40    emotional_intensity: float41    emotional_categories: Dict[str, float] = field(default_factory=dict)42 43 44@dataclass45class IntentResult:46    category: IntentCategory47    confidence: float48    subcategory: Optional[str] = None49    urgency_level: float = 0.050    complexity_level: float = 0.051 52 53@dataclass54class Entity:55    text: str56    label: str57    start_pos: int58    end_pos: int59    confidence: float60    metadata: Dict[str, Any] = field(default_factory=dict)61 62 63@dataclass64class NLPResult:65    original_text: str66    sentiment: SentimentResult67    intent: IntentResult68    entities: List[Entity]69    key_phrases: List[str]70    language: str = "en"71    processing_time: float = 0.072 73 74class SentimentAnalyzer:75    """Advanced sentiment analysis with emotional intelligence"""76    77    def __init__(self):78        self.sentiment_pipeline = pipeline(79            "sentiment-analysis",80            model="cardiffnlp/twitter-roberta-base-sentiment-latest",81            return_all_scores=True82        )83        self.emotion_pipeline = pipeline(84            "text-classification",85            model="j-hartmann/emotion-english-distilroberta-base",86            return_all_scores=True87        )88    89    async def analyze(self, text: str) -> SentimentResult:90        """Perform comprehensive sentiment analysis"""91        # Basic sentiment92        sentiment_scores = self.sentiment_pipeline(text)[0]93        94        # Map model labels to our enum95        label_mapping = {96            "LABEL_0": SentimentLabel.NEGATIVE,97            "LABEL_1": SentimentLabel.NEUTRAL,98            "LABEL_2": SentimentLabel.POSITIVE99        }100        101        # Find highest confidence sentiment102        best_sentiment = max(sentiment_scores, key=lambda x: x['score'])103        sentiment_label = label_mapping.get(best_sentiment['label'], SentimentLabel.NEUTRAL)104        105        # Emotional analysis106        emotion_scores = self.emotion_pipeline(text)[0]107        emotional_categories = {item['label']: item['score'] for item in emotion_scores}108        109        # Calculate emotional intensity110        emotional_intensity = max(emotional_categories.values())111        112        # Check for mixed emotions113        top_emotions = sorted(emotion_scores, key=lambda x: x['score'], reverse=True)[:2]114        if abs(top_emotions[0]['score'] - top_emotions[1]['score']) < 0.1:115            sentiment_label = SentimentLabel.MIXED116        117        return SentimentResult(118            label=sentiment_label,119            confidence=best_sentiment['score'],120            emotional_intensity=emotional_intensity,121            emotional_categories=emotional_categories122        )123 124 125class IntentClassifier:126    """Intent recognition and classification"""127    128    def __init__(self):129        self.intent_patterns = {130            IntentCategory.QUESTION: [131                r'\b(what|how|why|when|where|who|which)\b',132                r'\?',133                r'\b(explain|tell me|help me understand)\b'134            ],135            IntentCategory.REQUEST: [136                r'\b(please|can you|could you|would you)\b',137                r'\b(help|assist|support)\b',138                r'\b(need|want|require)\b'139            ],140            IntentCategory.COMPLAINT: [141                r'\b(problem|issue|error|bug|wrong|broken)\b',142                r'\b(not working|doesn\'t work|failed)\b',143                r'\b(frustrated|annoyed|upset)\b'144            ],145            IntentCategory.COMPLIMENT: [146                r'\b(thank|thanks|appreciate|grateful)\b',147                r'\b(great|excellent|amazing|wonderful)\b',148                r'\b(good job|well done)\b'149            ],150            IntentCategory.TASK_EXECUTION: [151                r'\b(do|execute|run|perform|start|begin)\b',152                r'\b(create|make|build|generate)\b',153                r'\b(calculate|compute|analyze)\b'154            ]155        }156        157        self.urgency_keywords = [158            "urgent", "asap", "immediately", "emergency", "critical", "now"159        ]160        161        self.complexity_keywords = [162            "complex", "complicated", "multiple", "several", "various", "detailed"163        ]164    165    async def classify(self, text: str) -> IntentResult:166        """Classify intent of the text"""167        text_lower = text.lower()168        169        # Pattern matching for intent categories170        intent_scores = {}171        for category, patterns in self.intent_patterns.items():172            score = 0173            for pattern in patterns:174                matches = len(re.findall(pattern, text_lower))175                score += matches176            intent_scores[category] = score177        178        # Determine primary intent179        if max(intent_scores.values()) == 0:180            primary_intent = IntentCategory.UNKNOWN181            confidence = 0.3182        else:183            primary_intent = max(intent_scores, key=intent_scores.get)184            total_matches = sum(intent_scores.values())185            confidence = intent_scores[primary_intent] / total_matches if total_matches > 0 else 0.5186        187        # Calculate urgency level188        urgency_level = sum(1 for keyword in self.urgency_keywords if keyword in text_lower) / len(self.urgency_keywords)189        190        # Calculate complexity level191        complexity_level = sum(1 for keyword in self.complexity_keywords if keyword in text_lower) / len(self.complexity_keywords)192        complexity_level += len(text.split()) / 100  # Longer text = more complex193        complexity_level = min(complexity_level, 1.0)194        195        return IntentResult(196            category=primary_intent,197            confidence=min(confidence, 1.0),198            urgency_level=urgency_level,199            complexity_level=complexity_level200        )201 202 203class EntityExtractor:204    """Named Entity Recognition and extraction"""205    206    def __init__(self):207        self.ner_pipeline = pipeline(208            "ner",209            model="dbmdz/bert-large-cased-finetuned-conll03-english",210            aggregation_strategy="simple"211        )212        213        # Custom entity patterns214        self.custom_patterns = {215            "EMAIL": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',216            "PHONE": r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b',217            "URL": r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+',218            "MONEY": r'\$\d+(?:,\d{3})*(?:\.\d{2})?',219            "DATE_TIME": r'\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b|\b\d{4}-\d{2}-\d{2}\b'220        }221    222    async def extract(self, text: str) -> List[Entity]:223        """Extract entities from text"""224        entities = []225        226        # Standard NER227        ner_results = self.ner_pipeline(text)228        for result in ner_results:229            entities.append(Entity(230                text=result['word'],231                label=result['entity_group'],232                start_pos=result['start'],233                end_pos=result['end'],234                confidence=result['score']235            ))236        237        # Custom pattern matching238        for label, pattern in self.custom_patterns.items():239            matches = re.finditer(pattern, text)240            for match in matches:241                entities.append(Entity(242                    text=match.group(),243                    label=label,244                    start_pos=match.start(),245                    end_pos=match.end(),246                    confidence=0.9247                ))248        249        # Remove duplicates and overlaps250        entities = self._remove_overlapping_entities(entities)251        252        return entities253    254    def _remove_overlapping_entities(self, entities: List[Entity]) -> List[Entity]:255        """Remove overlapping entities, keeping the one with higher confidence"""256        entities.sort(key=lambda x: x.start_pos)257        filtered_entities = []258        259        for entity in entities:260            overlap = False261            for existing in filtered_entities:262                if (entity.start_pos < existing.end_pos and 263                    entity.end_pos > existing.start_pos):264                    if entity.confidence <= existing.confidence:265                        overlap = True266                        break267                    else:268                        # Remove the existing entity with lower confidence269                        filtered_entities.remove(existing)270                        break271            272            if not overlap:273                filtered_entities.append(entity)274        275        return filtered_entities276 277 278class KeyPhraseExtractor:279    """Extract key phrases and topics from text"""280    281    def __init__(self):282        self.embedding_model = SentenceTransformer('all-MiniLM-L6-v2')283    284    async def extract(self, text: str, max_phrases: int = 10) -> List[str]:285        """Extract key phrases using various techniques"""286        # Simple n-gram extraction287        words = text.lower().split()288        289        # Remove stop words290        stop_words = {291            'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',292            'of', 'with', 'by', 'is', 'are', 'was', 'were', 'be', 'been', 'have',293            'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should'294        }295        296        filtered_words = [word for word in words if word not in stop_words and len(word) > 2]297        298        # Extract bigrams and trigrams299        phrases = []300        301        # Unigrams (important single words)302        word_freq = {}303        for word in filtered_words:304            word_freq[word] = word_freq.get(word, 0) + 1305        306        # Get top unigrams307        top_unigrams = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)[:max_phrases//2]308        phrases.extend([word for word, _ in top_unigrams])309        310        # Bigrams311        for i in range(len(filtered_words) - 1):312            bigram = f"{filtered_words[i]} {filtered_words[i+1]}"313            phrases.append(bigram)314        315        # Trigrams316        for i in range(len(filtered_words) - 2):317            trigram = f"{filtered_words[i]} {filtered_words[i+1]} {filtered_words[i+2]}"318            phrases.append(trigram)319        320        # Remove duplicates and limit321        unique_phrases = list(set(phrases))[:max_phrases]322        323        return unique_phrases324 325 326class TextGenerator:327    """Advanced text generation with transformer models"""328    329    def __init__(self, llm_client=None):330        self.llm_client = llm_client or openai.AsyncOpenAI()331    332    async def generate_response(self, 333                              prompt: str, 334                              style: str = "professional",335                              max_tokens: int = 500,336                              temperature: float = 0.7) -> str:337        """Generate contextual response"""338        339        style_prompts = {340            "professional": "Respond in a professional, clear, and helpful manner.",341            "casual": "Respond in a casual, friendly, and conversational tone.",342            "empathetic": "Respond with empathy, understanding, and emotional support.",343            "technical": "Respond with technical accuracy and detailed explanations.",344            "concise": "Respond concisely and directly to the point."345        }346        347        system_prompt = style_prompts.get(style, style_prompts["professional"])348        349        try:350            response = await self.llm_client.chat.completions.create(351                model="gpt-4",352                messages=[353                    {"role": "system", "content": system_prompt},354                    {"role": "user", "content": prompt}355                ],356                max_tokens=max_tokens,357                temperature=temperature358            )359            return response.choices[0].message.content360        except Exception as e:361            return f"Error generating response: {str(e)}"362 363 364class NLPProcessor:365    """Main NLP processing orchestrator"""366    367    def __init__(self, llm_client=None):368        self.sentiment_analyzer = SentimentAnalyzer()369        self.intent_classifier = IntentClassifier()370        self.entity_extractor = EntityExtractor()371        self.keyphrase_extractor = KeyPhraseExtractor()372        self.text_generator = TextGenerator(llm_client)373        self.logger = logging.getLogger(__name__)374    375    async def process(self, text: str) -> NLPResult:376        """Comprehensive NLP processing"""377        import time378        start_time = time.time()379        380        try:381            # Run all analyses concurrently382            sentiment_task = self.sentiment_analyzer.analyze(text)383            intent_task = self.intent_classifier.classify(text)384            entities_task = self.entity_extractor.extract(text)385            keyphrases_task = self.keyphrase_extractor.extract(text)386            387            sentiment, intent, entities, key_phrases = await asyncio.gather(388                sentiment_task, intent_task, entities_task, keyphrases_task389            )390            391            processing_time = time.time() - start_time392            393            result = NLPResult(394                original_text=text,395                sentiment=sentiment,396                intent=intent,397                entities=entities,398                key_phrases=key_phrases,399                processing_time=processing_time400            )401            402            self.logger.info(f"NLP processing completed in {processing_time:.2f}s")403            return result404            405        except Exception as e:406            self.logger.error(f"NLP processing failed: {e}")407            # Return basic result on error408            return NLPResult(409                original_text=text,410                sentiment=SentimentResult(SentimentLabel.NEUTRAL, 0.0, 0.0),411                intent=IntentResult(IntentCategory.UNKNOWN, 0.0),412                entities=[],413                key_phrases=[],414                processing_time=time.time() - start_time415            )416    417    async def generate_contextual_response(self, 418                                         nlp_result: NLPResult,419                                         context: Dict[str, Any] = None) -> str:420        """Generate contextual response based on NLP analysis"""421        422        # Determine response style based on sentiment and intent423        if nlp_result.sentiment.label == SentimentLabel.NEGATIVE:424            style = "empathetic"425        elif nlp_result.intent.category == IntentCategory.COMPLAINT:426            style = "empathetic"427        elif nlp_result.intent.category in [IntentCategory.QUESTION, IntentCategory.INFORMATION_SEEKING]:428            style = "professional"429        elif nlp_result.intent.urgency_level > 0.5:430            style = "concise"431        else:432            style = "professional"433        434        # Create enhanced prompt with NLP insights435        prompt = f"""436        User message: {nlp_result.original_text}437        438        Analysis:439        - Sentiment: {nlp_result.sentiment.label.value} (confidence: {nlp_result.sentiment.confidence:.2f})440        - Intent: {nlp_result.intent.category.value} (urgency: {nlp_result.intent.urgency_level:.2f})441        - Key entities: {', '.join([e.text for e in nlp_result.entities[:5]])}442        - Key phrases: {', '.join(nlp_result.key_phrases[:5])}443        444        Context: {context or {}}445        446        Please provide an appropriate response.447        """448        449        return await self.text_generator.generate_response(prompt, style)450    451    async def initialize(self):452        """Initialize NLP processor"""453        self.logger.info("NLP processor initialized")454    455    async def shutdown(self):456        """Shutdown NLP processor"""457        self.logger.info("NLP processor shutdown")458