ragcheck-eval 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. ragcheck_eval-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  2. ragcheck_eval-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +16 -0
  3. ragcheck_eval-0.1.0/.github/workflows/ci.yaml +23 -0
  4. ragcheck_eval-0.1.0/.github/workflows/smoke-eval.yaml +51 -0
  5. ragcheck_eval-0.1.0/.gitignore +15 -0
  6. ragcheck_eval-0.1.0/CHANGELOG.md +19 -0
  7. ragcheck_eval-0.1.0/CODE_OF_CONDUCT.md +7 -0
  8. ragcheck_eval-0.1.0/CONTRIBUTING.md +36 -0
  9. ragcheck_eval-0.1.0/LICENSE +21 -0
  10. ragcheck_eval-0.1.0/PKG-INFO +190 -0
  11. ragcheck_eval-0.1.0/README.md +145 -0
  12. ragcheck_eval-0.1.0/benchmarks/README.md +70 -0
  13. ragcheck_eval-0.1.0/benchmarks/benchmark_dataset.jsonl +16 -0
  14. ragcheck_eval-0.1.0/benchmarks/corpus/fetch_corpus.py +101 -0
  15. ragcheck_eval-0.1.0/benchmarks/cost_quality_frontier.png +0 -0
  16. ragcheck_eval-0.1.0/benchmarks/generate_qa.py +87 -0
  17. ragcheck_eval-0.1.0/benchmarks/pipelines/agentic_rag.py +109 -0
  18. ragcheck_eval-0.1.0/benchmarks/pipelines/common.py +116 -0
  19. ragcheck_eval-0.1.0/benchmarks/pipelines/hybrid_rag.py +41 -0
  20. ragcheck_eval-0.1.0/benchmarks/pipelines/naive_rag.py +26 -0
  21. ragcheck_eval-0.1.0/benchmarks/pipelines/reranked_rag.py +36 -0
  22. ragcheck_eval-0.1.0/benchmarks/run_benchmark.py +295 -0
  23. ragcheck_eval-0.1.0/benchmarks/synthetic_dataset.jsonl +28 -0
  24. ragcheck_eval-0.1.0/docs/ci-integration.md +54 -0
  25. ragcheck_eval-0.1.0/docs/judge-validation.md +48 -0
  26. ragcheck_eval-0.1.0/docs/metrics.md +53 -0
  27. ragcheck_eval-0.1.0/docs/quickstart.md +73 -0
  28. ragcheck_eval-0.1.0/examples/judge_labels_sample.jsonl +10 -0
  29. ragcheck_eval-0.1.0/examples/quickstart.ipynb +197 -0
  30. ragcheck_eval-0.1.0/examples/toy_config.yaml +13 -0
  31. ragcheck_eval-0.1.0/examples/toy_config_groq.yaml +13 -0
  32. ragcheck_eval-0.1.0/examples/toy_dataset.jsonl +10 -0
  33. ragcheck_eval-0.1.0/examples/toy_pipeline.py +85 -0
  34. ragcheck_eval-0.1.0/pyproject.toml +68 -0
  35. ragcheck_eval-0.1.0/ragcheck/__init__.py +18 -0
  36. ragcheck_eval-0.1.0/ragcheck/adapters/__init__.py +7 -0
  37. ragcheck_eval-0.1.0/ragcheck/adapters/base.py +39 -0
  38. ragcheck_eval-0.1.0/ragcheck/adapters/function.py +19 -0
  39. ragcheck_eval-0.1.0/ragcheck/adapters/langchain.py +59 -0
  40. ragcheck_eval-0.1.0/ragcheck/cache.py +67 -0
  41. ragcheck_eval-0.1.0/ragcheck/cli.py +210 -0
  42. ragcheck_eval-0.1.0/ragcheck/config.py +54 -0
  43. ragcheck_eval-0.1.0/ragcheck/datasets/__init__.py +6 -0
  44. ragcheck_eval-0.1.0/ragcheck/datasets/loaders.py +35 -0
  45. ragcheck_eval-0.1.0/ragcheck/datasets/models.py +32 -0
  46. ragcheck_eval-0.1.0/ragcheck/datasets/synthetic.py +255 -0
  47. ragcheck_eval-0.1.0/ragcheck/demo.py +168 -0
  48. ragcheck_eval-0.1.0/ragcheck/judge/__init__.py +5 -0
  49. ragcheck_eval-0.1.0/ragcheck/judge/judge.py +93 -0
  50. ragcheck_eval-0.1.0/ragcheck/judge/prompts/answer_relevance.md +21 -0
  51. ragcheck_eval-0.1.0/ragcheck/judge/prompts/answers_equivalent.md +16 -0
  52. ragcheck_eval-0.1.0/ragcheck/judge/prompts/chunk_relevance.md +13 -0
  53. ragcheck_eval-0.1.0/ragcheck/judge/prompts/citation_support.md +13 -0
  54. ragcheck_eval-0.1.0/ragcheck/judge/prompts/dataset_paraphrase.md +10 -0
  55. ragcheck_eval-0.1.0/ragcheck/judge/prompts/dataset_qa_cross.md +18 -0
  56. ragcheck_eval-0.1.0/ragcheck/judge/prompts/dataset_qa_multi.md +15 -0
  57. ragcheck_eval-0.1.0/ragcheck/judge/prompts/dataset_qa_single.md +15 -0
  58. ragcheck_eval-0.1.0/ragcheck/judge/prompts/dataset_unanswerable.md +15 -0
  59. ragcheck_eval-0.1.0/ragcheck/judge/prompts/faithfulness_decompose.md +18 -0
  60. ragcheck_eval-0.1.0/ragcheck/judge/prompts/faithfulness_verify.md +13 -0
  61. ragcheck_eval-0.1.0/ragcheck/judge/prompts/refusal_detection.md +13 -0
  62. ragcheck_eval-0.1.0/ragcheck/judge/validation.py +119 -0
  63. ragcheck_eval-0.1.0/ragcheck/llm.py +186 -0
  64. ragcheck_eval-0.1.0/ragcheck/metrics/__init__.py +59 -0
  65. ragcheck_eval-0.1.0/ragcheck/metrics/base.py +56 -0
  66. ragcheck_eval-0.1.0/ragcheck/metrics/generation/__init__.py +7 -0
  67. ragcheck_eval-0.1.0/ragcheck/metrics/generation/citation_accuracy.py +87 -0
  68. ragcheck_eval-0.1.0/ragcheck/metrics/generation/faithfulness.py +99 -0
  69. ragcheck_eval-0.1.0/ragcheck/metrics/generation/relevance.py +55 -0
  70. ragcheck_eval-0.1.0/ragcheck/metrics/retrieval/__init__.py +8 -0
  71. ragcheck_eval-0.1.0/ragcheck/metrics/retrieval/context_precision.py +70 -0
  72. ragcheck_eval-0.1.0/ragcheck/metrics/retrieval/context_recall.py +81 -0
  73. ragcheck_eval-0.1.0/ragcheck/metrics/retrieval/hit_rate.py +42 -0
  74. ragcheck_eval-0.1.0/ragcheck/metrics/retrieval/mrr.py +41 -0
  75. ragcheck_eval-0.1.0/ragcheck/metrics/robustness/__init__.py +6 -0
  76. ragcheck_eval-0.1.0/ragcheck/metrics/robustness/paraphrase_consistency.py +96 -0
  77. ragcheck_eval-0.1.0/ragcheck/metrics/robustness/refusal_calibration.py +81 -0
  78. ragcheck_eval-0.1.0/ragcheck/report/__init__.py +5 -0
  79. ragcheck_eval-0.1.0/ragcheck/report/cli_summary.py +45 -0
  80. ragcheck_eval-0.1.0/ragcheck/report/html.py +145 -0
  81. ragcheck_eval-0.1.0/ragcheck/report/models.py +55 -0
  82. ragcheck_eval-0.1.0/ragcheck/report/regression.py +94 -0
  83. ragcheck_eval-0.1.0/ragcheck/runner.py +166 -0
  84. ragcheck_eval-0.1.0/tests/__init__.py +0 -0
  85. ragcheck_eval-0.1.0/tests/conftest.py +71 -0
  86. ragcheck_eval-0.1.0/tests/test_adapters.py +24 -0
  87. ragcheck_eval-0.1.0/tests/test_cache.py +26 -0
  88. ragcheck_eval-0.1.0/tests/test_datasets.py +37 -0
  89. ragcheck_eval-0.1.0/tests/test_demo.py +48 -0
  90. ragcheck_eval-0.1.0/tests/test_html_report.py +82 -0
  91. ragcheck_eval-0.1.0/tests/test_judge.py +32 -0
  92. ragcheck_eval-0.1.0/tests/test_langchain_adapter.py +50 -0
  93. ragcheck_eval-0.1.0/tests/test_llm.py +70 -0
  94. ragcheck_eval-0.1.0/tests/test_metrics_generation.py +143 -0
  95. ragcheck_eval-0.1.0/tests/test_metrics_retrieval.py +147 -0
  96. ragcheck_eval-0.1.0/tests/test_metrics_robustness.py +137 -0
  97. ragcheck_eval-0.1.0/tests/test_regression.py +79 -0
  98. ragcheck_eval-0.1.0/tests/test_report.py +44 -0
  99. ragcheck_eval-0.1.0/tests/test_runner.py +119 -0
  100. ragcheck_eval-0.1.0/tests/test_synthetic.py +111 -0
  101. ragcheck_eval-0.1.0/tests/test_validation.py +71 -0
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something broken or behaving unexpectedly
4
+ labels: bug
5
+ ---
6
+
7
+ **What happened**
8
+
9
+ **What you expected**
10
+
11
+ **Reproduction**
12
+
13
+ ```bash
14
+ # minimal command / config that triggers it
15
+ ```
16
+
17
+ **Environment**
18
+ - ragcheck version:
19
+ - Python version:
20
+ - Judge provider/model (if metric-related):
21
+ - Judge prompt version (from the report JSON):
22
+
23
+ **Report snippet** (if applicable — the relevant `MetricResult` from your report JSON, with `judge_model` and `prompt_version`)
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: Feature request
3
+ about: Propose a metric, adapter, or capability
4
+ labels: enhancement
5
+ ---
6
+
7
+ **Problem it solves**
8
+ What can't you measure or do today?
9
+
10
+ **Proposed behavior**
11
+ CLI/config/API sketch if you have one.
12
+
13
+ **Alternatives considered**
14
+
15
+ **Scope check**
16
+ RAGCheck deliberately excludes web UIs, hosted services, and non-eval features (see README "Explicit non-goals"). If this touches those, say why it should be an exception.
@@ -0,0 +1,23 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ checks:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.10", "3.12"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ cache: pip
20
+ - run: pip install -e ".[dev]"
21
+ - run: ruff check .
22
+ - run: mypy ragcheck/
23
+ - run: pytest -q
@@ -0,0 +1,51 @@
1
+ # Live smoke eval on merges to main: runs the toy example against a real judge
2
+ # and regression-checks it against the committed baseline (dogfooding
3
+ # `ragcheck compare`). Skips gracefully when no API key secret is configured.
4
+ name: smoke-eval
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ workflow_dispatch:
10
+
11
+ jobs:
12
+ smoke:
13
+ runs-on: ubuntu-latest
14
+ env:
15
+ GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
16
+ RAGCHECK_MODEL: ${{ vars.RAGCHECK_MODEL || 'meta-llama/llama-4-scout-17b-16e-instruct' }}
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - name: Check for API key
20
+ id: gate
21
+ run: |
22
+ if [ -n "$GROQ_API_KEY" ]; then
23
+ echo "run=true" >> "$GITHUB_OUTPUT"
24
+ else
25
+ echo "No GROQ_API_KEY secret configured - skipping live smoke eval."
26
+ fi
27
+ - uses: actions/setup-python@v5
28
+ if: steps.gate.outputs.run == 'true'
29
+ with:
30
+ python-version: "3.12"
31
+ cache: pip
32
+ - name: Run toy eval
33
+ if: steps.gate.outputs.run == 'true'
34
+ run: |
35
+ pip install -e .
36
+ ragcheck run examples/toy_config_groq.yaml --yes
37
+ - name: Regression check against baseline
38
+ if: steps.gate.outputs.run == 'true'
39
+ run: |
40
+ if [ -f examples/smoke_baseline.json ]; then
41
+ ragcheck compare examples/smoke_baseline.json ragcheck_output/toy-demo-groq.json \
42
+ --fail-if "faithfulness<-0.1" --fail-if "hit_rate@3<-0.1"
43
+ else
44
+ echo "No baseline committed yet - skipping compare."
45
+ fi
46
+ - name: Upload report
47
+ if: steps.gate.outputs.run == 'true'
48
+ uses: actions/upload-artifact@v4
49
+ with:
50
+ name: smoke-report
51
+ path: ragcheck_output/
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ build/
7
+ .venv/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .pytest_cache/
11
+ *.sqlite
12
+ ragcheck_output/
13
+ benchmarks/corpus/data/
14
+ benchmarks/results/
15
+ .DS_Store
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (unreleased)
4
+
5
+ Initial release.
6
+
7
+ - `RAGAdapter` / `FunctionAdapter` / `LangChainAdapter` pipeline wrappers
8
+ - Retrieval metrics: `hit_rate@k`, `mrr`, `context_precision`, `context_recall`
9
+ - Generation metrics: `faithfulness`, `answer_relevance`, `citation_accuracy`
10
+ - Robustness metrics: `refusal_calibration`, `paraphrase_consistency`
11
+ - Judge validation: `ragcheck validate-judge` (Cohen's kappa, confusion matrix vs. human labels)
12
+ - Synthetic datasets: `ragcheck generate-dataset` (difficulty tiers, unanswerables, paraphrase groups, resumable)
13
+ - SQLite judgment cache keyed on judge model + prompt version + inputs
14
+ - Versioned judge prompts (markdown + YAML frontmatter)
15
+ - Judge providers: Anthropic, Groq; parallel judging with rate-limit backoff
16
+ - JSON + self-contained HTML reports with worst-failing-sample drilldowns
17
+ - `ragcheck compare` regression diffing with `--fail-if` thresholds (CI-friendly exit codes)
18
+ - Reproducible 4-architecture benchmark (naive / hybrid / reranked / agentic RAG) on SEC 10-Ks
19
+ - Cost guard: large LLM-judged runs require `--yes`
@@ -0,0 +1,7 @@
1
+ # Code of Conduct
2
+
3
+ This project follows the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
4
+
5
+ In short: be respectful, be constructive, assume good faith. Harassment, personal attacks, and derailing behavior are not tolerated.
6
+
7
+ Report unacceptable behavior by opening a confidential issue or emailing the maintainer. All reports are reviewed and handled with discretion.
@@ -0,0 +1,36 @@
1
+ # Contributing to RAGCheck
2
+
3
+ Thanks for considering a contribution. This project values honest metrics and small, reviewable changes.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/Ekta-Shah/ragcheck && cd ragcheck
9
+ python3.10+ -m venv .venv && source .venv/bin/activate
10
+ pip install -e ".[dev]"
11
+ ```
12
+
13
+ ## Before you open a PR
14
+
15
+ ```bash
16
+ ruff check . # lint (line length 100)
17
+ mypy ragcheck/ # types are required on all public APIs
18
+ pytest # must stay green; no live API calls in unit tests
19
+ ```
20
+
21
+ - **No live LLM calls in unit tests.** Judges are mocked (see `tests/conftest.py`). Live calls belong only in the smoke-eval workflow and `benchmarks/`.
22
+ - **Every LLM-judged metric needs known-answer tests** — hand-constructed cases where the correct score is verifiable by inspection (aim for 3+).
23
+ - **Judge prompts are versioned.** Changing a prompt's wording means bumping `version:` in its frontmatter; cached judgments key on the version, so old results stay traceable.
24
+ - **Docstrings (Google style) + type hints** on all public classes and functions.
25
+ - One logical change per commit; keep PRs focused.
26
+
27
+ ## What's welcome
28
+
29
+ - New metrics (deterministic ones are the easiest entry point — see `ragcheck/metrics/retrieval/mrr.py` as a template)
30
+ - Judge prompt improvements (with before/after validation via `ragcheck validate-judge`)
31
+ - Adapters for other frameworks (LlamaIndex, Haystack) — open an issue first
32
+ - Benchmark reproductions and corrections
33
+
34
+ ## Filing issues
35
+
36
+ Use the issue templates. For metric-quality issues, include the judge model, prompt version, and a minimal failing sample — all three are recorded in every report.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ekta Shah
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.4
2
+ Name: ragcheck-eval
3
+ Version: 0.1.0
4
+ Summary: pytest for RAG systems - evaluate retrieval-augmented generation pipelines
5
+ Author: Ekta Shah
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Ekta Shah
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE
28
+ Requires-Python: >=3.10
29
+ Requires-Dist: anthropic>=0.40
30
+ Requires-Dist: httpx>=0.27
31
+ Requires-Dist: pydantic>=2.5
32
+ Requires-Dist: pyyaml>=6.0
33
+ Requires-Dist: rich>=13.0
34
+ Requires-Dist: typer>=0.12
35
+ Provides-Extra: benchmarks
36
+ Requires-Dist: matplotlib>=3.8; extra == 'benchmarks'
37
+ Requires-Dist: rank-bm25>=0.2; extra == 'benchmarks'
38
+ Requires-Dist: sentence-transformers>=3.0; extra == 'benchmarks'
39
+ Provides-Extra: dev
40
+ Requires-Dist: mypy>=1.10; extra == 'dev'
41
+ Requires-Dist: pytest>=8.0; extra == 'dev'
42
+ Requires-Dist: ruff>=0.6; extra == 'dev'
43
+ Requires-Dist: types-pyyaml; extra == 'dev'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # RAGCheck
47
+
48
+ [![CI](https://github.com/Ekta-Shah/ragcheck/actions/workflows/ci.yaml/badge.svg)](https://github.com/Ekta-Shah/ragcheck/actions/workflows/ci.yaml)
49
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
50
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)
51
+
52
+ **pytest for RAG systems.** Wrap your retrieval-augmented generation pipeline in a one-method adapter and get quality scores, a failure taxonomy, cost/latency profiles, and CI-friendly regression checks — with a judge you can actually verify.
53
+
54
+ ```bash
55
+ pip install ragcheck-eval # imports and CLI are `ragcheck`
56
+ ragcheck demo # no API key needed - see the whole workflow in 10 seconds
57
+ ```
58
+
59
+ The demo runs a canned pipeline with two planted failures — faithfulness catches the hallucinated claim, refusal calibration flags the invented answer to an unanswerable question, and the HTML report shows both with their retrieved contexts. ([Or open the quickstart in Colab.](https://colab.research.google.com/github/Ekta-Shah/ragcheck/blob/main/examples/quickstart.ipynb))
60
+
61
+ For real runs:
62
+
63
+ ```bash
64
+ export ANTHROPIC_API_KEY=sk-ant-... # or GROQ_API_KEY for open-weight judges
65
+ ragcheck run examples/toy_config.yaml
66
+ ```
67
+
68
+ Real output from the bundled toy pipeline (keyword retrieval over a 10-doc corpus):
69
+
70
+ ```text
71
+ RAGCheck - toy-demo-groq
72
+ ┏━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
73
+ ┃ Metric ┃ Score ┃ Samples ┃ Judge ┃
74
+ ┡━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
75
+ │ hit_rate@3 │ 1.000 │ 10 │ - │
76
+ │ faithfulness │ 1.000 │ 10 │ llama-3.3-70b-versatile (v1) │
77
+ └──────────────┴───────┴─────────┴──────────────────────────────┘
78
+ pipeline tokens: {'input_tokens': 1410, 'output_tokens': 184} |
79
+ judge tokens: {'input_tokens': 4338, 'output_tokens': 274} | cache: 0 hits / 26 misses
80
+ ```
81
+
82
+ Alongside the terminal scorecard you get a JSON report and a **self-contained HTML report** — scorecard, judge-validation status, latency/cost, and the worst-failing samples with question, answer, and retrieved context inline.
83
+
84
+ ## Why another RAG eval tool?
85
+
86
+ Existing tools score your pipeline with an LLM judge and stop there. RAGCheck is built around the questions that actually block shipping:
87
+
88
+ - **Can you trust the judge?** `ragcheck validate-judge labels.jsonl --metric faithfulness` measures LLM-judge vs. human agreement (Cohen's kappa + confusion matrix) *before* you trust judged metrics. On our own labels it caught a real miscalibration: the default pass threshold scored κ=0.40; the corrected threshold scores κ=1.00. Reports display "validated at κ=0.XX" — or a visible "not validated" flag.
89
+ - **Does your system know when to say "I don't know"?** `refusal_calibration` reports the false-answer rate (hallucinated answers to unanswerable questions) and over-refusal rate separately.
90
+ - **Is it robust?** `paraphrase_consistency` judges pairwise answer agreement within paraphrase groups — catching pipelines that only work on one phrasing.
91
+ - **No eval set?** `ragcheck generate-dataset corpus_dir/` builds one from your documents: easy/medium/hard tiers, unanswerable questions, paraphrase groups, chunk-level provenance. Cached and resumable.
92
+ - **What does quality cost?** The benchmark harness produces a cost-quality frontier across architectures.
93
+ - **Did this PR make it worse?** `ragcheck compare old.json new.json --fail-if "faithfulness<-0.05"` exits non-zero on regression and prints a markdown diff for PR comments.
94
+
95
+ ## Compared to existing tools
96
+
97
+ | Capability | RAGCheck | RAGAS | DeepEval | TruLens |
98
+ |---|:---:|:---:|:---:|:---:|
99
+ | Core RAG metrics (faithfulness, context precision/recall, relevance) | ✅ | ✅ | ✅ | ✅ |
100
+ | Built-in judge validation vs. human labels (Cohen's κ) | ✅ | ❌ | ❌ | ❌ |
101
+ | Refusal calibration (false-answer + over-refusal rates) | ✅ | ❌ | ❌ | ❌ |
102
+ | Paraphrase-consistency robustness testing | ✅ | ❌ | ❌ | ❌ |
103
+ | Persistent judgment cache keyed on judge model + prompt version | ✅ | ❌ | partial | ❌ |
104
+ | Report diffing with CI exit codes | ✅ | ❌ | ✅ | ❌ |
105
+ | Runs entirely local — no dashboard, account, or service | ✅ | ✅ | partial | partial |
106
+
107
+ *Comparison reflects our reading of each tool's docs at the time of writing; corrections welcome via issues.*
108
+
109
+ ## How it works
110
+
111
+ Wrap any pipeline in a `RAGAdapter` (or just a function):
112
+
113
+ ```python
114
+ from ragcheck import RAGResponse, RetrievedChunk
115
+ from ragcheck.adapters import FunctionAdapter
116
+
117
+ def my_pipeline(question: str) -> RAGResponse:
118
+ chunks = my_retriever(question)
119
+ answer = my_generator(question, chunks)
120
+ return RAGResponse(
121
+ answer=answer,
122
+ retrieved_chunks=[RetrievedChunk(content=c.text, source_id=c.id) for c in chunks],
123
+ )
124
+
125
+ adapter = FunctionAdapter(my_pipeline)
126
+ ```
127
+
128
+ LangChain users: `LangChainAdapter(retriever, chain)` wraps a retriever + chain directly.
129
+
130
+ Point a YAML config at your adapter, dataset, and metrics, then `ragcheck run config.yaml`. Every LLM judgment is cached in SQLite (keyed on judge model, prompt version, and inputs) so re-runs on unchanged data are free — and every judged result records exactly which judge and prompt produced it.
131
+
132
+ ## Benchmark: which RAG architecture?
133
+
134
+ Four pipelines — naive dense, hybrid (BM25+dense RRF), cross-encoder reranked, and agentic (query decomposition) — on 8 SEC 10-K filings, identical chunking/generator, retrieval as the only variable. Preliminary 12-sample free-tier run (differences under ~0.2 are noise; full 300-500 sample run pending):
135
+
136
+ | Pipeline | faithfulness | refusal_calibration | paraphrase_consistency | tok/q | retrieval p50 |
137
+ |---|---:|---:|---:|---:|---:|
138
+ | naive (dense) | 0.950 | 0.583 | 0.700 | 1535 | 43 ms |
139
+ | hybrid (BM25+dense RRF) | 0.958 | 0.500 | **1.000** | 1448 | 75 ms |
140
+ | reranked (cross-encoder) | 0.958 | 0.500 | **1.000** | 1511 | 359 ms |
141
+ | agentic (decomposition) | 0.958 | **0.750** | 0.450 | 2683 | 6602 ms |
142
+
143
+ Early hypothesis worth noticing: agentic RAG refuses unanswerables best but answers paraphrases least consistently, at 1.75× the cost — exactly the trade-offs single-metric evals miss. Methodology, frontier chart, and honest limitations: [benchmarks/](benchmarks/README.md).
144
+
145
+ ## Design decisions
146
+
147
+ - **Judge validation is a feature, not a footnote.** Judged metrics are untrustworthy by default; the κ workflow makes trust measurable, and reports flag unvalidated judges.
148
+ - **Deterministic first.** Where a metric can be computed without an LLM (hit_rate, MRR), it is. LLM judging is reserved for what actually needs it.
149
+ - **Everything cached, everything versioned.** Judgments are cached on (judge model, prompt version, inputs); prompts live as versioned markdown. Interrupted dataset generation resumes for free; changing a prompt never silently reuses stale verdicts.
150
+ - **CI is the target environment.** JSON reports, exit codes, markdown diffs, an env-gated smoke workflow, and a cost guard (`--yes` required above a configurable sample count).
151
+ - **No UI.** The self-contained HTML report is the only visual output. No dashboard, no server, no account.
152
+
153
+ ## Docs
154
+
155
+ [Quickstart](docs/quickstart.md) · [Metrics reference](docs/metrics.md) · [Judge validation](docs/judge-validation.md) · [CI integration](docs/ci-integration.md)
156
+
157
+ ## Status
158
+
159
+ | Area | Status |
160
+ |---|---|
161
+ | Zero-key demo (`ragcheck demo`) + Colab quickstart notebook | ✅ |
162
+ | Adapters: `FunctionAdapter`, `LangChainAdapter` | ✅ |
163
+ | 9 metrics: retrieval (4) · generation (3) · robustness (2) | ✅ |
164
+ | Judge validation (`validate-judge`, Cohen's κ) | ✅ |
165
+ | Synthetic datasets (`generate-dataset`) | ✅ |
166
+ | JSON + self-contained HTML reports | ✅ |
167
+ | Regression diffing (`compare --fail-if`) | ✅ |
168
+ | CI workflows (lint/type/test + env-gated live smoke eval) | ✅ |
169
+ | 4-architecture benchmark harness | ✅ |
170
+ | 4-pipeline benchmark: preliminary 12-sample results | ✅ |
171
+ | Full benchmark run (300-500 samples) | 🔜 needs funded API budget |
172
+ | PyPI release (`ragcheck-eval` 0.1.0) | 🔜 with benchmark results |
173
+ | LlamaIndex/Haystack adapters | ❌ not in v0.1 (issues welcome) |
174
+
175
+ Explicit non-goals: web UI/dashboards, hosted services, fine-tuning or embedding-model evaluation.
176
+
177
+ ## Development
178
+
179
+ ```bash
180
+ pip install -e ".[dev]"
181
+ pytest # unit tests (no API calls - judges are mocked)
182
+ ruff check .
183
+ mypy ragcheck/
184
+ ```
185
+
186
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
187
+
188
+ ## License
189
+
190
+ MIT
@@ -0,0 +1,145 @@
1
+ # RAGCheck
2
+
3
+ [![CI](https://github.com/Ekta-Shah/ragcheck/actions/workflows/ci.yaml/badge.svg)](https://github.com/Ekta-Shah/ragcheck/actions/workflows/ci.yaml)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)
6
+
7
+ **pytest for RAG systems.** Wrap your retrieval-augmented generation pipeline in a one-method adapter and get quality scores, a failure taxonomy, cost/latency profiles, and CI-friendly regression checks — with a judge you can actually verify.
8
+
9
+ ```bash
10
+ pip install ragcheck-eval # imports and CLI are `ragcheck`
11
+ ragcheck demo # no API key needed - see the whole workflow in 10 seconds
12
+ ```
13
+
14
+ The demo runs a canned pipeline with two planted failures — faithfulness catches the hallucinated claim, refusal calibration flags the invented answer to an unanswerable question, and the HTML report shows both with their retrieved contexts. ([Or open the quickstart in Colab.](https://colab.research.google.com/github/Ekta-Shah/ragcheck/blob/main/examples/quickstart.ipynb))
15
+
16
+ For real runs:
17
+
18
+ ```bash
19
+ export ANTHROPIC_API_KEY=sk-ant-... # or GROQ_API_KEY for open-weight judges
20
+ ragcheck run examples/toy_config.yaml
21
+ ```
22
+
23
+ Real output from the bundled toy pipeline (keyword retrieval over a 10-doc corpus):
24
+
25
+ ```text
26
+ RAGCheck - toy-demo-groq
27
+ ┏━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
28
+ ┃ Metric ┃ Score ┃ Samples ┃ Judge ┃
29
+ ┡━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
30
+ │ hit_rate@3 │ 1.000 │ 10 │ - │
31
+ │ faithfulness │ 1.000 │ 10 │ llama-3.3-70b-versatile (v1) │
32
+ └──────────────┴───────┴─────────┴──────────────────────────────┘
33
+ pipeline tokens: {'input_tokens': 1410, 'output_tokens': 184} |
34
+ judge tokens: {'input_tokens': 4338, 'output_tokens': 274} | cache: 0 hits / 26 misses
35
+ ```
36
+
37
+ Alongside the terminal scorecard you get a JSON report and a **self-contained HTML report** — scorecard, judge-validation status, latency/cost, and the worst-failing samples with question, answer, and retrieved context inline.
38
+
39
+ ## Why another RAG eval tool?
40
+
41
+ Existing tools score your pipeline with an LLM judge and stop there. RAGCheck is built around the questions that actually block shipping:
42
+
43
+ - **Can you trust the judge?** `ragcheck validate-judge labels.jsonl --metric faithfulness` measures LLM-judge vs. human agreement (Cohen's kappa + confusion matrix) *before* you trust judged metrics. On our own labels it caught a real miscalibration: the default pass threshold scored κ=0.40; the corrected threshold scores κ=1.00. Reports display "validated at κ=0.XX" — or a visible "not validated" flag.
44
+ - **Does your system know when to say "I don't know"?** `refusal_calibration` reports the false-answer rate (hallucinated answers to unanswerable questions) and over-refusal rate separately.
45
+ - **Is it robust?** `paraphrase_consistency` judges pairwise answer agreement within paraphrase groups — catching pipelines that only work on one phrasing.
46
+ - **No eval set?** `ragcheck generate-dataset corpus_dir/` builds one from your documents: easy/medium/hard tiers, unanswerable questions, paraphrase groups, chunk-level provenance. Cached and resumable.
47
+ - **What does quality cost?** The benchmark harness produces a cost-quality frontier across architectures.
48
+ - **Did this PR make it worse?** `ragcheck compare old.json new.json --fail-if "faithfulness<-0.05"` exits non-zero on regression and prints a markdown diff for PR comments.
49
+
50
+ ## Compared to existing tools
51
+
52
+ | Capability | RAGCheck | RAGAS | DeepEval | TruLens |
53
+ |---|:---:|:---:|:---:|:---:|
54
+ | Core RAG metrics (faithfulness, context precision/recall, relevance) | ✅ | ✅ | ✅ | ✅ |
55
+ | Built-in judge validation vs. human labels (Cohen's κ) | ✅ | ❌ | ❌ | ❌ |
56
+ | Refusal calibration (false-answer + over-refusal rates) | ✅ | ❌ | ❌ | ❌ |
57
+ | Paraphrase-consistency robustness testing | ✅ | ❌ | ❌ | ❌ |
58
+ | Persistent judgment cache keyed on judge model + prompt version | ✅ | ❌ | partial | ❌ |
59
+ | Report diffing with CI exit codes | ✅ | ❌ | ✅ | ❌ |
60
+ | Runs entirely local — no dashboard, account, or service | ✅ | ✅ | partial | partial |
61
+
62
+ *Comparison reflects our reading of each tool's docs at the time of writing; corrections welcome via issues.*
63
+
64
+ ## How it works
65
+
66
+ Wrap any pipeline in a `RAGAdapter` (or just a function):
67
+
68
+ ```python
69
+ from ragcheck import RAGResponse, RetrievedChunk
70
+ from ragcheck.adapters import FunctionAdapter
71
+
72
+ def my_pipeline(question: str) -> RAGResponse:
73
+ chunks = my_retriever(question)
74
+ answer = my_generator(question, chunks)
75
+ return RAGResponse(
76
+ answer=answer,
77
+ retrieved_chunks=[RetrievedChunk(content=c.text, source_id=c.id) for c in chunks],
78
+ )
79
+
80
+ adapter = FunctionAdapter(my_pipeline)
81
+ ```
82
+
83
+ LangChain users: `LangChainAdapter(retriever, chain)` wraps a retriever + chain directly.
84
+
85
+ Point a YAML config at your adapter, dataset, and metrics, then `ragcheck run config.yaml`. Every LLM judgment is cached in SQLite (keyed on judge model, prompt version, and inputs) so re-runs on unchanged data are free — and every judged result records exactly which judge and prompt produced it.
86
+
87
+ ## Benchmark: which RAG architecture?
88
+
89
+ Four pipelines — naive dense, hybrid (BM25+dense RRF), cross-encoder reranked, and agentic (query decomposition) — on 8 SEC 10-K filings, identical chunking/generator, retrieval as the only variable. Preliminary 12-sample free-tier run (differences under ~0.2 are noise; full 300-500 sample run pending):
90
+
91
+ | Pipeline | faithfulness | refusal_calibration | paraphrase_consistency | tok/q | retrieval p50 |
92
+ |---|---:|---:|---:|---:|---:|
93
+ | naive (dense) | 0.950 | 0.583 | 0.700 | 1535 | 43 ms |
94
+ | hybrid (BM25+dense RRF) | 0.958 | 0.500 | **1.000** | 1448 | 75 ms |
95
+ | reranked (cross-encoder) | 0.958 | 0.500 | **1.000** | 1511 | 359 ms |
96
+ | agentic (decomposition) | 0.958 | **0.750** | 0.450 | 2683 | 6602 ms |
97
+
98
+ Early hypothesis worth noticing: agentic RAG refuses unanswerables best but answers paraphrases least consistently, at 1.75× the cost — exactly the trade-offs single-metric evals miss. Methodology, frontier chart, and honest limitations: [benchmarks/](benchmarks/README.md).
99
+
100
+ ## Design decisions
101
+
102
+ - **Judge validation is a feature, not a footnote.** Judged metrics are untrustworthy by default; the κ workflow makes trust measurable, and reports flag unvalidated judges.
103
+ - **Deterministic first.** Where a metric can be computed without an LLM (hit_rate, MRR), it is. LLM judging is reserved for what actually needs it.
104
+ - **Everything cached, everything versioned.** Judgments are cached on (judge model, prompt version, inputs); prompts live as versioned markdown. Interrupted dataset generation resumes for free; changing a prompt never silently reuses stale verdicts.
105
+ - **CI is the target environment.** JSON reports, exit codes, markdown diffs, an env-gated smoke workflow, and a cost guard (`--yes` required above a configurable sample count).
106
+ - **No UI.** The self-contained HTML report is the only visual output. No dashboard, no server, no account.
107
+
108
+ ## Docs
109
+
110
+ [Quickstart](docs/quickstart.md) · [Metrics reference](docs/metrics.md) · [Judge validation](docs/judge-validation.md) · [CI integration](docs/ci-integration.md)
111
+
112
+ ## Status
113
+
114
+ | Area | Status |
115
+ |---|---|
116
+ | Zero-key demo (`ragcheck demo`) + Colab quickstart notebook | ✅ |
117
+ | Adapters: `FunctionAdapter`, `LangChainAdapter` | ✅ |
118
+ | 9 metrics: retrieval (4) · generation (3) · robustness (2) | ✅ |
119
+ | Judge validation (`validate-judge`, Cohen's κ) | ✅ |
120
+ | Synthetic datasets (`generate-dataset`) | ✅ |
121
+ | JSON + self-contained HTML reports | ✅ |
122
+ | Regression diffing (`compare --fail-if`) | ✅ |
123
+ | CI workflows (lint/type/test + env-gated live smoke eval) | ✅ |
124
+ | 4-architecture benchmark harness | ✅ |
125
+ | 4-pipeline benchmark: preliminary 12-sample results | ✅ |
126
+ | Full benchmark run (300-500 samples) | 🔜 needs funded API budget |
127
+ | PyPI release (`ragcheck-eval` 0.1.0) | 🔜 with benchmark results |
128
+ | LlamaIndex/Haystack adapters | ❌ not in v0.1 (issues welcome) |
129
+
130
+ Explicit non-goals: web UI/dashboards, hosted services, fine-tuning or embedding-model evaluation.
131
+
132
+ ## Development
133
+
134
+ ```bash
135
+ pip install -e ".[dev]"
136
+ pytest # unit tests (no API calls - judges are mocked)
137
+ ruff check .
138
+ mypy ragcheck/
139
+ ```
140
+
141
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
142
+
143
+ ## License
144
+
145
+ MIT
@@ -0,0 +1,70 @@
1
+ # RAGCheck Architecture Benchmark
2
+
3
+ Reproducible comparison of RAG retrieval architectures on a financial-filings corpus. Everything except the retrieval strategy is held constant: same chunking (1200 chars, 200 overlap), same embedding model (`all-MiniLM-L6-v2`), same generator model and prompt.
4
+
5
+ > **Status: preliminary results below (12 samples, free-tier run).** All 4 pipelines and the metric suite run end-to-end. The full run (300-500 samples) needs a funded API budget and will replace these numbers; at n=12, differences under ~0.2 are noise.
6
+
7
+ ## Pipelines
8
+
9
+ | Pipeline | Retrieval |
10
+ |---|---|
11
+ | `naive_rag` | Dense (MiniLM cosine) top-5 |
12
+ | `hybrid_rag` | BM25 + dense top-20 each, reciprocal rank fusion (k=60), top-5 |
13
+ | `reranked_rag` | Dense top-20 → cross-encoder (`ms-marco-MiniLM-L-6-v2`) rerank → top-5 |
14
+ | `agentic_rag` | LLM query decomposition → iterative dense retrieval (max 3 rounds) → synthesis |
15
+
16
+ All four share the same chunking, embeddings, generator model, and generation prompt (which requires `[source_id]` citations) - the retrieval strategy is the only variable. Agentic's decomposition/sufficiency LLM calls are charged to its cost per query. Generator/judge models are selected with `RAGCHECK_BENCH_MODEL` / `RAGCHECK_JUDGE_MODEL`.
17
+
18
+ ## Corpus
19
+
20
+ Latest 10-K filings of Apple, Microsoft, NVIDIA, Alphabet, Amazon, Meta, Tesla, and JPMorgan, fetched from SEC EDGAR and converted to plain text (~3.8 MB). Raw data is gitignored; the fetch script is deterministic.
21
+
22
+ ## Reproduce
23
+
24
+ ```bash
25
+ pip install -e ".[benchmarks]"
26
+ export GROQ_API_KEY=...
27
+ export RAGCHECK_BENCH_MODEL=meta-llama/llama-4-scout-17b-16e-instruct
28
+ python benchmarks/corpus/fetch_corpus.py # ~4 MB from EDGAR
29
+ ragcheck generate-dataset benchmarks/corpus/data --n 400 \
30
+ --paraphrase-groups 20 --out benchmarks/synthetic_dataset.jsonl \
31
+ --provider groq --model $RAGCHECK_BENCH_MODEL # resumable
32
+ python benchmarks/run_benchmark.py # full; --quick for 20 samples
33
+ ```
34
+
35
+ Outputs land in `benchmarks/results/`: per-pipeline JSON reports, `comparison.json`/`comparison.md`, and `cost_quality_frontier.png` (cost/query vs. faithfulness). `--pipelines`, `--metrics`, and `--n` subset a run for cheap smoke tests.
36
+
37
+ ## Preliminary results (4 pipelines, 12 stratified samples, 2026-07-06)
38
+
39
+ Generator + judge: `meta-llama/llama-4-scout-17b-16e-instruct`. Samples include 2 unanswerable questions and one full paraphrase group. Chunk-level judged metrics (context precision/recall, citation accuracy) were excluded from this budget-constrained run.
40
+
41
+ | Pipeline | hit_rate@5 | mrr | faithfulness | answer_relevance | refusal_calibration | paraphrase_consistency | tok/q | $/q | retrieval p50 |
42
+ |---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
43
+ | naive_rag | 0.500 | 0.450 | 0.950 | 0.917 | 0.583 | 0.700 | 1535 | $0.00018 | 43 ms |
44
+ | hybrid_rag | 0.400 | 0.245 | 0.958 | 0.938 | 0.500 | **1.000** | 1448 | $0.00017 | 75 ms |
45
+ | reranked_rag | 0.500 | 0.183 | 0.958 | 0.917 | 0.500 | **1.000** | 1511 | $0.00018 | 359 ms |
46
+ | agentic_rag | 0.500 | 0.450 | 0.958 | 0.854 | **0.750** | 0.450 | 2683 | $0.00032 | 6602 ms |
47
+
48
+ ![Cost-quality frontier](cost_quality_frontier.png)
49
+
50
+ **Preliminary reads (all within noise at this sample size, offered as hypotheses for the full run):**
51
+ - **Agentic RAG is the refusal-calibration leader but the consistency laggard** - its multi-round retrieval gathers enough to decline unanswerables more often (0.75 vs 0.50-0.58), but different decompositions per paraphrase produce different answers (0.45 consistency). It also costs 1.75x tokens/query and ~90x retrieval latency.
52
+ - **Hybrid and reranked are perfectly paraphrase-consistent** on this subset - stabler retrieval under rephrasing.
53
+ - **Faithfulness is uniformly high (~0.95)** across architectures: the shared generator rarely asserts beyond its context; retrieval quality, robustness, and cost are where architectures actually differ.
54
+
55
+ ## Early results (2 pipelines, 16 samples, 2026-07-02)
56
+
57
+ | Pipeline | hit_rate@5 | faithfulness | tokens/query | retrieval p50 (ms) |
58
+ |---|---:|---:|---:|---:|
59
+ | `naive_rag` (dense) | 0.438 | 1.000 | 1666 | 40 |
60
+ | `hybrid_rag` (BM25 + dense, RRF) | **0.750** | 0.948 | 1555 | 47 |
61
+
62
+ **Read:** on 10-K filings — dense-heavy with exact figures, entity names, and legal terms — adding BM25 lexical matching via reciprocal rank fusion lifts hit_rate@5 from 0.44 to 0.75 for ~7 ms extra retrieval latency. Both pipelines stay highly faithful *to what they retrieve*; the naive pipeline's perfect faithfulness with poor retrieval illustrates why retrieval and generation metrics must be read together (a pipeline can faithfully report that the answer isn't in its badly-retrieved context).
63
+
64
+ ## Honest limitations
65
+
66
+ - **Small sample (16).** Differences of a few points are noise at this size.
67
+ - **QA pairs are single-chunk and auto-generated.** Each question is grounded in exactly one chunk, and `hit_rate` counts only that chunk as relevant - a retrieved *duplicate* passage with the same fact counts as a miss. Some generated questions lean on filing boilerplate rather than substantive financials.
68
+ - **No judge validation yet.** Faithfulness scores come from an unvalidated LLM judge; the judge-vs-human agreement module is Phase 2.
69
+ - **Corpus text extraction is rough.** HTML tables flatten into text streams; some chunks are noisy.
70
+ - **10-K filings shift over time.** The fetch script pulls each company's *latest* 10-K, so exact numbers will drift as new filings land; re-generate the dataset after re-fetching.
@@ -0,0 +1,16 @@
1
+ {"question": "When was Ms. Hood appointed Executive Vice President and Chief Financial Officer of Microsoft?", "ground_truth_answer": "July 2013", "relevant_source_ids": ["MSFT_10K_0085"], "difficulty": "easy"}
2
+ {"question": "What type of breaches provides evidence of an external environment hostile to information security, according to MSFT's 10K filing?", "ground_truth_answer": "high-profile data breaches", "relevant_source_ids": ["MSFT_10K_0117"], "difficulty": "easy"}
3
+ {"question": "What international standard for information security management does NVDA follow?", "ground_truth_answer": "ISO 27001", "relevant_source_ids": ["NVDA_10K_0194"], "difficulty": "easy"}
4
+ {"question": "What percentage of NVIDIA's outstanding common stock would trigger Microsoft's first and last rights of refusal to purchase the stock?", "ground_truth_answer": "30%", "relevant_source_ids": ["NVDA_10K_0193"], "difficulty": "easy"}
5
+ {"question": "What was the balance of other long-term liabilities for NVDA as of January 25, 2026?", "ground_truth_answer": "$7,306", "relevant_source_ids": ["NVDA_10K_0314"], "difficulty": "easy"}
6
+ {"question": "What is the date listed for the signature of John O. Dabiri in the NVDA 10-K filing?", "ground_truth_answer": "February 25, 2026", "relevant_source_ids": ["NVDA_10K_0362"], "difficulty": "easy"}
7
+ {"question": "What is the date of the Officer\u2019s Certificate of the Registrant mentioned in 4.12?", "ground_truth_answer": "August 4, 2016", "relevant_source_ids": ["AAPL_10K_0211"], "difficulty": "easy"}
8
+ {"question": "In what year did the USG publish the AI Diffusion IFR in the Federal Register, according to NVDA's 10-K filing?", "ground_truth_answer": "2025", "relevant_source_ids": ["NVDA_10K_0160"], "difficulty": "easy"}
9
+ {"question": "What operating system do Microsoft's server software applications support?", "ground_truth_answer": "Windows Server", "relevant_source_ids": ["MSFT_10K_0056"], "difficulty": "easy"}
10
+ {"question": "What entity might NVIDIA need to notify regarding details of some of its Trustworthy AI processes?", "ground_truth_answer": "European Commission", "relevant_source_ids": ["NVDA_10K_0175"], "difficulty": "easy"}
11
+ {"question": "What was the effective tax rate of Apple as of September 27, 2025?", "ground_truth_answer": "15.6%", "relevant_source_ids": ["AAPL_10K_0171"], "difficulty": "easy"}
12
+ {"question": "What is the name of the AI-powered business and productivity solutions platform mentioned in the MSFT 10K excerpt?", "ground_truth_answer": "Microsoft 365 Commercial", "relevant_source_ids": ["MSFT_10K_0050"], "difficulty": "easy"}
13
+ {"question": "What department's list is mentioned in the NVDA 10K excerpt as having restricted parties?", "ground_truth_answer": "U.S. Department of Commerce", "relevant_source_ids": ["NVDA_10K_0149"], "difficulty": "easy"}
14
+ {"question": "What is the name of the independent registered public accounting firm that audited NVDA's internal control over financial reporting as of January 25, 2026?", "ground_truth_answer": "PricewaterhouseCoopers LLP", "relevant_source_ids": ["NVDA_10K_0244"], "difficulty": "easy"}
15
+ {"question": "What date was the Officer\u2019s Certificate of the Registrant issued that included forms of global notes representing the 0.875% Notes due 2025 and 1.375% Notes due 2029 for AAPL?", "ground_truth_answer": "May 24, 2017", "relevant_source_ids": ["AAPL_10K_0212"], "difficulty": "easy"}
16
+ {"question": "What method does AAPL use to recognize revenue for product-related bundled services?", "ground_truth_answer": "straight-line basis", "relevant_source_ids": ["AAPL_10K_0154"], "difficulty": "easy"}