# Dynamic Messaging Styles System

**Date:** 2025-11-13
**Status:** Design Complete, Ready for Implementation
**Author:** Claude Code

## Overview

This design adds realistic, personality-driven messaging styles to NPC conversations in BaoLife. Each character will have unique texting patterns that evolve dynamically based on relationship progression, conversations, mood states, and life events.

## Problem Statement

Currently, all NPCs communicate in similar ways. The AI generates responses based on relationship type (partner, friend, boss) and basic affinity levels, but lacks individual personality in how characters actually text. Real people have distinct messaging styles - some overshare, some ask lots of questions, some barely respond, some spam emojis. This system makes conversations feel more authentic and characters feel more alive.

## Goals

1. **Personality-driven communication**: Each character texts differently based on their traits
2. **Dynamic evolution**: Messaging styles change based on relationship progression and context
3. **Contextual awareness**: Characters respond differently when stressed, happy, or discussing topics they care about
4. **Natural variety**: Create diverse, realistic conversation patterns like real-life messaging apps

## Core Design

### Trait System

Characters have **8 core messaging trait dimensions**, each rated 0-100:

| Trait | Low (0-30) | High (70-100) |
|-------|-----------|---------------|
| **Verbosity** | Brief, concise responses ("k", "cool") | Rambling, detailed messages with lots of context |
| **Inquisitiveness** | Never asks questions, passive | Constantly curious, asks follow-ups |
| **Expressiveness** | Dry, flat tone | Animated, enthusiastic, exclamation marks |
| **Responsiveness** | Low effort, kills conversation | Engaged, builds on topics, matches energy |
| **Openness** | Guarded, surface-level only | Overshares, volunteers personal details |
| **Emoji Usage** | Never uses emojis | Emoji-heavy messages (2-4+ per message) |
| **Formality** | Txtspeak, lowercase, no punctuation | Proper grammar, spelling, punctuation |
| **Response Timing** | Ghosts for hours/days | Instant replies |

### Three-Layer Architecture

```
┌─────────────────────────────────────────┐
│     Effective Trait Value (0-100)       │
│  = Base + Relationship Modifier + Mood  │
└─────────────────────────────────────────┘
            ↓
┌─────────────────────────────────────────┐
│      AI Prompt Generation               │
│  Convert traits → natural language      │
└─────────────────────────────────────────┘
            ↓
┌─────────────────────────────────────────┐
│      Character Response                 │
│  AI follows style instructions          │
└─────────────────────────────────────────┘
```

#### Layer 1: Base Traits (personClass)

The character's natural personality, set at creation:

```python
self.messaging_traits = {
    'verbosity': random.randint(30, 70),
    'inquisitiveness': random.randint(20, 80),
    'expressiveness': random.randint(20, 80),
    'responsiveness': random.randint(40, 80),
    'openness': random.randint(20, 60),
    'emoji_usage': random.randint(10, 90),
    'formality': random.randint(20, 80),
    'response_timing': random.randint(40, 85)
}

self.messaging_patterns = {
    'time_of_day_preference': random.choice(['morning', 'night', 'neutral']),
    'weekend_availability': random.randint(30, 90),
    'typing_style': random.choice(['proper', 'casual', 'chaotic'])
}
```

**Examples:**
- **Girlfriend**: verbosity 70, inquisitiveness 80, expressiveness 90, emoji_usage 85
- **Boss**: verbosity 40, inquisitiveness 30, expressiveness 20, formality 90
- **Shy classmate**: verbosity 20, inquisitiveness 10, responsiveness 35, openness 5

#### Layer 2: Relationship Modifiers (relationshipClass)

How they text *you specifically*, evolves over time (-40 to +40 range):

```python
self.messaging_modifiers = {
    # Permanent/evolving modifiers (change gradually)
    'verbosity': 0,
    'inquisitiveness': 0,
    'expressiveness': 0,
    'responsiveness': 0,
    'openness': 0,
    'emoji_usage': 0,
    'formality': 0,
    'response_timing': 0,

    # Contextual state (changes frequently)
    'mood_state': 'neutral',  # 'great', 'bad', 'stressed', 'neutral'
    'current_topic_engagement': 0,  # -30 to +30, per-message
}
```

**Example Evolution:**
```
New classmate → After 3 months of friendship → After big fight → 2 weeks later
Modifiers: 0    Modifiers: +15 openness     Modifiers: -25     Modifiers: -10
                           +10 responsiveness  responsiveness    responsiveness
                           +8 expressiveness    -15 openness      -5 openness
```

#### Layer 3: Mood Modifiers (Temporary)

Current emotional state affects all conversations:

