from typing import Literal from pydantic import BaseModel, Field class SourceReference(BaseModel): """Reference to a source artifact chunk used in RAG response.""" artifact_file: str = Field(..., description="Path to the YAML artifact file") source_file: str = Field(..., description="Path to the source code file") chunk_type: str = Field(..., description="Type of chunk (factual_summary, interpretive_summary, method, invariants)") relevance_score: float = Field(..., description="Similarity score from FAISS search") class ChatRequest(BaseModel): """Request model for the /chat endpoint.""" message: str = Field(..., description="The user's message") conversation_id: str | None = Field( default=None, description="Optional conversation ID for continuity" ) class ChatResponse(BaseModel): """Response model for the /chat endpoint.""" conversation_id: str = Field(..., description="Conversation ID (generated if not provided)") response: str = Field(..., description="The LLM's response") mode: Literal["local", "remote", "openai", "asksage"] = Field(..., description="Which adapter was used") intent: Literal["codebase", "general", "clarification"] = Field( default="general", description="Classified intent of the user message" ) sources: list[SourceReference] = Field(default_factory=list, description="Source artifact references used in response") class HealthResponse(BaseModel): """Response model for the /health endpoint.""" status: str = Field(default="ok") faiss_loaded: bool = Field(default=False, description="Whether FAISS index is loaded") index_size: int = Field(default=0, description="Number of chunks in FAISS index") class ErrorResponse(BaseModel): """Standard error response model.""" detail: str = Field(..., description="Error description") # --- SSE Streaming Event Models --- class StreamChunkEvent(BaseModel): """SSE event for content chunks during streaming.""" type: Literal["chunk"] = "chunk" content: str = Field(..., description="Content chunk from the LLM") conversation_id: str = Field(..., description="Conversation ID") class StreamDoneEvent(BaseModel): """SSE event signaling completion of streaming.""" type: Literal["done"] = "done" conversation_id: str = Field(..., description="Conversation ID") mode: Literal["local", "remote", "openai", "asksage"] = Field(..., description="Which adapter was used") intent: Literal["codebase", "general", "clarification"] = Field( default="general", description="Classified intent of the user message" ) sources: list[SourceReference] = Field(default_factory=list, description="Source artifact references used in response") class IndexResponse(BaseModel): """Response model for the /index endpoint.""" status: str = Field(..., description="Indexing status") chunks_indexed: int = Field(default=0, description="Number of chunks indexed") artifacts_processed: int = Field(default=0, description="Number of YAML files processed") class StreamErrorEvent(BaseModel): """SSE event for errors during streaming.""" type: Literal["error"] = "error" message: str = Field(..., description="Error message") code: int = Field(default=500, description="HTTP status code")