Files
erp-nutzfahrzeuge/backend/app/models/copilot.py
T

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,
}