metropolis-chess / app /models /evolution.py
Forkei's picture
Phase 4.0–4.3: demo prep — auth, Elo fairness, rooms, lobbies, conversational entry, evolution
7bad702
"""Phase 4.3 — character evolution state.
One row per character (lazy-created on first post-match evolution run).
Holds cumulative drift across all post-match applications:
- slider_drift: signed deltas on the four personality sliders
- opening_scores: per-opening EMA of result signal in [-1, +1]
- trap_memory: list of trap patterns with fell_for / avoided counters
- tone_drift: confidence + tilt baselines (fed into MoodState initialisation)
See docs/phase_4_evolution.md for the math.
"""
from __future__ import annotations
from datetime import datetime
from typing import Any
from sqlalchemy import JSON, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base
def _now() -> datetime:
return datetime.utcnow()
class CharacterEvolutionState(Base):
__tablename__ = "character_evolution_state"
# Primary key = character_id (one row per character). FK enforces
# deletion cleanup when a character is removed.
character_id: Mapped[str] = mapped_column(
String(36),
ForeignKey("characters.id", ondelete="CASCADE"),
primary_key=True,
)
slider_drift: Mapped[dict[str, float]] = mapped_column(
JSON, nullable=False, default=dict
)
opening_scores: Mapped[dict[str, float]] = mapped_column(
JSON, nullable=False, default=dict
)
trap_memory: Mapped[list[dict[str, Any]]] = mapped_column(
JSON, nullable=False, default=list
)
tone_drift: Mapped[dict[str, float]] = mapped_column(
JSON, nullable=False, default=dict
)
matches_processed: Mapped[int] = mapped_column(
Integer, nullable=False, default=0, server_default="0"
)
# Idempotency guard — we skip re-running evolution on a match we've
# already processed. Nullable on first-ever application.
last_match_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
last_updated_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=_now, onupdate=_now
)