```python
mood_effects = {
    'great': {'expressiveness': +15, 'verbosity': +10, 'emoji_usage': +20},
    'bad': {'expressiveness': -20, 'responsiveness': -15, 'verbosity': -10},
    'stressed': {'responsiveness': -25, 'verbosity': +20, 'openness': -10},
    'neutral': {}
}
```

### Final Calculation

```python
effective_trait = clamp(
    base_trait + relationship_modifier + mood_modifier,
    min=0, max=100
)
```

## AI Prompt Integration

### Trait-to-Instruction Mapping

The system converts numeric traits into natural language instructions for the AI:

```python
def get_messaging_style_prompt(character, relationship, player):
    """
    Generates natural language style instructions from calculated traits.
    """
    effective_traits = calculate_effective_traits(character, relationship)
    instructions = []

    # Verbosity
    if effective_traits['verbosity'] < 30:
        instructions.append("Keep responses extremely brief - 1-2 short sentences max, often just a few words.")
    elif effective_traits['verbosity'] > 70:
        instructions.append("You tend to ramble and share lots of details. Don't hold back - elaborate freely.")

    # Inquisitiveness
    if effective_traits['inquisitiveness'] < 30:
        instructions.append("You rarely ask questions. Just respond without pushing the conversation forward.")
    elif effective_traits['inquisitiveness'] > 70:
        instructions.append("You're naturally curious - frequently ask follow-up questions and show genuine interest.")

    # ... similar for other traits ...

    return " ".join(instructions)
```

### Prompt Injection Point

In `getOpenAIResponse()`:

```python
# Calculate messaging style for this character + relationship
messaging_style = get_messaging_style_prompt(character, relationship, player)

# Build prompt with style instructions
prompt_template = (
    f"{verbosity_hint}\n\n"
    "You are not an assistant. You are a real person having a text conversation. "
    f"{relationship_config['prompt']} "
    f"{relationship_config['boundaries']} "
    f"{messaging_style} "  # <-- INJECT HERE
    "Maintain a {affinity_description} attitude..."
)
```

**Example Prompt Snippet:**
> "You tend to ramble and share lots of details. You're naturally curious - frequently ask follow-up questions. Be animated and enthusiastic! Use exclamation marks, show excitement. Use emojis frequently to express yourself (2-4 per message is normal). Text casually - lowercase, abbreviations (lol, idk, ur), minimal punctuation."

## Evolution System

### During Conversations

After each AI response, modifiers update based on sentiment:

```python
def update_conversation_messaging_modifiers(relationship, sentiment, conversation_length):
    """Update modifiers based on conversation dynamics."""

    if sentiment == "positive":
        relationship.messaging_modifiers['openness'] += 1
        relationship.messaging_modifiers['expressiveness'] += 1
        relationship.messaging_modifiers['responsiveness'] += 1

    elif sentiment == "negative":
        relationship.messaging_modifiers['responsiveness'] -= 3
        relationship.messaging_modifiers['openness'] -= 2
        relationship.messaging_modifiers['expressiveness'] -= 2

    # Long conversations increase comfort
    if conversation_length > 20:
        relationship.messaging_modifiers['verbosity'] += 1
        relationship.messaging_modifiers['formality'] -= 1  # Get more casual

    # Cap modifiers at -40 to +40
    for key in relationship.messaging_modifiers:
        if isinstance(relationship.messaging_modifiers[key], (int, float)):
            relationship.messaging_modifiers[key] = max(-40, min(40, relationship.messaging_modifiers[key]))
```

### External Event Triggers

Major life events trigger significant modifier changes:

| Event | Modifier Changes |
|-------|-----------------|
| **Start dating** | +15 openness, +10 expressiveness, +10 emoji_usage |
| **Breakup** | -40 responsiveness, -30 openness, -20 emoji_usage |
| **Become best friends** | +20 openness, +15 responsiveness, -15 formality |
| **Get in fight** | -25 responsiveness, -15 openness, -10 expressiveness |
| **Graduate college** | mood → 'great' (temporary boost to all traits) |
| **Lose job** | mood → 'stressed' for 2 weeks |
| **Parent dies** | mood → 'bad', -30 all traits temporarily |

### Daily Mood Updates

Characters' mood states update daily based on their life situation:

```python
def update_daily_moods(player):
    """Update mood states based on character's current life situation."""
    for character in player.r:
        if character.stress > 70:
            set_mood_state(character, player, 'stressed')
        elif character.happiness > 80:
            set_mood_state(character, player, 'great')
        elif character.happiness < 30:
            set_mood_state(character, player, 'bad')
        else:
            set_mood_state(character, player, 'neutral')
```

