File size: 4,033 Bytes
ee62336
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373ff24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee62336
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""Juror configuration and memory models."""

from dataclasses import dataclass, field
from typing import Literal


@dataclass
class JurorConfig:
    """Configuration for a single juror agent."""

    # Identity
    juror_id: str
    seat_number: int
    name: str
    emoji: str  # For display until sprites ready

    # Personality (affects reasoning style)
    archetype: str  # "rationalist", "empath", "cynic", etc.
    personality_prompt: str  # Detailed persona prompt

    # Behavior modifiers
    stubbornness: float  # 0.0-1.0, how hard to convince
    volatility: float    # 0.0-1.0, how much conviction swings
    influence: float     # 0.0-1.0, how persuasive to others
    verbosity: float = 0.5  # 0.0-1.0, how long their arguments are

    # Initial stance
    initial_lean: str = "neutral"  # "prosecution", "defense", "neutral", etc.

    # Model configuration
    model_provider: str = "gemini"  # "gemini", "openai", "anthropic", "local"
    model_id: str = "gemini-2.5-flash"  # Specific model ID
    temperature: float = 0.7

    # Tools (future expansion)
    tools: list[str] = field(default_factory=list)

    # Memory
    memory_window: int = 10  # How many turns to remember in detail

    def is_player(self) -> bool:
        """Check if this is the player seat."""
        return self.archetype == "player"


@dataclass
class ArgumentMemory:
    """Memory of a single argument heard."""

    speaker_id: str
    content_summary: str
    argument_type: str
    persuasiveness: float  # How convincing it was to this juror
    counter_points: list[str] = field(default_factory=list)  # Thoughts against it
    round_heard: int = 0


@dataclass
class JurorMemory:
    """Memory state for a single juror."""

    juror_id: str

    # Case understanding
    case_summary: str = ""
    key_evidence: list[str] = field(default_factory=list)
    evidence_interpretations: dict[str, str] = field(default_factory=dict)

    # Deliberation memory
    arguments_heard: list[ArgumentMemory] = field(default_factory=list)
    arguments_made: list[str] = field(default_factory=list)

    # Compressed history (replaces old arguments_heard entries)
    deliberation_summary: str = ""

    # Relationships
    opinions_of_others: dict[str, float] = field(default_factory=dict)

    # Internal state
    current_conviction: float = 0.5  # 0.0-1.0
    conviction_history: list[float] = field(default_factory=list)
    reasoning_chain: list[str] = field(default_factory=list)
    doubts: list[str] = field(default_factory=list)

    def get_current_vote(self) -> Literal["guilty", "not_guilty"]:
        """Get vote based on current conviction with hysteresis."""
        # If no history, use simple threshold
        if len(self.conviction_history) < 2:
            return "guilty" if self.current_conviction > 0.5 else "not_guilty"

        # Determine previous vote from previous conviction
        prev_conviction = self.conviction_history[-2]
        prev_vote_guilty = prev_conviction > 0.5

        # Apply hysteresis thresholds to prevent flip-flopping
        if prev_vote_guilty:
            # Currently guilty - need to drop below 0.4 to flip
            return "not_guilty" if self.current_conviction < 0.4 else "guilty"
        else:
            # Currently not guilty - need to rise above 0.6 to flip
            return "guilty" if self.current_conviction > 0.6 else "not_guilty"

    def add_argument(self, argument: ArgumentMemory) -> None:
        """Add a heard argument to memory."""
        self.arguments_heard.append(argument)

    def update_conviction(self, delta: float) -> None:
        """Update conviction score, clamping to valid range."""
        self.current_conviction = max(0.0, min(1.0, self.current_conviction + delta))
        self.conviction_history.append(self.current_conviction)

    def get_recent_arguments(self, count: int = 5) -> list[ArgumentMemory]:
        """Get the most recent arguments heard."""
        return self.arguments_heard[-count:] if self.arguments_heard else []