Spaces:
Running on Zero
Running on Zero
File size: 6,354 Bytes
599eb60 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | """
memory/models.py β SQLAlchemy ORM models + Pydantic schemas for the memory service.
Tables: episodes, decisions, causal_edges, lessons
Schema matches init.sql exactly.
"""
from __future__ import annotations
from pydantic import ConfigDict
import uuid
from datetime import datetime
from typing import Any, Dict, List, Optional
from pgvector.sqlalchemy import Vector
from pydantic import BaseModel, Field
from sqlalchemy import (
Boolean,
DateTime,
Float,
ForeignKey,
Integer,
String,
Text,
)
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
from memory.db import Base
# ββ ORM Models ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class Episode(Base):
__tablename__ = "episodes"
episode_id: Mapped[str] = mapped_column(UUID(as_uuid=False), primary_key=True, default=lambda: str(uuid.uuid4()))
task_id: Mapped[str] = mapped_column(String, nullable=False)
agent_version: Mapped[str] = mapped_column(String, nullable=False)
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
ended_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
outcome: Mapped[Optional[str]] = mapped_column(String, nullable=True)
final_reward: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
resolution_summary: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
decisions: Mapped[List["Decision"]] = relationship(back_populates="episode")
class Decision(Base):
__tablename__ = "decisions"
decision_id: Mapped[str] = mapped_column(UUID(as_uuid=False), primary_key=True, default=lambda: str(uuid.uuid4()))
episode_id: Mapped[str] = mapped_column(UUID(as_uuid=False), ForeignKey("episodes.episode_id"), nullable=False)
step_index: Mapped[int] = mapped_column(Integer, nullable=False)
state_signature: Mapped[str] = mapped_column(Text, nullable=False)
state_embedding: Mapped[Optional[Any]] = mapped_column(Vector(384), nullable=True)
action_type: Mapped[str] = mapped_column(String, nullable=False)
action_payload: Mapped[Dict] = mapped_column(JSONB, nullable=False, default=dict)
result_stdout: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
result_stderr: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
exit_code: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
credit_label: Mapped[Optional[str]] = mapped_column(String, nullable=True) # backfilled
quarantine_flag: Mapped[bool] = mapped_column(Boolean, default=False)
no_match_flag: Mapped[bool] = mapped_column(Boolean, default=False)
quarantine_reason: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow)
episode: Mapped["Episode"] = relationship(back_populates="decisions")
outgoing_edges: Mapped[List["CausalEdge"]] = relationship(
foreign_keys="CausalEdge.from_decision",
back_populates="from_decision_rel",
)
incoming_edges: Mapped[List["CausalEdge"]] = relationship(
foreign_keys="CausalEdge.to_decision",
back_populates="to_decision_rel",
)
class CausalEdge(Base):
__tablename__ = "causal_edges"
edge_id: Mapped[str] = mapped_column(UUID(as_uuid=False), primary_key=True, default=lambda: str(uuid.uuid4()))
from_decision: Mapped[str] = mapped_column(UUID(as_uuid=False), ForeignKey("decisions.decision_id"), nullable=False)
to_decision: Mapped[str] = mapped_column(UUID(as_uuid=False), ForeignKey("decisions.decision_id"), nullable=False)
relation_type: Mapped[Optional[str]] = mapped_column(String, nullable=True) # caused/preceded/remediated_by
confidence: Mapped[float] = mapped_column(Float, default=1.0)
last_reinforced: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow)
from_decision_rel: Mapped["Decision"] = relationship(foreign_keys=[from_decision], back_populates="outgoing_edges")
to_decision_rel: Mapped["Decision"] = relationship(foreign_keys=[to_decision], back_populates="incoming_edges")
class Lesson(Base):
__tablename__ = "lessons"
lesson_id: Mapped[str] = mapped_column(UUID(as_uuid=False), primary_key=True, default=lambda: str(uuid.uuid4()))
state_cluster_embedding: Mapped[Optional[Any]] = mapped_column(Vector(384), nullable=True)
best_action: Mapped[Optional[Dict]] = mapped_column(JSONB, nullable=True)
outcome_summary: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
sample_count: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
success_rate: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
last_updated: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
# ββ Pydantic schemas (for validation / serialization) ββββββββββββββββββββββββ
class EpisodeSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
episode_id: str
task_id: str
agent_version: str
started_at: datetime
ended_at: Optional[datetime] = None
outcome: Optional[str] = None
final_reward: Optional[float] = None
resolution_summary: Optional[str] = None
class DecisionSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
decision_id: str
episode_id: str
step_index: int
state_signature: str
action_type: str
action_payload: Dict[str, Any]
result_stdout: Optional[str] = None
result_stderr: Optional[str] = None
exit_code: Optional[int] = None
credit_label: Optional[str] = None
quarantine_flag: bool = False
no_match_flag: bool = False
quarantine_reason: Optional[str] = None
class LessonSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
lesson_id: str
best_action: Optional[Dict[str, Any]] = None
outcome_summary: Optional[str] = None
sample_count: Optional[int] = None
success_rate: Optional[float] = None
similarity: Optional[float] = None # set by retriever, not stored
|