### Time-Based Decay

Relationship modifiers gradually drift back toward 0 over time (relationships normalize). This prevents permanent extremes:

```python
# Weekly decay (run once per week)
def decay_messaging_modifiers(relationship):
    """Gradually return modifiers toward neutral (0)."""
    for trait in relationship.messaging_modifiers:
        if isinstance(relationship.messaging_modifiers[trait], (int, float)):
            current = relationship.messaging_modifiers[trait]
            # Move 10% toward 0 each week
            relationship.messaging_modifiers[trait] = current * 0.9
```

## Implementation Details

### File Structure

```
ws/
├── messaging_style.py              # NEW - Core messaging style logic
├── core/
│   └── models.py                   # MODIFIED - Add traits to personClass/relationshipClass
├── character/
│   └── character_manager.py        # MODIFIED - Initialize traits on character creation
├── conversationEvents.py           # MODIFIED - Integrate style prompts, update modifiers
├── relationships/
│   └── relationship_manager.py     # MODIFIED - Add event triggers
├── events.py                       # MODIFIED - Add mood state changes
└── app.py                          # MODIFIED - Daily mood updates
```

### messaging_style.py API

```python
# Core Functions
def calculate_effective_traits(character, relationship) -> dict:
    """Calculate final trait values: base + modifiers + mood."""

def get_messaging_style_prompt(character, relationship, player) -> str:
    """Generate natural language style instructions for AI prompt."""

def get_mood_modifier(character, mood_state, trait) -> int:
    """Get temporary mood-based adjustment for a trait."""

# Evolution Functions
def update_conversation_messaging_modifiers(relationship, sentiment, conversation_length, topic_engagement=0):
    """Update modifiers after each conversation message."""

def set_mood_state(character, player, mood: str):
    """Set character's mood state (updates relationship modifiers)."""

def decay_messaging_modifiers(relationship):
    """Weekly decay toward neutral (0)."""

# Initialization
def initialize_messaging_traits(character):
    """Assign random base traits to new character."""

def initialize_relationship_modifiers(relationship, character):
    """Initialize relationship-specific starting modifiers."""
```

### Database Schema

**Option 1: JSON Column (Recommended)**

Add JSON columns to existing tables:

```sql
ALTER TABLE persons ADD COLUMN messaging_traits JSON;
ALTER TABLE persons ADD COLUMN messaging_patterns JSON;
ALTER TABLE relationships ADD COLUMN messaging_modifiers JSON;
```

**Option 2: Separate Tables**

Create dedicated tables if more complex querying needed:

```sql
CREATE TABLE messaging_traits (
    person_id VARCHAR(32) PRIMARY KEY,
    verbosity INT,
    inquisitiveness INT,
    expressiveness INT,
    responsiveness INT,
    openness INT,
    emoji_usage INT,
    formality INT,
    response_timing INT,
    FOREIGN KEY (person_id) REFERENCES persons(id)
);
```

### Migration Strategy

For existing characters without messaging traits:

```python
def migrate_existing_characters():
    """Assign random messaging traits to existing characters."""
    from database.db_operations import connect_to_database
    from messaging_style import initialize_messaging_traits

    conn = connect_to_database()
    cursor = conn.cursor(dictionary=True)

    cursor.execute("SELECT id FROM persons WHERE messaging_traits IS NULL")
    persons = cursor.fetchall()

    for person in persons:
        traits = initialize_messaging_traits(None)  # Returns dict
        patterns = {
            'time_of_day_preference': random.choice(['morning', 'night', 'neutral']),
            'weekend_availability': random.randint(30, 90),
            'typing_style': random.choice(['proper', 'casual', 'chaotic'])
        }

        cursor.execute("""
            UPDATE persons
            SET messaging_traits = %s, messaging_patterns = %s
            WHERE id = %s
        """, (json.dumps(traits), json.dumps(patterns), person['id']))

    conn.commit()
```

## Testing Strategy

### Unit Tests

```python
# Test trait calculation
def test_effective_traits():
    character = personClass()
    character.messaging_traits['verbosity'] = 50

    relationship = relationshipClass()
    relationship.messaging_modifiers['verbosity'] = 20
    relationship.messaging_modifiers['mood_state'] = 'great'  # +10 verbosity

    effective = calculate_effective_traits(character, relationship)
    assert effective['verbosity'] == 80  # 50 + 20 + 10

# Test prompt generation
def test_prompt_generation():
    character = create_test_character(verbosity=90, emoji_usage=85)
    relationship = create_test_relationship(openness_modifier=20)

    prompt = get_messaging_style_prompt(character, relationship, player)
    assert "ramble" in prompt.lower()
    assert "emoji" in prompt.lower()

# Test modifier updates
def test_conversation_evolution():
    relationship = relationshipClass()
    initial_openness = relationship.messaging_modifiers['openness']

    # Simulate positive conversation
    for i in range(10):
        update_conversation_messaging_modifiers(relationship, 'positive', i+1)

    assert relationship.messaging_modifiers['openness'] > initial_openness
```

