DataVision CI/CD Bot
release: clean production build for HuggingFace Space
09801ca
Raw
History Blame Contribute Delete
5.21 kB
"""
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}>"