Files

96 lines
2.6 KiB
Python

"""Chunk-level lexical and hybrid contracts; the item-level baseline stays distinct."""
from datetime import datetime
from typing import Literal
from uuid import UUID
from pydantic import BaseModel, Field, model_validator
from app.schemas.catalog import Category, Input
from app.schemas.indexing import IndexConfig
from app.schemas.retrieval import RetrievalConfig
from app.schemas.vectors import SpaceRead, VectorHit
class ChunkKeywordRequest(Input):
query: str = Field(min_length=1, max_length=500)
mode: Literal["keyword_chunks"] = "keyword_chunks"
top_k: int = Field(5, ge=1, le=20)
category: Category | None = None
class HybridRequest(ChunkKeywordRequest):
mode: Literal["hybrid"] = "hybrid"
candidate_k: int = Field(50, ge=1, le=200)
@model_validator(mode="after")
def sufficient_candidates(self):
if self.candidate_k < self.top_k:
raise ValueError("candidate_k 不得小于 top_k")
return self
class ChunkRetrievalConfig(BaseModel):
ranking_unit: Literal["chunk"] = "chunk"
lexical: RetrievalConfig
chunking: IndexConfig
tie_break: Literal["knowledge_id,chunk_ordinal,chunk_id"] = (
"knowledge_id,chunk_ordinal,chunk_id"
)
class HybridConfig(ChunkRetrievalConfig):
scorer: Literal["rrf-v1"] = "rrf-v1"
rrf_k: Literal[60] = 60
weights: tuple[Literal[1], Literal[1]] = (1, 1)
candidate_k: int
vector_scorer: Literal["pgvector-cosine-exact-v1"] = "pgvector-cosine-exact-v1"
space: SpaceRead
class RankedCandidate(BaseModel):
chunk_id: UUID
rank: int
score: float
class HybridHit(VectorHit):
lexical_rank: int | None
lexical_score: float | None
vector_rank: int | None
vector_score: float | None
lexical_contribution: float
vector_contribution: float
class ChunkKeywordResponse(BaseModel):
run_id: UUID
query: str
mode: Literal["keyword_chunks"] = "keyword_chunks"
status: Literal["ok", "no_evidence"]
top_k: int
category: str | None
query_terms: list[str]
text_index_version_id: UUID
text_generation: int
catalog_revision: int
corpus_fingerprint: str
corpus_size: int
total_hits: int
results: list[VectorHit]
elapsed_ms: float
created_at: datetime
config: ChunkRetrievalConfig
class HybridResponse(ChunkKeywordResponse):
mode: Literal["hybrid"] = "hybrid"
vector_version_id: UUID
generation: int
lexical_total_hits: int
vector_total_hits: int
lexical_candidates: list[RankedCandidate]
vector_candidates: list[RankedCandidate]
results: list[HybridHit]
config: HybridConfig