Spaces:
Running
Running
| """ | |
| Chat & Agent Models — Conversations, messages, AI memory, and AI insights. | |
| """ | |
| import uuid | |
| from datetime import datetime | |
| from typing import Optional | |
| from sqlalchemy import String, Boolean, Text, Integer, Float, Index, ForeignKey | |
| from sqlalchemy.dialects.postgresql import UUID, JSONB | |
| from sqlalchemy.orm import Mapped, mapped_column, relationship | |
| from app.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin | |
| class Conversation(UUIDPrimaryKeyMixin, TimestampMixin, Base): | |
| """Chat conversation history container.""" | |
| __tablename__ = "conversations" | |
| __table_args__ = ( | |
| Index("ix_conversations_user_id", "user_id"), | |
| Index("ix_conversations_created_at", "created_at"), | |
| ) | |
| user_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), | |
| ForeignKey("users.id", ondelete="CASCADE"), | |
| nullable=False, | |
| ) | |
| title: Mapped[str] = mapped_column(String(255), default="New Chat", nullable=False) | |
| mode: Mapped[str] = mapped_column(String(50), default="auto", nullable=False) | |
| is_archived: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) | |
| message_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) | |
| last_message_at: Mapped[Optional[datetime]] = mapped_column(nullable=True) | |
| # Relationships | |
| messages: Mapped[list["Message"]] = relationship( | |
| back_populates="conversation", cascade="all, delete-orphan", lazy="selectin" | |
| ) | |
| def __repr__(self) -> str: | |
| return f"<Conversation id={self.id} title={self.title}>" | |
| class Message(UUIDPrimaryKeyMixin, Base): | |
| """Chat message in a conversation.""" | |
| __tablename__ = "messages" | |
| __table_args__ = ( | |
| Index("ix_messages_conversation_id", "conversation_id"), | |
| Index("ix_messages_created_at", "created_at"), | |
| ) | |
| conversation_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), | |
| ForeignKey("conversations.id", ondelete="CASCADE"), | |
| nullable=False, | |
| ) | |
| user_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), | |
| ForeignKey("users.id", ondelete="CASCADE"), | |
| nullable=False, | |
| ) | |
| role: Mapped[str] = mapped_column(String(20), nullable=False) # user, assistant, system | |
| content: Mapped[str] = mapped_column(Text, nullable=False) | |
| mode: Mapped[Optional[str]] = mapped_column(String(50), nullable=True) | |
| sources: Mapped[dict] = mapped_column(JSONB, default=list, nullable=False) | |
| metadata_json: Mapped[dict] = mapped_column(JSONB, default=dict, nullable=False) | |
| created_at: Mapped[datetime] = mapped_column( | |
| nullable=False, server_default="now()" | |
| ) | |
| # Relationships | |
| conversation: Mapped["Conversation"] = relationship(back_populates="messages") | |
| def __repr__(self) -> str: | |
| return f"<Message id={self.id} role={self.role}>" | |
| class UserMemory(UUIDPrimaryKeyMixin, TimestampMixin, Base): | |
| """Long-term and short-term semantic memory for AI agents.""" | |
| __tablename__ = "user_memory" | |
| __table_args__ = ( | |
| Index("ix_user_memory_user_id", "user_id"), | |
| Index("ix_user_memory_layer", "memory_layer"), | |
| Index("ix_user_memory_type", "memory_type"), | |
| ) | |
| user_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), | |
| ForeignKey("users.id", ondelete="CASCADE"), | |
| nullable=False, | |
| ) | |
| memory_layer: Mapped[str] = mapped_column(String(50), nullable=False) | |
| memory_type: Mapped[str] = mapped_column(String(50), nullable=False) | |
| content: Mapped[dict] = mapped_column(JSONB, nullable=False) | |
| embedding: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True) | |
| metadata_json: Mapped[dict] = mapped_column(JSONB, default=dict, nullable=False) | |
| confidence: Mapped[Optional[float]] = mapped_column(Float, nullable=True) | |
| expires_at: Mapped[Optional[datetime]] = mapped_column(nullable=True) | |
| access_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) | |
| last_accessed: Mapped[Optional[datetime]] = mapped_column(nullable=True) | |
| def __repr__(self) -> str: | |
| return f"<UserMemory user_id={self.user_id} layer={self.memory_layer}>" | |
| class AIInsight(UUIDPrimaryKeyMixin, Base): | |
| """Proactive AI insights generated for workspaces/users.""" | |
| __tablename__ = "ai_insights" | |
| __table_args__ = ( | |
| Index("ix_ai_insights_user_id", "user_id"), | |
| ) | |
| user_id: Mapped[uuid.UUID] = mapped_column( | |
| UUID(as_uuid=True), | |
| ForeignKey("users.id", ondelete="CASCADE"), | |
| nullable=False, | |
| ) | |
| workspace_id: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) | |
| title: Mapped[str] = mapped_column(String(255), nullable=False) | |
| description: Mapped[str] = mapped_column(Text, nullable=False) | |
| insight_type: Mapped[str] = mapped_column(String(50), default="anomaly", nullable=False) | |
| severity: Mapped[str] = mapped_column(String(20), default="medium", nullable=False) | |
| metadata_json: Mapped[dict] = mapped_column(JSONB, default=dict, nullable=False) | |
| created_at: Mapped[datetime] = mapped_column( | |
| nullable=False, server_default="now()" | |
| ) | |
| def __repr__(self) -> str: | |
| return f"<AIInsight title={self.title}>" | |