"""Provider-independent, non-streaming Chat Completions preparation contracts.""" from datetime import datetime from typing import Literal from uuid import UUID from pydantic import BaseModel, Field from app.schemas.catalog import Input from app.schemas.indexing import ChunkProvenance class CompletionOptions(Input): model: str = Field(pattern=r"^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$") output_tokens: int = Field(512, ge=1, le=4096) token_parameter: Literal["max_completion_tokens", "max_tokens"] = "max_completion_tokens" instruction_role: Literal["system", "developer"] = "system" temperature: float | None = Field(None, ge=0, le=2) class PrepareContext(Input): retrieval_run_id: UUID completion: CompletionOptions max_input_characters: int = Field(10000, ge=1024, le=24000) max_evidence: int = Field(5, ge=1, le=10) class Evidence(ChunkProvenance): citation_id: str chunk_id: UUID chunk_ordinal: int retrieval_rank: int text: str text_sha256: str start_offset: int end_offset: int class ExcludedEvidence(BaseModel): chunk_id: UUID reason: Literal["overlap", "budget", "evidence_limit"] class ContextRead(BaseModel): id: UUID kind: Literal["prepared_request"] = "prepared_request" model_called: Literal[False] = False status: Literal["prepared", "insufficient_evidence"] reason: Literal["candidates_available", "no_candidates", "context_budget"] retrieval_run_id: UUID retrieval_mode: Literal["keyword_chunks", "vector", "hybrid"] query: str text_index_version_id: UUID text_generation: int catalog_revision: int vector_version_id: UUID | None vector_generation: int | None corpus_fingerprint: str prompt_version: Literal["nalati-evidence-v1"] = "nalati-evidence-v1" completion: CompletionOptions max_input_characters: int input_characters: int max_evidence: int evidence: list[Evidence] excluded: list[ExcludedEvidence] request_body: dict | None request_sha256: str | None created_at: datetime class CompletionUsage(BaseModel): prompt_tokens: int = Field(ge=0, strict=True) completion_tokens: int = Field(ge=0, strict=True) total_tokens: int = Field(ge=0, strict=True) class ParsedCompletion(BaseModel): status: Literal["answer_candidate", "insufficient_evidence", "incomplete", "refused"] completion_id: str model: str answer: str | None citations: list[str] usage: CompletionUsage | None