ArchitSharma commited on
Commit
8cfc5e2
·
1 Parent(s): f0307a2

Release RAGForge v2.0 final

Browse files
.github/workflows/ci.yml CHANGED
@@ -13,5 +13,7 @@ jobs:
13
  python-version: "3.11"
14
  cache: pip
15
  - run: pip install -r requirements-dev.txt
16
- - run: ruff check src tests app.py
17
  - run: pytest -q
 
 
 
13
  python-version: "3.11"
14
  cache: pip
15
  - run: pip install -r requirements-dev.txt
16
+ - run: ruff check src tests scripts app.py
17
  - run: pytest -q
18
+ - run: python -m compileall -q src scripts app.py
19
+ - run: python scripts/release_check.py
CHANGELOG.md ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Changelog
2
+
3
+ RAGForge used iterative numbered releases during development. v2.0.0 is the final planned portfolio release.
4
+
5
+ ## v2.0.0 - Final
6
+
7
+ - polished product UI and final README/docs hierarchy
8
+ - aligned the multi-hop Hard Mode case with the recommended Auto + Balanced semantic route
9
+ - added final release verification tooling and CI consistency checks
10
+ - fixed duplicate runtime-header presentation and cleaned architecture documentation
11
+ - preserved the v1.9 adaptive retrieval, context-budget, compression, scale-stress and release-readiness architecture
12
+
13
+ ## v1.9 - Scale and release readiness
14
+
15
+ - adaptive retrieval depth by corpus size
16
+ - dynamic 2-5 chunk focused context budgets
17
+ - focused sentence compression
18
+ - 1x/5x/20x deterministic distractor-scale stress harness
19
+ - prompt/context token economics
20
+ - workspace diagnostics and release-readiness checklist
21
+
22
+ ## v1.8 - Context budget optimization
23
+
24
+ - focused post-retrieval pruning
25
+ - context/prompt telemetry
26
+ - grounded-absence hardening
27
+ - zero-Gemini context-budget ablation
28
+
29
+ ## v1.7 - Evaluation correctness and adaptive efficiency
30
+
31
+ - Markdown-safe source cards and graphical node latency waterfall
32
+ - grounded absence and improved citation scoring/repair
33
+ - evaluation run provenance
34
+ - measured small-corpus reranker skip policy
35
+
36
+ ## v1.6 - Hard Mode and analytical synthesis
37
+
38
+ - `insight_synthesis` / `analytical` path
39
+ - `[T#]` table citations
40
+ - harder robustness benchmark
41
+ - chunk-level reranker labels
42
+ - optional profile benchmark and node latency summaries
43
+
44
+ ## v1.5 - Evaluation history and adaptive retrieval
45
+
46
+ - saved Quick/Standard/Deep reports
47
+ - incremental Deep evaluation
48
+ - typed Text2SQL scoring
49
+ - zero-call citation repair
50
+ - adaptive reranking
51
+
52
+ ## v1.4 - Evaluation and API observability
53
+
54
+ - proper evaluation score cards and quality gates
55
+ - fixed retrieval AP metrics
56
+ - API/architecture runtime inspection
57
+ - cache-bypassed evaluation timing
58
+
59
+ ## v1.3 - Component evaluation
60
+
61
+ - retrieval, citation, planner, SQL and latency metrics
62
+ - reranker ablation
63
+ - optional Deep judge
64
+
65
+ ## v1.2 - UX/lifecycle hardening
66
+
67
+ - browser session continuity
68
+ - lazy demo recovery
69
+ - explicit abstention and workspace preflight
70
+ - indexing/query progress improvements
71
+
72
+ ## v1.1 - Semantic routing redesign
73
+
74
+ - schema-constrained semantic query analysis
75
+ - source-profile index
76
+ - global/hierarchical retrieval
77
+ - correction before conditional web fallback
78
+
79
+ ## v1.0 - Initial production-style RAGForge
80
+
81
+ - FastAPI + Gradio + LangGraph
82
+ - hybrid dense/BM25 retrieval
83
+ - reranking, HyDE, multi-query, CRAG, Self-RAG
84
+ - Text2SQL, Ask-the-Web, citations, guardrails and Docker/Hugging Face deployment
Makefile CHANGED
@@ -1,4 +1,4 @@
1
- .PHONY: install run test lint docker
2
 
3
  install:
4
  python -m pip install -r requirements-dev.txt
@@ -10,7 +10,17 @@ test:
10
  pytest -q
11
 
12
  lint:
13
- ruff check src tests app.py
 
 
 
 
 
 
 
 
 
 
14
 
15
  docker:
16
  docker build -t ragforge .
 
1
+ .PHONY: install run test lint compile smoke verify docker
2
 
3
  install:
4
  python -m pip install -r requirements-dev.txt
 
10
  pytest -q
11
 
12
  lint:
13
+ ruff check src tests scripts app.py
14
+
15
+ compile:
16
+ python -m compileall -q src scripts app.py
17
+
18
+ smoke:
19
+ python scripts/release_check.py
20
+ pytest -q tests/test_v20_final.py tests/test_security.py tests/test_citations.py tests/test_eval_metrics.py
21
+
22
+ verify: lint test compile
23
+ python scripts/release_check.py
24
 
25
  docker:
26
  docker build -t ragforge .
README.md CHANGED
@@ -10,391 +10,362 @@ pinned: false
10
 
11
  # RAGForge
12
 
13
- **RAGForge v1.9 - a production-style, portfolio-ready agentic RAG and analytical synthesis system for Hugging Face Spaces.**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- RAGForge combines hybrid document retrieval, source-level/hierarchical retrieval, semantic query planning, corrective RAG, Self-RAG-style verification, Text2SQL and an “Ask-the-Web” research path in one CPU-friendly application. The default LLM is **Google Gemini 3.5 Flash-Lite**; the UI also exposes Gemini 3.1 Flash-Lite and stronger Flash models.
16
-
17
-
18
- ## What is new in v1.9
19
-
20
- v1.9 is the scale, context-economics and release-readiness release. It generalizes v1.8's successful fixed top-3 pruning into a corpus-aware policy and bundles the remaining production/evaluation work needed before treating RAGForge as a finished portfolio system. The new local ablations do not add Gemini requests.
21
-
22
- - **Adaptive retrieval depth** - candidate depth expands from 6 to 8/10/12 as corpus size grows, while broad tasks retain additional breadth.
23
- - **Dynamic 2-5 chunk context budgets** - focused local lookups choose their generation budget from corpus scale, retrieval confidence, score separation and source ambiguity instead of always keeping three chunks.
24
- - **Focused evidence compression** - a zero-LLM sentence selector trims only the generation copy of focused evidence; source cards and citation evidence retain the original chunks.
25
- - **Three-way context ablation** - Standard/Deep compare full top-k, the v1.8 fixed top-3 policy and the v1.9 adaptive budget. A second ablation measures adaptive context with/without sentence compression and checks labeled answer-signal retention.
26
- - **1x/5x/20x scale stress** - a deterministic distractor harness clones long-document chunks and reuses existing vectors to exercise the real hybrid retriever at roughly demo, medium and ~1000-chunk scale without embedding or Gemini cost.
27
- - **Overview/insight consistency** - the planner now enforces task/strategy structural consistency, while the overview evaluator accepts semantically richer insight synthesis only when route, breadth, citations and no-web behavior remain correct.
28
- - **Prompt and evidence economics** - traces expose retrieval depth, confidence/gap, context budget, compression, prompt/output/total token estimates and cited-source utilization.
29
- - **Pacing-corrected node latency** - evaluation node summaries remove deliberate free-tier pacing from model-service node time while preserving separate wall/pacing telemetry.
30
- - **Workspace health diagnostics** - capacity utilization, corpus scale, index readiness, estimated vector memory, TTL/idle age and evaluation-history counts are available in the Architecture + API tab and REST diagnostics endpoint.
31
- - **Release readiness** - Standard/Deep apply a transparent checklist across answer quality, grounding, routing, SQL, hard mode, adaptive-budget recall, compression retention and largest-scale stress recall.
32
-
33
- No new runtime dependency is introduced in v1.9.
34
-
35
-
36
-
37
-
38
- ## What is new in v1.8
39
-
40
- v1.8 is a context-budget and generation-efficiency release driven by the v1.7 benchmark: Recall@5/Hit@1/MRR were saturated, citation quality reached 100%, but focused QA still carried distractor sources and generation dominated end-to-end latency. The release therefore reduces unnecessary model input without narrowing broad synthesis tasks.
41
-
42
- - **Adaptive focused context pruning** - focused local `fact_lookup`/`followup` queries using semantic or hierarchical retrieval keep a conservative three-chunk safety floor and drop only the low-value context tail. Overview, comparison, cross-document and insight/analytical tasks retain broad evidence.
43
- - **Context-budget trace telemetry** - Pipeline Inspector reports chunks, distinct sources, estimated context tokens and reduction percentage before/after pruning, plus the policy reason.
44
- - **Focused prompt budgeting** - ordinary corpus fact lookups no longer include the full session corpus manifest in the generation prompt after routing has already established scope. Broad/mixed tasks retain the manifest. Generation traces expose evidence-context size and estimated prompt tokens.
45
- - **Zero-call context-budget ablation** - Standard/Deep evaluation compare full top-k versus focused pruning on source Precision@5, Recall@5, Hit@1, MRR, context chunks/sources/chars/tokens and median reduction without spending any extra Gemini requests.
46
- - **Grounded-absence hardening** - Hard Mode missing-answer cases pass when either the natural-language absence matcher succeeds or the pipeline explicitly records `grounded_absence=true`, and the table exposes both signals.
47
- - **Reranker diagnostic aligned with runtime policy** - evaluation now states that small-corpus reranking is already skipped and treats the expensive cross-encoder run as an explicit ablation, not as the default recommendation.
48
- - **Focused efficiency telemetry in QA rows** - benchmark rows record pruning use, pre/post context size, manifest inclusion and estimated generation-prompt tokens so latency changes can be tied to actual input-budget changes.
49
-
50
-
51
-
52
- ## What is new in v1.7
53
-
54
- v1.7 is an evidence-driven correctness, provenance and efficiency release based on the first full v1.6 Hard Mode/profile benchmark. It fixes evaluator/UI artifacts before changing retrieval behavior and turns measured profile/reranker results into explicit runtime policy.
55
-
56
- - **Markdown-safe source cards** - retrieved Markdown is rendered as escaped plain text inside uniform source cards, so a snippet beginning with `#` can no longer become a giant UI heading.
57
- - **Graphical latency waterfall** - Pipeline Inspector replaces ASCII `#####` bars with proportional HTML latency bars and exposes the reranker decision reason plus grounded-absence state.
58
- - **Grounded absence handling** - answers such as “the retrieved policy does not mention a dispute fee” are treated as calibrated no-answer responses instead of low-confidence hallucinations. This avoids unnecessary revise calls and gives Hard Mode a robust missing-answer matcher.
59
- - **Markdown-aware citation coverage** - numbered/bulleted claims count even when short, while generic list introductions/headings do not. This fixes false 0% coverage for answers such as the four NIST AI RMF functions.
60
- - **Table-source validity for overviews** - global corpus overviews surface deterministic DuckDB table evidence as `[T#]` sources, so structured claims can be cited validly instead of referring to a table ID absent from the source list.
61
- - **Fresh-vs-saved evaluation provenance** - each saved report gets a run ID and server-boot ID. Fresh execution messages are no longer overwritten by the saved-run selector, and saved-run loading is triggered only by explicit user input.
62
- - **Profile-policy summary** - optional Fast/Balanced/Agentic benchmarking now produces aggregated profile metrics and an evidence-scoped recommendation rather than only six raw rows.
63
- - **Context-efficiency diagnostics** - evaluation now calls out the case where Recall@5 is excellent but Precision@5 is low, separating “found the right source” from “sent too many distractors to generation”.
64
- - **Small-corpus reranker policy tightened** - because v1.6 showed identical source and chunk metrics with/without reranking while adding multi-second latency, the cross-encoder is skipped even in Agentic on small corpora. It remains available for larger corpora.
65
- - **Diagnostic readability** - each `Next:` recommendation starts on its own line.
66
-
67
- ## What is new in v1.6
68
-
69
- v1.6 moves RAGForge from a mostly saturated demo benchmark into harder evaluation and analytical synthesis. The goal is to make the next improvements measurable rather than simply adding more RAG components.
70
-
71
- - **Insight Synthesis** - the semantic planner can classify `insight_synthesis` and choose an `analytical` retrieval strategy for questions such as “What does this collection reveal?” or “What trends stand out?”.
72
- - **Document + table analysis in one grounded answer** - analytical retrieval combines source-balanced original document evidence with deterministic DuckDB table summaries/rows. Table-derived claims are cited as `[T#]` alongside `[D#]` documents and `[W#]` web sources.
73
- - **Hard Mode benchmark** - Standard/Deep evaluation now includes paraphrase, distractor, missing-answer, multi-hop, local-freshness, structured filtering, analytical-synthesis and prompt-injection cases. Hard-mode performance contributes to the deterministic score and quality gates.
74
- - **Chunk-level reranker evaluation** - the explicit RRF vs cross-encoder ablation now reports chunk Hit@1/MRR on cases with auditable chunk-content labels, in addition to source-level metrics. This tests whether reranking helps *inside the correct source*, not only whether it finds the right file.
75
- - **Optional profile benchmark** - users can explicitly compare Fast, Balanced and Agentic on a small labeled subset, including accuracy, citation coverage, service latency, estimated LLM calls and reranker/correction usage. It is off by default to protect free-tier quota.
76
- - **Semantic citation attribution** - zero-call citation repair keeps its conservative lexical checks but can use the already-loaded local embedding model as a high-threshold fallback for clearly supported uncited claims.
77
- - **Node latency observability** - query inspection includes a dependency-free node-latency waterfall, and evaluation reports aggregate mean/p50/p95 timings per LangGraph node.
78
- - **Evaluation run history** - each completed run is timestamp-archived inside the workspace. The UI/API expose historical grade/score/citation/hard-mode/latency/request data plus deltas against the previous run of the same depth.
79
- - **v1.5.2 stability retained** - JSON-safe saved reports, table export, incremental Deep, quota pacing, typed Text2SQL evaluation, adaptive reranking and citation cleanup remain intact.
80
-
81
- ## What is new in v1.5.2
82
-
83
- v1.5.2 is a narrow runtime hotfix over v1.5.1. It does not change retrieval, evaluation semantics, dependency pins or the demo benchmark.
84
-
85
- - **Quick/Standard/Deep evaluation crash fixed** - `pipeline.py` now explicitly imports the citation normalization and repair helpers introduced in v1.5.1. This fixes `NameError: name 'repair_missing_citations' is not defined` during generated-answer evaluation.
86
- - **Packaging regression coverage** - a dependency-free test now verifies the citation helpers are imported into the pipeline namespace, and the LangGraph-backed helper test invokes the repair path directly when optional runtime dependencies are available.
87
- - **v1.5.1 behavior retained** - saved-report JSON, table export, grouped-citation parsing and conservative citation repair are unchanged.
88
- - **Benchmark compatibility retained** - the benchmark remains `1.5.1` because this hotfix changes runtime wiring only, not benchmark questions or scoring semantics.
89
-
90
- ## What is new in v1.5
91
-
92
- - **Saved evaluation history** - Quick, Standard and Deep reports are stored per workspace with model, benchmark version, corpus version and timestamp. The Evaluation tab can switch between saved runs instantly and compare them side by side without spending Gemini quota again.
93
- - **Incremental Deep evaluation** - when a matching Standard run is saved, Deep reuses that deterministic baseline and runs only the representative judge layer. A Standard → Deep workflow therefore drops from roughly 31 Deep-run requests to about 5 judge requests.
94
- - **Typed Text2SQL evaluation** - benchmark checks compare computed DuckDB scalar values directly, so booleans/numerics are judged as typed values rather than fragile Markdown strings. Correct SQL such as `weekend_support = true` is no longer penalized because of rendering differences.
95
- - **Adaptive reranking** - the cross-encoder remains available, but Fast mode and small/easy corpus queries skip it when the benchmark shows no measurable source-ranking gain. Comparison/cross-document work, larger corpora and Agentic mode can still use it.
96
- - **Zero-call citation repair** - an evidence-aware deterministic pass repairs only clearly supported uncited factual units, improving citation completeness without adding another Gemini call.
97
- - **Planner taxonomy refinement** - structured-data examples explicitly distinguish direct table lookups from cross-row min/max aggregation.
98
- - **Evaluation API history** - FastAPI can list and retrieve saved Quick/Standard/Deep reports, and `POST /api/v1/evaluate/demo` can reuse compatible cached evaluations.
99
- - **v1.4.1 quota protections retained** - rolling RPM pacing, provider-aware 429 backoff, pacing-aware latency, sampled Deep judging and request telemetry remain enabled.
100
-
101
- ## The retrieval philosophy
102
-
103
- RAGForge separates three questions that simple RAG demos often collapse into one:
104
-
105
- 1. **What does the user mean?** A schema-constrained semantic planner classifies knowledge scope, task type, freshness, retrieval strategy and independent document/web queries.
106
- 2. **Where should evidence come from?** Uploaded/private corpus, structured tables, the external web, or a genuinely mixed task.
107
- 3. **How should that evidence be retrieved?** Focused semantic chunk search, global source-balanced retrieval, hierarchical source-to-chunk retrieval, analytical document+table synthesis, Text2SQL, or web search.
108
-
109
- This matters for ambiguous language. For example, **“What is the current corpus about?”** is session-local: “current” modifies the indexed corpus and does *not* imply current-world freshness. Conversely, **“Compare our NIST document with the latest NIST guidance online”** is a mixed task and legitimately needs both document and web evidence.
110
-
111
- The behavior is learned/planned semantically; the application does not hard-code phrases such as “what is the corpus about”. Those phrases exist only as regression tests.
112
-
113
- ## Why this is more than “chat with a PDF”
114
-
115
- ### Semantic planning and task-aware routing
116
- - **Balanced/Agentic semantic query analyzer** with structured JSON output
117
- - distinguishes `corpus`, `external`, `mixed`, and `structured_data` knowledge scopes
118
- - task classification: fact lookup, overview, cross-document synthesis, comparison, aggregation, insight synthesis, follow-up
119
- - retrieval strategy selection: `semantic`, `global`, `hierarchical`, `analytical`, `table`, `none`
120
- - separate `document_queries` and `web_queries` instead of blindly sending the same string to every retriever
121
- - explicit `web_relevance = required | useful | irrelevant`
122
- - corpus manifest and recent conversation are supplied to the planner so pronouns/session-local references can be resolved
123
- - manual Auto/Documents/Web/Hybrid/Data(SQL) controls remain available and override the semantic route when selected
124
-
125
- ### Hierarchical and global retrieval
126
- RAGForge maintains **two retrieval indexes per session**:
127
-
128
- 1. **Chunk index** - normal evidence chunks for answer generation.
129
- 2. **Source-profile index** - one compact, deterministic profile per ingested source, built from source metadata and representative excerpts.
130
-
131
- The source-profile index is retrieval-only; generated answers are still cited against original document chunks.
132
 
