From a8c7baa58e10944b1405a1bba65ccb5bf9b6a76a Mon Sep 17 00:00:00 2001 From: Afdaan Date: Wed, 22 Oct 2025 02:11:16 +0700 Subject: [PATCH 1/6] feat: add zero-shot classification model for intent detection and enhance message context analysis --- config/settings.py | 8 ++ core/nlp.py | 273 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 279 insertions(+), 2 deletions(-) diff --git a/config/settings.py b/config/settings.py index 11bf4e0..ae62c67 100644 --- a/config/settings.py +++ b/config/settings.py @@ -146,6 +146,14 @@ "AnasAlokla/multilingual_go_emotions" ) +# Zero-shot classification model for intent detection +# Using mDeBERTa which is explicitly trained for multilingual (incl. Indonesian) +ZERO_SHOT_MODEL: str = os.getenv( + "ZERO_SHOT_MODEL", + "MoritzLaurer/mDeBERTa-v3-base-mnli-xnli" # Better multilingual support (ID + EN) +) +ZERO_SHOT_CONFIDENCE_THRESHOLD: float = 0.3 # Minimum confidence for intent classification + # Feature Flags FEATURES: Dict[str, bool] = { "memory": True, diff --git a/core/nlp.py b/core/nlp.py index a05c178..ace2311 100644 --- a/core/nlp.py +++ b/core/nlp.py @@ -14,6 +14,8 @@ SLIDING_WINDOW_SIZE, EMOTION_MODEL_ID, EMOTION_MODEL_EN, + ZERO_SHOT_MODEL, + ZERO_SHOT_CONFIDENCE_THRESHOLD, DEFAULT_LANGUAGE ) from database.database_manager import db_manager, DatabaseManager @@ -25,7 +27,9 @@ class NLPEngine: def __init__(self): self.emotion_classifier_id: Optional[Pipeline] = None self.emotion_classifier_en: Optional[Pipeline] = None + self.zero_shot_classifier: Optional[Pipeline] = None self._emotion_cache: Dict[str, Tuple[str, float]] = {} + self._intent_cache: Dict[str, Tuple[str, float]] = {} self._cache_ttl = 300 self._max_cache_size = 1000 self._initialize_models() @@ -64,10 +68,18 @@ def _initialize_models(self) -> None: top_k=3 ) logger.info("multilingual_go_emotions loaded.") + # Load zero-shot classification model for intent detection + logger.info(f"Loading zero-shot classifier: {ZERO_SHOT_MODEL}") + self.zero_shot_classifier = pipeline( + task="zero-shot-classification", + model=ZERO_SHOT_MODEL + ) + logger.info("Zero-shot classifier loaded.") except Exception as e: logger.error(f"Error initializing emotion models: {str(e)}") self.emotion_classifier_id = None self.emotion_classifier_en = None + self.zero_shot_classifier = None def detect_emotion(self, text: str, user_id: int = None) -> Optional[str]: """ @@ -116,11 +128,268 @@ def detect_emotion(self, text: str, user_id: int = None) -> Optional[str]: return None def get_message_context(self, text: str, user_id: int = None) -> Dict[str, Any]: - """Analyze message for emotion only.""" + """Analyze message for emotion, intent, and relationship signals. + + Detects: + - Emotion: User's emotional state (joy, anger, sadness, etc.) + - Intent: What the user is trying to do (gratitude, insult, greeting, etc.) + - Relationship signals: Positive/negative behaviors affecting affection + + Args: + text: User's message text + user_id: User ID for language detection + + Returns: + Dict with emotion, intent, relationship_signals, and directed_at_alya flag + """ + # Periodic cache cleanup + self._cleanup_cache(self._emotion_cache) + self._cleanup_cache(self._intent_cache) + emotion = self.detect_emotion(text, user_id) + intent = self._detect_intent(text, user_id) + relationship_signals = self._detect_relationship_signals(text, emotion, intent) + directed_at_alya = self._is_directed_at_alya(text) + return { - "emotion": emotion + "emotion": emotion, + "intent": intent, + "relationship_signals": relationship_signals, + "directed_at_alya": directed_at_alya } + + def _detect_intent(self, text: str, user_id: int = None) -> str: + """Detect user's intent from message text using zero-shot classification. + + Uses zero-shot classification for semantic intent detection with multilingual support + (Indonesian & English): + - gratitude: thanks, appreciation + - apology: sorry, apologetic + - greeting: hello, casual opening + - compliment: praise, positive feedback + - insult: derogatory, negative + - romantic_interest: romantic signals + - affection: emotional attachment + - question: information seeking + - toxic_behavior: harmful, bullying + - rudeness: impolite language + - asking_about_alya: questions about Alya + - remembering_details: referencing past conversations + - meaningful_conversation: substantive discussion + - normal: default/neutral + + Args: + text: User's message text + user_id: User ID for caching + + Returns: + str: Detected intent category + """ + if not self.zero_shot_classifier: + logger.warning("Zero-shot classifier not loaded, defaulting to 'normal'") + return "normal" + + # Check cache first + text_hash = self._get_text_hash(f"intent:{text}") + if text_hash in self._intent_cache: + intent, timestamp = self._intent_cache[text_hash] + if self._is_cache_valid(timestamp): + return intent + + # Get user language for bilingual label selection + lang = DEFAULT_LANGUAGE + if user_id: + user_settings = db_manager.get_user_settings(user_id) + lang = user_settings.get("language", DEFAULT_LANGUAGE) + + # Define candidate intent labels (bilingual support) + if lang == "id": + candidate_labels = [ + "terima kasih atau apresiasi", + "permintaan maaf atau menyesal", + "salam atau pembukaan ramah", + "pujian atau feedback positif", + "hinaan atau kata-kata negatif", + "minat romantis atau cinta", + "kasih sayang atau kelekatan emosional", + "pertanyaan atau bertanya", + "perilaku toxic atau bullying", + "katakasar atau tidak sopan", + "bertanya tentang bot", + "mengingat percakapan sebelumnya", + "percakapan bermakna atau substansial", + "normal atau netral" + ] + else: # English + candidate_labels = [ + "gratitude or thanks", + "apology or sorry", + "greeting or hello", + "compliment or praise", + "insult or derogatory", + "romantic or love interest", + "affection or emotional attachment", + "question or asking", + "toxic behavior or bullying", + "rude or impolite", + "asking about the bot", + "remembering past conversations", + "meaningful or substantive conversation", + "normal or neutral" + ] + + try: + # Use zero-shot classification + result = self.zero_shot_classifier( + text, + candidate_labels, + multi_class=False + ) + + # Check if top result meets confidence threshold + if result and result.get("scores") and result["scores"][0] >= ZERO_SHOT_CONFIDENCE_THRESHOLD: + # Map classification result back to intent + top_label = result["labels"][0] + intent = self._map_label_to_intent(top_label, lang) + self._intent_cache[text_hash] = (intent, time.time()) + return intent + else: + # Fallback if confidence is too low + logger.debug(f"Intent confidence too low for: {text}") + self._intent_cache[text_hash] = ("normal", time.time()) + return "normal" + + except Exception as e: + logger.error(f"Error in zero-shot intent detection: {e}") + return "normal" + + def _map_label_to_intent(self, label: str, lang: str = "en") -> str: + """Map zero-shot classification label to intent category (bilingual). + + Args: + label: Zero-shot classification label (Indonesian or English) + lang: Language of the label ("id" or "en") + + Returns: + str: Intent category + """ + label_lower = label.lower() + + # Common patterns across both languages + if "gratitude" in label_lower or "thanks" in label_lower or "terima kasih" in label_lower or "apresiasi" in label_lower: + return "gratitude" + elif "apology" in label_lower or "sorry" in label_lower or "maaf" in label_lower or "menyesal" in label_lower: + return "apology" + elif "greeting" in label_lower or "hello" in label_lower or "salam" in label_lower or "pembukaan" in label_lower: + return "greeting" + elif "compliment" in label_lower or "praise" in label_lower or "pujian" in label_lower or "positif" in label_lower: + return "compliment" + elif "insult" in label_lower or "derogatory" in label_lower or "hinaan" in label_lower or "negatif" in label_lower: + return "insult" + elif "romantic" in label_lower or "love" in label_lower or "romantis" in label_lower or "cinta" in label_lower: + return "romantic_interest" + elif "affection" in label_lower or "attachment" in label_lower or "kasih sayang" in label_lower or "kelekatan" in label_lower: + return "affection" + elif "question" in label_lower or "asking" in label_lower or "pertanyaan" in label_lower or "bertanya" in label_lower: + return "question" + elif "toxic" in label_lower or "bullying" in label_lower or "katakasar" in label_lower: + return "toxic_behavior" + elif "rude" in label_lower or "impolite" in label_lower or "tidak sopan" in label_lower: + return "rudeness" + elif "bot" in label_lower or "you" in label_lower or "kamu" in label_lower: + return "asking_about_alya" + elif "remembering" in label_lower or "past" in label_lower or "mengingat" in label_lower or "sebelumnya" in label_lower: + return "remembering_details" + elif "meaningful" in label_lower or "substantive" in label_lower or "bermakna" in label_lower or "substansial" in label_lower: + return "meaningful_conversation" + else: + return "normal" + + def _detect_relationship_signals(self, text: str, emotion: str, intent: str) -> Dict[str, float]: + """Detect relationship signals (positive/negative behaviors). + + Returns a dict with: + - friendliness: 0 = hostile, 1 = very friendly + - romantic_interest: 0 = none, 1 = strong romantic signal + - conflict: 0 = none, 1 = conflict present + + Args: + text: User's message + emotion: Detected emotion + intent: Detected intent + + Returns: + Dict with relationship signal scores + """ + signals = { + "friendliness": 0, + "romantic_interest": 0, + "conflict": 0 + } + + text_lower = text.lower() + + # Friendliness signals + if intent in ["gratitude", "compliment", "greeting", "affection"]: + signals["friendliness"] = 1 + elif emotion in ["joy", "happiness", "love"]: + signals["friendliness"] = 0.8 + elif intent in ["asking_about_alya", "meaningful_conversation", "remembering_details"]: + signals["friendliness"] = 0.7 + elif emotion in ["sadness", "fear", "worry"]: + # Showing vulnerability builds connection + signals["friendliness"] = 0.5 + elif intent in ["insult", "rudeness", "toxic_behavior"]: + signals["conflict"] = 1 + + # Romantic interest signals + if intent == "affection": + signals["romantic_interest"] = 1 + elif intent == "romantic_interest": + signals["romantic_interest"] = 0.8 + elif any(word in text_lower for word in ["jadi pacarnya", "pacaran", "istri", "suami", "marry"]): + signals["romantic_interest"] = 0.9 + elif emotion == "love": + signals["romantic_interest"] = 0.6 + + # Conflict signals + if emotion in ["anger", "frustration", "disgust"]: + signals["conflict"] = 0.7 + elif intent in ["insult", "rudeness", "toxic_behavior"]: + signals["conflict"] = 1 + elif intent == "apology": + signals["conflict"] = -0.5 # Resolve conflict + + return signals + + def _is_directed_at_alya(self, text: str) -> bool: + """Check if message is directed at Alya specifically. + + Args: + text: User's message + + Returns: + bool: True if message is directed at Alya + """ + text_lower = text.lower() + + # Check for direct address patterns + alya_patterns = [ + "alya", "kamu", "lu", "elu", "lo", "mu", "kau", + "you", "ur", "yourself", "yourself" + ] + + # If message starts with or contains direct address, likely directed at Alya + for pattern in alya_patterns: + if text_lower.startswith(pattern) or f" {pattern} " in text_lower: + return True + + # If message is a question or response in conversation context, likely directed at Alya + if text_lower.endswith("?") or any(word in text_lower for word in ["apa", "siapa", "bagaimana"]): + return True + + # Default to True if not clear (safer assumption) + return True def suggest_mood_for_response(self, user_context: Dict[str, Any], relationship_level: int) -> str: """Suggest Alya's mood for response based on user context and relationship. From d522da6605ca59777f78994172ac2414b4a74f6c Mon Sep 17 00:00:00 2001 From: Afdaan Date: Wed, 22 Oct 2025 02:26:41 +0700 Subject: [PATCH 2/6] feat: lower zero-shot confidence threshold for improved --- config/settings.py | 2 +- core/nlp.py | 115 +++++++++++++++++++++++++++------------------ 2 files changed, 71 insertions(+), 46 deletions(-) diff --git a/config/settings.py b/config/settings.py index ae62c67..d39ff4c 100644 --- a/config/settings.py +++ b/config/settings.py @@ -152,7 +152,7 @@ "ZERO_SHOT_MODEL", "MoritzLaurer/mDeBERTa-v3-base-mnli-xnli" # Better multilingual support (ID + EN) ) -ZERO_SHOT_CONFIDENCE_THRESHOLD: float = 0.3 # Minimum confidence for intent classification +ZERO_SHOT_CONFIDENCE_THRESHOLD: float = 0.25 # Lowered from 0.3 for better sensitivity # Feature Flags FEATURES: Dict[str, bool] = { diff --git a/core/nlp.py b/core/nlp.py index ace2311..f45881f 100644 --- a/core/nlp.py +++ b/core/nlp.py @@ -205,37 +205,37 @@ def _detect_intent(self, text: str, user_id: int = None) -> str: # Define candidate intent labels (bilingual support) if lang == "id": candidate_labels = [ - "terima kasih atau apresiasi", - "permintaan maaf atau menyesal", - "salam atau pembukaan ramah", - "pujian atau feedback positif", - "hinaan atau kata-kata negatif", - "minat romantis atau cinta", - "kasih sayang atau kelekatan emosional", - "pertanyaan atau bertanya", - "perilaku toxic atau bullying", - "katakasar atau tidak sopan", - "bertanya tentang bot", - "mengingat percakapan sebelumnya", - "percakapan bermakna atau substansial", - "normal atau netral" + "ungkapan rasa terima kasih atau apresiasi", + "permintaan maaf atau penyesalan", + "sapaan atau pembukaan ramah", + "pujian atau komplimen positif", + "hinaan atau kata-kata merendahkan", + "ekspresi cinta atau minat romantis", + "ungkapan kasih sayang atau keterikatan emosional", + "pertanyaan atau permintaan informasi", + "perilaku toxic atau ancaman", + "kata-kata kasar atau tidak sopan", + "pertanyaan tentang bot atau penanya", + "mengingat atau mereferensikan percakapan masa lalu", + "percakapan bermakna atau diskusi substansial", + "percakapan biasa atau netral" ] else: # English candidate_labels = [ - "gratitude or thanks", - "apology or sorry", - "greeting or hello", - "compliment or praise", - "insult or derogatory", - "romantic or love interest", - "affection or emotional attachment", - "question or asking", - "toxic behavior or bullying", - "rude or impolite", - "asking about the bot", - "remembering past conversations", - "meaningful or substantive conversation", - "normal or neutral" + "expressing gratitude or appreciation", + "apologizing or expressing regret", + "greeting or friendly opening", + "compliment or positive praise", + "insult or derogatory language", + "expressing love or romantic interest", + "expressing affection or emotional attachment", + "asking a question or seeking information", + "toxic behavior or threatening language", + "crude or impolite language", + "asking about the bot or about the person", + "remembering or referencing past conversations", + "meaningful or substantive discussion", + "casual or neutral conversation" ] try: @@ -243,7 +243,7 @@ def _detect_intent(self, text: str, user_id: int = None) -> str: result = self.zero_shot_classifier( text, candidate_labels, - multi_class=False + multi_label=False ) # Check if top result meets confidence threshold @@ -275,35 +275,60 @@ def _map_label_to_intent(self, label: str, lang: str = "en") -> str: """ label_lower = label.lower() - # Common patterns across both languages - if "gratitude" in label_lower or "thanks" in label_lower or "terima kasih" in label_lower or "apresiasi" in label_lower: + # Gratitude patterns (Indonesian & English) + if any(word in label_lower for word in ["gratitude", "thanks", "terima kasih", "apresiasi", "appreciation"]): return "gratitude" - elif "apology" in label_lower or "sorry" in label_lower or "maaf" in label_lower or "menyesal" in label_lower: + + # Apology patterns + if any(word in label_lower for word in ["apology", "sorry", "maaf", "penyesalan", "regret"]): return "apology" - elif "greeting" in label_lower or "hello" in label_lower or "salam" in label_lower or "pembukaan" in label_lower: + + # Greeting patterns + if any(word in label_lower for word in ["greeting", "hello", "sapaan", "pembukaan", "friendly opening"]): return "greeting" - elif "compliment" in label_lower or "praise" in label_lower or "pujian" in label_lower or "positif" in label_lower: + + # Compliment patterns (improved for "compliment" or "praise") + if any(word in label_lower for word in ["compliment", "praise", "pujian", "positif", "positive praise"]): return "compliment" - elif "insult" in label_lower or "derogatory" in label_lower or "hinaan" in label_lower or "negatif" in label_lower: + + # Insult patterns + if any(word in label_lower for word in ["insult", "derogatory", "hinaan", "merendahkan"]): return "insult" - elif "romantic" in label_lower or "love" in label_lower or "romantis" in label_lower or "cinta" in label_lower: + + # Romantic interest / Love patterns (improved detection) + if any(word in label_lower for word in ["romantic", "love", "cinta", "minat romantis", "expressing love"]): return "romantic_interest" - elif "affection" in label_lower or "attachment" in label_lower or "kasih sayang" in label_lower or "kelekatan" in label_lower: + + # Affection patterns (improved for "I love you" -> affection) + if any(word in label_lower for word in ["affection", "attachment", "kasih sayang", "keterikatan", "emotional"]): return "affection" - elif "question" in label_lower or "asking" in label_lower or "pertanyaan" in label_lower or "bertanya" in label_lower: + + # Question patterns + if any(word in label_lower for word in ["question", "asking", "pertanyaan", "bertanya", "seeking information"]): return "question" - elif "toxic" in label_lower or "bullying" in label_lower or "katakasar" in label_lower: + + # Toxic behavior patterns + if any(word in label_lower for word in ["toxic", "threatening", "ancaman", "threat"]): return "toxic_behavior" - elif "rude" in label_lower or "impolite" in label_lower or "tidak sopan" in label_lower: + + # Rudeness patterns + if any(word in label_lower for word in ["rude", "impolite", "crude", "kasar", "tidak sopan"]): return "rudeness" - elif "bot" in label_lower or "you" in label_lower or "kamu" in label_lower: + + # Asking about bot/person patterns + if any(word in label_lower for word in ["bot", "person", "penanya", "about the"]): return "asking_about_alya" - elif "remembering" in label_lower or "past" in label_lower or "mengingat" in label_lower or "sebelumnya" in label_lower: + + # Remembering patterns + if any(word in label_lower for word in ["remembering", "past", "mengingat", "masa lalu", "referencing"]): return "remembering_details" - elif "meaningful" in label_lower or "substantive" in label_lower or "bermakna" in label_lower or "substansial" in label_lower: + + # Meaningful conversation patterns + if any(word in label_lower for word in ["meaningful", "substantive", "bermakna", "substansial", "discussion"]): return "meaningful_conversation" - else: - return "normal" + + # Default to normal + return "normal" def _detect_relationship_signals(self, text: str, emotion: str, intent: str) -> Dict[str, float]: """Detect relationship signals (positive/negative behaviors). From 4bc2d47f690ffe962ece80445401cd732c19f599 Mon Sep 17 00:00:00 2001 From: Afdaan Date: Wed, 22 Oct 2025 02:36:36 +0700 Subject: [PATCH 3/6] feat: enhance intent detection labels for improved zero-shot classification accuracy --- core/nlp.py | 103 ++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/core/nlp.py b/core/nlp.py index f45881f..72fd2b9 100644 --- a/core/nlp.py +++ b/core/nlp.py @@ -203,39 +203,40 @@ def _detect_intent(self, text: str, user_id: int = None) -> str: lang = user_settings.get("language", DEFAULT_LANGUAGE) # Define candidate intent labels (bilingual support) + # Labels are crafted to be semantically distinct for better zero-shot classification if lang == "id": candidate_labels = [ - "ungkapan rasa terima kasih atau apresiasi", - "permintaan maaf atau penyesalan", - "sapaan atau pembukaan ramah", - "pujian atau komplimen positif", - "hinaan atau kata-kata merendahkan", - "ekspresi cinta atau minat romantis", - "ungkapan kasih sayang atau keterikatan emosional", - "pertanyaan atau permintaan informasi", - "perilaku toxic atau ancaman", - "kata-kata kasar atau tidak sopan", - "pertanyaan tentang bot atau penanya", - "mengingat atau mereferensikan percakapan masa lalu", - "percakapan bermakna atau diskusi substansial", - "percakapan biasa atau netral" + "ucapan terima kasih dan rasa syukur", + "permintaan maaf dan penyesalan", + "salam sapaan dan pembukaan percakapan", + "memuji keindahan atau kualitas baik", + "menghina atau merendahkan dengan kata buruk", + "ungkapan cinta dan minat romantis", + "ungkapan sayang dan perhatian kasih", + "pertanyaan atau meminta informasi penjelasan", + "ancaman berbahaya atau perilaku jahat", + "kata-kata kasar tidak sopan atau menyerang", + "bertanya spesifik tentang bot atau dirinya", + "mereferensikan atau mengingat percakapan sebelumnya", + "diskusi mendalam penting atau bermakna", + "obrolan santai tanpa maksud khusus" ] else: # English candidate_labels = [ - "expressing gratitude or appreciation", - "apologizing or expressing regret", - "greeting or friendly opening", - "compliment or positive praise", - "insult or derogatory language", - "expressing love or romantic interest", - "expressing affection or emotional attachment", - "asking a question or seeking information", - "toxic behavior or threatening language", - "crude or impolite language", - "asking about the bot or about the person", - "remembering or referencing past conversations", - "meaningful or substantive discussion", - "casual or neutral conversation" + "thanking expressing gratitude and appreciation", + "apologizing saying sorry and feeling regret", + "greeting saying hello and starting chat", + "praising complimenting beauty and quality", + "insulting name calling and putting down", + "declaring love expressing romantic feelings", + "showing care affection and tenderness", + "questioning asking seeking information", + "threatening dangerous behavior and harm", + "cursing being rude impolite language", + "asking about the bot personal questions", + "referencing remembering previous chats", + "discussing important meaningful topics", + "chatting casual neutral normal talk" ] try: @@ -276,55 +277,55 @@ def _map_label_to_intent(self, label: str, lang: str = "en") -> str: label_lower = label.lower() # Gratitude patterns (Indonesian & English) - if any(word in label_lower for word in ["gratitude", "thanks", "terima kasih", "apresiasi", "appreciation"]): + if any(word in label_lower for word in ["gratitude", "thanks", "terima kasih", "apresiasi", "appreciation", "syukur"]): return "gratitude" # Apology patterns - if any(word in label_lower for word in ["apology", "sorry", "maaf", "penyesalan", "regret"]): + if any(word in label_lower for word in ["apology", "sorry", "maaf", "penyesalan", "regret", "permintaan maaf"]): return "apology" # Greeting patterns - if any(word in label_lower for word in ["greeting", "hello", "sapaan", "pembukaan", "friendly opening"]): + if any(word in label_lower for word in ["greeting", "hello", "sapaan", "pembukaan", "friendly", "salam", "opening"]): return "greeting" - # Compliment patterns (improved for "compliment" or "praise") - if any(word in label_lower for word in ["compliment", "praise", "pujian", "positif", "positive praise"]): + # Compliment patterns (matching "memuji", "puja", "keindahan", "compliment", "praise", "praising", "beautiful", "quality") + if any(word in label_lower for word in ["compliment", "praise", "pujian", "positif", "positive", "muji", "keindahan", "beautiful", "quality", "praising"]): return "compliment" - # Insult patterns - if any(word in label_lower for word in ["insult", "derogatory", "hinaan", "merendahkan"]): + # Insult patterns (matching "menghina", "merendahkan", "insult", "name calling", "putting down") + if any(word in label_lower for word in ["insult", "derogatory", "hinaan", "merendahkan", "name calling", "putting down"]): return "insult" - # Romantic interest / Love patterns (improved detection) - if any(word in label_lower for word in ["romantic", "love", "cinta", "minat romantis", "expressing love"]): + # Romantic interest / Love patterns (matching "declaring", "love", "cinta", "romantic", "minat romantis") + if any(word in label_lower for word in ["romantic", "love", "cinta", "minat romantis", "declaring", "expressing love"]): return "romantic_interest" - # Affection patterns (improved for "I love you" -> affection) - if any(word in label_lower for word in ["affection", "attachment", "kasih sayang", "keterikatan", "emotional"]): + # Affection patterns (matching "sayang", "perhatian", "kasih", "affection", "care", "tenderness", "emotional attachment") + if any(word in label_lower for word in ["affection", "attachment", "kasih", "sayang", "keterikatan", "emotional", "care", "tenderness", "perhatian"]): return "affection" - # Question patterns - if any(word in label_lower for word in ["question", "asking", "pertanyaan", "bertanya", "seeking information"]): + # Question patterns (matching "tanya", "informasi", "pertanyaan", "question", "asking", "seeking") + if any(word in label_lower for word in ["question", "asking", "pertanyaan", "tanya", "seeking", "informasi", "penjelasan"]): return "question" - # Toxic behavior patterns - if any(word in label_lower for word in ["toxic", "threatening", "ancaman", "threat"]): + # Toxic behavior patterns (matching "ancam", "bahaya", "jahat", "threat", "dangerous", "toxic") + if any(word in label_lower for word in ["toxic", "threatening", "threat", "dangerous", "ancam", "bahaya", "jahat", "harmful"]): return "toxic_behavior" - # Rudeness patterns - if any(word in label_lower for word in ["rude", "impolite", "crude", "kasar", "tidak sopan"]): + # Rudeness patterns (matching "kasar", "sopan", "menyerang", "rude", "impolite", "crude") + if any(word in label_lower for word in ["rude", "impolite", "crude", "kasar", "tidak sopan", "menyerang", "cursing"]): return "rudeness" - # Asking about bot/person patterns - if any(word in label_lower for word in ["bot", "person", "penanya", "about the"]): + # Asking about bot/person patterns (matching "bot", "spesifik", "personal") + if any(word in label_lower for word in ["bot", "person", "penanya", "about the", "spesifik", "personal"]): return "asking_about_alya" - # Remembering patterns - if any(word in label_lower for word in ["remembering", "past", "mengingat", "masa lalu", "referencing"]): + # Remembering patterns (matching "referensikan", "mengingat", "percakapan", "remembering", "past", "previous", "chats") + if any(word in label_lower for word in ["remembering", "past", "mengingat", "masa lalu", "referencing", "percakapan", "referensikan", "previous", "chats"]): return "remembering_details" - # Meaningful conversation patterns - if any(word in label_lower for word in ["meaningful", "substantive", "bermakna", "substansial", "discussion"]): + # Meaningful conversation patterns (matching "diskusi", "mendalam", "penting", "bermakna", "meaningful", "important", "substantive") + if any(word in label_lower for word in ["meaningful", "substantive", "bermakna", "substansial", "discussion", "diskusi", "mendalam", "penting", "important"]): return "meaningful_conversation" # Default to normal From 8c4b029bec3d5cf4eda5889067d8371a7d2034a7 Mon Sep 17 00:00:00 2001 From: Afdaan Date: Wed, 22 Oct 2025 02:44:59 +0700 Subject: [PATCH 4/6] feat: simplify intent detection labels for clearer classification --- core/nlp.py | 117 +++++++++++++++++++++------------------------------- 1 file changed, 46 insertions(+), 71 deletions(-) diff --git a/core/nlp.py b/core/nlp.py index 72fd2b9..5c6285d 100644 --- a/core/nlp.py +++ b/core/nlp.py @@ -203,40 +203,40 @@ def _detect_intent(self, text: str, user_id: int = None) -> str: lang = user_settings.get("language", DEFAULT_LANGUAGE) # Define candidate intent labels (bilingual support) - # Labels are crafted to be semantically distinct for better zero-shot classification + # Labels kept simple and distinct to avoid model confusion if lang == "id": candidate_labels = [ - "ucapan terima kasih dan rasa syukur", - "permintaan maaf dan penyesalan", - "salam sapaan dan pembukaan percakapan", - "memuji keindahan atau kualitas baik", - "menghina atau merendahkan dengan kata buruk", - "ungkapan cinta dan minat romantis", - "ungkapan sayang dan perhatian kasih", - "pertanyaan atau meminta informasi penjelasan", - "ancaman berbahaya atau perilaku jahat", - "kata-kata kasar tidak sopan atau menyerang", - "bertanya spesifik tentang bot atau dirinya", - "mereferensikan atau mengingat percakapan sebelumnya", - "diskusi mendalam penting atau bermakna", - "obrolan santai tanpa maksud khusus" + "terima kasih", + "maaf", + "salam", + "pujian", + "hinaan", + "cinta", + "sayang", + "pertanyaan", + "ancaman", + "kasar", + "tentang bot", + "mengingat", + "diskusi bermakna", + "obrolan biasa" ] else: # English candidate_labels = [ - "thanking expressing gratitude and appreciation", - "apologizing saying sorry and feeling regret", - "greeting saying hello and starting chat", - "praising complimenting beauty and quality", - "insulting name calling and putting down", - "declaring love expressing romantic feelings", - "showing care affection and tenderness", - "questioning asking seeking information", - "threatening dangerous behavior and harm", - "cursing being rude impolite language", - "asking about the bot personal questions", - "referencing remembering previous chats", - "discussing important meaningful topics", - "chatting casual neutral normal talk" + "thanks", + "sorry", + "hello", + "praise", + "insult", + "love", + "affection", + "question", + "threat", + "rude", + "about bot", + "remember", + "meaningful", + "normal" ] try: @@ -274,62 +274,37 @@ def _map_label_to_intent(self, label: str, lang: str = "en") -> str: Returns: str: Intent category """ - label_lower = label.lower() + label_lower = label.lower().strip() - # Gratitude patterns (Indonesian & English) - if any(word in label_lower for word in ["gratitude", "thanks", "terima kasih", "apresiasi", "appreciation", "syukur"]): + # Direct matching for simple labels + if label_lower in ["terima kasih", "thanks", "thank"]: return "gratitude" - - # Apology patterns - if any(word in label_lower for word in ["apology", "sorry", "maaf", "penyesalan", "regret", "permintaan maaf"]): + elif label_lower in ["maaf", "sorry", "apology"]: return "apology" - - # Greeting patterns - if any(word in label_lower for word in ["greeting", "hello", "sapaan", "pembukaan", "friendly", "salam", "opening"]): + elif label_lower in ["salam", "hello", "greeting"]: return "greeting" - - # Compliment patterns (matching "memuji", "puja", "keindahan", "compliment", "praise", "praising", "beautiful", "quality") - if any(word in label_lower for word in ["compliment", "praise", "pujian", "positif", "positive", "muji", "keindahan", "beautiful", "quality", "praising"]): + elif label_lower in ["pujian", "praise", "compliment"]: return "compliment" - - # Insult patterns (matching "menghina", "merendahkan", "insult", "name calling", "putting down") - if any(word in label_lower for word in ["insult", "derogatory", "hinaan", "merendahkan", "name calling", "putting down"]): + elif label_lower in ["hinaan", "insult"]: return "insult" - - # Romantic interest / Love patterns (matching "declaring", "love", "cinta", "romantic", "minat romantis") - if any(word in label_lower for word in ["romantic", "love", "cinta", "minat romantis", "declaring", "expressing love"]): + elif label_lower in ["cinta", "love"]: return "romantic_interest" - - # Affection patterns (matching "sayang", "perhatian", "kasih", "affection", "care", "tenderness", "emotional attachment") - if any(word in label_lower for word in ["affection", "attachment", "kasih", "sayang", "keterikatan", "emotional", "care", "tenderness", "perhatian"]): + elif label_lower in ["sayang", "affection"]: return "affection" - - # Question patterns (matching "tanya", "informasi", "pertanyaan", "question", "asking", "seeking") - if any(word in label_lower for word in ["question", "asking", "pertanyaan", "tanya", "seeking", "informasi", "penjelasan"]): + elif label_lower in ["pertanyaan", "question"]: return "question" - - # Toxic behavior patterns (matching "ancam", "bahaya", "jahat", "threat", "dangerous", "toxic") - if any(word in label_lower for word in ["toxic", "threatening", "threat", "dangerous", "ancam", "bahaya", "jahat", "harmful"]): + elif label_lower in ["ancaman", "threat"]: return "toxic_behavior" - - # Rudeness patterns (matching "kasar", "sopan", "menyerang", "rude", "impolite", "crude") - if any(word in label_lower for word in ["rude", "impolite", "crude", "kasar", "tidak sopan", "menyerang", "cursing"]): + elif label_lower in ["kasar", "rude"]: return "rudeness" - - # Asking about bot/person patterns (matching "bot", "spesifik", "personal") - if any(word in label_lower for word in ["bot", "person", "penanya", "about the", "spesifik", "personal"]): + elif label_lower in ["tentang bot", "about bot"]: return "asking_about_alya" - - # Remembering patterns (matching "referensikan", "mengingat", "percakapan", "remembering", "past", "previous", "chats") - if any(word in label_lower for word in ["remembering", "past", "mengingat", "masa lalu", "referencing", "percakapan", "referensikan", "previous", "chats"]): + elif label_lower in ["mengingat", "remember"]: return "remembering_details" - - # Meaningful conversation patterns (matching "diskusi", "mendalam", "penting", "bermakna", "meaningful", "important", "substantive") - if any(word in label_lower for word in ["meaningful", "substantive", "bermakna", "substansial", "discussion", "diskusi", "mendalam", "penting", "important"]): + elif label_lower in ["diskusi bermakna", "meaningful"]: return "meaningful_conversation" - - # Default to normal - return "normal" + else: + return "normal" def _detect_relationship_signals(self, text: str, emotion: str, intent: str) -> Dict[str, float]: """Detect relationship signals (positive/negative behaviors). From 22deef517364235af81078d717da916f81fe29fb Mon Sep 17 00:00:00 2001 From: Afdaan Date: Wed, 22 Oct 2025 02:55:14 +0700 Subject: [PATCH 5/6] feat: simplify and enhance intent detection labels for improved clarity and multilingual support --- core/nlp.py | 144 +++++++++++++++++++++++++--------------------------- 1 file changed, 70 insertions(+), 74 deletions(-) diff --git a/core/nlp.py b/core/nlp.py index 5c6285d..e593f30 100644 --- a/core/nlp.py +++ b/core/nlp.py @@ -161,26 +161,26 @@ def get_message_context(self, text: str, user_id: int = None) -> Dict[str, Any]: def _detect_intent(self, text: str, user_id: int = None) -> str: """Detect user's intent from message text using zero-shot classification. - Uses zero-shot classification for semantic intent detection with multilingual support - (Indonesian & English): - - gratitude: thanks, appreciation - - apology: sorry, apologetic - - greeting: hello, casual opening - - compliment: praise, positive feedback - - insult: derogatory, negative - - romantic_interest: romantic signals - - affection: emotional attachment - - question: information seeking - - toxic_behavior: harmful, bullying - - rudeness: impolite language - - asking_about_alya: questions about Alya - - remembering_details: referencing past conversations - - meaningful_conversation: substantive discussion - - normal: default/neutral + Uses zero-shot ML for semantic intent detection (no hardcoded keywords). + Supports multilingual intent detection (Indonesian & English) with + simplified, distinct labels for model clarity. + + Intent categories: + - gratitude: thanks, appreciation, terima kasih + - apology: sorry, apologetic, maaf + - greeting: hello, casual opening, salam + - compliment: praise, positive feedback, pujian + - insult: derogatory, negative remarks, hinaan + - affection: emotional attachment, sayang + - romantic: romantic interest, cinta, jatuh cinta + - question: information seeking, pertanyaan + - toxic: harmful, bullying, threats, ancaman + - rude: impolite language, kasar + - normal: neutral, everyday conversation Args: text: User's message text - user_id: User ID for caching + user_id: User ID for caching (optional) Returns: str: Detected intent category @@ -199,65 +199,67 @@ def _detect_intent(self, text: str, user_id: int = None) -> str: # Get user language for bilingual label selection lang = DEFAULT_LANGUAGE if user_id: - user_settings = db_manager.get_user_settings(user_id) - lang = user_settings.get("language", DEFAULT_LANGUAGE) - - # Define candidate intent labels (bilingual support) - # Labels kept simple and distinct to avoid model confusion + try: + user_settings = db_manager.get_user_settings(user_id) + lang = user_settings.get("language", DEFAULT_LANGUAGE) + except Exception as e: + logger.debug(f"Could not get user language for {user_id}: {e}") + + # Define candidate intent labels - SIMPLIFIED & DISTINCT for clarity + # Using multi-word descriptive labels to make intent clear to model if lang == "id": candidate_labels = [ - "terima kasih", - "maaf", - "salam", - "pujian", - "hinaan", - "cinta", - "sayang", - "pertanyaan", - "ancaman", - "kasar", - "tentang bot", - "mengingat", - "diskusi bermakna", - "obrolan biasa" + "mengucapkan terima kasih", + "meminta maaf", + "memberi salam", + "memberikan pujian", + "menghina atau mengatai", + "menunjukkan kasih sayang", + "menunjukkan minat romantis", + "mengajukan pertanyaan", + "membuat ancaman atau menghina parah", + "berbicara dengan tidak sopan", + "percakapan biasa" ] else: # English candidate_labels = [ - "thanks", - "sorry", - "hello", - "praise", - "insult", - "love", - "affection", - "question", - "threat", - "rude", - "about bot", - "remember", - "meaningful", - "normal" + "expressing thanks", + "apologizing", + "greeting", + "giving compliment", + "insulting or calling names", + "showing affection", + "showing romantic interest", + "asking question", + "threatening or severe insult", + "speaking rudely", + "normal conversation" ] try: - # Use zero-shot classification + # Use zero-shot classification (purely ML-based, no keyword fallback) result = self.zero_shot_classifier( text, candidate_labels, multi_label=False ) - # Check if top result meets confidence threshold - if result and result.get("scores") and result["scores"][0] >= ZERO_SHOT_CONFIDENCE_THRESHOLD: - # Map classification result back to intent + if result and result.get("scores"): + top_score = result["scores"][0] top_label = result["labels"][0] + + # Map to intent category intent = self._map_label_to_intent(top_label, lang) + + # Cache result self._intent_cache[text_hash] = (intent, time.time()) + + logger.debug( + f"Intent detection: '{text[:50]}...' → {intent} " + f"(confidence: {top_score:.2f})" + ) return intent else: - # Fallback if confidence is too low - logger.debug(f"Intent confidence too low for: {text}") - self._intent_cache[text_hash] = ("normal", time.time()) return "normal" except Exception as e: @@ -276,33 +278,27 @@ def _map_label_to_intent(self, label: str, lang: str = "en") -> str: """ label_lower = label.lower().strip() - # Direct matching for simple labels - if label_lower in ["terima kasih", "thanks", "thank"]: + # Map zero-shot labels to intent categories + if "terima kasih" in label_lower or "thanks" in label_lower or "expressing thanks" in label_lower: return "gratitude" - elif label_lower in ["maaf", "sorry", "apology"]: + elif "maaf" in label_lower or "apologizing" in label_lower or "sorry" in label_lower: return "apology" - elif label_lower in ["salam", "hello", "greeting"]: + elif "salam" in label_lower or "greeting" in label_lower or "hello" in label_lower: return "greeting" - elif label_lower in ["pujian", "praise", "compliment"]: + elif "pujian" in label_lower or "compliment" in label_lower or "praise" in label_lower: return "compliment" - elif label_lower in ["hinaan", "insult"]: + elif "hinaan" in label_lower or "insulting" in label_lower or "insult" in label_lower or "calling names" in label_lower: return "insult" - elif label_lower in ["cinta", "love"]: + elif "cinta" in label_lower or "romantic" in label_lower or "love" in label_lower or "romantic interest" in label_lower: return "romantic_interest" - elif label_lower in ["sayang", "affection"]: + elif "sayang" in label_lower or "affection" in label_lower or "showing affection" in label_lower: return "affection" - elif label_lower in ["pertanyaan", "question"]: + elif "pertanyaan" in label_lower or "question" in label_lower or "asking question" in label_lower: return "question" - elif label_lower in ["ancaman", "threat"]: + elif "ancaman" in label_lower or "threatening" in label_lower or "threat" in label_lower or "severe insult" in label_lower: return "toxic_behavior" - elif label_lower in ["kasar", "rude"]: + elif "kasar" in label_lower or "rude" in label_lower or "rudely" in label_lower or "not sopan" in label_lower: return "rudeness" - elif label_lower in ["tentang bot", "about bot"]: - return "asking_about_alya" - elif label_lower in ["mengingat", "remember"]: - return "remembering_details" - elif label_lower in ["diskusi bermakna", "meaningful"]: - return "meaningful_conversation" else: return "normal" From 04677ab8ce58aadb92896cd6ce754d9fce95242d Mon Sep 17 00:00:00 2001 From: Afdaan Date: Wed, 22 Oct 2025 03:13:11 +0700 Subject: [PATCH 6/6] feat: enhance affection tracking with detailed logging of relationship signals and user interactions --- core/nlp.py | 24 ++++++++++++++++++++++++ handlers/conversation.py | 30 +++++++++++++++++++++++++----- handlers/response/stats.py | 24 +++++++----------------- 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/core/nlp.py b/core/nlp.py index e593f30..7907adf 100644 --- a/core/nlp.py +++ b/core/nlp.py @@ -151,6 +151,13 @@ def get_message_context(self, text: str, user_id: int = None) -> Dict[str, Any]: relationship_signals = self._detect_relationship_signals(text, emotion, intent) directed_at_alya = self._is_directed_at_alya(text) + # Log analysis results for affection tracking + logger.info( + f"[NLP] User {user_id} message context: " + f"emotion={emotion}, intent={intent}, " + f"signals={relationship_signals}, directed_at_alya={directed_at_alya}" + ) + return { "emotion": emotion, "intent": intent, @@ -325,37 +332,54 @@ def _detect_relationship_signals(self, text: str, emotion: str, intent: str) -> } text_lower = text.lower() + signal_reasons = [] # Track why signals were assigned # Friendliness signals if intent in ["gratitude", "compliment", "greeting", "affection"]: signals["friendliness"] = 1 + signal_reasons.append(f"friendliness=1.0 (intent={intent})") elif emotion in ["joy", "happiness", "love"]: signals["friendliness"] = 0.8 + signal_reasons.append(f"friendliness=0.8 (emotion={emotion})") elif intent in ["asking_about_alya", "meaningful_conversation", "remembering_details"]: signals["friendliness"] = 0.7 + signal_reasons.append(f"friendliness=0.7 (intent={intent})") elif emotion in ["sadness", "fear", "worry"]: # Showing vulnerability builds connection signals["friendliness"] = 0.5 + signal_reasons.append(f"friendliness=0.5 (vulnerable emotion={emotion})") elif intent in ["insult", "rudeness", "toxic_behavior"]: signals["conflict"] = 1 + signal_reasons.append(f"conflict=1.0 (negative intent={intent})") # Romantic interest signals if intent == "affection": signals["romantic_interest"] = 1 + signal_reasons.append(f"romantic=1.0 (intent=affection)") elif intent == "romantic_interest": signals["romantic_interest"] = 0.8 + signal_reasons.append(f"romantic=0.8 (intent=romantic_interest)") elif any(word in text_lower for word in ["jadi pacarnya", "pacaran", "istri", "suami", "marry"]): signals["romantic_interest"] = 0.9 + signal_reasons.append(f"romantic=0.9 (romantic keywords detected)") elif emotion == "love": signals["romantic_interest"] = 0.6 + signal_reasons.append(f"romantic=0.6 (emotion=love)") # Conflict signals if emotion in ["anger", "frustration", "disgust"]: signals["conflict"] = 0.7 + signal_reasons.append(f"conflict=0.7 (emotion={emotion})") elif intent in ["insult", "rudeness", "toxic_behavior"]: signals["conflict"] = 1 + signal_reasons.append(f"conflict=1.0 (intent={intent})") elif intent == "apology": signals["conflict"] = -0.5 # Resolve conflict + signal_reasons.append(f"conflict=-0.5 (apology resolves conflict)") + + # Log signal detection for affection tracking + if signal_reasons: + logger.debug(f"[NLP] Relationship signals detected: {', '.join(signal_reasons)}") return signals diff --git a/handlers/conversation.py b/handlers/conversation.py index 0ff79f7..9acab26 100644 --- a/handlers/conversation.py +++ b/handlers/conversation.py @@ -533,9 +533,20 @@ def _update_affection_from_context(self, user_id: int, message_context: Dict[str # === RELATIONSHIP SIGNALS === relationship_signals = message_context.get("relationship_signals", {}) - affection_delta += relationship_signals.get("friendliness", 0) * AFFECTION_POINTS.get("friendliness", 6) - affection_delta += relationship_signals.get("romantic_interest", 0) * AFFECTION_POINTS.get("romantic_interest", 10) - affection_delta += relationship_signals.get("conflict", 0) * AFFECTION_POINTS.get("conflict", -3) + signal_delta = 0 + signal_delta += relationship_signals.get("friendliness", 0) * AFFECTION_POINTS.get("friendliness", 6) + signal_delta += relationship_signals.get("romantic_interest", 0) * AFFECTION_POINTS.get("romantic_interest", 10) + signal_delta += relationship_signals.get("conflict", 0) * AFFECTION_POINTS.get("conflict", -3) + + if signal_delta != 0: + logger.debug( + f"[AFFECTION] User {user_id} signal bonuses: " + f"friendliness={relationship_signals.get('friendliness', 0):.1f}*6={relationship_signals.get('friendliness', 0) * 6:.1f}, " + f"romantic={relationship_signals.get('romantic_interest', 0):.1f}*10={relationship_signals.get('romantic_interest', 0) * 10:.1f}, " + f"conflict={relationship_signals.get('conflict', 0):.1f}*(-3)={relationship_signals.get('conflict', 0) * -3:.1f}" + ) + + affection_delta += signal_delta # Apply minimum penalty limit min_penalty = AFFECTION_POINTS.get("min_penalty", -4) @@ -549,6 +560,15 @@ def _update_affection_from_context(self, user_id: int, message_context: Dict[str # Apply affection change if significant enough if abs(affection_delta) >= 1: affection_delta = round(affection_delta) - logger.debug(f"Updating affection for user {user_id}: {affection_delta:+d} points") + logger.info( + f"[AFFECTION] User {user_id}: {affection_delta:+d} points | " + f"Breakdown: emotion={emotion}, intent={intent}, " + f"signals={relationship_signals}" + ) # Note: update_affection() automatically recalculates relationship level using max(affection_level, interaction_level) - self.db.update_affection(user_id, affection_delta) \ No newline at end of file + self.db.update_affection(user_id, affection_delta) + else: + logger.debug( + f"[AFFECTION] User {user_id}: no significant change " + f"(delta={affection_delta:.1f})" + ) \ No newline at end of file diff --git a/handlers/response/stats.py b/handlers/response/stats.py index e624aac..c6abf64 100644 --- a/handlers/response/stats.py +++ b/handlers/response/stats.py @@ -80,15 +80,13 @@ def get_stats_response(lang: Literal['id', 'en'] = DEFAULT_LANGUAGE, db_manager: } stats = { "total_messages": interaction_count, - "positive_interactions": 0, - "negative_interactions": 0, "role": role_name } else: # Default values for new users relationship = {"level": 0, "name": "Stranger", "progress_percent": 0, "next_level_at_interaction": 50, "next_level_at_affection": 100} affection = {"points": 0, "progress_percent": 0} - stats = {"total_messages": 0, "positive_interactions": 0, "negative_interactions": 0, "role": "User"} + stats = {"total_messages": 0, "role": "User"} user_data = { "name": user_name, @@ -103,7 +101,7 @@ def get_stats_response(lang: Literal['id', 'en'] = DEFAULT_LANGUAGE, db_manager: "name": "User", "relationship": {"level": 1, "name": "Acquaintance", "progress_percent": 50.0, "next_level_at_interaction": 100, "next_level_at_affection": 200}, "affection": {"points": 75, "progress_percent": 37.5}, - "stats": {"total_messages": 150, "positive_interactions": 0, "negative_interactions": 0, "role": "User"} + "stats": {"total_messages": 150, "role": "User"} } name = user_data["name"] @@ -137,10 +135,8 @@ def get_level_emoji(level: int) -> str: interactions_text = "interaksi" affection_req_text = "affection" max_level_text = "Level maksimal tercapai! 🎉" - interaction_stats_text = "📊 Interaksi:" - total_messages_text = "📨 Total Pesan:" - positive_interactions_text = "😊 Interaksi Positif:" - negative_interactions_text = "😠 Interaksi Negatif:" + interaction_stats_text = "📊 Statistik:" + total_messages_text = "📨 Total Interaksi:" else: level_footers = { 0: ["Hmm? W-why do you want to know? Alya doesn't know you yet! 😤", "Alya is not familiar with you yet, don't get too full of yourself... 😳"], @@ -157,10 +153,8 @@ def get_level_emoji(level: int) -> str: interactions_text = "interactions" affection_req_text = "affection" max_level_text = "Max level reached! 🎉" - interaction_stats_text = "📊 Interactions:" - total_messages_text = "📨 Total Messages:" - positive_interactions_text = "😊 Positive Interactions:" - negative_interactions_text = "😠 Negative Interactions:" + interaction_stats_text = "📊 Statistics:" + total_messages_text = "📨 Total Interactions:" level = relationship.get('level', 0) level_name = level_names.get(level, "Unknown") @@ -174,8 +168,6 @@ def get_level_emoji(level: int) -> str: # Extract interaction stats total_messages = stats.get('total_messages', 0) - positive_interactions = stats.get('positive_interactions', 0) - negative_interactions = stats.get('negative_interactions', 0) footer = random.choice(level_footers.get(level, ["..."])) level_emoji = get_level_emoji(level) @@ -193,8 +185,6 @@ def get_level_emoji(level: int) -> str: f"💕 {affection_text}: {affection_points}\n" f"{progress_bar(affection_percent)} {affection_percent:.1f}%\n\n" f"{interaction_stats_text}\n" - f"├ {total_messages_text} {total_messages}\n" - f"├ {positive_interactions_text} {positive_interactions}\n" - f"└ {negative_interactions_text} {negative_interactions}\n\n" + f"└ {total_messages_text} {total_messages}\n\n" f"{footer}" ) \ No newline at end of file