### Integration Tests

1. **Create extreme test characters:**
   - "One-Word Andy": All traits at minimum
   - "Chatty Cathy": All traits at maximum
   - "Emoji Queen": Only emoji_usage at 100
   - "The Ghost": responsiveness at 5

2. **Test conversation flows:**
   - Start conversation with test character
   - Verify AI response matches expected style
   - Send multiple messages, check modifier evolution
   - Trigger event (breakup, promotion), verify style changes

3. **Test mood transitions:**
   - Set character to stressed mood
   - Verify responses become shorter, less engaged
   - Change to great mood, verify boost in expressiveness

### Manual QA Checklist

- [ ] New characters get assigned random traits
- [ ] Traits affect AI conversation style noticeably
- [ ] Positive conversations gradually increase openness/expressiveness
- [ ] Negative conversations decrease responsiveness
- [ ] Relationship events trigger appropriate changes
- [ ] Mood states visibly affect conversation tone
- [ ] Modifiers cap at -40/+40 (don't overflow)
- [ ] Database saves/loads traits correctly
- [ ] Existing characters get migrated properly

## Example Scenarios

### Scenario 1: New Relationship Evolution

```
Day 1 - First message from classmate (shy, base openness=25):
> "hey"

Week 2 - After a few positive chats (openness modifier +5):
> "hey! how was the exam?"

Month 3 - Good friends now (openness +20, expressiveness +15):
> "omg the exam was brutal 😅 did you study for question 5? i totally bombed it lol"

After argument (responsiveness -25, openness -15):
> "fine"

2 weeks later (decay applied, modifiers recovering):
> "im good, you?"
```

### Scenario 2: Mood State Impact

```
Boss (normally formal=90, verbosity=40) on a normal day:
> "The report looks good. Please submit by Friday."

Boss on a stressed day (formality -20, verbosity +20, responsiveness -25):
> "The report... there are issues with the Q3 numbers, I need you to
   fix them and the formatting is off and marketing wants changes too.
   Can you just handle it? I have three meetings."

Boss after company success (mood='great', expressiveness +15):
> "Excellent work on the report! Really impressed with your analysis.
   Keep it up! 👍"
```

### Scenario 3: Partner After Breakup

```
Before breakup (girlfriend, base: expressive, emoji-heavy):
> "miss you babe 💕 can't wait for our date tonight!! 😍"

Immediately after breakup (responsiveness -40, openness -30, emoji -20):
> "k"

Week later (modifiers slowly recovering):
> "thanks for asking. im ok"

Month later (back to acquaintance level):
> "hey! yeah im doing better. how have you been?"
```

## Future Enhancements

### Phase 2 Additions (Not in Initial Implementation)

1. **Learning from Player:**
   - If player uses lots of emojis, character's emoji_usage gradually increases
   - Mirror verbosity levels over long relationships

2. **Topic-Specific Modifiers:**
   - Store per-topic engagement levels
   - Character lights up when discussing their interests, shuts down on dislikes

3. **Personality Archetypes:**
   - Pre-defined style combinations ("The Therapist", "The Meme Lord", "The Ghost")
   - Easy character creation shortcuts

4. **Advanced Timing:**
   - Actually delay AI-initiated messages based on response_timing trait
   - "Seen" indicators and realistic response delays

5. **Group Dynamics:**
   - Different messaging styles in group chats vs 1-on-1
   - Social pressure effects (formal in family group chat)

## Success Metrics

After implementation, success means:

1. **Variety**: Clear, distinct personalities in how different characters text
2. **Evolution**: Noticeable changes as relationships progress
3. **Realism**: Messaging styles feel authentic to real-life texting
4. **Contextual**: Characters respond appropriately to mood and events
5. **Performance**: No significant impact on AI response time or API costs

## Conclusion

This system transforms NPC conversations from generic AI responses into authentic, personality-driven messaging that evolves naturally with relationships. Characters will feel alive, with distinct voices that change based on their life circumstances and how they feel about the player.

The trait slider approach provides maximum flexibility while remaining simple to implement and debug. By injecting natural language style instructions into AI prompts, we leverage the existing conversation system without complex architectural changes.

Ready for implementation.
