"""Request and response shapes for conversations and their turns.""" import uuid from datetime import datetime from pydantic import BaseModel, ConfigDict, Field from app.models import ConversationMode, MessageRole class ConversationCreate(BaseModel): mode: ConversationMode class ConversationSummary(BaseModel): model_config = ConfigDict(from_attributes=True) id: uuid.UUID title: str | None = None class MessageSource(BaseModel): document_id: uuid.UUID title: str heading_path: str excerpt: str = "" # True when the passage was passed to the model; False for passages that # were retrieved but dropped as too weak (a no-answer turn). Old messages # predate the flag, so it defaults to True (they were all cited). used: bool = True # The cited document has an unanswered request to check it. Snapshotted # with the citation, so a reload shows what was true when it was answered. review_pending: bool = False class MessageOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: uuid.UUID role: MessageRole content: str created_at: datetime sources: list[MessageSource] = [] # Set when no model answered this turn and `sources` is a plain full-text # result list instead: the `llm_*` code that caused it, which the frontend # phrases. Null on every normal turn. fallback: str | None = None class ConversationDetail(ConversationSummary): messages: list[MessageOut] = [] class SendMessage(BaseModel): content: str = Field(min_length=1, max_length=8000)