from pydantic import BaseModel, Field class DocumentInfo(BaseModel): document_id: str name: str chunks_count: int file_type: str created_at: str metadata: dict = Field(default_factory=dict) class ChunkPreview(BaseModel): index: int section: str = "" page_number: int = 0 text_preview: str = "" char_length: int = 0 class DocumentUploadResponse(BaseModel): document_id: str name: str chunks_count: int status: str = "indexed" created_at: str chunks_preview: list[ChunkPreview] = Field(default_factory=list) class DocumentListResponse(BaseModel): documents: list[DocumentInfo] total: int class ChunkDetail(BaseModel): index: int section: str = "" page_number: int = 0 text: str = "" char_length: int = 0 embedding: list[float] = Field(default_factory=list) embedding_dim: int = 0 class DocumentChunksResponse(BaseModel): document_id: str name: str file_type: str chunks_count: int chunks: list[ChunkDetail] = Field(default_factory=list) class DocumentDeleteResponse(BaseModel): ok: bool = True deleted_chunks: int class IntentDocumentsResponse(BaseModel): intent_code: str document_ids: list[str] = Field(default_factory=list) class DocumentIntentsResponse(BaseModel): document_id: str intent_codes: list[str] = Field(default_factory=list) class SourceInfo(BaseModel): document_id: str document_name: str chunk_text: str section: str = "" page: int = 0 relevance_score: float = 0.0 class QueryResponse(BaseModel): answer: str sources: list[SourceInfo] model_used: str assembled_prompt: str = "" intent_code: str = "_debug" config_version: int | None = None rag_subscription: dict | None = None # {"subscribed_count": int, "found_count": int} — Спринт 7 class HealthResponse(BaseModel): status: str = "ok" chromadb: str embedding_model: str documents_count: int chunks_count: int class MessageInfo(BaseModel): id: int role: str text: str created_at: str sources: list[SourceInfo] = Field(default_factory=list) assembled_prompt: str = "" intent_code: str = "" intent_name: str = "" meta: dict | None = None escalation_reason: str | None = None class ThreadInfo(BaseModel): id: int name: str created_at: str updated_at: str messages_count: int first_message_preview: str = "" class ThreadListResponse(BaseModel): threads: list[ThreadInfo] total: int class ThreadStateInfo(BaseModel): current_intent_code: str | None = None current_step: int = 0 current_step_code: str | None = None slots: dict = Field(default_factory=dict) handoff_count: int = 0 soft_insertion_count: int = 0 suspended_intent: str | None = None resumable_step_code: str | None = None resumable_slots: dict = Field(default_factory=dict) pending_guard: dict | None = None class BounceInfo(BaseModel): from_: str = Field(alias="from") to: str preface: str = "" model_config = {"populate_by_name": True} class ValidationEventInfo(BaseModel): current_step: str requested_step: str reason: str guard_name: str | None = None missing_slots: list[str] = Field(default_factory=list) guard_description: str = "" class ThreadDetailResponse(BaseModel): id: int name: str created_at: str updated_at: str messages: list[MessageInfo] = Field(default_factory=list) thread_state: ThreadStateInfo | None = None class ChatResponse(BaseModel): thread_id: int thread_name: str message_id: int intent_code: str = "" intent_name: str = "" router_intent_code: str = "" config_version: int = 0 router_version: int | None = None answer: str sources: list[SourceInfo] model_used: str assembled_prompt: str = "" thread_state: ThreadStateInfo = Field(default_factory=ThreadStateInfo) bounces: list[BounceInfo] = Field(default_factory=list) validation_events: list[ValidationEventInfo] = Field(default_factory=list) parse_error: str | None = None routing_loop_triggered: bool = False resumed_from_suspended: bool = False message_meta: dict | None = None escalation_reason: str | None = None operator_summary: dict | None = None router_assembled_prompt: str = "" rag_subscription: dict | None = None # {"subscribed_count": int, "found_count": int} — Спринт 7 class ThreadDeleteResponse(BaseModel): ok: bool = True deleted_messages: int class AgentConfigInfo(BaseModel): id: int intent_id: int | None = None intent_code: str = "" intent_name: str = "" version: int name: str | None = None system_prompt: str rules_text: str = "" exit_conditions_text: str = "" is_active: bool created_at: str class AgentConfigListResponse(BaseModel): configs: list[AgentConfigInfo] total: int class AgentConfigDeleteResponse(BaseModel): ok: bool = True class IntentInfo(BaseModel): id: int code: str name: str description: str = "" is_enabled: bool order_index: int active_config_id: int | None = None active_config_version: int | None = None class IntentListResponse(BaseModel): intents: list[IntentInfo] total: int class IntentStepInfo(BaseModel): id: int intent_id: int intent_code: str = "" code: str name: str order_index: int system_prompt: str = "" allowed_next: list[str] = Field(default_factory=list) guards: dict = Field(default_factory=dict) updated_at: str class IntentStepListResponse(BaseModel): intent_code: str steps: list[IntentStepInfo] total: int