DocDoeAI / app /models /chat_session.py
asnannp's picture
deploy: sync backend to Space root (learn-lesson HF cache fix)
3bcdb36
Raw
History Blame Contribute Delete
3.01 kB
"""Chat session and message persistence models."""
from datetime import datetime
from sqlalchemy import JSON, DateTime, ForeignKey, Index, String, Text, UniqueConstraint, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.core.database import Base
from app.utils.ids import prefixed_id
class ChatSession(Base):
__tablename__ = "chat_sessions"
__table_args__ = (
Index("ix_chat_sessions_user_updated_at", "user_id", "updated_at"),
)
id: Mapped[str] = mapped_column(
String(40), primary_key=True, default=lambda: prefixed_id("csess"),
)
user_id: Mapped[str] = mapped_column(
String(40), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True,
)
source_id: Mapped[str | None] = mapped_column(String(40), nullable=True, index=True)
subject: Mapped[str] = mapped_column(String(120), nullable=False, default="")
title: Mapped[str] = mapped_column(String(255), nullable=False, default="New chat")
# Structured academic origin (Tuition question, chapter/topic, uploaded
# source, and safe return route). This keeps continuity independent of the
# display title or free-form first message.
context_data: Mapped[dict[str, object]] = mapped_column(
JSON, nullable=False, default=dict,
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False,
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False,
)
messages: Mapped[list["ChatMessageRecord"]] = relationship(
back_populates="session", cascade="all, delete-orphan", order_by="ChatMessageRecord.created_at",
)
class ChatMessageRecord(Base):
__tablename__ = "chat_messages"
__table_args__ = (
UniqueConstraint(
"session_id",
"client_turn_id",
"role",
name="uq_chat_messages_session_turn_role",
),
)
id: Mapped[str] = mapped_column(
String(40), primary_key=True, default=lambda: prefixed_id("cmsg"),
)
session_id: Mapped[str] = mapped_column(
String(40), ForeignKey("chat_sessions.id", ondelete="CASCADE"), nullable=False, index=True,
)
role: Mapped[str] = mapped_column(String(20), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
intent: Mapped[str | None] = mapped_column(String(40), nullable=True)
evidence_label: Mapped[str | None] = mapped_column(String(255), nullable=True)
client_turn_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
web_sources: Mapped[list[dict[str, object]]] = mapped_column(
JSON, nullable=False, default=list,
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False,
)
session: Mapped["ChatSession"] = relationship(back_populates="messages")