from datetime import datetime from typing import Any from sqlalchemy import JSON, BigInteger, DateTime, Integer, Numeric, String, Text from sqlalchemy.orm import Mapped, mapped_column from app.model.base import Base from app.model.profile import ProfileSnapshot __all__ = [ "MemoryUnit", "MemoryEvidence", "MemorySyncOutbox", "MemoryConflict", "ProfileSnapshot", ] class MemoryUnit(Base): """中期记忆单元;字段与 docs/00 第 669-696 行基线一致。 生成列 `active_memory_key` 由数据库计算(仅 status='active' 时返回 memory_key), ORM 不映射该列,唯一键 `(customer_id, active_memory_key)` 负责"每客户每键仅一条有效记忆"。 """ __tablename__ = "memory_unit" id: Mapped[int] = mapped_column(BigInteger, primary_key=True) memory_uuid: Mapped[str] = mapped_column(String(36), unique=True, nullable=False) customer_id: Mapped[int] = mapped_column(BigInteger, nullable=False) memory_key: Mapped[str] = mapped_column(String(128), nullable=False) content: Mapped[str] = mapped_column(Text, nullable=False) structured_value: Mapped[dict[str, Any] | None] = mapped_column(JSON) memory_type: Mapped[str] = mapped_column(String(24), nullable=False) source_type: Mapped[str] = mapped_column(String(24), nullable=False) source_confidence: Mapped[float] = mapped_column(Numeric(5, 4), nullable=False) confidence: Mapped[float] = mapped_column(Numeric(5, 4), nullable=False) evidence_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0) conflict_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0) recall_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0) status: Mapped[str] = mapped_column(String(16), nullable=False) valid_from: Mapped[datetime] = mapped_column(DateTime, nullable=False) valid_until: Mapped[datetime | None] = mapped_column(DateTime) last_evidenced_at: Mapped[datetime | None] = mapped_column(DateTime) last_recall_at: Mapped[datetime | None] = mapped_column(DateTime) promoted_at: Mapped[datetime | None] = mapped_column(DateTime) version: Mapped[int] = mapped_column(BigInteger, nullable=False, default=1) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class MemoryEvidence(Base): """记忆证据;`idempotency_key` 唯一键是重复消费的幂等边界。""" __tablename__ = "memory_evidence" id: Mapped[int] = mapped_column(BigInteger, primary_key=True) memory_id: Mapped[int] = mapped_column(BigInteger, nullable=False) evidence_type: Mapped[str] = mapped_column(String(24), nullable=False) source_table: Mapped[str | None] = mapped_column(String(64)) source_record_id: Mapped[str | None] = mapped_column(String(64)) source_episode_id: Mapped[int | None] = mapped_column(BigInteger) evidence_excerpt: Mapped[str | None] = mapped_column(Text) evidence_snapshot: Mapped[dict[str, Any] | None] = mapped_column(JSON) weight: Mapped[float] = mapped_column(Numeric(5, 4), nullable=False) idempotency_key: Mapped[str] = mapped_column(String(128), unique=True, nullable=False) occurred_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class MemorySyncOutbox(Base): __tablename__ = "memory_sync_outbox" id: Mapped[int] = mapped_column(BigInteger, primary_key=True) event_uuid: Mapped[str] = mapped_column(String(36), nullable=False) aggregate_type: Mapped[str] = mapped_column(String(24), nullable=False) aggregate_uuid: Mapped[str] = mapped_column(String(36), nullable=False) aggregate_version: Mapped[int] = mapped_column(BigInteger, nullable=False) target_store: Mapped[str] = mapped_column(String(16), nullable=False) operation: Mapped[str] = mapped_column(String(16), nullable=False) payload: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False) status: Mapped[str] = mapped_column(String(16), nullable=False) retry_count: Mapped[int] = mapped_column(BigInteger, nullable=False, default=0) next_retry_at: Mapped[datetime | None] = mapped_column(DateTime) last_error: Mapped[str | None] = mapped_column(Text) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) processed_at: Mapped[datetime | None] = mapped_column(DateTime) class MemoryConflict(Base): """记忆冲突;库列名为 `status`,`resolution`/`severity`/`resolved_by` 必须映射。""" __tablename__ = "memory_conflict" id: Mapped[int] = mapped_column(BigInteger, primary_key=True) left_memory_id: Mapped[int] = mapped_column(BigInteger, nullable=False) right_memory_id: Mapped[int] = mapped_column(BigInteger, nullable=False) conflict_type: Mapped[str] = mapped_column(String(24), nullable=False) severity: Mapped[str] = mapped_column(String(8), nullable=False) status: Mapped[str] = mapped_column(String(16), nullable=False) resolution: Mapped[str | None] = mapped_column(Text) winner_memory_id: Mapped[int | None] = mapped_column(BigInteger) resolved_by: Mapped[int | None] = mapped_column(BigInteger) resolved_at: Mapped[datetime | None] = mapped_column(DateTime) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)