"""Chat-Sessions und Nachrichten (→ Konzept 6.1).""" import uuid from datetime import datetime from typing import Any from sqlalchemy import JSON, Boolean, ForeignKey, String, Text, Uuid from sqlalchemy import false as sa_false from sqlalchemy.orm import Mapped, mapped_column from app.core.database import Base, now class ChatSession(Base): __tablename__ = "chat_sessions" id: Mapped[int] = mapped_column(primary_key=True) workspace_id: Mapped[int] = mapped_column(ForeignKey("workspaces.id", ondelete="CASCADE"), index=True) title: Mapped[str] = mapped_column(String(200)) created_by: Mapped[int] = mapped_column(ForeignKey("users.id")) created: Mapped[datetime] = mapped_column(default=now) # Arbeitsnotiz: automatische Zusammenfassung der Nachrichten bis summary_upto, die nicht mehr # vollständig in den Kontext passen (→ lange Coding-Sessions) summary: Mapped[str] = mapped_column(Text, default="", server_default="") summary_upto: Mapped[int | None] # Plan der KI (update_plan): [{"text": …, "status": "open" | "in_progress" | "done"}] plan: Mapped[list] = mapped_column(JSON, default=list, server_default="[]") # Planungsmodus: nur lesende Tools und update_plan, bis jemand „Plan umsetzen“ klickt plan_mode: Mapped[bool] = mapped_column(Boolean, default=False, server_default=sa_false()) # Ausgangs-Branch je Git-Quelle ({"": "feature/x"}), wenn der Chat nicht auf dem Ziel-Branch # beginnt; der Chat-Branch zweigt davon ab und hat ihn als Ziel (Abgleich, Pull Request) git_base: Mapped[dict] = mapped_column(JSON, default=dict, server_default="{}") class Message(Base): __tablename__ = "messages" id: Mapped[int] = mapped_column(primary_key=True) chat_session_id: Mapped[int] = mapped_column(ForeignKey("chat_sessions.id", ondelete="CASCADE"), index=True) role: Mapped[str] = mapped_column(String(20)) # user | assistant content: Mapped[str] = mapped_column(Text) # Antwort-Elemente der Responses API (u. a. verschlüsseltes Reasoning), unverändert # beim nächsten Aufruf zurückgegeben (→ Konzept 8.1) items: Mapped[list[dict[str, Any]]] = mapped_column(JSON, default=list) # Verwendetes Modell und Verbrauch, für Nachvollziehbarkeit model: Mapped[str] = mapped_column(String(200), default="") usage: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict) # Run, der die Nachricht ausgelöst (user) bzw. erzeugt (assistant) hat run_id: Mapped[uuid.UUID | None] = mapped_column(Uuid, ForeignKey("task_runs.id", ondelete="SET NULL")) author_id: Mapped[int | None] = mapped_column(ForeignKey("users.id")) created: Mapped[datetime] = mapped_column(default=now)