Spaces:
Running
Running
Commit ·
8cfc5e2
1
Parent(s): f0307a2
Release RAGForge v2.0 final
Browse files- .github/workflows/ci.yml +3 -1
- CHANGELOG.md +84 -0
- Makefile +12 -2
- README.md +290 -319
- SECURITY.md +7 -1
- docs/ARCHITECTURE_API.md +6 -2
- docs/EVALUATION.md +8 -2
- docs/FEATURE_MATRIX.md +4 -2
- docs/FINAL_RESULTS.md +88 -0
- docs/MIGRATION_2.0.md +42 -0
- docs/PORTFOLIO_GUIDE.md +116 -0
- docs/QUERY_PLANNING.md +6 -2
- docs/RESUME_BULLETS.md +10 -0
- docs/SOURCES.md +6 -2
- docs/UX_LIFECYCLE.md +6 -2
- docs/architecture.mmd +43 -55
- evals/README.md +4 -2
- evals/demo_benchmark.json +8 -3
- pyproject.toml +1 -1
- scripts/release_check.py +87 -0
- src/ragforge/__init__.py +1 -1
- src/ragforge/api.py +1 -1
- src/ragforge/evaluation.py +38 -2
- src/ragforge/ui.py +83 -18
- tests/test_evaluation_assets.py +2 -2
- tests/test_ui_copy.py +1 -1
- tests/test_v16_features.py +1 -1
- tests/test_v20_final.py +57 -0
.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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
-
|
| 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 |
-
|
| 134 |
-
-
|
| 135 |
-
-
|
| 136 |
-
-
|
| 137 |
-
-
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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
|
| 218 |
-
G --> P{Semantic
|
| 219 |
-
P -->|structured
|
| 220 |
P -->|external| W[Ask-the-Web]
|
| 221 |
-
P -->|corpus
|
| 222 |
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
|
| 229 |
-
C --> D[Dense FastEmbed/Qdrant]
|
| 230 |
C --> B[BM25]
|
| 231 |
D --> F[RRF]
|
| 232 |
B --> F
|
| 233 |
-
F -->
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
|
|
|
|
|
|
| 244 |
W --> A
|
| 245 |
|
| 246 |
-
A --> V{Self-RAG audit}
|
| 247 |
-
V -->|revise once| RV[
|
| 248 |
RV --> V
|
| 249 |
-
V -->
|
| 250 |
SQL --> O
|
| 251 |
AB --> O
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
```
|
| 253 |
|
| 254 |
-
|
| 255 |
|
| 256 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
|
| 264 |
-
|
| 265 |
-
>
|
| 266 |
-
> **Privacy caveat:** use only documents appropriate for the terms of the model/API tier you select.
|
| 267 |
|
| 268 |
-
##
|
| 269 |
|
| 270 |
```bash
|
| 271 |
cp .env.example .env
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
```
|
| 277 |
|
| 278 |
-
|
|
|
|
|
|
|
| 279 |
|
| 280 |
```bash
|
| 281 |
-
|
| 282 |
-
|
|
|
|
| 283 |
```
|
| 284 |
|
| 285 |
-
|
| 286 |
|
| 287 |
-
|
| 288 |
-
curl -X POST http://localhost:7860/api/v1/session
|
| 289 |
|
| 290 |
-
|
| 291 |
-
-F session_id=SESSION_ID \
|
| 292 |
-
-F files=@demo_documents/acme_cloud_runbook.md
|
| 293 |
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 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 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
```
|
| 313 |
|
| 314 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
|
| 316 |
-
|
| 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 |
-
|
| 323 |
|
| 324 |
## Evaluation
|
| 325 |
|
| 326 |
-
|
| 327 |
|
| 328 |
-
###
|
| 329 |
|
| 330 |
-
|
| 331 |
|
|
|
|
| 332 |
|
| 333 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 349 |
|
| 350 |
-
|
| 351 |
|
| 352 |
-
|
| 353 |
|
| 354 |
-
Evaluation
|
| 355 |
|
| 356 |
-
|
| 357 |
|
| 358 |
-
|
| 359 |
|
| 360 |
-
|
| 361 |
|
| 362 |
-
|
| 363 |
|
| 364 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
|
| 366 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 367 |
|
| 368 |
-
|
| 369 |
|
| 370 |
-
|
| 371 |
|
| 372 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
|
| 382 |
-
|
| 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 |
-
|
|
|
|
| 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 -
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
| 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 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 4 |
-
UI -->|ready| G[Input guardrails]
|
| 5 |
DI --> G
|
| 6 |
-
|
|
|
|
| 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
|
| 12 |
|
| 13 |
RS -->|semantic| CI[Chunk index]
|
| 14 |
-
RS -->|global| GB[Source-balanced
|
| 15 |
RS -->|hierarchical| SPI[Source-profile index]
|
| 16 |
-
RS -->|analytical| AN[
|
| 17 |
-
SPI --> SEL[
|
| 18 |
-
SEL -->
|
| 19 |
-
SCI --> 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 -->
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 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
|
| 37 |
COR --> RS
|
| 38 |
-
EG -->|
|
| 39 |
-
EG -->|
|
| 40 |
W --> A
|
| 41 |
|
| 42 |
-
A --> V{Self-RAG
|
| 43 |
-
V -->|revise once| RV[
|
| 44 |
RV --> V
|
| 45 |
-
V -->
|
| 46 |
SQL --> O
|
| 47 |
AB --> O
|
| 48 |
|
| 49 |
-
O -. cache-bypassed
|
| 50 |
-
EV -->
|
| 51 |
-
EV -->
|
| 52 |
-
EV -->
|
| 53 |
-
EV -->
|
| 54 |
-
EV -->
|
| 55 |
-
EV --> HM[Hard
|
| 56 |
-
EV -->
|
| 57 |
-
EV -->
|
| 58 |
-
EV -->
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 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 |
-
|
| 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
|
|
|
|
|
|
|
| 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": "
|
| 3 |
-
"description": "RAGForge
|
| 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 = "
|
| 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 |
"""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="
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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": "
|
| 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:** `
|
| 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.
|
| 508 |
-
"
|
| 509 |
-
|
| 510 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 511 |
)
|
| 512 |
|
| 513 |
with gr.Tabs():
|
| 514 |
-
with gr.Tab("Chat
|
| 515 |
with gr.Row():
|
| 516 |
with gr.Column(scale=4):
|
| 517 |
-
gr.Markdown("###
|
| 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("###
|
| 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 |
-
|
| 569 |
-
|
|
|
|
|
|
|
| 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 |
-
"###
|
| 814 |
-
"
|
| 815 |
-
"against the transparent
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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"] == "
|
| 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="
|
| 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": "
|
| 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"] == "
|
| 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
|