""" SQLAlchemy ORM models for player profiles and conversation history. Persistent storage for: - Player profiles (names, stats, relationship state) - Conversation messages (for context retrieval) """ from datetime import datetime from typing import Optional, List from sqlalchemy import create_engine, Column, String, Integer, Float, DateTime, ForeignKey, Text, Index from sqlalchemy.orm import declarative_base from sqlalchemy.orm import sessionmaker, relationship, Session Base = declarative_base() class PlayerProfile(Base): """ Persistent profile for a player across multiple games. Tracks relationship state, skill level, play patterns, and history so the Chess Master can remember and adapt to individual players. """ __tablename__ = "player_profiles" player_id = Column(String(255), primary_key=True) player_name = Column(String(255), nullable=False) # Timeline first_seen = Column(DateTime, default=datetime.now, nullable=False) last_played = Column(DateTime, default=datetime.now, nullable=False) # Stats total_games = Column(Integer, default=0) wins_against_agent = Column(Integer, default=0) losses_against_agent = Column(Integer, default=0) draws = Column(Integer, default=0) # Preferences and skill preferred_difficulty = Column(String(50), default="intermediate") estimated_elo = Column(Integer, default=1400) # Relationship state: "new" -> "familiar" -> "rival" relationship_state = Column(String(50), default="new") # Notes notes = Column(Text, nullable=True) # Relationship to conversations conversations = relationship( "ConversationMessage", back_populates="player", cascade="all, delete-orphan", ) # Timestamps created_at = Column(DateTime, default=datetime.now, nullable=False) updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False) def __repr__(self) -> str: return ( f"" ) def to_dict(self) -> dict: """Convert to dictionary for easy serialization.""" return { "player_id": self.player_id, "player_name": self.player_name, "first_seen": self.first_seen.isoformat() if self.first_seen else None, "last_played": self.last_played.isoformat() if self.last_played else None, "total_games": self.total_games, "wins": self.wins_against_agent, "losses": self.losses_against_agent, "draws": self.draws, "preferred_difficulty": self.preferred_difficulty, "estimated_elo": self.estimated_elo, "relationship_state": self.relationship_state, "notes": self.notes, } def update_last_played(self) -> None: """Update last_played timestamp.""" self.last_played = datetime.now() def record_game(self, outcome: str) -> None: """ Record a game outcome. Args: outcome: "win", "loss", or "draw" """ self.total_games += 1 if outcome == "win": self.wins_against_agent += 1 elif outcome == "loss": self.losses_against_agent += 1 elif outcome == "draw": self.draws += 1 self.update_last_played() def get_win_rate(self) -> float: """Calculate win rate against agent.""" if self.total_games == 0: return 0.0 return self.wins_against_agent / self.total_games def update_relationship(self) -> None: """Update relationship state based on interaction history.""" if self.total_games == 0: self.relationship_state = "new" elif self.total_games < 5: self.relationship_state = "familiar" else: self.relationship_state = "rival" class ConversationMessage(Base): """ A single message in a conversation between Chess Master and a player. Used to store conversation history per player so the agent can reference recent interactions and build relationships. """ __tablename__ = "conversation_messages" id = Column(Integer, primary_key=True) player_id = Column(String(255), ForeignKey("player_profiles.player_id"), nullable=False) timestamp = Column(DateTime, default=datetime.now, nullable=False) speaker = Column(String(50), nullable=False) # "chess_master" or "player" content = Column(Text, nullable=False) # Optional context (game state, move info, etc.) as JSON string context_json = Column(Text, nullable=True) # Relationship to player player = relationship( "PlayerProfile", back_populates="conversations", foreign_keys=[player_id], ) # Timestamp for ordering created_at = Column(DateTime, default=datetime.now, nullable=False) __table_args__ = ( Index("idx_player_timestamp", "player_id", "timestamp"), ) def __repr__(self) -> str: return ( f"" ) def to_dict(self) -> dict: """Convert to dictionary.""" return { "id": self.id, "player_id": self.player_id, "timestamp": self.timestamp.isoformat(), "speaker": self.speaker, "content": self.content, "context": self.context_json, }