133
- This supports:
134
- - **semantic retrieval** - dense + BM25 over all chunks for focused questions
135
- - **hierarchical retrieval** - source-profile retrieval first, then chunk retrieval restricted to selected sources
136
- - **global/source-balanced retrieval** - one query-relevant representative evidence chunk per source for corpus overviews and broad synthesis
137
- - source diversity for cross-document/comparison tasks, reducing long-document domination
 
 
 
138
 
139
- A 48-page PDF therefore cannot monopolize an overview simply because it produced more chunks than four short files.
140
-
141
- ### Hybrid search
142
- - local CPU embeddings with **FastEmbed / BAAI bge-small-en-v1.5**
143
- - embedded **Qdrant** vector store per user session
144
- - in-memory normalized embedding matrix for efficient source-scoped hierarchical search
145
- - **BM25** lexical retrieval
146
- - **reciprocal-rank fusion (RRF)**
147
- - local **cross-encoder reranking** (`Xenova/ms-marco-MiniLM-L-6-v2`) with an adaptive runtime policy: Fast/small-corpus cases can skip it when the measured latency cost is not justified, while harder/larger cases can retain it
148
- - sentence-aware chunking plus optional **semantic breakpoint chunking**
149
- - source/page metadata
150
- - suspicious retrieved prompt-injection text is down-weighted
151
-
152
- ### Corrective RAG (CRAG)
153
- The corrective loop no longer means “low score → Google”. It is:
154
-
155
- `retrieve → task-aware evidence grade → correct/rewrite retrieval plan → retrieve again → re-grade → conditional web only if web is semantically relevant`
156
-
157
- Evidence grading considers:
158
- - dense/BM25 relevance signals rather than raw RRF or reranker logits
159
- - top-hit and top-3 evidence quality
160
- - lexical/semantic retriever agreement
161
- - distinct-source coverage when the task requires breadth
162
- - an optional semantic LLM grader for borderline/Agentic cases
163
-
164
- If a corpus-only query remains unsupported after correction, RAGForge can **abstain** instead of contaminating the answer with unrelated internet search.
165
 
166
- ### Better relevance observability
167
- Cross-encoder logits are useful for ranking but are not calibrated human probabilities. The UI therefore shows:
168
- - retrieval rank
169
- - bounded hybrid relevance signal from dense/BM25 evidence
170
-
171
- Raw RRF/dense/sparse/reranker values remain available in the returned source metadata/pipeline trace for debugging.
172
-
173
- ### Agentic RAG
174
- - **LangGraph** state machine with conditional edges
175
- - history-aware semantic rewriting
176
- - optional **multi-query expansion**
177
- - optional **HyDE** hypothetical-document retrieval in Agentic mode
178
- - query correction/retrieval retry loop
179
- - **Self-RAG-style** answer audit and one bounded revision loop
180
- - response confidence score and full pipeline trace, including whether reranking was used and how many citations were deterministically repaired
181
- - process-level TTL response caching, isolated by session + corpus version
182
- - bounded exponential-backoff retries for transient Gemini API failures
183
 
184
- ### Ask-the-Web
185
- - free/keyless **DuckDuckGo** fallback
186
- - optional Tavily provider
187
- - native Gemini Google Search provider using a separately configurable grounding submodel (`gemini-2.5-flash-lite` by default)
188
- - independently planned web queries
189
- - query fan-out, parallel page fetching, main-text extraction with Trafilatura, local reranking, Gemini synthesis and URLs in the source panel
190
- - SSRF-oriented URL checks; local/private network targets are rejected
191
- - web permission and web relevance are separate: checking **Allow web fallback** does not force web search
192
-
193
- ### Documents and data
194
- - PDF, TXT, Markdown, DOCX, PPTX, CSV, XLS/XLSX, JSON, HTML, source-code/text formats, images and **ZIP archives**
195
- - safe ZIP extraction (path traversal, file-count, uncompressed-size and type limits)
196
- - page-aware PDF extraction
197
- - optional **Gemini OCR/document transcription** for scanned PDFs/images
198
- - CSV/XLSX indexed as text **and** loaded into isolated **DuckDB**
199
- - natural-language **Text2SQL** with single-statement read-only SQL validation and row limits
200
- - one-click bundled demo corpus
201
-
202
- ### Production/demo engineering
203
- - **FastAPI** REST backend + **Gradio** UI in one Docker Space
204
- - optional Bearer auth for API write endpoints
205
- - per-session corpora and in-memory databases; TTL cleanup
206
- - UI + REST per-IP rate limiting
207
- - Prometheus `/metrics`
208
- - health/info/session/status/ingest/query/evaluation endpoints plus saved-evaluation listing/retrieval
209
- - no API keys committed to the repo
210
- - pytest tests + GitHub Actions CI
211
- - pipeline inspector exposes semantic plan, retrieval strategy, source selection, evidence grade, corrective plan, web decision, Self-RAG result and cache hits
212
 
213
  ## Architecture
214
 
215
  ```mermaid
216
  flowchart TD
217
- U[User / API] --> G[Input + upload guardrails]
218
- G --> P{Semantic query planner}
219
- P -->|structured data| SQL[Read-only DuckDB Text2SQL]
220
  P -->|external| W[Ask-the-Web]
221
- P -->|corpus / mixed| RS{Retrieval strategy}
222
 
223
- RS -->|semantic| C[Chunk index]
224
- RS -->|global| GB[Source-balanced global retrieval]
225
- RS -->|hierarchical| SP[Source-profile index]
226
- SP --> SS[Select relevant sources]
227
- SS --> C
228
 
229
- C --> D[Dense FastEmbed/Qdrant]
230
  C --> B[BM25]
231
  D --> F[RRF]
232
  B --> F
233
- F --> RP{Adaptive reranker policy}
234
- RP -->|skip easy/small| E{Task-aware evidence grader}
235
- RP -->|use harder/larger| X[Cross-encoder reranker]
236
- GB --> E
237
- X --> E
238
-
239
- E -->|sufficient| A[Gemini generation]
240
- E -->|weak, first attempt| CR[Correct query / strategy]
241
- CR --> RS
242
- E -->|weak + web relevant| W
243
- E -->|weak + web irrelevant| AB[Abstain]
 
 
244
  W --> A
245
 
246
- A --> V{Self-RAG audit}
247
- V -->|revise once| RV[Faithfulness revision]
248
  RV --> V
249
- V -->|pass| O[Cited answer + sources + trace]
250
  SQL --> O
251
  AB --> O
 
 
 
 
 
 
 
 
252
  ```
253
 
254
- ## Deploy on Hugging Face Spaces
255
 
256
- 1. Create a **Docker** Space.
257
- 2. Extract/copy this repository into the Space repo root.
258
- 3. In **Settings → Secrets**, add `GEMINI_API_KEY`; optionally `TAVILY_API_KEY` and `APP_API_TOKEN`.
259
- 4. Push. The Dockerfile serves `uvicorn` on port `7860`.
260
- 5. Leave **Use bundled demo files** checked and either click **Index corpus** or ask a non-Web question; v1.4 can lazily initialize the demo corpus. Then test the Balanced/Auto profile.
261
 
262
- The Dockerfile explicitly owns `/tmp/ragforge` and model-cache directories as the non-root runtime user so per-session workspaces can be created safely on Hugging Face Spaces.
 
 
 
 
 
 
 
 
263
 
264
- > **Public demo key warning:** public visitors consume your server-side Gemini quota. RAGForge adds per-IP limits, but a heavily shared Space should lower limits, add authentication, or require bring-your-own-key.
265
- >
266
- > **Privacy caveat:** use only documents appropriate for the terms of the model/API tier you select.
267
 
268
- ## Run locally
269
 
270
  ```bash
271
  cp .env.example .env
272
- python -m venv .venv
273
- source .venv/bin/activate # Windows: .venv\\Scripts\\activate
274
- pip install -r requirements-dev.txt
275
- uvicorn app:app --reload --port 7860
276
  ```
277
 
278
- Or:
 
 
279
 
280
  ```bash
281
- docker build -t ragforge .
282
- docker run --rm -p 7860:7860 -e GEMINI_API_KEY=YOUR_KEY ragforge
 
283
  ```
284
 
285
- ## API example
286
 
287
- ```bash
288
- curl -X POST http://localhost:7860/api/v1/session
289
 
290
- curl -X POST http://localhost:7860/api/v1/ingest \
291
- -F session_id=SESSION_ID \
292
- -F files=@demo_documents/acme_cloud_runbook.md
293
 
294
- curl -X POST http://localhost:7860/api/v1/query \
295
- -H 'Content-Type: application/json' \
296
- -d '{
297
- "session_id": "SESSION_ID",
298
- "query": "What is this corpus about?",
299
- "config": {"mode": "Auto", "profile": "Balanced", "model": "gemini-3.5-flash-lite"}
300
- }'
301
 
302
- curl -X POST http://localhost:7860/api/v1/evaluate/demo \
303
- -H 'Content-Type: application/json' \
304
- -d '{
305
- "session_id": "SESSION_ID",
306
- "level": "Standard",
307
- "model": "gemini-3.5-flash-lite",
308
- "target_rpm": 12,
309
- "reuse_saved": true,
310
- "include_profile_benchmark": false
311
- }'
312
  ```
313
 
314
- ## Pipeline profiles
 
 
 
 
315
 
316
- | Profile | Intended use | Behavior |
317
- |---|---|---|
318
- | **Fast** | cheapest interactive path | narrow deterministic route, single document query, hybrid retrieval, answer generation |
319
- | **Balanced** | default portfolio UX | semantic structured planner, task-aware retrieval, optional borderline evidence grader, one corrective retrieval retry, conditional web, answer |
320
- | **Agentic** | showcases advanced RAG | semantic planner, multi-query, optional HyDE, semantic evidence grading, correction loop, conditional web, answer, Self-RAG verification and bounded revision |
321
 
322
- The feature switches remain independent so interviewers can see what each technique changes.
323
 
324
  ## Evaluation
325
 
326
- The built-in **Evaluation** tab is now a layered benchmark instead of a single smoke-test JSON dump. The labels live in `evals/demo_benchmark.json`, so the expected behavior is reviewable rather than hidden in evaluator prompts.
327
 
328
- ### Standard deterministic metrics
329
 
330
- The original demo benchmark became nearly saturated by v1.5. v1.6 deliberately adds harder robustness cases so future changes must improve behavior rather than merely preserve an easy score.
331
 
 
332
 
333
- - focused answer-key accuracy
334
- - source Precision@5, Recall@5, MRR and AP@5
335
- - citation validity and sentence-level citation coverage across `[D#]`, `[T#]` and `[W#]` evidence
336
- - planner route, task and retrieval-strategy accuracy
337
- - web-use precision/recall and unnecessary-web rate
338
- - corpus-overview source coverage and no-unnecessary-web regressions
339
- - explicit empty-workspace abstention correctness
340
- - Text2SQL read-only SQL generation/execution checks; SQL routing is measured separately in the planner suite
341
- - service latency p50/p95, pacing/wall time, correction rate, runtime web-use rate, request count and estimated LLM calls
342
- - retrieval ablation comparing Hybrid RRF with Hybrid + local cross-encoder reranking, including chunk Hit@1/MRR on explicitly labeled cases
343
- - hard-mode robustness across paraphrase, distractor, missing-answer, multi-hop, insight-synthesis, structured filtering, local-freshness and prompt-injection cases
344
- - optional Fast/Balanced/Agentic profile benchmark for quality/latency/LLM-call tradeoffs
345
- - aggregated LangGraph node latency mean/p50/p95
346
- - timestamped evaluation history and score/latency deltas inside the current workspace
347
 
