# Mood System Deep Dive The mood system gives the bot emotional states that evolve over time and affect how it responds to users. ## Psychological Model The mood system uses the **Valence-Arousal Model** from affective psychology: ``` High Arousal (+1) │ Annoyed │ Excited ● │ ● │ Curious │ ● │ Low Valence ────────────────┼──────────────── High Valence (-1) │ (+1) │ Bored │ Happy ● │ ● │ Calm │ ● │ │ Low Arousal (-1) ``` ### Dimensions **Valence** (-1 to +1) - Represents the positive/negative quality of the emotional state - -1 = Very negative (sad, frustrated, upset) - 0 = Neutral - +1 = Very positive (happy, joyful, content) **Arousal** (-1 to +1) - Represents the energy level or activation - -1 = Very low energy (calm, sleepy, relaxed) - 0 = Neutral energy - +1 = Very high energy (excited, alert, agitated) --- ## Mood Labels The system classifies the current mood into seven labels: | Label | Valence | Arousal | Description | |-------|---------|---------|-------------| | **Excited** | > 0.3 | > 0.3 | High energy, positive emotions | | **Happy** | > 0.3 | ≤ 0.3 | Positive but calm contentment | | **Calm** | -0.3 to 0.3 | < -0.3 | Peaceful, serene state | | **Neutral** | -0.3 to 0.3 | -0.3 to 0.3 | Baseline, unremarkable state | | **Bored** | < -0.3 | ≤ 0.3 | Low engagement, understimulated | | **Annoyed** | < -0.3 | > 0.3 | Frustrated, irritated | | **Curious** | -0.3 to 0.3 | > 0.3 | Interested, engaged, questioning | ### Classification Logic ```python def _classify_mood(valence: float, arousal: float) -> MoodLabel: if valence > 0.3: return MoodLabel.EXCITED if arousal > 0.3 else MoodLabel.HAPPY elif valence < -0.3: return MoodLabel.ANNOYED if arousal > 0.3 else MoodLabel.BORED else: if arousal > 0.3: return MoodLabel.CURIOUS elif arousal < -0.3: return MoodLabel.CALM return MoodLabel.NEUTRAL ``` --- ## Mood Intensity Intensity measures how strong the current mood is: ```python intensity = (abs(valence) + abs(arousal)) / 2 ``` - **0.0 - 0.2**: Very weak, doesn't affect behavior - **0.2 - 0.5**: Moderate, subtle behavioral changes - **0.5 - 0.7**: Strong, noticeable behavioral changes - **0.7 - 1.0**: Very strong, significant behavioral changes --- ## Time Decay Mood naturally decays toward neutral over time: ```python hours_since_update = (now - last_update).total_seconds() / 3600 decay_factor = max(0, 1 - (decay_rate * hours_since_update)) current_valence = stored_valence * decay_factor current_arousal = stored_arousal * decay_factor ``` **Configuration:** - `MOOD_DECAY_RATE` = 0.1 (default) - After 10 hours, mood is fully neutral **Decay Examples:** | Hours | Decay Factor | Effect | |-------|--------------|--------| | 0 | 1.0 | Full mood | | 2 | 0.8 | 80% of mood remains | | 5 | 0.5 | 50% of mood remains | | 10 | 0.0 | Fully neutral | --- ## Mood Updates When an interaction occurs, mood is updated: ```python new_valence = current_valence + (sentiment_delta * 0.3) new_arousal = current_arousal + (engagement_delta * 0.3) ``` ### Dampening (Inertia) Changes are dampened by 70% (only 30% absorption): - Prevents wild mood swings - Creates emotional stability - Makes mood feel more natural ### Update Triggers | Trigger Type | Sentiment Source | Engagement Source | |--------------|------------------|-------------------| | `conversation` | Message sentiment | Message engagement | | `event` | Event nature | Event importance | | `time` | Scheduled | Scheduled | ### Input Parameters **sentiment_delta** (-1 to +1) - Positive: Happy interactions, compliments, fun conversations - Negative: Arguments, frustration, rude messages - Derived from AI analysis or keyword detection **engagement_delta** (-1 to +1) - Positive: Long conversations, interesting topics, active engagement - Negative: Short dismissive messages, ignored responses - Derived from message length, conversation turns, topic interest --- ## Prompt Modifiers Based on current mood, the system generates prompt text: ### Excited (High valence, High arousal) ``` You're feeling enthusiastic and energetic right now! Be expressive, use exclamation marks, show genuine excitement. ``` ### Happy (High valence, Low arousal) ``` You're in a good mood - warm, friendly, and content. Be positive and encouraging in your responses. ``` ### Calm (Neutral valence, Low arousal) ``` You're feeling peaceful and relaxed. Respond thoughtfully and with a serene demeanor. ``` ### Neutral ``` (No modifier) ``` ### Bored (Low valence, Low arousal) ``` You're feeling a bit understimulated. Keep responses shorter, maybe try to steer toward more interesting topics. ``` ### Annoyed (Low valence, High arousal) ``` You're slightly irritated. Be a bit more terse, less patient with repetition or vague questions. ``` ### Curious (Neutral valence, High arousal) ``` You're feeling inquisitive and engaged! Ask follow-up questions, show genuine interest in what the user is saying. ``` ### Intensity Prefix For strong moods (intensity > 0.7): ``` [Strong mood] You're feeling enthusiastic... ``` --- ## Mood History All mood changes are recorded in `mood_history` table: | Field | Description | |-------|-------------| | `guild_id` | Guild where mood changed | | `valence` | New valence value | | `arousal` | New arousal value | | `trigger_type` | What caused the change | | `trigger_user_id` | Who triggered it (if any) | | `trigger_description` | Description of event | | `recorded_at` | When change occurred | This enables: - Mood trend analysis - Understanding what affects mood - Debugging mood issues - User impact tracking --- ## Bot Statistics The mood service also tracks global statistics: | Statistic | Description | |-----------|-------------| | `total_messages_sent` | Lifetime message count | | `total_facts_learned` | Facts extracted from conversations | | `total_users_known` | Unique users interacted with | | `first_activated_at` | Bot "birth date" | Used for self-awareness and the `!botstats` command. --- ## API Reference ### MoodService ```python class MoodService: def __init__(self, session: AsyncSession) async def get_current_mood( self, guild_id: int | None = None ) -> MoodState async def update_mood( self, guild_id: int | None, sentiment_delta: float, engagement_delta: float, trigger_type: str, trigger_user_id: int | None = None, trigger_description: str | None = None, ) -> MoodState async def increment_stats( self, guild_id: int | None, messages_sent: int = 0, facts_learned: int = 0, users_known: int = 0, ) -> None async def get_stats( self, guild_id: int | None = None ) -> dict def get_mood_prompt_modifier( self, mood: MoodState ) -> str ``` ### MoodState ```python @dataclass class MoodState: valence: float # -1 to 1 arousal: float # -1 to 1 label: MoodLabel # Classified label intensity: float # 0 to 1 ``` --- ## Configuration | Variable | Default | Description | |----------|---------|-------------| | `MOOD_ENABLED` | `true` | Enable/disable mood system | | `MOOD_DECAY_RATE` | `0.1` | Decay per hour toward neutral | --- ## Example Usage ```python from loyal_companion.services.mood_service import MoodService async with get_session() as session: mood_service = MoodService(session) # Get current mood mood = await mood_service.get_current_mood(guild_id=123) print(f"Current mood: {mood.label.value} (intensity: {mood.intensity})") # Update mood after interaction new_mood = await mood_service.update_mood( guild_id=123, sentiment_delta=0.5, # Positive interaction engagement_delta=0.3, # Moderately engaging trigger_type="conversation", trigger_user_id=456, trigger_description="User shared good news" ) # Get prompt modifier modifier = mood_service.get_mood_prompt_modifier(new_mood) print(f"Prompt modifier: {modifier}") await session.commit() ```