Agentic_SRE / memory /models.py
LordofMonarchs's picture
Version-1
599eb60
Raw
History Blame Contribute Delete
6.35 kB
"""
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