File size: 4,854 Bytes
5ea3240
 
 
 
 
 
 
13784a6
 
 
 
 
 
 
b0af996
13784a6
 
b0af996
13784a6
 
 
5ea3240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13784a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ef2c57c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ea3240
 
 
 
 
 
 
 
f79a242
f0307a2
 
5ea3240
 
 
 
 
 
 
 
 
 
 
 
 
ef2c57c
 
 
 
bb5d2bb
27716f7
b0af996
ef2c57c
 
5ea3240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13784a6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Literal
from pydantic import BaseModel, Field


KnowledgeScope = Literal["corpus", "external", "mixed", "structured_data"]
TaskType = Literal[
    "fact_lookup",
    "overview",
    "cross_document_synthesis",
    "comparison",
    "aggregation",
    "insight_synthesis",
    "followup",
]
RetrievalStrategy = Literal["semantic", "global", "hierarchical", "analytical", "table", "none"]
WebRelevance = Literal["required", "useful", "irrelevant"]


@dataclass(slots=True)
class Document:
    text: str
    source: str
    page: int | None = None
    section: str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)


@dataclass(slots=True)
class Chunk:
    id: str
    text: str
    source: str
    page: int | None = None
    section: str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)


@dataclass(slots=True)
class SearchHit:
    chunk: Chunk
    score: float
    dense_score: float | None = None
    sparse_score: float | None = None
    rerank_score: float | None = None
    origin: Literal["document", "web"] = "document"
    url: str | None = None
    title: str | None = None


@dataclass(slots=True)
class SourceProfile:
    source: str
    file_type: str
    document_units: int
    chunk_count: int
    page_count: int
    section_count: int
    representative_chunk_ids: list[str]
    profile_text: str


class QueryPlan(BaseModel):
    """Semantic plan produced before retrieval.

    The fields deliberately separate *where knowledge lives* from *how it should
    be retrieved*. That keeps phrases such as "current corpus" from being
    mistaken for current-world/fresh-web intent.
    """

    route: Literal["documents", "web", "hybrid", "sql"] = "documents"
    knowledge_scope: KnowledgeScope = "corpus"
    task_type: TaskType = "fact_lookup"
    retrieval_strategy: RetrievalStrategy = "semantic"
    web_relevance: WebRelevance = "irrelevant"
    requires_fresh_web: bool = False
    rewritten_query: str = ""
    document_queries: list[str] = Field(default_factory=list)
    web_queries: list[str] = Field(default_factory=list)
    hyde: str = ""
    rationale: str = ""


class EvidenceAssessment(BaseModel):
    score: float = Field(default=0.0, ge=0.0, le=1.0)
    top_relevance: float = Field(default=0.0, ge=0.0, le=1.0)
    mean_relevance: float = Field(default=0.0, ge=0.0, le=1.0)
    method_agreement: float = Field(default=0.0, ge=0.0, le=1.0)
    source_coverage: float = Field(default=0.0, ge=0.0, le=1.0)
    unique_sources: int = 0
    corpus_sources: int = 0
    sufficient: bool = False
    reason: str = ""


class RAGEvalJudgement(BaseModel):
    """Auxiliary LLM-as-judge scores for benchmark cases.

    These scores complement deterministic labels; they are never treated as
    ground truth because judge models can be noisy or biased.
    """

    faithfulness: float = Field(default=0.0, ge=0.0, le=1.0)
    answer_relevance: float = Field(default=0.0, ge=0.0, le=1.0)
    completeness: float = Field(default=0.0, ge=0.0, le=1.0)
    citation_support: float = Field(default=0.0, ge=0.0, le=1.0)
    overall: float = Field(default=0.0, ge=0.0, le=1.0)
    pass_: bool = Field(default=False, alias="pass")
    reason: str = ""

    model_config = {"populate_by_name": True}


class PipelineConfig(BaseModel):
    mode: Literal["Auto", "Documents", "Web", "Hybrid", "Data (SQL)"] = "Auto"
    profile: Literal["Fast", "Balanced", "Agentic"] = "Balanced"
    model: str = "gemini-3.5-flash-lite"
    web_provider: Literal["Auto", "DuckDuckGo", "Tavily", "Gemini Search"] = "Auto"
    use_hyde: bool = True
    use_multi_query: bool = True
    use_reranker: bool = True
    use_context_pruning: bool = True
    use_adaptive_top_k: bool = True
    use_evidence_compression: bool = True
    use_crag: bool = True
    use_self_rag: bool = True
    allow_web_fallback: bool = True
    use_history: bool = True
    top_k: int = Field(default=6, ge=2, le=12)


class QueryRequest(BaseModel):
    session_id: str
    query: str = Field(min_length=1, max_length=8000)
    config: PipelineConfig = Field(default_factory=PipelineConfig)


class EvaluationRequest(BaseModel):
    session_id: str
    level: Literal["Quick", "Standard", "Deep"] = "Standard"
    model: str = "gemini-3.5-flash-lite"
    target_rpm: int = Field(default=12, ge=0, le=60)
    reuse_saved: bool = True
    include_profile_benchmark: bool = False


class QueryResponse(BaseModel):
    answer: str
    sources: list[dict[str, Any]]
    trace: dict[str, Any]
    confidence: float


class SessionResponse(BaseModel):
    session_id: str


class CorpusSummary(BaseModel):
    session_id: str
    documents: int
    chunks: int
    tables: list[str]
    sources: list[str]
    source_profiles: int = 0