Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import enum | |
| import uuid | |
| from datetime import datetime | |
| from typing import TYPE_CHECKING | |
| from sqlalchemy import JSON, DateTime, Enum, Float, ForeignKey, Integer, String, Text | |
| from sqlalchemy.orm import Mapped, mapped_column, relationship | |
| from app.models.base import Base | |
| if TYPE_CHECKING: | |
| from app.models.character import Character | |
| class MemoryScope(str, enum.Enum): | |
| CHARACTER_LORE = "character_lore" | |
| OPPONENT_SPECIFIC = "opponent_specific" | |
| CROSS_PLAYER = "cross_player" | |
| MATCH_RECAP = "match_recap" | |
| class MemoryType(str, enum.Enum): | |
| FORMATIVE = "formative" | |
| RIVALRY = "rivalry" | |
| TRAVEL = "travel" | |
| TRIUMPH = "triumph" | |
| DEFEAT = "defeat" | |
| HABIT = "habit" | |
| OPINION = "opinion" | |
| OBSERVATION = "observation" | |
| # Phase 4.3 — learned patterns the character has picked up over | |
| # matches (e.g. trap patterns that burned them once). Fed into the | |
| # Subconscious's normal retrieval flow. | |
| LEARNING = "learning" | |
| def _uuid() -> str: | |
| return str(uuid.uuid4()) | |
| def _now() -> datetime: | |
| return datetime.utcnow() | |
| class Memory(Base): | |
| __tablename__ = "memories" | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid) | |
| # character_id is NULL for agent-scoped memories (agent_id is set instead). | |
| character_id: Mapped[str | None] = mapped_column( | |
| String(36), ForeignKey("characters.id", ondelete="CASCADE"), nullable=True, index=True | |
| ) | |
| # Block 13: agent-scoped memories have agent_id set, character_id=NULL. | |
| agent_id: Mapped[str | None] = mapped_column( | |
| String(36), ForeignKey("player_agents.id", ondelete="CASCADE"), nullable=True, index=True | |
| ) | |
| # player_id / match_id are not FK'd to real tables yet (those arrive in Phase 2). | |
| # Nullable strings keep the schema future-proof without requiring those tables now. | |
| player_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) | |
| match_id: Mapped[str | None] = mapped_column(String(36), nullable=True, index=True) | |
| scope: Mapped[MemoryScope] = mapped_column(Enum(MemoryScope, name="memory_scope"), nullable=False) | |
| type: Mapped[MemoryType] = mapped_column(Enum(MemoryType, name="memory_type"), nullable=False) | |
| emotional_valence: Mapped[float] = mapped_column(Float, nullable=False, default=0.0) | |
| triggers: Mapped[list[str]] = mapped_column(JSON, nullable=False, default=list) | |
| narrative_text: Mapped[str] = mapped_column(Text, nullable=False) | |
| relevance_tags: Mapped[list[str]] = mapped_column(JSON, nullable=False, default=list) | |
| created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=_now) | |
| last_surfaced_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) | |
| surface_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0) | |
| # Phase 2b: semantic retrieval vector. NULL until the memory is embedded | |
| # (either at creation via `embed_and_persist` or by the backfill script). | |
| embedding: Mapped[list[float] | None] = mapped_column(JSON, nullable=True) | |
| character: Mapped["Character"] = relationship(back_populates="memories") | |