348
- ### Deep LLM-as-judge metrics
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
- Deep mode adds Gemini scores for **faithfulness, answer relevance, completeness and citation support** on a representative labeled sample, reducing free-tier request pressure while retaining diverse judge coverage. These judge scores are kept separate from deterministic metrics because an LLM judge is probabilistic and should not be treated as ground truth. The metric families mirror common RAG evaluation practice: retrieval quality is evaluated separately from generation faithfulness/relevance.
351
 
352
- The UI exposes **Quick**, **Standard** and **Deep** modes and renders a score card plus per-layer tables, with the full report still available as JSON. v1.5 saves the latest run of each depth per workspace, provides a side-by-side comparison table, and lets users switch among saved reports without rerunning. A compatible saved Standard report can act as the deterministic baseline for incremental Deep judging. The benchmark is intentionally small and corpus-specific; it is a regression/architecture-validation suite, not a claim of general RAG benchmark performance.
353
 
354
- Evaluation defaults to **quota-safe pacing at 12 RPM**. The active Gemini limit is project/model specific, so use the value shown for your project in Google AI Studio and set the evaluation target below it. A Standard run uses typed one-call Text2SQL component checks; a Deep run after a compatible saved Standard normally needs only the representative judge calls rather than repeating the full deterministic benchmark.
355
 
356
- ## Model and dependency note
357
 
358
- The Space pins a compatible deployment set around Gradio 5 (`gradio==5.49.1`, `pydantic==2.11.10`, `google-genai==2.8.0`, `fastapi==0.116.1`) to avoid dependency-resolver conflicts while retaining the Gemini Interactions API used by RAGForge. Model names are still runtime-selectable in the UI.
359
 
360
- ## ZIP support
361
 
362
- ZIP upload is useful for testing a miniature knowledge base, but archives are treated as hostile input. Defaults cap compressed upload size, extracted file count, total uncompressed content and supported types; traversal paths are rejected.
363
 
364
- ## Privacy and persistence
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
 
366
- The UI persists only an opaque session ID in browser local storage. Corpus contents, embeddings, DuckDB tables, chat history and saved evaluation reports remain server-side. A normal browser refresh can reconnect while the Space process is alive; a Hugging Face container restart still removes the in-memory/ephemeral workspace. Demo mode can rebuild automatically, while custom uploads and saved evaluations must be recreated after a restart.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
 
368
- This public-demo build intentionally uses per-session ephemeral storage, embedded Qdrant and in-memory DuckDB. A real multi-tenant deployment should replace these with authenticated object storage, tenant-filtered managed retrieval, durable sessions and governed structured-data access.
369
 
370
- ## Enterprise migration path
371
 
372
- - Qdrant local Qdrant Cloud / managed vector DB
373
- - in-process TTL cache → Redis
374
- - in-memory DuckDB → governed warehouse / Postgres read replica
375
- - process-local workspace registry → durable session/tenant service
376
- - local metrics → OpenTelemetry + centralized observability
377
- - simple API token → OAuth/OIDC + tenant-aware authorization
378
- - synchronous ingestion → object-storage events + workers
379
 
380
- ## Project documentation
 
 
 
 
381
 
382
- - `docs/FEATURE_MATRIX.md` - implementation checklist and interview rationale
383
- - `docs/EVALUATION.md` - benchmark methodology, saved-run reuse, typed Text2SQL checks and quota behavior
384
- - `docs/QUERY_PLANNING.md` - semantic planner, hierarchical retrieval and CRAG policy
385
- - `docs/UX_LIFECYCLE.md` - browser/session lifecycle, lazy demo initialization and indexing UX
386
- - `docs/MIGRATION_1.3.md` - v1.2 to v1.3 upgrade notes
387
- - `docs/MIGRATION_1.4.md` - v1.3 to v1.4 upgrade notes
388
- - `docs/MIGRATION_1.4.1.md` - quota-safe evaluation and runtime-view stabilization patch
389
- - `docs/MIGRATION_1.5.md` - saved/incremental evaluation, typed Text2SQL checks and adaptive-reranking upgrade notes
390
- - `docs/ARCHITECTURE_API.md` - live architecture/API surface and endpoint examples
391
- - `docs/MIGRATION_1.2.md` - v1.1 to v1.2 upgrade notes
392
- - `docs/architecture.mmd` - Mermaid architecture source
393
- - `docs/RESUME_BULLETS.md` - resume-ready wording
394
- - `docs/DEMO_DATASETS.md` - corpus-extension ideas
395
- - `docs/SOURCES.md` - architectural references and official implementation docs
396
- - `SECURITY.md` - threat model and residual risk
397
 
398
  ## License
399
 
400
- MIT for this project’s source and synthetic demo files. Bundled/third-party documents retain their original terms.
 
10
 
11
  # RAGForge
12
 
13
+ **RAGForge v2.0.0 - final portfolio release.**
14
+
15
+ RAGForge is an evidence-first RAG system for **documents, structured tables, and the web**. It combines hybrid retrieval, semantic query planning, source-balanced and hierarchical search, analytical synthesis, read-only Text2SQL, corrective retrieval, conditional web research, explicit citations, adaptive context budgeting, and a built-in evaluation harness in one Dockerized FastAPI + Gradio application.
16
+
17
+ The project is designed as a production-style portfolio system rather than a claim of production SaaS readiness. It makes retrieval decisions, evidence quality, citation provenance, evaluation tradeoffs, and runtime limitations visible instead of hiding them behind a chat box.
18
+
19
+ ## Verified pre-final benchmark
20
+
21
+ The final release preserves the runtime architecture measured in the user-verified **v1.9 Standard** run. v2.0 changes benchmark alignment, UI, documentation, and release tooling; it does not claim a new provider-backed benchmark result before deployment.
22
+
23
+ | Metric | Verified result |
24
+ |---|---:|
25
+ | Deterministic quality | **0.994 / Grade A** |
26
+ | Answer accuracy | **100%** |
27
+ | Source Recall@5 | **100%** |
28
+ | Citation validity / coverage | **100% / 100%** |
29
+ | Planner route / task / strategy | **100% / 100% / 100%** |
30
+ | Text2SQL pass | **100%** |
31
+ | Corpus overview pass | **100%** |
32
+ | Adaptive context Recall@5 | **100%** |
33
+ | Median adaptive context reduction | **41%** |
34
+ | Evidence compression signal retention | **100%** |
35
+ | Additional evidence-token reduction | **58%** |
36
+ | Synthetic scale stress | **1,724 chunks / 24 sources** |
37
+ | Scale-stress Recall@5 | **100%** |
38
+ | Release readiness | **READY** |
39
+ | Pipeline latency p50 / p95 | **1.79 s / 5.07 s** |
40
+
41
+ See [`docs/FINAL_RESULTS.md`](docs/FINAL_RESULTS.md) for the full measured baseline, methodology caveats, and optimization findings.
42
+
43
+ ## Why RAGForge
44
+
45
+ Most RAG demos collapse several different questions into one similarity search. RAGForge separates them:
46
+
47
+ 1. **What does the user mean?** A schema-constrained semantic planner identifies knowledge scope, task type, freshness, and retrieval strategy.
48
+ 2. **Where should the evidence come from?** Session-local documents, structured tables, the external web, or a mixed path.
49
+ 3. **How much evidence should reach generation?** Retrieval depth and generation context are adapted to corpus scale, retrieval confidence, source ambiguity, and task breadth.
50
+ 4. **Is the answer actually grounded?** Citations, evidence grading, grounded absence handling, optional Self-RAG verification, and transparent traces make the decision inspectable.
51
+ 5. **Did an optimization help?** The bundled benchmark separately measures retrieval, routing, SQL, citations, robustness, latency, context economics, and scale stress.
52
+
53
+ That distinction is why a query such as **“What is the current corpus about?”** stays local, while **“Compare our NIST document with the latest guidance online”** can legitimately become a hybrid document + web task.
54
+
55
+ ## Core capabilities
56
+
57
+ ### Semantic planning and routing
58
+
59
+ - `corpus`, `external`, `mixed`, and `structured_data` knowledge scopes
60
+ - task types for fact lookup, overview, comparison, cross-document synthesis, aggregation, insight synthesis, and follow-up
61
+ - retrieval strategies: `semantic`, `global`, `hierarchical`, `analytical`, `table`, and `none`
62
+ - independent document and web queries
63
+ - explicit `required | useful | irrelevant` web relevance
64
+ - conversation-aware query rewriting
65
+ - manual Auto / Documents / Web / Hybrid / Data(SQL) overrides
66
+
67
+ ### Retrieval
68
+
69
+ - local FastEmbed dense embeddings with embedded Qdrant
70
+ - BM25 lexical retrieval
71
+ - reciprocal-rank fusion
72
+ - source-profile index for source-first hierarchical retrieval
73
+ - source-balanced global retrieval for corpus overviews
74
+ - analytical document + DuckDB table evidence
75
+ - adaptive cross-encoder reranker policy
76
+ - corpus-scale adaptive retrieval depth
77
+ - dynamic 2-5 chunk focused context budgets
78
+ - deterministic focused sentence compression after retrieval
79
+
80
+ ### Grounded generation
81
+
82
+ - document citations as `[D#]`
83
+ - structured table citations as `[T#]`
84
+ - web citations as `[W#]`
85
+ - deterministic citation normalization and conservative repair
86
+ - grounded-absence handling for genuinely missing information
87
+ - optional CRAG correction loop
88
+ - optional bounded Self-RAG-style verification and revision
89
+ - confidence and complete pipeline trace
90
+
91
+ ### Structured data
92
+
93
+ CSV/XLS/XLSX content is both indexed as text and loaded into an isolated in-memory DuckDB database. The Text2SQL path:
94
+
95
+ - generates a single read-only query
96
+ - validates `SELECT` / CTE-only SQL
97
+ - blocks mutation/admin statements
98
+ - enforces row limits
99
+ - returns typed scalar/table results
100
+ - is independently evaluated against expected computed values
101
 
102
+ ### Ask-the-Web
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
+ - DuckDuckGo keyless fallback
105
+ - optional Tavily
106
+ - optional Gemini Search grounding path
107
+ - independently planned web queries
108
+ - parallel fetch + main-text extraction
109
+ - SSRF-oriented URL checks
110
+ - local reranking and cited synthesis
111
+ - web permission is separate from web relevance, so a weak local retrieval does not automatically leak a private corpus query to the internet
112
 
113
+ ### File support
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
+ PDF, TXT, Markdown, DOCX, PPTX, CSV, XLS/XLSX, JSON, HTML, XML/YAML, code/text formats, common images, and ZIP archives.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
+ ZIP ingestion includes path-traversal, file-count, size, and extension controls. Optional Gemini OCR can transcribe scanned PDFs/images.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
  ## Architecture
120
 
121
  ```mermaid
122
  flowchart TD
123
+ U[User / API] --> G[Input and upload guardrails]
124
+ G --> P{Semantic QueryPlan}
125
+ P -->|structured| SQL[Read-only DuckDB Text2SQL]
126
  P -->|external| W[Ask-the-Web]
127
+ P -->|corpus or mixed| R{Retrieval strategy}
128
 
129
+ R -->|semantic| C[Chunk index]
130
+ R -->|global| GB[Source-balanced retrieval]
131
+ R -->|hierarchical| SP[Source-profile index]
132
+ R -->|analytical| AN[Documents + deterministic table evidence]
133
+ SP --> C
134
 
135
+ C --> D[Dense FastEmbed / Qdrant]
136
  C --> B[BM25]
137
  D --> F[RRF]
138
  B --> F
139
+ F --> RR{Adaptive reranker policy}
140
+ RR --> RD[Scale-aware retrieval depth]
141
+ RD --> CB{Adaptive context budget}
142
+ CB --> EC[Focused evidence compression]
143
+
144
+ GB --> EG{Evidence grade}
145
+ AN --> EG
146
+ EC --> EG
147
+ EG -->|sufficient| A[Grounded Gemini generation]
148
+ EG -->|weak first attempt| CR[Correct retrieval]
149
+ CR --> R
150
+ EG -->|external relevant| W
151
+ EG -->|unsupported local| AB[Abstain]
152
  W --> A
153
 
154
+ A --> V{Optional Self-RAG audit}
155
+ V -->|revise once| RV[Bounded revision]
156
  RV --> V
157
+ V --> O[Cited answer + sources + trace]
158
  SQL --> O
159
  AB --> O
160
+
161
+ O -. cache-bypassed benchmark .-> EV[Evaluation harness]
162
+ EV --> Q[Quality + robustness]
163
+ EV --> EFF[Context / compression / latency]
164
+ EV --> ST[1x / 5x / 20x scale stress]
165
+ Q --> READY[Release readiness]
166
+ EFF --> READY
167
+ ST --> READY
168
  ```
169
 
170
+ The detailed Mermaid source lives in [`docs/architecture.mmd`](docs/architecture.mmd).
171
 
172
+ ## Quick start
 
 
 
 
173
 
174
+ ### Hugging Face Spaces
175
+
176
+ 1. Create a **Docker** Space.
177
+ 2. Copy this repository into the Space root.
178
+ 3. Add `GEMINI_API_KEY` in **Settings -> Secrets**.
179
+ 4. Optionally add `TAVILY_API_KEY` and `APP_API_TOKEN`.
180
+ 5. Push the repository.
181
+ 6. Open the Space with **Use bundled demo files** enabled.
182
+ 7. Use the recommended **Auto + Balanced** profile.
183
 
184
+ The bundled demo corpus can be indexed explicitly or initialized lazily on the first non-Web question.
 
 
185
 
186
+ ### Local Docker
187
 
188
  ```bash
189
  cp .env.example .env
190
+ # add GEMINI_API_KEY to .env
191
+
192
+ docker build -t ragforge .
193
+ docker run --rm -p 7860:7860 --env-file .env ragforge
194
  ```
195
 
196
+ Open `http://localhost:7860`.
197
+
198
+ ### Python development
199
 
200
  ```bash
201
+ python -m pip install -r requirements-dev.txt
202
+ make verify
203
+ make run
204
  ```
205
 
206
+ `make verify` runs linting, tests, Python compilation, and the final release-consistency check.
207
 
208
+ ## Recommended demo flow
 
209
 
210
+ Start with the bundled corpus and **Auto + Balanced**:
 
 
211
 
212
+ ```text
213
+ What is the Sev-1 acknowledgement target?
214
+ ```
 
 
 
 
215
 
216
+ This demonstrates focused retrieval, adaptive context budgeting, sentence compression, a citation, and the latency waterfall.
217
+
218
+ ```text
219
+ What is the collection about?
220
+ ```
221
+
222
+ This demonstrates source-balanced global retrieval and document + table citations.
223
+
224
+ ```text
225
+ What exactly does this collection reveal? Identify important trends and caveats.
226
  ```
227
 
228
+ This demonstrates `insight_synthesis -> analytical` retrieval with `[D#]` and `[T#]` evidence.
229
+
230
+ ```text
231
+ Which support tier has the shortest first-response SLA?
232
+ ```
233
 
234
+ This demonstrates semantic routing into read-only Text2SQL.
 
 
 
 
235
 
236
+ A short portfolio/demo script is available in [`docs/PORTFOLIO_GUIDE.md`](docs/PORTFOLIO_GUIDE.md).
237
 
238
  ## Evaluation
239
 
