Spaces:
Running
Running
File size: 5,212 Bytes
09801ca | 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 | """
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}>"
|