124 lines
3.8 KiB
Python
124 lines
3.8 KiB
Python
|
|
"""SQLAlchemy models for Copilot chat sessions and messages.
|
||
|
|
|
||
|
|
Stores per-user chat history with action proposals from the AI assistant.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import enum
|
||
|
|
import uuid
|
||
|
|
from datetime import datetime
|
||
|
|
|
||
|
|
from sqlalchemy import DateTime, Enum, ForeignKey, String, Text, func
|
||
|
|
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||
|
|
|
||
|
|
from app.database import Base
|
||
|
|
|
||
|
|
|
||
|
|
class CopilotRole(str, enum.Enum):
|
||
|
|
"""Roles for chat messages."""
|
||
|
|
user = "user"
|
||
|
|
assistant = "assistant"
|
||
|
|
|
||
|
|
|
||
|
|
class CopilotSession(Base):
|
||
|
|
"""A chat session belonging to a user. Groups related messages."""
|
||
|
|
|
||
|
|
__tablename__ = "copilot_sessions"
|
||
|
|
|
||
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
||
|
|
UUID(as_uuid=True),
|
||
|
|
primary_key=True,
|
||
|
|
default=uuid.uuid4,
|
||
|
|
)
|
||
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
||
|
|
UUID(as_uuid=True),
|
||
|
|
ForeignKey("users.id", ondelete="CASCADE"),
|
||
|
|
nullable=False,
|
||
|
|
index=True,
|
||
|
|
)
|
||
|
|
title: Mapped[str] = mapped_column(
|
||
|
|
String(255), nullable=False, default="Neue Konversation",
|
||
|
|
)
|
||
|
|
created_at: Mapped[datetime] = mapped_column(
|
||
|
|
DateTime(timezone=True), nullable=False, server_default=func.now(),
|
||
|
|
)
|
||
|
|
updated_at: Mapped[datetime] = mapped_column(
|
||
|
|
DateTime(timezone=True),
|
||
|
|
nullable=False,
|
||
|
|
server_default=func.now(),
|
||
|
|
onupdate=func.now(),
|
||
|
|
)
|
||
|
|
|
||
|
|
messages: Mapped[list["CopilotChat"]] = relationship(
|
||
|
|
back_populates="session",
|
||
|
|
cascade="all, delete-orphan",
|
||
|
|
order_by="CopilotChat.created_at.asc()",
|
||
|
|
lazy="selectin",
|
||
|
|
)
|
||
|
|
|
||
|
|
def __repr__(self) -> str:
|
||
|
|
return f"<CopilotSession id={self.id} user_id={self.user_id} title={self.title}>"
|
||
|
|
|
||
|
|
def to_dict(self) -> dict:
|
||
|
|
"""Serialize session for API responses."""
|
||
|
|
return {
|
||
|
|
"id": str(self.id),
|
||
|
|
"user_id": str(self.user_id),
|
||
|
|
"title": self.title,
|
||
|
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||
|
|
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||
|
|
"messages": [m.to_dict() for m in (self.messages or [])],
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
class CopilotChat(Base):
|
||
|
|
"""A single chat message (user or assistant) within a session."""
|
||
|
|
|
||
|
|
__tablename__ = "copilot_chats"
|
||
|
|
|
||
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
||
|
|
UUID(as_uuid=True),
|
||
|
|
primary_key=True,
|
||
|
|
default=uuid.uuid4,
|
||
|
|
)
|
||
|
|
session_id: Mapped[uuid.UUID] = mapped_column(
|
||
|
|
UUID(as_uuid=True),
|
||
|
|
ForeignKey("copilot_sessions.id", ondelete="CASCADE"),
|
||
|
|
nullable=False,
|
||
|
|
index=True,
|
||
|
|
)
|
||
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
||
|
|
UUID(as_uuid=True),
|
||
|
|
ForeignKey("users.id", ondelete="CASCADE"),
|
||
|
|
nullable=False,
|
||
|
|
index=True,
|
||
|
|
)
|
||
|
|
role: Mapped[CopilotRole] = mapped_column(
|
||
|
|
Enum(CopilotRole, name="copilot_role"),
|
||
|
|
nullable=False,
|
||
|
|
)
|
||
|
|
content: Mapped[str] = mapped_column(Text, nullable=False)
|
||
|
|
actions: Mapped[list | None] = mapped_column(
|
||
|
|
JSONB, nullable=True, default=None,
|
||
|
|
)
|
||
|
|
created_at: Mapped[datetime] = mapped_column(
|
||
|
|
DateTime(timezone=True), nullable=False, server_default=func.now(),
|
||
|
|
)
|
||
|
|
|
||
|
|
session: Mapped["CopilotSession"] = relationship(back_populates="messages")
|
||
|
|
|
||
|
|
def __repr__(self) -> str:
|
||
|
|
return f"<CopilotChat id={self.id} role={self.role} session_id={self.session_id}>"
|
||
|
|
|
||
|
|
def to_dict(self) -> dict:
|
||
|
|
"""Serialize chat message for API responses."""
|
||
|
|
return {
|
||
|
|
"id": str(self.id),
|
||
|
|
"session_id": str(self.session_id),
|
||
|
|
"user_id": str(self.user_id),
|
||
|
|
"role": self.role.value if isinstance(self.role, CopilotRole) else self.role,
|
||
|
|
"content": self.content,
|
||
|
|
"actions": self.actions,
|
||
|
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||
|
|
}
|