240
+ RAGForge treats evaluation as part of the application rather than a notebook afterthought.
241
 
242
+ ### Quick
243
 
244
+ Small smoke test for deployment sanity.
245
 
246
+ ### Standard
247
 
248
+ Full deterministic suite covering:
 
 
 
 
 
 
 
 
 
 
 
 
 
249
 
250
+ - focused QA
251
+ - route/task/strategy planning
252
+ - web-use precision and recall
253
+ - corpus overview
254
+ - typed Text2SQL
255
+ - abstention
256
+ - Hard Mode robustness
257
+ - retrieval + reranker ablation
258
+ - adaptive context-budget ablation
259
+ - evidence-compression ablation
260
+ - node latency
261
+ - synthetic 1x / 5x / 20x scale stress
262
+ - release-readiness checklist
263
 
264
+ ### Deep
265
 
266
+ Reuses a compatible saved Standard baseline when possible and adds a small calibrated Gemini judge sample. This keeps free-tier request pressure bounded.
267
 
268
+ Evaluation uses rolling request pacing and honors provider 429 retry guidance. Saved Quick/Standard/Deep runs can be compared without rerunning them.
269
 
270
+ See [`docs/EVALUATION.md`](docs/EVALUATION.md).
271
 
272
+ ## REST API
273
 
274
+ FastAPI exposes Swagger at `/docs` and OpenAPI at `/openapi.json`.
275
 
276
+ Key endpoints:
277
 
278
+ ```text
279
+ GET /api/health
280
+ GET /api/v1/info
281
+ POST /api/v1/session
282
+ GET /api/v1/session/{session_id}
283
+ GET /api/v1/session/{session_id}/diagnostics
284
+ POST /api/v1/ingest
285
+ POST /api/v1/query
286
+ POST /api/v1/evaluate/demo
287
+ GET /api/v1/evaluation/benchmark
288
+ GET /api/v1/evaluation/saved/{session_id}
289
+ GET /api/v1/evaluation/history/{session_id}
290
+ GET /metrics
291
+ ```
292
+
293
+ Example:
294
+
295
+ ```bash
296
+ curl -X POST "$BASE_URL/api/v1/query" \
297
+ -H "Content-Type: application/json" \
298
+ -d '{
299
+ "session_id": "YOUR_SESSION_ID",
300
+ "query": "What is the collection about?",
301
+ "config": {"mode": "Auto", "profile": "Balanced"}
302
+ }'
303
+ ```
304
 
305
+ See [`docs/ARCHITECTURE_API.md`](docs/ARCHITECTURE_API.md).
306
+
307
+ ## Security model and honest limitations
308
+
309
+ RAGForge demonstrates defense-in-depth controls for archive ingestion, retrieved prompt injection, SQL safety, SSRF-oriented web fetching, session isolation, secrets, and rate limiting. It is **not** a compliance-certified multi-tenant service.
310
+
311
+ Important limitations:
312
+
313
+ - workspace/vector/SQL state is process-local and ephemeral on a standard Space
314
+ - browser persistence stores only an opaque workspace ID and does not make server data durable
315
+ - large production deployments need durable tenant state, storage-level tenant filters, worker isolation, external vector/database infrastructure, and stronger egress controls
316
+ - prompt-injection detection remains heuristic
317
+ - token counts in traces are estimates for engineering comparison, not provider billing records
318
+ - the 1,724-chunk scale result is a deterministic synthetic distractor stress test, not a substitute for a real enterprise corpus benchmark
319
+ - free-tier Gemini quotas and data-use terms depend on the active provider project/tier
320
+
321
+ See [`SECURITY.md`](SECURITY.md).
322
+
323
+ ## Repository map
324
+
325
+ ```text
326
+ src/ragforge/
327
+ pipeline.py LangGraph RAG orchestration
328
+ llm.py Gemini planning/generation/judging
329
+ retrieval.py dense + BM25 + RRF + reranking
330
+ context_budget.py adaptive retrieval/context policy
331
+ evidence_compression.py focused local sentence compression
332
+ workspace.py session corpus, indexes, SQL, eval history
333
+ evaluation.py benchmark + diagnostics + readiness
334
+ stress_eval.py zero-Gemini scale-stress harness
335
+ sql_agent.py read-only Text2SQL
336
+ web_search.py external research path
337
+ security.py ingestion/query/web guardrails
338
+ ui.py Gradio application
339
+ api.py FastAPI surface
340
+
341
+ evals/
342
+ demo_benchmark.json transparent benchmark labels
343
+
344
+ docs/
345
+ FINAL_RESULTS.md verified pre-final benchmark
346
+ PORTFOLIO_GUIDE.md demo and interview walkthrough
347
+ EVALUATION.md evaluation methodology
348
+ ARCHITECTURE_API.md architecture/API reference
349
+ FEATURE_MATRIX.md feature inventory
350
+ QUERY_PLANNING.md planner semantics
351
+ UX_LIFECYCLE.md session/UI lifecycle
352
+ RESUME_BULLETS.md concise project bullets
353
+ ```
354
 
355
+ ## Final project status
356
 
357
+ **v2.0.0 is the final planned feature release.** Future work should be driven by a genuinely larger real-world corpus or deployment requirement, not by adding more RAG acronyms.
358
 
359
+ The measured engineering conclusions are already useful:
 
 
 
 
 
 
360
 
361
+ - source recall is saturated on the demo benchmark, so context economics mattered more than another retriever
362
+ - the cross-encoder reranker was hundreds of times slower on the small demo with no measured source/chunk ranking gain, so runtime policy skips it there
363
+ - adaptive context budgeting and sentence compression reduced generation input while preserving benchmark recall/signals
364
+ - generation dominates latency, making prompt/context reduction more valuable than micro-optimizing millisecond retrieval
365
+ - evaluation exposed multiple evaluator/UI bugs during development, demonstrating why the benchmark itself needed to be audited as carefully as the RAG pipeline
366
 
367
+ For project history, see [`CHANGELOG.md`](CHANGELOG.md).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
 
369
  ## License
370
 
371
+ See [`LICENSE`](LICENSE).
SECURITY.md CHANGED
@@ -1,5 +1,11 @@
1
  # Security model
2
 
 
 
 
 
 
 
3
  ## v1.9 resource and scale controls
4
 
5
  v1.9 adds explicit workspace-health telemetry for chunk-cap utilization, corpus scale, approximate in-memory vector size, session TTL/idle age and index readiness. Adaptive retrieval depth remains bounded (up to 12 candidates by policy), focused generation budgets remain bounded (2-5 chunks), and sentence compression only changes the generator copy of retrieved text. The synthetic scale-stress evaluator reuses already-computed vectors and is isolated so a local stress-harness failure cannot crash the primary Standard/Deep report.
