"""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 []