@@ -63,4 +69,4 @@ Saved evaluation metadata contains only operational provenance (short run ID, se
63
 
64
  ## Context pruning safety (v1.8)
65
 
66
- Focused context pruning is intentionally post-retrieval and task-scoped. It does not weaken prompt-injection scanning, source access controls, archive hardening or web SSRF protections. Broad/multi-source/analytical tasks bypass pruning, and the three-chunk safety floor is evaluated against source recall before the optimization is treated as successful.
 
1
  # Security model
2
 
3
+ ## v2.0 final security posture
4
+
5
+ v2.0 does not add a new external execution surface. The final release keeps the existing archive, SQL, SSRF, session-isolation, prompt-injection, secret-handling and rate-limit controls, while clarifying operational limits in the README and portfolio documentation. `make verify` now includes a dependency-free release-consistency check so version/benchmark/documentation drift is caught in CI.
6
+
7
+ The project remains a hardened **portfolio/demo** application, not a compliance-certified multi-tenant SaaS.
8
+
9
  ## v1.9 resource and scale controls
10
 
11
  v1.9 adds explicit workspace-health telemetry for chunk-cap utilization, corpus scale, approximate in-memory vector size, session TTL/idle age and index readiness. Adaptive retrieval depth remains bounded (up to 12 candidates by policy), focused generation budgets remain bounded (2-5 chunks), and sentence compression only changes the generator copy of retrieved text. The synthetic scale-stress evaluator reuses already-computed vectors and is isolated so a local stress-harness failure cannot crash the primary Standard/Deep report.
 
69
 
70
  ## Context pruning safety (v1.8)
71
 
72
+ Focused context pruning is intentionally post-retrieval and task-scoped. It does not weaken prompt-injection scanning, source access controls, archive hardening or web SSRF protections. Broad/multi-source/analytical tasks bypass pruning, and the three-chunk safety floor is evaluated against source recall before the optimization is treated as successful.
docs/ARCHITECTURE_API.md CHANGED
@@ -1,4 +1,8 @@
1
- # Architecture and API - v1.9
 
 
 
 
2
 
3
  ## v1.9 adaptive scale and context pipeline
4
 
@@ -154,4 +158,4 @@ Evaluation cache metadata includes a short run ID and server-boot ID. These fiel
154
 
155
  The verify path now recognizes a grounded absence answer: an evidence-cited statement that the requested fact is not present in the selected sources. This state skips the normal low-confidence revise branch, preventing a second generation call whose only purpose would be to restate the same absence.
156
 
157
- For corpus overviews, structured tables are surfaced as deterministic `[T#]` evidence alongside the source-balanced document set. Analytical synthesis continues to use the same table evidence path.
 
1
+ # Architecture and API - v2.0 final
2
+
3
+ ## Final system shape
4
+
5
+ v2.0 freezes the feature architecture around semantic routing, hybrid/source-balanced/hierarchical/analytical retrieval, adaptive context budgeting, focused evidence compression, Text2SQL, conditional web research, grounded generation, transparent traces, and component-level evaluation. The final release focuses on product presentation, benchmark alignment, documentation, and release verification rather than adding another retrieval subsystem.
6
 
7
  ## v1.9 adaptive scale and context pipeline
8
 
 
158
 
159
  The verify path now recognizes a grounded absence answer: an evidence-cited statement that the requested fact is not present in the selected sources. This state skips the normal low-confidence revise branch, preventing a second generation call whose only purpose would be to restate the same absence.
160
 
161
+ For corpus overviews, structured tables are surfaced as deterministic `[T#]` evidence alongside the source-balanced document set. Analytical synthesis continues to use the same table evidence path.
docs/EVALUATION.md CHANGED
@@ -1,4 +1,10 @@
1
- # RAGForge evaluation - v1.9
 
 
 
 
 
 
2
 
3
  ## v1.9 scale, compression and readiness evaluation
4
 
@@ -354,4 +360,4 @@ Citation evaluation accepts both one-ID-per-bracket syntax such as `[D1] [D2]` a
354
  syntax such as `[D1, D2]`. Runtime answer rendering normalizes grouped citations and removes
355
  duplicate trailing citation sets. The deterministic zero-call citation repair skips list-introduction
356
  or preamble lines ending in `:` so it does not attach a source merely because a broad introductory
357
- sentence overlaps many evidence snippets.
 
1
+ # RAGForge evaluation - v2.0 final
2
+
3
+ ## Final benchmark alignment
4
+
5
+ v2.0 preserves the v1.9 deterministic evaluation architecture and bumps the benchmark to `2.0` because the multi-hop comparison Hard Mode case is now executed through the recommended `Auto + Balanced` semantic route. The case must resolve to `documents -> comparison -> hierarchical` and still satisfy the answer/retrieval labels. This prevents Fast mode's intentionally cheap fallback from being treated as the reference behavior for a multi-source reasoning test.
6
+
7
+ The last provider-backed user-verified Standard baseline is documented in [`FINAL_RESULTS.md`](FINAL_RESULTS.md).
8
 
9
  ## v1.9 scale, compression and readiness evaluation
10
 
 
360
  syntax such as `[D1, D2]`. Runtime answer rendering normalizes grouped citations and removes
361
  duplicate trailing citation sets. The deterministic zero-call citation repair skips list-introduction
362
  or preamble lines ending in `:` so it does not attach a source merely because a broad introductory
363
+ sentence overlaps many evidence snippets.
docs/FEATURE_MATRIX.md CHANGED
@@ -1,4 +1,6 @@
1
- # Feature matrix and design rationale
 
 
2
 
3
  | Capability | RAGForge implementation | Why it matters |
4
  |---|---|---|
@@ -79,4 +81,4 @@ The demo uses embedded Qdrant, in-memory DuckDB, deterministic source profiles,
79
 
80
  | Evaluation provenance | run ID + server-boot ID + fresh/reused status | makes cache/rebuild behavior auditable and prevents fresh runs from being mistaken for saved reuse |
81
  | Context-efficiency diagnostics | source Precision@5 beside Recall@5 + targeted recommendations | exposes distractor-heavy context without sacrificing overview/synthesis breadth prematurely |
82
- | Grounded absence | evidence-cited missing-information answers skip unnecessary revise | rewards calibrated uncertainty and reduces extra model calls on unanswerable local questions |
 
1
+ # Feature matrix - v2.0 final
2
+
3
+ The table below describes the final planned portfolio release. Future changes should be driven by real deployment/corpus requirements rather than feature-count growth.
4
 
5
  | Capability | RAGForge implementation | Why it matters |
6
  |---|---|---|
 
81
 
82
  | Evaluation provenance | run ID + server-boot ID + fresh/reused status | makes cache/rebuild behavior auditable and prevents fresh runs from being mistaken for saved reuse |
83
  | Context-efficiency diagnostics | source Precision@5 beside Recall@5 + targeted recommendations | exposes distractor-heavy context without sacrificing overview/synthesis breadth prematurely |
84
+ | Grounded absence | evidence-cited missing-information answers skip unnecessary revise | rewards calibrated uncertainty and reduces extra model calls on unanswerable local questions |
docs/FINAL_RESULTS.md ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Final measured results
2
+
3
+ This page records the **user-verified v1.9 Standard benchmark** immediately before the v2.0 final productization release. v2.0 preserves the measured runtime architecture but changes benchmark alignment for one multi-hop Hard Mode case, plus UI/docs/release tooling. Therefore these numbers are presented as the verified pre-final baseline rather than silently relabeled as a v2.0 provider-backed run.
4
+
5
+ ## Quality
6
+
7
+ | Metric | Result |
8
+ |---|---:|
9
+ | Deterministic quality | 0.994 (Grade A) |
10
+ | Answer accuracy | 100% |
11
+ | Source Precision@5 | 48% |
12
+ | Source Recall@5 | 100% |
13
+ | Hit@1 | 100% |
14
+ | MRR | 1.000 |
15
+ | AP@5 | 1.000 |
16
+ | nDCG@5 | 1.000 |
17
+ | Citation validity | 100% |
18
+ | Citation coverage | 100% |
19
+ | Corpus overview pass | 100% |
20
+ | Text2SQL pass | 100% |
21
+ | Planner route/task/strategy | 100% / 100% / 100% |
22
+ | Web precision/recall | 100% / 100% |
23
+ | Hard Mode | 88% |
24
+ | Release readiness | READY (100% critical gates) |
25
+
26
+ The single v1.9 Hard Mode miss was the multi-hop Acme-vs-OrbitPay timing comparison. That case was being executed through Fast mode's cheap fallback even though the application recommendation is Auto + Balanced for semantic multi-source reasoning. v2.0 aligns that case with the recommended route and explicitly requires `comparison -> hierarchical` planning.
27
+
28
+ ## Context economics
29
+
30
+ | Configuration | Precision@5 | Recall@5 | Median chunks | Median context tokens | Reduction |
31
+ |---|---:|---:|---:|---:|---:|
32
+ | Full top-k | 47.2% | 100% | 6 | 2,101 | 0% |
33
+ | v1.8 fixed top-3 | 50.0% | 100% | 3 | 977 | 54.9% |
34
+ | v1.9 adaptive budget | 48.1% | 100% | 4 | 1,099 | 41.0% |
35
+
36
+ The final runtime keeps adaptive budgeting rather than the globally tighter fixed top-3 policy because it retains extra safety margin for ambiguous and larger-corpus cases.
37
+
38
+ ## Evidence compression
39
+
40
+ | Configuration | Answer-signal retention | Median evidence tokens | Additional reduction |
41
+ |---|---:|---:|---:|
42
+ | Adaptive context only | 100% | 1,056 | 0% |
43
+ | Adaptive + sentence compression | 100% | 503 | 58% |
44
+
45
+ Compression modifies only the generation copy of retrieved evidence. Source cards and citation provenance retain the original retrieved chunks.
46
+
47
+ ## Scale stress
48
+
49
+ The zero-Gemini synthetic stress harness reused existing long-document vectors and exercised the real hybrid retriever with distractor clones.
50
+
51
+ | Scale | Chunks | Sources | Recall@5 | Hit@1 | MRR | Adaptive-pruned Recall@5 | Retrieval median |
52
+ |---|---:|---:|---:|---:|---:|---:|---:|
53
+ | Base | 90 | 5 | 100% | 100% | 1.000 | 100% | 5.5 ms |
54
+ | +4x long-doc distractors | 434 | 9 | 100% | 100% | 1.000 | 100% | 7.5 ms |
55
+ | +19x long-doc distractors | 1,724 | 24 | 100% | 87.5% | 0.938 | 100% | 48.2 ms |
56
+
57
+ This is evidence that Recall@5 remained robust under synthetic distractor scale. It is not a substitute for a real enterprise corpus benchmark.
58
+
59
+ ## Reranker ablation
60
+
61
+ The demo benchmark repeatedly found no source- or chunk-level ranking gain from the cross-encoder on the small corpus while retrieval latency increased by orders of magnitude. v1.9 measured roughly a **290x** retrieval-time multiplier in the explicit ablation.
62
+
63
+ RAGForge therefore keeps the reranker available but skips it under the measured small-corpus policy. Larger corpora remain eligible because the demo does not prove the reranker is universally unnecessary.
64
+
65
+ ## Runtime
66
+
67
+ | Metric | Result |
68
+ |---|---:|
69
+ | Pipeline p50 | 1.79 s |
70
+ | Pipeline p95 | 5.07 s |
71
+ | Planner p50 | 1.71 s |
72
+ | Planner p95 | 1.84 s |
73
+ | Mean estimated LLM calls | 0.76 |
74
+ | Standard Gemini requests | 36 |
75
+ | Evaluation target | 12 RPM |
76
+ | 429 retries | 0 |
77
+
78
+ Generation remained the dominant node by a wide margin, which motivated context/prompt optimization rather than further millisecond-scale retrieval tuning.
79
+
80
+ ## Interpretation
81
+
82
+ The final engineering conclusions are more important than the headline grade:
83
+
84
+ - retrieval recall is strong enough that **context selection** is now the more useful optimization surface
85
+ - deterministic evaluation caught several bugs in the evaluator itself, including invalid AP calculations, missing table-citation sources, false citation-coverage failures, and missing-answer false negatives
86
+ - measured reranker cost changed runtime policy rather than remaining a decorative architecture component
87
+ - benchmark request pacing made evaluation repeatable under a 15-RPM free-tier project without 429 failures
88
+ - the project exposes limitations rather than treating a synthetic stress run as proof of production scale
docs/MIGRATION_2.0.md ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Migration to RAGForge v2.0.0
2
+
3
+ v2.0 is the final planned feature release over v1.9. It is primarily a productization and benchmark-alignment release.
4
+
5
+ ## Runtime behavior
6
+
7
+ The retrieval/generation architecture from v1.9 is preserved:
8
+
9
+ - corpus-scale adaptive retrieval depth
10
+ - dynamic 2-5 chunk focused context budgets
11
+ - focused evidence sentence compression
12
+ - source-balanced/global/hierarchical/analytical retrieval
13
+ - adaptive reranker policy
14
+ - CRAG/Self-RAG controls
15
+ - Text2SQL and conditional web research
16
+
17
+ The Hard Mode multi-hop comparison case is now evaluated through `Auto + Balanced` and must resolve to `comparison -> hierarchical`. This aligns the benchmark with the recommended semantic path.
18
+
19
+ ## UI
20
+
21
+ - final v2.0 product hero and measured-baseline cards
22
+ - Chat/Evaluation hierarchy simplified
23
+ - recommended profile guidance surfaced
24
+ - evaluation score-card metric tiles
25
+ - duplicate Architecture runtime header fixed
26
+ - final project-status footer
27
+
28
+ ## Documentation/release tooling
29
+
30
+ - README rewritten as a finished product page
31
+ - `CHANGELOG.md` added
32
+ - `docs/FINAL_RESULTS.md` added
33
+ - `docs/PORTFOLIO_GUIDE.md` added
34
+ - `scripts/release_check.py` added
35
+ - `make smoke` and `make verify` added
36
+ - CI now runs compilation and the release consistency checker
37
+
38
+ ## Benchmark compatibility
39
+
40
+ The benchmark version is `2.0`. Existing v1.9 saved reports remain historical and are not reused as current v2.0 Standard/Deep baselines.
41
+
42
+ No new runtime dependency is introduced.
docs/PORTFOLIO_GUIDE.md ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Portfolio and interview guide
2
+
3
+ ## 2-minute demo
4
+
5
+ Use the bundled corpus with **Auto + Balanced**.
6
+
7
+ ### 1. Focused retrieval
8
+
9
+ Ask:
10
+
11
+ ```text
12
+ What is the Sev-1 acknowledgement target?
13
+ ```
14
+
15
+ Show:
16
+
17
+ - one-sentence grounded answer with `[D1]`
18
+ - semantic route / fact lookup
19
+ - adaptive context budget
20
+ - evidence sentence compression
21
+ - reranker skipped with an explicit policy reason
22
+ - node latency waterfall
23
+
24
+ Talking point: RAGForge separates retrieval depth from generation context. It can retrieve broadly enough for safety, then reduce what reaches the model.
25
+
26
+ ### 2. Global corpus overview
27
+
28
+ Ask:
29
+
30
+ ```text
31
+ What is the collection about?
32
+ ```
33
+
34
+ Show:
35
+
36
+ - source-balanced global retrieval
37
+ - all five source families represented
38
+ - `[D#]` document citations and `[T#]` table evidence
39
+ - broad tasks bypass focused pruning/compression
40
+
41
+ Talking point: long PDFs do not dominate simply because they create more chunks.
42
+
43
+ ### 3. Analytical synthesis
44
+
45
+ Ask:
46
+
47
+ ```text
48
+ What exactly does this collection reveal? Identify important trends and caveats.
49
+ ```
50
+
51
+ Show:
52
+
53
+ - `insight_synthesis -> analytical`
54
+ - deterministic DuckDB table evidence combined with document evidence
55
+ - quantitative table claims cited as `[T#]`
56
+
57
+ Talking point: the analytical route is different from a generic document overview and can reason across text + structured data.
58
+
59
+ ### 4. Text2SQL
60
+
61
+ Ask:
62
+
63
+ ```text
64
+ Which support tier has the shortest first-response SLA?
65
+ ```
66
+
67
+ Show the SQL route and typed result.
68
+
69
+ Talking point: routing and SQL execution are independently evaluated. The SQL path is restricted to validated read-only queries.
70
+
71
+ ### 5. Evaluation
72
+
73
+ Open Standard evaluation results and show:
74
+
75
+ - component metrics instead of one opaque score
76
+ - context-budget and compression ablations
77
+ - scale stress
78
+ - node latency
79
+ - release readiness
80
+
81
+ Talking point: evaluation findings changed runtime policy. For example, the small-corpus reranker is skipped because the ablation showed large latency cost without measured ranking gain.
82
+
83
+ ## 30-second architecture explanation
84
+
85
+ > RAGForge first uses a schema-constrained planner to decide whether the question needs documents, SQL, the web, or a mixed path. Document retrieval can be semantic, source-balanced global, hierarchical source-to-chunk, or analytical document-plus-table retrieval. Dense and BM25 results are fused with RRF, then an adaptive policy decides retrieval depth, reranking, context budget, and focused sentence compression. Evidence is graded before generation, CRAG can retry retrieval, web search is conditional rather than automatic, and answers return explicit document/table/web citation IDs plus a full execution trace.
86
+
87
+ ## Strong engineering tradeoffs to discuss
88
+
89
+ ### Reranking
90
+
91
+ The implementation contains a cross-encoder, but the runtime does not blindly pay for it. The demo ablation showed no source/chunk gain at small scale while adding multi-second retrieval latency, so the policy skips it there and preserves it for larger/harder workloads.
92
+
93
+ ### Context precision vs recall
94
+
95
+ The demo reached 100% Recall@5 with modest Precision@5. Instead of lowering top-k globally, RAGForge retrieves enough candidates, then applies a generation-specific adaptive context budget. That preserves recall while reducing model input.
96
+
97
+ ### Free-tier evaluation
98
+
99
+ A full benchmark can exceed provider RPM limits if every component fires immediately. RAGForge uses a rolling request pacer, honors retry guidance, stores reports, and lets Deep reuse Standard so evaluation itself is operationally realistic.
100
+
101
+ ### Evaluation bugs are real bugs
102
+
103
+ During development, the evaluator itself produced invalid AP values, missed grouped citations, penalized list citations, and misclassified correct missing-information answers. Fixing the measurement system was part of the engineering work.
104
+
105
+ ## Honest limitations to mention
106
+
107
+ - standard Hugging Face runtime state is ephemeral
108
+ - no durable multi-tenant storage or distributed vector infrastructure
109
+ - prompt-injection filtering is heuristic defense-in-depth
110
+ - synthetic 1,724-chunk stress is not an enterprise-scale proof
111
+ - token telemetry is estimated, not provider billing data
112
+ - provider model quotas and privacy terms depend on the active tier/project
113
+
114
+ ## Resume-ready one-liner
115
+
116
+ > Built RAGForge, a Dockerized FastAPI/Gradio agentic RAG system with semantic routing, hybrid + hierarchical retrieval, analytical document/table synthesis, read-only Text2SQL, conditional web research, adaptive context compression, cited answers, and an evaluation suite spanning robustness, citation quality, retrieval ablations, scale stress, latency, and release-readiness gates.
docs/QUERY_PLANNING.md CHANGED
@@ -1,4 +1,8 @@
1
- # Semantic planning, hierarchical retrieval and corrective policy
 
 
 
 
2
 
3
  ## v1.9 task/strategy consistency and scale-aware retrieval
4
 
@@ -177,4 +181,4 @@ cross_document_synthesis
177
  analytical/global retrieval
178
  ```
179
 
180
- This separation prevents a latency optimization from silently redefining the user's information need. For focused fact lookups, generation also omits the full corpus manifest because the planner has already established corpus scope; broad and mixed tasks retain it.
 
1
+ # Query planning - v2.0 final
2
+
3
+ ## Final planner contract
4
+
5
+ The recommended general-purpose profile remains `Auto + Balanced`. v2.0 also aligns the multi-hop Hard Mode reference case with that semantic path so multi-source comparison is evaluated as `comparison -> hierarchical` rather than through Fast mode's intentionally cheap fallback. No phrase-specific production route was added.
6
 
7
  ## v1.9 task/strategy consistency and scale-aware retrieval
8
 
 
181
  analytical/global retrieval
182
  ```
183
 
184
+ This separation prevents a latency optimization from silently redefining the user's information need. For focused fact lookups, generation also omits the full corpus manifest because the planner has already established corpus scope; broad and mixed tasks retain it.
docs/RESUME_BULLETS.md CHANGED
@@ -1,3 +1,13 @@
 
 
 
 
 
 
 
 
 
 
1
  - Extended RAGForge with an **Insight Synthesis** path that semantically distinguishes collection overviews from trend/pattern analysis, combines source-balanced document evidence with deterministic DuckDB descriptive context, and grounds structured claims with `[T#]` citations.
2
  - Evolved the evaluation suite into a **hard-mode robustness benchmark** covering paraphrase, distractors, missing answers, multi-hop reasoning, local freshness semantics, analytical synthesis, structured filtering and prompt-injection checks; added hard-mode quality gates.
3
  - Added **chunk-level reranker ablations** and an opt-in Fast/Balanced/Agentic profile benchmark, quantifying evidence quality, latency, LLM-call estimates, correction use and reranker tradeoffs rather than assuming more agentic steps are always better.
 
1
+ # Resume bullets - v2.0 final
2
+
3
+ ## Recommended final bullet
4
+
5
+ - Built **RAGForge**, a Dockerized FastAPI/Gradio agentic RAG system with schema-constrained semantic routing, hybrid + hierarchical retrieval, analytical document/table synthesis, read-only Text2SQL, conditional web research, adaptive context compression, cited answers, and a benchmark covering retrieval, citations, robustness, scale stress, latency and release-readiness gates.
6
+
7
+ ## Measured supporting result
8
+
9
+ - On the user-verified v1.9 Standard baseline, achieved 100% answer accuracy, Recall@5, citation validity/coverage, planner route/task/strategy accuracy and Text2SQL pass; adaptive context budgeting preserved 100% Recall@5 while reducing median context by 41%, with sentence compression retaining 100% labeled answer signals and cutting selected evidence tokens by a further 58%.
10
+
11
  - Extended RAGForge with an **Insight Synthesis** path that semantically distinguishes collection overviews from trend/pattern analysis, combines source-balanced document evidence with deterministic DuckDB descriptive context, and grounds structured claims with `[T#]` citations.
12
  - Evolved the evaluation suite into a **hard-mode robustness benchmark** covering paraphrase, distractors, missing answers, multi-hop reasoning, local freshness semantics, analytical synthesis, structured filtering and prompt-injection checks; added hard-mode quality gates.
13
  - Added **chunk-level reranker ablations** and an opt-in Fast/Balanced/Agentic profile benchmark, quantifying evidence quality, latency, LLM-call estimates, correction use and reranker tradeoffs rather than assuming more agentic steps are always better.
docs/SOURCES.md CHANGED
@@ -1,4 +1,8 @@
1
- # Reference projects and implementation sources
 
 
 
 
2
 
3
  No tutorial source code is copied into RAGForge. The requested projects were used as architectural inspiration/checklists.
4
 
@@ -75,4 +79,4 @@ v1.8 adds no new external benchmark or hosted dependency. The context-budget pol
75
 
76
  ## v1.9 implementation note
77
 
78
- v1.9 adds no external runtime dependency and no new third-party architecture source. Adaptive budgets, sentence compression, scale stress, workspace diagnostics and readiness scoring are implemented locally on top of the existing retrieval/evaluation stack. The scale harness reuses existing embedding vectors rather than calling an embedding or generation API.
 
1
+ # Sources and implementation references - v2.0 final
2
+
3
+ ## Final-release note
4
+
5
+ v2.0 introduces no new runtime dependency or external architectural component. The final changes are benchmark alignment, UI/documentation polish, and local release-verification tooling. Existing official/source references below continue to document the underlying Gradio, FastAPI, Gemini, LangGraph, Qdrant and retrieval behavior.
6
 
7
  No tutorial source code is copied into RAGForge. The requested projects were used as architectural inspiration/checklists.
8
 
 
79
 
80
  ## v1.9 implementation note
81
 
82
+ v1.9 adds no external runtime dependency and no new third-party architecture source. Adaptive budgets, sentence compression, scale stress, workspace diagnostics and readiness scoring are implemented locally on top of the existing retrieval/evaluation stack. The scale harness reuses existing embedding vectors rather than calling an embedding or generation API.
docs/UX_LIFECYCLE.md CHANGED
@@ -1,4 +1,8 @@
1
- # RAGForge UI and workspace lifecycle
 
 
 
 
2
 
3
  ## v1.9 runtime health and capacity visibility
4
 
@@ -103,4 +107,4 @@ Saved evaluation switching is bound to user input rather than generic change eve
103
 
104
  Pipeline Inspector now exposes the focused context-budget decision directly after the reranker decision. For eligible fact lookups it shows chunks and estimated tokens before/after pruning plus reduction percentage. Broad tasks show a skip reason rather than a misleading zero.
105
 
106
- The Evaluation tab adds a **Context budget** table and export option. This ablation is deterministic and uses no additional Gemini calls, so users can inspect context-efficiency tradeoffs without increasing free-tier request pressure.
 
1
+ # UX and lifecycle - v2.0 final
2
+
3
+ ## Final product polish
4
+
5
+ v2.0 gives the UI a compact product hero, verified-baseline cards, clearer Chat/Evaluation hierarchy, recommended-profile guidance, evaluation metric cards, a cleaned runtime snapshot, and a final project-status footer. The underlying session/lazy-demo/indexing/query/evaluation lifecycle remains the same.
6
 
7
  ## v1.9 runtime health and capacity visibility
8
 
 
107
 
108
  Pipeline Inspector now exposes the focused context-budget decision directly after the reranker decision. For eligible fact lookups it shows chunks and estimated tokens before/after pruning plus reduction percentage. Broad tasks show a skip reason rather than a misleading zero.
109
 
110
+ The Evaluation tab adds a **Context budget** table and export option. This ablation is deterministic and uses no additional Gemini calls, so users can inspect context-efficiency tradeoffs without increasing free-tier request pressure.
docs/architecture.mmd CHANGED
@@ -1,81 +1,69 @@
1
  flowchart TD
2
  U[User / API] --> UI{UI lifecycle state}
3
- UI -->|demo selected + empty| DI[Lazy demo indexing]
4
- UI -->|ready| G[Input guardrails]
5
  DI --> G
6
- G --> P{Semantic QueryPlan}
 
7
  P --> PF{Workspace preflight}
8
  PF -->|missing local data| AB[Abstain]
9
  PF -->|structured data| SQL[Read-only Text2SQL / DuckDB]
10
  PF -->|external| W[Ask-the-Web]
11
- PF -->|corpus or mixed| RS{Task-aware retrieval strategy}
12
 
13
  RS -->|semantic| CI[Chunk index]
14
- RS -->|global| GB[Source-balanced global evidence]
15
  RS -->|hierarchical| SPI[Source-profile index]
16
- RS -->|analytical| AN[Source-balanced docs + DuckDB analytical context]
17
- SPI --> SEL[Semantic source selection]
18
- SEL --> SCI[Source-scoped chunk search]
19
- SCI --> CI
20
 
21
  CI --> D[FastEmbed dense / Qdrant]
22
- CI --> B[BM25 sparse]
23
  D --> F[RRF]
24
  B --> F
25
  F --> RP{Adaptive reranker policy}
26
- RP -->|skip when policy says RRF is enough| RD{Scale-aware retrieval depth}
27
- RP -->|use for harder / larger tasks| X[Cross-encoder reranker]
28
- X --> RD
29
- RD --> CB{Adaptive 2-5 chunk context budget}
30
- CB -->|focused lookup| EC[Query-focused sentence compression]
31
- CB -->|broad task / disabled| EG{Task-aware evidence grader}
32
- EC --> EG
33
  GB --> EG
34
  AN --> EG
 
35
  EG -->|sufficient| A[Grounded Gemini generation]
36
- EG -->|weak first attempt| COR[Correct queries / strategy]
37
  COR --> RS
38
- EG -->|weak + web relevant + allowed| W
39
- EG -->|weak + web irrelevant| AB
40
  W --> A
41
 
42
- A --> V{Self-RAG verifier}
43
- V -->|revise once| RV[Faithfulness revision]
44
  RV --> V
45
- V -->|pass| O[Cited answer + sources + trace]
46
  SQL --> O
47
  AB --> O
48
 
49
- O -. cache-bypassed fresh benchmark .-> EV[Evaluation harness]
50
- EV --> ER[Bounded source metrics + reranker ablation]
51
- EV --> CBA[Focused context-budget ablation]
52
- EV --> EP[Planner / web-policy metrics]
53
- EV --> EC[Citation + answer-key metrics]
54
- EV --> ES[Text2SQL + abstention + latency]
55
- EV --> HM[Hard-mode robustness]
56
- EV --> PB[Optional profile benchmark]
57
- EV --> NL[Node latency summary]
58
- EV --> EJ[Calibrated Gemini Deep judge]
59
- ER --> QG[Quality gates + diagnostics]
60
- CBA --> QG
61
- EP --> QG
62
- EC --> QG
63
- ES --> QG
64
- EJ --> QG
65
- HM --> QG
66
- PB --> QG
67
- NL --> QG
68
- QG --> EH[Saved Quick / Standard / Deep reports]
69
- EH --> CMP[In-app comparison + saved-report API]
70
- EH --> HIST[Timestamped evaluation history + deltas]
71
- EH -. compatible Standard baseline .-> EJ
72
-
73
- API[FastAPI /docs + OpenAPI + Prometheus] -. live introspection .-> UI
74
-
75
- %% v1.8: focused local lookups can prune the distractor tail after ranking; broad synthesis retains full evidence breadth.
76
-
77
 
78
- O --> EV[Evaluation harness]
79
- EV --> ST[Scale stress 1x / 5x / 20x]
80
- EV --> CA[Context + compression ablations]
81
- EV --> RR[Release-readiness checklist]
 
1
  flowchart TD
2
  U[User / API] --> UI{UI lifecycle state}
3
+ UI -->|demo + empty| DI[Lazy demo indexing]
4
+ UI -->|ready| G[Input / upload guardrails]
5
  DI --> G
6
+
7
+ G --> P{Schema-constrained QueryPlan}
8
  P --> PF{Workspace preflight}
9
  PF -->|missing local data| AB[Abstain]
10
  PF -->|structured data| SQL[Read-only Text2SQL / DuckDB]
11
  PF -->|external| W[Ask-the-Web]
12
+ PF -->|corpus or mixed| RS{Task-aware retrieval}
13
 
14
  RS -->|semantic| CI[Chunk index]
15
+ RS -->|global| GB[Source-balanced evidence]
16
  RS -->|hierarchical| SPI[Source-profile index]
17
+ RS -->|analytical| AN[Documents + deterministic table evidence]
18
+ SPI --> SEL[Select sources]
19
+ SEL --> CI
 
20
 
21
  CI --> D[FastEmbed dense / Qdrant]
22
+ CI --> B[BM25]
23
  D --> F[RRF]
24
  B --> F
25
  F --> RP{Adaptive reranker policy}
26
+ RP --> RD[Corpus-scale retrieval depth]
27
+ RD --> CB{Adaptive 2-5 chunk budget}
28
+ CB -->|focused| CMP[Query-focused sentence compression]
29
+ CB -->|broad| EG{Evidence grade}
30
+ CMP --> EG
 
 
31
  GB --> EG
32
  AN --> EG
33
+
34
  EG -->|sufficient| A[Grounded Gemini generation]
35
+ EG -->|weak first attempt| COR[Correct retrieval plan]
36
  COR --> RS
37
+ EG -->|external relevant + allowed| W
38
+ EG -->|unsupported local| AB
39
  W --> A
40
 
41
+ A --> V{Optional Self-RAG audit}
42
+ V -->|revise once| RV[Bounded grounded revision]
43
  RV --> V
44
+ V --> O[Cited answer + sources + trace]
45
  SQL --> O
46
  AB --> O
47
 
48
+ O -. cache-bypassed benchmark .-> EV[Evaluation harness]
49
+ EV --> RM[Source + chunk retrieval metrics]
50
+ EV --> CTX[Context budget / compression ablations]
51
+ EV --> PL[Planner / web policy]
52
+ EV --> CIT[Citation / answer checks]
53
+ EV --> SQ[Text2SQL / abstention]
54
+ EV --> HM[Hard Mode]
55
+ EV --> ST[1x / 5x / 20x scale stress]
56
+ EV --> LAT[Service node latency]
57
+ EV --> DJ[Optional calibrated Deep judge]
58
+ RM --> READY[Quality gates + release readiness]
59
+ CTX --> READY
60
+ PL --> READY
61
+ CIT --> READY
62
+ SQ --> READY
63
+ HM --> READY
64
+ ST --> READY
65
+ LAT --> READY
66
+ DJ --> READY
67
+ READY --> HIST[Saved runs + history + deltas]
 
 
 
 
 
 
 
 
68
 
69
+ API[FastAPI /docs + OpenAPI + Prometheus] -. runtime introspection .-> UI
 
 
 
evals/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # RAGForge evaluation set
 
 
2
 
3
  ## v1.9 local ablations and scale stress
4
 
@@ -51,4 +53,4 @@ Citation coverage is Markdown-aware, missing-answer cases accept natural grounde
51
 
52
  Standard/Deep now add a deterministic `context_budget_ablation` table. It compares the original six-chunk focused context with the adaptive three-chunk safety floor and reports source Precision@5, Recall@5, Hit@1, MRR, median chunks/sources/chars, estimated tokens and reduction percentage. The ablation issues no Gemini requests.
53
 
54
- The missing-answer Hard Mode row also exposes `missing_answer_match` and `grounded_absence`. Either calibrated signal can satisfy the missing-information decision, avoiding false failures caused by one exact wording.
 
1
+ # RAGForge benchmark - v2.0 final
2
+
3
+ The final benchmark keeps the v1.9 retrieval/context/compression/scale methodology and changes one Hard Mode reference path: `hard_multihop_time_compare` now runs under `Auto + Balanced` and explicitly expects `documents -> comparison -> hierarchical`. This matches the recommended semantic runtime instead of using Fast mode as the reference for a multi-source comparison.
4
 
5
  ## v1.9 local ablations and scale stress
6
 
 
53
 
54
  Standard/Deep now add a deterministic `context_budget_ablation` table. It compares the original six-chunk focused context with the adaptive three-chunk safety floor and reports source Precision@5, Recall@5, Hit@1, MRR, median chunks/sources/chars, estimated tokens and reduction percentage. The ablation issues no Gemini requests.
55
 
56
+ The missing-answer Hard Mode row also exposes `missing_answer_match` and `grounded_absence`. Either calibrated signal can satisfy the missing-information decision, avoiding false failures caused by one exact wording.
evals/demo_benchmark.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
- "version": "1.9",
3
- "description": "RAGForge v1.9 benchmark with adaptive context budgets, deterministic evidence compression, synthetic larger-corpus stress evaluation, grounded-absence robustness, hard-mode cases, and source/chunk retrieval ablations.",
4
  "qa_cases": [
5
  {
6
  "id": "qa_acme_sev1_ack",
@@ -341,7 +341,12 @@
341
  "relevant_sources": [
342
  "acme_cloud_runbook.md",
343
  "orbitpay_policy.txt"
344
- ]
 
 
 
 
 
345
  },
346
  {
347
  "id": "hard_insight_synthesis",
 
1
  {
2
+ "version": "2.0",
3
+ "description": "RAGForge v2.0 final benchmark with adaptive context budgets, evidence compression, scale stress, grounded absence, hard-mode robustness, release readiness, and a default-profile multi-hop comparison case.",
4
  "qa_cases": [
5
  {
6
  "id": "qa_acme_sev1_ack",
 
341
  "relevant_sources": [
342
  "acme_cloud_runbook.md",
343
  "orbitpay_policy.txt"
344
+ ],
345
+ "mode": "Auto",
346
+ "profile": "Balanced",
347
+ "expected_route": "documents",
348
+ "expected_task": "comparison",
349
+ "expected_strategy": "hierarchical"
350
  },
351
  {
352
  "id": "hard_insight_synthesis",
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
 
5
  [project]
6
  name = "ragforge"
7
- version = "1.9.0"
8
  description = "Production-style agentic RAG demo for Hugging Face Spaces"
9
  requires-python = ">=3.11"
10
  dependencies = []
 
4
 
5
  [project]
6
  name = "ragforge"
7
+ version = "2.0.0"
8
  description = "Production-style agentic RAG demo for Hugging Face Spaces"
9
  requires-python = ">=3.11"
10
  dependencies = []
scripts/release_check.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Dependency-free consistency checks for the final RAGForge release."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ import re
8
+ from pathlib import Path
9
+
10
+ ROOT = Path(__file__).resolve().parents[1]
11
+ APP_VERSION = "2.0.0"
12
+ BENCHMARK_VERSION = "2.0"
13
+
14
+
15
+ def read(path: str) -> str:
16
+ return (ROOT / path).read_text(encoding="utf-8")
17
+
18
+
19
+ def require(condition: bool, message: str) -> None:
20
+ if not condition:
21
+ raise SystemExit(f"RELEASE CHECK FAILED: {message}")
22
+
23
+
24
+ def main() -> None:
25
+ pyproject = read("pyproject.toml")
26
+ init_py = read("src/ragforge/__init__.py")
27
+ api_py = read("src/ragforge/api.py")
28
+ ui_py = read("src/ragforge/ui.py")
29
+ readme = read("README.md")
30
+ benchmark = json.loads(read("evals/demo_benchmark.json"))
31
+
32
+ require(f'version = "{APP_VERSION}"' in pyproject, "pyproject version mismatch")
33
+ require(f'__version__ = "{APP_VERSION}"' in init_py, "package version mismatch")
34
+ require(f'version="{APP_VERSION}"' in api_py, "FastAPI version mismatch")
35
+ require(f'RAGForge v{APP_VERSION}' in ui_py, "UI version mismatch")
36
+ require(benchmark.get("version") == BENCHMARK_VERSION, "benchmark version mismatch")
37
+
38
+ require("—" not in ui_py and "–" not in ui_py, "UI contains long dash glyphs")
39
+ require("Final portfolio release" in ui_py, "final UI hero missing")
40
+ require("Verified pre-final benchmark" in readme, "final benchmark section missing from README")
41
+ require("v2.0.0 is the final planned feature release" in readme, "final project status missing")
42
+
43
+ required_docs = [
44
+ "CHANGELOG.md",
45
+ "SECURITY.md",
46
+ "docs/FINAL_RESULTS.md",
47
+ "docs/PORTFOLIO_GUIDE.md",
48
+ "docs/EVALUATION.md",
49
+ "docs/ARCHITECTURE_API.md",
50
+ ]
51
+ for path in required_docs:
52
+ require((ROOT / path).is_file(), f"missing {path}")
53
+
54
+ demo_files = [
55
+ "acme_cloud_runbook.md",
56
+ "orbitpay_policy.txt",
57
+ "release_notes.html",
58
+ "support_matrix.csv",
59
+ "NIST_AI_RMF_1.0.pdf",
60
+ ]
61
+ for name in demo_files:
62
+ require((ROOT / "demo_documents" / name).is_file(), f"missing demo file {name}")
63
+
64
+ hard_cases = {case.get("id"): case for case in benchmark.get("hard_mode_cases", [])}
65
+ multihop = hard_cases.get("hard_multihop_time_compare", {})
66
+ require(multihop.get("mode") == "Auto", "multi-hop Hard Mode must use Auto")
67
+ require(multihop.get("profile") == "Balanced", "multi-hop Hard Mode must use Balanced")
68
+ require(multihop.get("expected_task") == "comparison", "multi-hop Hard Mode expected task mismatch")
69
+ require(multihop.get("expected_strategy") == "hierarchical", "multi-hop Hard Mode expected strategy mismatch")
70
+
71
+ # Catch an earlier UI regression where the live runtime header was duplicated.
72
+ architecture_fn = re.search(
73
+ r"def _architecture_snapshot\(.*?\n\s*return sid, runtime, curl, runtime_json",
74
+ ui_py,
75
+ flags=re.S,
76
+ )
77
+ require(architecture_fn is not None, "architecture snapshot function missing")
78
+ require(architecture_fn.group(0).count("**RAGForge:**") == 1, "runtime header is duplicated")
79
+
80
+ print(
81
+ "RAGForge release check PASS - "
82
+ f"app {APP_VERSION}, benchmark {BENCHMARK_VERSION}, final docs/UI/benchmark aligned."
83
+ )
84
+
85
+
86
+ if __name__ == "__main__":
87
+ main()
src/ragforge/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
  """RAGForge: production-style agentic retrieval augmented generation demo."""
2
 
3
- __version__ = "1.9.0"
 
1
  """RAGForge: production-style agentic retrieval augmented generation demo."""
2
 
3
+ __version__ = "2.0.0"
src/ragforge/api.py CHANGED
@@ -29,7 +29,7 @@ def _auth(authorization: Annotated[str | None, Header()] = None) -> None:
29
 
30
 
31
  def create_api() -> FastAPI:
32
- app = FastAPI(title="RAGForge API", version="1.9.0")
33
 
34
  @app.get("/api/health")
35
  def health():
 
29
 
30
 
31
  def create_api() -> FastAPI:
32
+ app = FastAPI(title="RAGForge API", version="2.0.0")
33
 
34
  @app.get("/api/health")
35
  def health():
src/ragforge/evaluation.py CHANGED
@@ -824,7 +824,16 @@ def _hard_mode_eval(
824
  rows.append(row)
825
  continue
826
 
827
- cfg = PipelineConfig(mode="Documents", profile="Fast", model=model, use_crag=False, allow_web_fallback=False, use_self_rag=False)
 
 
 
 
 
 
 
 
 
828
  if kind == "insight":
829
  cfg = PipelineConfig(mode="Auto", profile="Balanced", model=model, use_crag=True, allow_web_fallback=False, use_self_rag=False)
830
  wait_before = request_pacer.total_sleep_seconds()
@@ -845,7 +854,34 @@ def _hard_mode_eval(
845
  passed = plan.get("task_type") == case.get("expected_task") and plan.get("retrieval_strategy") == case.get("expected_strategy") and float(evidence.get("source_coverage", 0.0) or 0.0) >= float(case.get("min_source_coverage", 0.0)) and (table_cited or not case.get("requires_table_citation"))
846
  else:
847
  passed = answer_key_match(result.answer, case) and float(retrieval.get("source_recall@5", 1.0)) >= 1.0
848
- row.update({"route":plan.get("route"),"task":plan.get("task_type"),"strategy":plan.get("retrieval_strategy"),"answer_key_match":answer_key_match(result.answer,case) if kind=="qa" else None,"missing_answer_match":missing_answer_match(result.answer, case) if kind=="missing" else None,"grounded_absence":bool(result.trace.get("metrics",{}).get("grounded_absence",False)) if kind=="missing" else None,"citation_validity":round(float(citations["citation_validity"]),3),"citation_coverage":round(float(citations["citation_coverage"]),3),"source_recall@5":round(float(retrieval.get("source_recall@5",1.0)),3),"latency_ms":round(max(0.0,wall-pace),1),"pass":passed,"gemini_calls":int(result.trace.get("metrics",{}).get("llm_calls_estimate",0) or 0),"_answer":result.answer,"_sources":result.sources,"_node_times":_trace_node_times(result.trace, pace)})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
849
  rows.append(row)
850
  return rows
851
 
 
824
  rows.append(row)
825
  continue
826
 
827
+ case_mode = str(case.get("mode") or "Documents")
828
+ case_profile = str(case.get("profile") or "Fast")
829
+ cfg = PipelineConfig(
830
+ mode=case_mode,
831
+ profile=case_profile,
832
+ model=model,
833
+ use_crag=bool(case.get("use_crag", False)),
834
+ allow_web_fallback=False,
835
+ use_self_rag=False,
836
+ )
837
  if kind == "insight":
838
  cfg = PipelineConfig(mode="Auto", profile="Balanced", model=model, use_crag=True, allow_web_fallback=False, use_self_rag=False)
839
  wait_before = request_pacer.total_sleep_seconds()
 
854
  passed = plan.get("task_type") == case.get("expected_task") and plan.get("retrieval_strategy") == case.get("expected_strategy") and float(evidence.get("source_coverage", 0.0) or 0.0) >= float(case.get("min_source_coverage", 0.0)) and (table_cited or not case.get("requires_table_citation"))
855
  else:
856
  passed = answer_key_match(result.answer, case) and float(retrieval.get("source_recall@5", 1.0)) >= 1.0
857
+ expected_route = case.get("expected_route")
858
+ expected_task = case.get("expected_task")
859
+ expected_strategy = case.get("expected_strategy")
860
+ if expected_route:
861
+ passed = passed and plan.get("route") == expected_route
862
+ if expected_task:
863
+ passed = passed and plan.get("task_type") == expected_task
864
+ if expected_strategy:
865
+ passed = passed and plan.get("retrieval_strategy") == expected_strategy
866
+ row.update({
867
+ "evaluation_mode": cfg.mode if kind not in {"sql", "planner", "security"} else None,
868
+ "evaluation_profile": cfg.profile if kind not in {"sql", "planner", "security"} else None,
869
+ "route": plan.get("route"),
870
+ "task": plan.get("task_type"),
871
+ "strategy": plan.get("retrieval_strategy"),
872
+ "answer_key_match": answer_key_match(result.answer, case) if kind == "qa" else None,
873
+ "missing_answer_match": missing_answer_match(result.answer, case) if kind == "missing" else None,
874
+ "grounded_absence": bool(result.trace.get("metrics", {}).get("grounded_absence", False)) if kind == "missing" else None,
875
+ "citation_validity": round(float(citations["citation_validity"]), 3),
876
+ "citation_coverage": round(float(citations["citation_coverage"]), 3),
877
+ "source_recall@5": round(float(retrieval.get("source_recall@5", 1.0)), 3),
878
+ "latency_ms": round(max(0.0, wall - pace), 1),
879
+ "pass": passed,
880
+ "gemini_calls": int(result.trace.get("metrics", {}).get("llm_calls_estimate", 0) or 0),
881
+ "_answer": result.answer,
882
+ "_sources": result.sources,
883
+ "_node_times": _trace_node_times(result.trace, pace),
884
+ })
885
  rows.append(row)
886
  return rows
887
 
src/ragforge/ui.py CHANGED
@@ -20,8 +20,18 @@ ROOT = Path(__file__).resolve().parents[2]
20
  DEMO_DIR = ROOT / "demo_documents"
21
 
22
  CSS = """
23
- #hero {max-width: 1200px; margin: 0 auto 8px auto;}
24
- #hero h1 {font-size: 2.35rem; margin-bottom: .25rem;}
 
 
 
 
 
 
 
 
 
 
25
  .muted {opacity: .75;}
26
  .status-ready {padding: 8px 10px; border-radius: 8px;}
27
  .status-line {padding: 8px 10px; border: 1px solid rgba(128,128,128,.25); border-radius: 8px; margin: 6px 0;}
@@ -35,6 +45,12 @@ CSS = """
35
  .latency-track {height: 9px; border-radius: 999px; background: rgba(128,128,128,.18); overflow: hidden;}
36
  .latency-fill {height: 100%; min-width: 2px; border-radius: 999px; background: var(--primary-500, currentColor);}
37
  .latency-time {font-size: .84rem; text-align: right; opacity: .8;}
 
 
 
 
 
 
38
  """
39
 
40
 
@@ -189,9 +205,18 @@ def _eval_summary_markdown(report: dict[str, Any]) -> str:
189
  summary = report.get("summary", {}) if report else {}
190
  if not summary:
191
  return "*Run an evaluation to see the score card.*"
 
 
 
 
 
 
 
 
 
192
  lines = [
193
  f"### Evaluation score card - {summary.get('evaluation_level', '-')} - grade {summary.get('quality_grade', '-')}",
194
- "",
195
  f"**Deterministic quality:** `{float(summary.get('deterministic_quality_score', 0.0)):.3f}` - "
196
  f"**answer accuracy:** `{float(summary.get('answer_accuracy', 0.0)):.0%}` - "
197
  f"**source Precision@5:** `{float(summary.get('source_precision@5', 0.0)):.0%}` - "
@@ -336,7 +361,7 @@ def _architecture_snapshot(session_id: str | None) -> tuple[str, str, str, dict[
336
  settings = get_settings()
337
  stats = ws.health_snapshot()
338
  runtime_json = {
339
- "ragforge_version": "1.9.0",
340
  "workspace": stats,
341
  "models": {
342
  "generation": settings.default_model,
@@ -357,7 +382,7 @@ def _architecture_snapshot(session_id: str | None) -> tuple[str, str, str, dict[
357
  }
358
  runtime = (
359
  "### Live runtime\n"
360
- f"**RAGForge:** `v1.9.0` - **workspace:** `{sid[:12]}...` - **status:** `{stats['status']}`\n\n"
361
  f"**Corpus:** `{stats['sources']}` sources - `{stats['chunks']}` chunks - "
362
  f"`{stats['source_profiles']}` source profiles - `{stats['tables']}` tables - "
363
  f"corpus version `{stats['version']}`\n\n"
@@ -504,17 +529,42 @@ def build_ui() -> gr.Blocks:
504
  else: # Compatibility fallback for older Gradio builds.
505
  session_state = gr.State("")
506
 
507
- gr.Markdown(
508
- "# RAGForge - production-style agentic RAG\n"
509
- "Hybrid search - analytical synthesis - reranking - HyDE - CRAG - Self-RAG - Text2SQL - Ask-the-Web - citations - evals - guardrails",
510
- elem_id="hero",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
511
  )
512
 
513
  with gr.Tabs():
514
- with gr.Tab("Chat + RAG"):
515
  with gr.Row():
516
  with gr.Column(scale=4):
517
- gr.Markdown("### 1) Build a corpus")
518
  uploads = gr.File(
519
  label="Upload documents or a ZIP",
520
  file_count="multiple",
@@ -538,9 +588,9 @@ def build_ui() -> gr.Blocks:
538
  "No corpus indexed yet. Click **Index corpus**, or leave demo files enabled and ask a question."
539
  )
540
 
541
- gr.Markdown("### 2) Retrieval controls")
542
  mode = gr.Dropdown(["Auto", "Documents", "Web", "Hybrid", "Data (SQL)"], value="Auto", label="Route")
543
- profile = gr.Radio(["Fast", "Balanced", "Agentic"], value="Balanced", label="Pipeline profile")
544
  model = gr.Dropdown(
545
  ["gemini-3.5-flash-lite", "gemini-3.1-flash-lite", "gemini-3.6-flash", "gemini-3.5-flash"],
546
  value=settings.default_model,
@@ -565,8 +615,10 @@ def build_ui() -> gr.Blocks:
565
  top_k = gr.Slider(2, 12, value=6, step=1, label="Final context chunks")
566
 
567
  with gr.Column(scale=7):
568
- chatbot = gr.Chatbot(label="RAG conversation", type="messages", height=510)
569
- query = gr.Textbox(label="Ask a question", placeholder="What does the corpus say about...?", lines=2)
 
 
570
  ask_btn = gr.Button("Ask", variant="primary")
571
  query_status = gr.Markdown("Ready.", elem_classes=["status-line"])
572
  with gr.Accordion("Sources", open=True):
@@ -810,9 +862,19 @@ def build_ui() -> gr.Blocks:
810
 
811
  with gr.Tab("Evaluation"):
812
  gr.Markdown(
813
- "### RAG evaluation benchmark\n"
814
- "Evaluate retrieval, routing, analytical synthesis, citations, abstention, Text2SQL, hard-mode robustness and runtime efficiency "
815
- "against the transparent bundled benchmark in `evals/demo_benchmark.json`."
 
 
 
 
 
 
 
 
 
 
816
  )
817
  eval_level = gr.Radio(
818
  ["Quick", "Standard", "Deep"],
@@ -1262,4 +1324,7 @@ The letter grade uses quality gates so one weak subsystem cannot be hidden by a
1262
  show_progress="hidden",
1263
  )
1264
 
 
 
 
1265
  return demo
 
20
  DEMO_DIR = ROOT / "demo_documents"
21
 
22
  CSS = """
23
+ #hero {max-width: 1220px; margin: 0 auto 14px auto;}
24
+ .hero-shell {padding: 20px 22px; border: 1px solid rgba(128,128,128,.22); border-radius: 16px; background: linear-gradient(135deg, rgba(99,102,241,.08), rgba(128,128,128,.03));}
25
+ .hero-kicker {font-size: .78rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; opacity: .72;}
26
+ .hero-title {font-size: 2.3rem; line-height: 1.05; font-weight: 780; margin: 5px 0 8px 0;}
27
+ .hero-subtitle {font-size: 1rem; line-height: 1.55; max-width: 900px; opacity: .84;}
28
+ .hero-badges {display: flex; flex-wrap: wrap; gap: 7px; margin-top: 13px;}
29
+ .hero-badge {font-size: .79rem; padding: 5px 9px; border: 1px solid rgba(128,128,128,.25); border-radius: 999px; background: rgba(128,128,128,.06);}
30
+ .baseline-grid {display: grid; grid-template-columns: repeat(4, minmax(120px, 1fr)); gap: 8px; margin: 10px 0 14px 0;}
31
+ .baseline-card {padding: 10px 12px; border: 1px solid rgba(128,128,128,.2); border-radius: 11px; background: rgba(128,128,128,.035);}
32
+ .baseline-label {font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; opacity: .66;}
33
+ .baseline-value {font-size: 1.12rem; font-weight: 720; margin-top: 2px;}
34
+ .section-note {font-size: .88rem; opacity: .78; line-height: 1.45;}
35
  .muted {opacity: .75;}
36
  .status-ready {padding: 8px 10px; border-radius: 8px;}
37
  .status-line {padding: 8px 10px; border: 1px solid rgba(128,128,128,.25); border-radius: 8px; margin: 6px 0;}
 
45
  .latency-track {height: 9px; border-radius: 999px; background: rgba(128,128,128,.18); overflow: hidden;}
46
  .latency-fill {height: 100%; min-width: 2px; border-radius: 999px; background: var(--primary-500, currentColor);}
47
  .latency-time {font-size: .84rem; text-align: right; opacity: .8;}
48
+ .eval-card-grid {display: grid; grid-template-columns: repeat(5, minmax(105px, 1fr)); gap: 8px; margin: 8px 0 14px 0;}
49
+ .eval-card {padding: 10px 12px; border: 1px solid rgba(128,128,128,.2); border-radius: 10px; background: rgba(128,128,128,.035);}
50
+ .eval-card-label {font-size: .70rem; text-transform: uppercase; letter-spacing: .045em; opacity: .65;}
51
+ .eval-card-value {font-size: 1.05rem; font-weight: 720; margin-top: 2px;}
52
+ .footer-note {text-align: center; opacity: .68; font-size: .82rem; padding: 15px 0 4px 0;}
53
+ @media (max-width: 900px) {.baseline-grid,.eval-card-grid {grid-template-columns: repeat(2, minmax(120px, 1fr));}}
54
  """
55
 
56
 
 
205
  summary = report.get("summary", {}) if report else {}
206
  if not summary:
207
  return "*Run an evaluation to see the score card.*"
208
+ cards = (
209
+ '<div class="eval-card-grid">'
210
+ f'<div class="eval-card"><div class="eval-card-label">Grade</div><div class="eval-card-value">{html.escape(str(summary.get("quality_grade", "-")))}</div></div>'
211
+ f'<div class="eval-card"><div class="eval-card-label">Answer accuracy</div><div class="eval-card-value">{float(summary.get("answer_accuracy", 0.0)):.0%}</div></div>'
212
+ f'<div class="eval-card"><div class="eval-card-label">Recall@5</div><div class="eval-card-value">{float(summary.get("source_recall@5", 0.0)):.0%}</div></div>'
213
+ f'<div class="eval-card"><div class="eval-card-label">Citation coverage</div><div class="eval-card-value">{float(summary.get("citation_coverage", 0.0)):.0%}</div></div>'
214
+ f'<div class="eval-card"><div class="eval-card-label">Release readiness</div><div class="eval-card-value">{html.escape(str(summary.get("release_readiness", "-")))}</div></div>'
215
+ '</div>'
216
+ )
217
  lines = [
218
  f"### Evaluation score card - {summary.get('evaluation_level', '-')} - grade {summary.get('quality_grade', '-')}",
219
+ cards,
220
  f"**Deterministic quality:** `{float(summary.get('deterministic_quality_score', 0.0)):.3f}` - "
221
  f"**answer accuracy:** `{float(summary.get('answer_accuracy', 0.0)):.0%}` - "
222
  f"**source Precision@5:** `{float(summary.get('source_precision@5', 0.0)):.0%}` - "
 
361
  settings = get_settings()
362
  stats = ws.health_snapshot()
363
  runtime_json = {
364
+ "ragforge_version": "2.0.0",
365
  "workspace": stats,
366
  "models": {
367
  "generation": settings.default_model,
 
382
  }
383
  runtime = (
384
  "### Live runtime\n"
385
+ f"**RAGForge:** `v2.0.0` - **workspace:** `{sid[:12]}...` - **status:** `{stats['status']}`\n\n"
386
  f"**Corpus:** `{stats['sources']}` sources - `{stats['chunks']}` chunks - "
387
  f"`{stats['source_profiles']}` source profiles - `{stats['tables']}` tables - "
388
  f"corpus version `{stats['version']}`\n\n"
 
529
  else: # Compatibility fallback for older Gradio builds.
530
  session_state = gr.State("")
531
 
532
+ gr.HTML(
533
+ """
534
+ <div id="hero" class="hero-shell">
535
+ <div class="hero-kicker">RAGForge v2.0.0 - Final portfolio release</div>
536
+ <div class="hero-title">Evidence-first RAG for documents, tables and the web</div>
537
+ <div class="hero-subtitle">
538
+ Hybrid retrieval, semantic planning, analytical synthesis, read-only Text2SQL, conditional web research,
539
+ evidence citations, adaptive context budgets and a built-in evaluation harness in one Hugging Face Space.
540
+ </div>
541
+ <div class="hero-badges">
542
+ <span class="hero-badge">Auto + Balanced recommended</span>
543
+ <span class="hero-badge">Adaptive 2-5 chunk budgets</span>
544
+ <span class="hero-badge">[D#] [T#] [W#] citations</span>
545
+ <span class="hero-badge">Quick / Standard / Deep evals</span>
546
+ <span class="hero-badge">FastAPI + Gradio + LangGraph</span>
547
+ </div>
548
+ </div>
549
+ """
550
+ )
551
+ gr.HTML(
552
+ """
553
+ <div class="baseline-grid">
554
+ <div class="baseline-card"><div class="baseline-label">Verified answer accuracy</div><div class="baseline-value">100%</div></div>
555
+ <div class="baseline-card"><div class="baseline-label">Verified Recall@5</div><div class="baseline-value">100%</div></div>
556
+ <div class="baseline-card"><div class="baseline-label">Synthetic scale stress</div><div class="baseline-value">1,724 chunks</div></div>
557
+ <div class="baseline-card"><div class="baseline-label">Pre-final readiness</div><div class="baseline-value">READY</div></div>
558
+ </div>
559
+ <div class="section-note">Verified on the bundled v1.9 Standard benchmark. v2.0 keeps the measured retrieval/runtime architecture and finalizes benchmark alignment, UI, documentation and release tooling.</div>
560
+ """
561
  )
562
 
563
  with gr.Tabs():
564
+ with gr.Tab("Chat"):
565
  with gr.Row():
566
  with gr.Column(scale=4):
567
+ gr.Markdown("### Corpus")
568
  uploads = gr.File(
569
  label="Upload documents or a ZIP",
570
  file_count="multiple",
 
588
  "No corpus indexed yet. Click **Index corpus**, or leave demo files enabled and ask a question."
589
  )
590
 
591
+ gr.Markdown("### Pipeline settings")
592
  mode = gr.Dropdown(["Auto", "Documents", "Web", "Hybrid", "Data (SQL)"], value="Auto", label="Route")
593
+ profile = gr.Radio(["Fast", "Balanced", "Agentic"], value="Balanced", label="Pipeline profile", info="Balanced is the recommended default. Fast minimizes model calls; Agentic enables the richest corrective/verification behavior.")
594
  model = gr.Dropdown(
595
  ["gemini-3.5-flash-lite", "gemini-3.1-flash-lite", "gemini-3.6-flash", "gemini-3.5-flash"],
596
  value=settings.default_model,
 
615
  top_k = gr.Slider(2, 12, value=6, step=1, label="Final context chunks")
616
 
617
  with gr.Column(scale=7):
618
+ gr.Markdown("### Ask RAGForge")
619
+ gr.Markdown("<small>Try: `What is the Sev-1 acknowledgement target?` - `What is the collection about?` - `Which support tier has the shortest SLA?`</small>")
620
+ chatbot = gr.Chatbot(label="Conversation", type="messages", height=510)
621
+ query = gr.Textbox(label="Question", placeholder="Ask about the indexed corpus, structured tables, or current web information...", lines=2)
622
  ask_btn = gr.Button("Ask", variant="primary")
623
  query_status = gr.Markdown("Ready.", elem_classes=["status-line"])
624
  with gr.Accordion("Sources", open=True):
 
862
 
863
  with gr.Tab("Evaluation"):
864
  gr.Markdown(
865
+ "### Evaluation lab\n"
866
+ "RAGForge evaluates retrieval, routing, analytical synthesis, citations, abstention, Text2SQL, hard-mode robustness, "
867
+ "context economics and scale stress against the transparent benchmark in `evals/demo_benchmark.json`."
868
+ )
869
+ gr.HTML(
870
+ """
871
+ <div class="baseline-grid">
872
+ <div class="baseline-card"><div class="baseline-label">Verified v1.9 grade</div><div class="baseline-value">A / 0.994</div></div>
873
+ <div class="baseline-card"><div class="baseline-label">Citations</div><div class="baseline-value">100% / 100%</div></div>
874
+ <div class="baseline-card"><div class="baseline-label">Context reduction</div><div class="baseline-value">41% + 58%</div></div>
875
+ <div class="baseline-card"><div class="baseline-label">20x stress recall</div><div class="baseline-value">100%</div></div>
876
+ </div>
877
+ """
878
  )
879
  eval_level = gr.Radio(
880
  ["Quick", "Standard", "Deep"],
 
1324
  show_progress="hidden",
1325
  )
1326
 
1327
+ gr.HTML(
1328
+ '<div class="footer-note">RAGForge v2.0.0 - final portfolio release - evidence-first retrieval, transparent evaluation, explicit limitations.</div>'
1329
+ )
1330
  return demo
tests/test_evaluation_assets.py CHANGED
@@ -5,7 +5,7 @@ from pathlib import Path
5
  def test_demo_benchmark_is_multilayer_and_auditable():
6
  path = Path("evals/demo_benchmark.json")
7
  data = json.loads(path.read_text(encoding="utf-8"))
8
- assert data["version"] == "1.9"
9
  assert len(data["qa_cases"]) >= 9
10
  assert len(data["planner_cases"]) >= 10
11
  assert len(data["overview_cases"]) >= 2
@@ -49,7 +49,7 @@ def test_demo_evaluation_and_introspection_are_available_through_api():
49
  assert "/api/v1/session/{session_id}" in text
50
  assert "/api/v1/evaluation/saved/{session_id}" in text
51
  assert "/api/v1/evaluation/saved/{session_id}/{level}" in text
52
- assert 'version="1.9.0"' in text
53
 
54
 
55
  def test_v15_evaluation_cache_and_incremental_deep_are_present():
 
5
  def test_demo_benchmark_is_multilayer_and_auditable():
6
  path = Path("evals/demo_benchmark.json")
7
  data = json.loads(path.read_text(encoding="utf-8"))
8
+ assert data["version"] == "2.0"
9
  assert len(data["qa_cases"]) >= 9
10
  assert len(data["planner_cases"]) >= 10
11
  assert len(data["overview_cases"]) >= 2
 
49
  assert "/api/v1/session/{session_id}" in text
50
  assert "/api/v1/evaluation/saved/{session_id}" in text
51
  assert "/api/v1/evaluation/saved/{session_id}/{level}" in text
52
+ assert 'version="2.0.0"' in text
53
 
54
 
55
  def test_v15_evaluation_cache_and_incremental_deep_are_present():
tests/test_ui_copy.py CHANGED
@@ -54,7 +54,7 @@ def test_ui_has_quota_safe_evaluation_controls_and_score_card_spacing():
54
 
55
  def test_architecture_snapshot_returns_complete_runtime_payload():
56
  text = Path("src/ragforge/ui.py").read_text(encoding="utf-8")
57
- assert '"ragforge_version": "1.9.0"' in text
58
  assert "return sid, runtime, curl, runtime_json" in text
59
  assert "curl = f\ndef _eval_frame" not in text
60
 
 
54
 
55
  def test_architecture_snapshot_returns_complete_runtime_payload():
56
  text = Path("src/ragforge/ui.py").read_text(encoding="utf-8")
57
+ assert '"ragforge_version": "2.0.0"' in text
58
  assert "return sid, runtime, curl, runtime_json" in text
59
  assert "curl = f\ndef _eval_frame" not in text
60
 
tests/test_v16_features.py CHANGED
@@ -4,7 +4,7 @@ from pathlib import Path
4
 
5
  def test_v16_benchmark_has_hard_mode_and_insight_plans():
6
  data = json.loads(Path("evals/demo_benchmark.json").read_text(encoding="utf-8"))
7
- assert data["version"] == "1.9"
8
  assert len(data.get("hard_mode_cases", [])) >= 8
9
  insight = [c for c in data["planner_cases"] if c.get("task") == "insight_synthesis"]
10
  assert insight
 
4
 
5
  def test_v16_benchmark_has_hard_mode_and_insight_plans():
6
  data = json.loads(Path("evals/demo_benchmark.json").read_text(encoding="utf-8"))
7
+ assert data["version"] == "2.0"
8
  assert len(data.get("hard_mode_cases", [])) >= 8
9
  insight = [c for c in data["planner_cases"] if c.get("task") == "insight_synthesis"]
10
  assert insight
tests/test_v20_final.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from pathlib import Path
3
+
4
+ ROOT = Path(__file__).resolve().parents[1]
5
+
6
+
7
+ def test_v20_versions_and_final_release_assets_are_aligned():
8
+ assert 'version = "2.0.0"' in (ROOT / "pyproject.toml").read_text(encoding="utf-8")
9
+ assert '__version__ = "2.0.0"' in (ROOT / "src/ragforge/__init__.py").read_text(encoding="utf-8")
10
+ assert 'version="2.0.0"' in (ROOT / "src/ragforge/api.py").read_text(encoding="utf-8")
11
+ benchmark = json.loads((ROOT / "evals/demo_benchmark.json").read_text(encoding="utf-8"))
12
+ assert benchmark["version"] == "2.0"
13
+ assert (ROOT / "CHANGELOG.md").is_file()
14
+ assert (ROOT / "docs/FINAL_RESULTS.md").is_file()
15
+ assert (ROOT / "docs/PORTFOLIO_GUIDE.md").is_file()
16
+ assert (ROOT / "scripts/release_check.py").is_file()
17
+
18
+
19
+ def test_v20_multihop_hard_case_uses_recommended_semantic_path():
20
+ benchmark = json.loads((ROOT / "evals/demo_benchmark.json").read_text(encoding="utf-8"))
21
+ case = next(c for c in benchmark["hard_mode_cases"] if c["id"] == "hard_multihop_time_compare")
22
+ assert case["mode"] == "Auto"
23
+ assert case["profile"] == "Balanced"
24
+ assert case["expected_route"] == "documents"
25
+ assert case["expected_task"] == "comparison"
26
+ assert case["expected_strategy"] == "hierarchical"
27
+
28
+
29
+ def test_v20_hard_mode_evaluator_honors_case_profile_and_plan_expectations():
30
+ text = (ROOT / "src/ragforge/evaluation.py").read_text(encoding="utf-8")
31
+ assert 'case_mode = str(case.get("mode") or "Documents")' in text
32
+ assert 'case_profile = str(case.get("profile") or "Fast")' in text
33
+ assert 'expected_task = case.get("expected_task")' in text
34
+ assert 'expected_strategy = case.get("expected_strategy")' in text
35
+ assert '"evaluation_profile": cfg.profile' in text
36
+
37
+
38
+ def test_v20_ui_is_productized_and_keeps_short_dash_constraint():
39
+ text = (ROOT / "src/ragforge/ui.py").read_text(encoding="utf-8")
40
+ assert "Final portfolio release" in text
41
+ assert 'with gr.Tab("Chat")' in text
42
+ assert "Evaluation lab" in text
43
+ assert "Verified answer accuracy" in text
44
+ assert "Release readiness" in text
45
+ assert "footer-note" in text
46
+ assert "—" not in text
47
+ assert "–" not in text
48
+
49
+
50
+ def test_v20_readme_leads_with_product_and_measured_results_not_release_diary():
51
+ text = (ROOT / "README.md").read_text(encoding="utf-8")
52
+ assert "Verified pre-final benchmark" in text
53
+ assert "Core capabilities" in text
54
+ assert "Recommended demo flow" in text
55
+ assert "Final project status" in text
56
+ assert "What is new in v1.9" not in text
57
+ assert "CHANGELOG.md" in text