rag-jev 0.2.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.
- rag_jev-0.2.0/.gitignore +15 -0
- rag_jev-0.2.0/CHANGELOG.md +23 -0
- rag_jev-0.2.0/CONTRIBUTING.md +28 -0
- rag_jev-0.2.0/LICENSE +21 -0
- rag_jev-0.2.0/MVP_SCOPE.md +28 -0
- rag_jev-0.2.0/PKG-INFO +340 -0
- rag_jev-0.2.0/README.md +319 -0
- rag_jev-0.2.0/SECURITY.md +25 -0
- rag_jev-0.2.0/THIRD_PARTY_NOTICES.md +24 -0
- rag_jev-0.2.0/benchmarks/public/README.md +83 -0
- rag_jev-0.2.0/benchmarks/public/RESEARCH.md +108 -0
- rag_jev-0.2.0/benchmarks/public/RESEARCH_RESULTS.md +28 -0
- rag_jev-0.2.0/benchmarks/public/RESULTS.md +50 -0
- rag_jev-0.2.0/benchmarks/public/__init__.py +1 -0
- rag_jev-0.2.0/benchmarks/public/common.py +94 -0
- rag_jev-0.2.0/benchmarks/public/download.py +32 -0
- rag_jev-0.2.0/benchmarks/public/frozen-policy.json +229 -0
- rag_jev-0.2.0/benchmarks/public/prepare.py +127 -0
- rag_jev-0.2.0/benchmarks/public/protocol.json +63 -0
- rag_jev-0.2.0/benchmarks/public/publish_report.py +226 -0
- rag_jev-0.2.0/benchmarks/public/report.py +115 -0
- rag_jev-0.2.0/benchmarks/public/research-v2-frozen.json +10 -0
- rag_jev-0.2.0/benchmarks/public/research-v2-protocol.json +74 -0
- rag_jev-0.2.0/benchmarks/public/research.py +496 -0
- rag_jev-0.2.0/benchmarks/public/research_publish.py +261 -0
- rag_jev-0.2.0/benchmarks/public/research_verify.py +124 -0
- rag_jev-0.2.0/benchmarks/public/run.py +288 -0
- rag_jev-0.2.0/benchmarks/research/README.md +208 -0
- rag_jev-0.2.0/benchmarks/research/RESULTS.md +48 -0
- rag_jev-0.2.0/benchmarks/research/RETRIEVAL_RESULTS.md +25 -0
- rag_jev-0.2.0/benchmarks/research/__init__.py +1 -0
- rag_jev-0.2.0/benchmarks/research/design.py +276 -0
- rag_jev-0.2.0/benchmarks/research/fetch_sources.py +47 -0
- rag_jev-0.2.0/benchmarks/research/frozen.json +28 -0
- rag_jev-0.2.0/benchmarks/research/metric-sources.json +12 -0
- rag_jev-0.2.0/benchmarks/research/neural.py +69 -0
- rag_jev-0.2.0/benchmarks/research/prior-exposure.json +646 -0
- rag_jev-0.2.0/benchmarks/research/protocol.json +186 -0
- rag_jev-0.2.0/benchmarks/research/publish.py +370 -0
- rag_jev-0.2.0/benchmarks/research/publish_retrieval.py +101 -0
- rag_jev-0.2.0/benchmarks/research/reranker-source.json +5 -0
- rag_jev-0.2.0/benchmarks/research/retrieval-protocol.json +48 -0
- rag_jev-0.2.0/benchmarks/research/retrieval.py +344 -0
- rag_jev-0.2.0/benchmarks/research/runner.py +300 -0
- rag_jev-0.2.0/benchmarks/research/source-verification.json +9 -0
- rag_jev-0.2.0/benchmarks/research/statistics.py +282 -0
- rag_jev-0.2.0/benchmarks/research/verification.json +16 -0
- rag_jev-0.2.0/benchmarks/research/verify.py +245 -0
- rag_jev-0.2.0/benchmarks/research/verify_retrieval.py +56 -0
- rag_jev-0.2.0/benchmarks/robustness/README.md +34 -0
- rag_jev-0.2.0/benchmarks/robustness/cases.json +371 -0
- rag_jev-0.2.0/benchmarks/robustness/protocol.json +38 -0
- rag_jev-0.2.0/benchmarks/robustness/results.json +679 -0
- rag_jev-0.2.0/benchmarks/robustness/run.py +224 -0
- rag_jev-0.2.0/docs/BENCHMARK_DEMO.md +45 -0
- rag_jev-0.2.0/docs/CALIBRATION.md +68 -0
- rag_jev-0.2.0/docs/HUMAN_REVIEW.md +28 -0
- rag_jev-0.2.0/docs/REAL_RAG.md +257 -0
- rag_jev-0.2.0/docs/RELEASING.md +32 -0
- rag_jev-0.2.0/docs/RESEARCH_DEMO.md +33 -0
- rag_jev-0.2.0/examples/eval-cases.json +80 -0
- rag_jev-0.2.0/examples/fixture-traces.json +80 -0
- rag_jev-0.2.0/examples/knowledge/refunds.md +7 -0
- rag_jev-0.2.0/examples/knowledge/shipping.md +5 -0
- rag_jev-0.2.0/examples/knowledge/subscriptions.md +5 -0
- rag_jev-0.2.0/examples/local_rag.py +124 -0
- rag_jev-0.2.0/examples/python_rag.py +28 -0
- rag_jev-0.2.0/examples/request.json +29 -0
- rag_jev-0.2.0/examples/typescript-rag.mjs +11 -0
- rag_jev-0.2.0/integrations/dify/.difyignore +6 -0
- rag_jev-0.2.0/integrations/dify/_assets/icon.svg +1 -0
- rag_jev-0.2.0/integrations/dify/main.py +6 -0
- rag_jev-0.2.0/integrations/dify/manifest.yaml +25 -0
- rag_jev-0.2.0/integrations/dify/models/rerank/_position.yaml +1 -0
- rag_jev-0.2.0/integrations/dify/models/rerank/jev.yaml +5 -0
- rag_jev-0.2.0/integrations/dify/models/rerank/rerank.py +93 -0
- rag_jev-0.2.0/integrations/dify/provider/rag_jev.py +7 -0
- rag_jev-0.2.0/integrations/dify/provider/rag_jev.yaml +52 -0
- rag_jev-0.2.0/integrations/dify/pyproject.toml +5 -0
- rag_jev-0.2.0/integrations/dify/requirements.txt +2 -0
- rag_jev-0.2.0/integrations/dify/uv.lock +796 -0
- rag_jev-0.2.0/pyproject.toml +66 -0
- rag_jev-0.2.0/src/rag_jev/__init__.py +15 -0
- rag_jev-0.2.0/src/rag_jev/calibration.py +353 -0
- rag_jev-0.2.0/src/rag_jev/cli.py +206 -0
- rag_jev-0.2.0/src/rag_jev/demo.py +76 -0
- rag_jev-0.2.0/src/rag_jev/evaluation.py +275 -0
- rag_jev-0.2.0/src/rag_jev/generation.py +223 -0
- rag_jev-0.2.0/src/rag_jev/integrations/__init__.py +1 -0
- rag_jev-0.2.0/src/rag_jev/integrations/langchain.py +124 -0
- rag_jev-0.2.0/src/rag_jev/models.py +142 -0
- rag_jev-0.2.0/src/rag_jev/provider.py +167 -0
- rag_jev-0.2.0/src/rag_jev/py.typed +0 -0
- rag_jev-0.2.0/src/rag_jev/review.py +57 -0
- rag_jev-0.2.0/src/rag_jev/selector.py +256 -0
- rag_jev-0.2.0/src/rag_jev/server.py +253 -0
- rag_jev-0.2.0/src/rag_jev/static/app.js +481 -0
- rag_jev-0.2.0/src/rag_jev/static/benchmark-results.svg +2115 -0
- rag_jev-0.2.0/src/rag_jev/static/benchmarks.html +38 -0
- rag_jev-0.2.0/src/rag_jev/static/fonts.css +1 -0
- rag_jev-0.2.0/src/rag_jev/static/index.html +236 -0
- rag_jev-0.2.0/src/rag_jev/static/replays/MUSIQUE_LICENSE +396 -0
- rag_jev-0.2.0/src/rag_jev/static/replays/catalog.json +14 -0
- rag_jev-0.2.0/src/rag_jev/static/replays/improvement.json +406 -0
- rag_jev-0.2.0/src/rag_jev/static/replays/regression.json +407 -0
- rag_jev-0.2.0/src/rag_jev/static/research-v2-protocol.json +74 -0
- rag_jev-0.2.0/src/rag_jev/static/research-v2-results.json +206 -0
- rag_jev-0.2.0/src/rag_jev/static/research-v2-results.svg +2401 -0
- rag_jev-0.2.0/src/rag_jev/static/research-v2.html +19 -0
- rag_jev-0.2.0/src/rag_jev/static/research-v3-protocol.json +186 -0
- rag_jev-0.2.0/src/rag_jev/static/research-v3-results.json +641 -0
- rag_jev-0.2.0/src/rag_jev/static/research-v3-results.svg +2323 -0
- rag_jev-0.2.0/src/rag_jev/static/research-v3-verification.json +16 -0
- rag_jev-0.2.0/src/rag_jev/static/research-v3.html +20 -0
- rag_jev-0.2.0/src/rag_jev/static/retrieval-v1-protocol.json +48 -0
- rag_jev-0.2.0/src/rag_jev/static/retrieval-v1-results.json +46 -0
- rag_jev-0.2.0/src/rag_jev/static/retrieval-v1-verification.json +15 -0
- rag_jev-0.2.0/src/rag_jev/static/retrieval-v1.html +12 -0
- rag_jev-0.2.0/src/rag_jev/static/review.html +16 -0
- rag_jev-0.2.0/src/rag_jev/static/review.js +51 -0
- rag_jev-0.2.0/src/rag_jev/static/style.css +538 -0
- rag_jev-0.2.0/src/rag_jev/tokens.py +16 -0
- rag_jev-0.2.0/src/rag_jev/workbench.py +281 -0
- rag_jev-0.2.0/uv.lock +3541 -0
rag_jev-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 — 2026-09-19
|
|
4
|
+
|
|
5
|
+
- MIT license, contribution/security guidance and third-party notices.
|
|
6
|
+
- Key-free browser replay mode with recorded improvement and regression examples.
|
|
7
|
+
- Contextual evaluation and development/held-out policy calibration with frozen choices,
|
|
8
|
+
shared usage, caller baselines and reusable shadow-mode configuration.
|
|
9
|
+
- Passage-token budgets that preserve pins/groups and explicitly report overruns.
|
|
10
|
+
- Browser-based blinded human review and label completeness validation.
|
|
11
|
+
- Synthetic long-passage, duplicate, missing-evidence, conversational and embedded-
|
|
12
|
+
instruction diagnostics, with live scoring latency at three concurrency levels.
|
|
13
|
+
- Reproducible research reports and immutable historical evidence bundle retained.
|
|
14
|
+
|
|
15
|
+
### Compatibility
|
|
16
|
+
|
|
17
|
+
New policy fields are optional. Policies without a token budget keep their original IDs,
|
|
18
|
+
so existing recorded comparisons import unchanged. The token budget estimates passage
|
|
19
|
+
text only with cl100k_base; it is not a provider context-window guarantee. Fail-open and
|
|
20
|
+
shadow responses can return more text than the proposed selection budget.
|
|
21
|
+
|
|
22
|
+
Historical research fingerprints intentionally require their original source snapshot.
|
|
23
|
+
Reproduce those studies from the release's frozen research archive, not updated core code.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Start with an issue describing the concrete pipeline, desired behavior, and a minimal
|
|
4
|
+
example using public or synthetic documents. Do not include keys or private documents.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
uv sync --locked --group benchmark
|
|
8
|
+
make setup
|
|
9
|
+
make check-all
|
|
10
|
+
make schema
|
|
11
|
+
uv build
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Python 3.11 and 3.13 are tested in CI. The Dify SDK check uses an isolated Python 3.12
|
|
15
|
+
environment. TypeScript requires Node 20+. Normal tests use controlled providers and
|
|
16
|
+
do not require paid keys. Live tests are explicit opt-in.
|
|
17
|
+
|
|
18
|
+
Keep source IDs, text and metadata intact. Add focused regression tests for changes to
|
|
19
|
+
selection, token budgets, failures, replay integrity and adapters. Regenerate OpenAPI and
|
|
20
|
+
TypeScript types after changing the wire contract. Document new configuration fields.
|
|
21
|
+
|
|
22
|
+
Benchmark changes must keep development and evaluation separate, preserve failures, and
|
|
23
|
+
report costs including scoring. Never tune against a held-out result and present it as
|
|
24
|
+
an independent improvement. Historical study bundles are immutable; use a new study ID.
|
|
25
|
+
|
|
26
|
+
Small pull requests with a problem statement, behavior change and actual validation are
|
|
27
|
+
welcome. Contributions are made under the project's MIT license; third-party content
|
|
28
|
+
retains its original terms. Be respectful, focus on evidence, and keep discussions technical.
|
rag_jev-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rag-jev contributors
|
|
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,28 @@
|
|
|
1
|
+
# MVP acceptance scope
|
|
2
|
+
|
|
3
|
+
- Python selector and HTTP API: filter, rerank, combined mode, explicit threshold, top-N
|
|
4
|
+
target, shadow, concurrency bound, total scoring deadline, and declared error behavior.
|
|
5
|
+
- Evidence controls: preserved source metadata, optional standalone retrieval query,
|
|
6
|
+
pinned documents, atomic groups, explicit empty-selection outcome.
|
|
7
|
+
- Browser workbench: custom query/chunks, caller-supplied baseline IDs, optional relevance
|
|
8
|
+
labels, live scoring, clearly labeled fixtures, score replay, retained/dropped evidence,
|
|
9
|
+
mobile layout, and JSON replay export/import. No server-side document persistence.
|
|
10
|
+
- Answer comparison: configurable server-side Chat Completions endpoint, same prompt/model,
|
|
11
|
+
citation/source mapping, empty-context fallback, provider usage, measured stage latency,
|
|
12
|
+
configured-rate cost estimates, and preserved errors for each branch.
|
|
13
|
+
- LangChain: native compressor plus LCEL runnable, sync/async, original-object mapping.
|
|
14
|
+
- Dify: native model-provider plugin, SDK discovery/invocation, index preservation, package.
|
|
15
|
+
- Runnable local-file retrieval example and real-pipeline adoption guide.
|
|
16
|
+
- Optional contextual scoring across Python/HTTP, TypeScript, LangChain, and Dify; shared
|
|
17
|
+
usage accounting, bounded context, and strategy-aware replay/input validation.
|
|
18
|
+
- Reproducible HotpotQA/MuSiQue subset evaluation, fixed development/evaluation separation,
|
|
19
|
+
official answer metrics, cost accounting, uncertainty, dashboard, and inspectable replays.
|
|
20
|
+
|
|
21
|
+
The configured GPT-5.6 Luna endpoint and Jev are tested live. Public-benchmark results are
|
|
22
|
+
scoped to the recorded subsets; they do not establish general factual-accuracy improvement.
|
|
23
|
+
The generation transport and browser comparison also have controlled HTTP tests. Dify SDK invocation
|
|
24
|
+
and packaging are tested locally; no remote Dify installation is provisioned by this project.
|
|
25
|
+
|
|
26
|
+
Not part of this MVP: a public hosted SaaS, public replay URLs, account/billing systems,
|
|
27
|
+
automated factual grading, a universal threshold, guaranteed quality gains, or demo-video
|
|
28
|
+
production. Exported replay files are the sharing mechanism.
|
rag_jev-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: rag-jev
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Inspectable context selection for existing RAG pipelines, powered by Jev.
|
|
5
|
+
Project-URL: Repository, https://github.com/EmreKaplaner/rag-jev
|
|
6
|
+
Project-URL: Issues, https://github.com/EmreKaplaner/rag-jev/issues
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: THIRD_PARTY_NOTICES.md
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: fastapi<1,>=0.115
|
|
12
|
+
Requires-Dist: httpx<1,>=0.28
|
|
13
|
+
Requires-Dist: pydantic<3,>=2.10
|
|
14
|
+
Requires-Dist: python-dotenv<2,>=1
|
|
15
|
+
Requires-Dist: tiktoken<1,>=0.9
|
|
16
|
+
Requires-Dist: typesafe-sdk<0.8,>=0.7
|
|
17
|
+
Requires-Dist: uvicorn<1,>=0.34
|
|
18
|
+
Provides-Extra: langchain
|
|
19
|
+
Requires-Dist: langchain-core<2,>=1; extra == 'langchain'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# rag-jev
|
|
23
|
+
|
|
24
|
+
**Filter and rerank retrieved passages before they reach your answering model.**
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
your retriever → rag-jev / Jev → your answering model
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Keep your existing ingestion, vector store and answer model. Inspect each selection,
|
|
31
|
+
replay different cutoffs, preserve connected evidence, and measure the tradeoff on your data.
|
|
32
|
+
Python, HTTP, TypeScript, LangChain and Dify integrations are included.
|
|
33
|
+
|
|
34
|
+
**MIT-licensed integration.** Live scoring uses the external TypeSafe Jev API and requires
|
|
35
|
+
its key. Query and passage text leave your application during live scoring; arbitrary
|
|
36
|
+
metadata and evaluation labels stay local. Optional answer comparison uses your configured
|
|
37
|
+
model endpoint. See [data handling](SECURITY.md).
|
|
38
|
+
|
|
39
|
+
## Try it without an API key
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
git clone https://github.com/EmreKaplaner/rag-jev.git
|
|
43
|
+
cd rag-jev
|
|
44
|
+
uv sync --locked
|
|
45
|
+
uv run rag-jev serve --replay-only
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Open **http://127.0.0.1:8000**. Explore a recorded improvement and a recorded regression,
|
|
49
|
+
inspect dropped passages, and change selection settings without scoring again. Recorded
|
|
50
|
+
answers are clearly labeled. Live inference is disabled in replay mode, even with keys
|
|
51
|
+
in the environment. The separate hand-authored fixtures are labeled as fixtures.
|
|
52
|
+
|
|
53
|
+
Python 3.11+ and [uv](https://docs.astral.sh/uv/) are required. Node 20+ is needed only for
|
|
54
|
+
TypeScript development. Release wheels and npm tarballs are available from
|
|
55
|
+
[GitHub Releases](https://github.com/EmreKaplaner/rag-jev/releases).
|
|
56
|
+
|
|
57
|
+
## Use live scoring
|
|
58
|
+
|
|
59
|
+
Copy `.env.example` to `.env`, set `TYPESAFE_API_KEY` from [TypeSafe](https://console.typesafe.ai/),
|
|
60
|
+
and restart without `--replay-only`. An answer-model key is optional.
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
uv run rag-jev serve
|
|
64
|
+
uv run rag-jev select examples/request.json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
For your application:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from rag_jev import ContextSelector, Document, Jev
|
|
71
|
+
|
|
72
|
+
async with Jev() as provider:
|
|
73
|
+
selector = ContextSelector(provider)
|
|
74
|
+
result = await selector.select(
|
|
75
|
+
query=question,
|
|
76
|
+
documents=[Document(id=c.id, text=c.text) for c in retrieved_chunks],
|
|
77
|
+
scoring_strategy="contextual",
|
|
78
|
+
mode="filter_and_rerank",
|
|
79
|
+
min_relevance=0.20, # Illustrative; calibrate on your data.
|
|
80
|
+
max_context_tokens=3000, # Passage-text estimate; leave room for prompts/output.
|
|
81
|
+
shadow=True, # Observe proposed selection while returning original context.
|
|
82
|
+
)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
[Real RAG integration guide](docs/REAL_RAG.md) · [Calibrate on your data](docs/CALIBRATION.md) ·
|
|
86
|
+
[Independent human review](docs/HUMAN_REVIEW.md) · [Contribute](CONTRIBUTING.md)
|
|
87
|
+
|
|
88
|
+
## What the measurements show
|
|
89
|
+
|
|
90
|
+
| Evaluation | Observed result | Scope |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| SciFact retrieval | NDCG@10: **75.13 Jev / 72.11 Ettin / 66.47 BM25** | All 300 test queries; same BM25 top20 candidates |
|
|
93
|
+
| HotpotQA answers | F1 **76.70 → 77.20**; estimated API cost **−19.7%** | 200 isolated questions; three answers per arm/question |
|
|
94
|
+
| MuSiQue answers | F1 **70.31 → 72.80**; estimated API cost **−22.8%** | 200 isolated questions; three answers per arm/question |
|
|
95
|
+
|
|
96
|
+
Both answer-F1 difference intervals include zero. The answer-accuracy superiority gate
|
|
97
|
+
was not met. Costs use uncached normalization and include Jev; Ettin CPU costs are separate.
|
|
98
|
+
These are scoped results, not universal improvements. Human adjudication is pending.
|
|
99
|
+
|
|
100
|
+
[Controlled study and uncertainty](benchmarks/research/RESULTS.md) ·
|
|
101
|
+
[SciFact results](benchmarks/research/RETRIEVAL_RESULTS.md) ·
|
|
102
|
+
[Methods and reproduction](benchmarks/research/README.md) ·
|
|
103
|
+
[Robustness diagnostics](benchmarks/robustness/README.md) ·
|
|
104
|
+
[Demo script](docs/RESEARCH_DEMO.md)
|
|
105
|
+
|
|
106
|
+
The historical research bundle preserves original code and raw records. Its fingerprints
|
|
107
|
+
should be checked inside that bundle; the current product has evolved since the experiment.
|
|
108
|
+
|
|
109
|
+
## Python
|
|
110
|
+
|
|
111
|
+
Install this checkout with `pip install .` or use `uv sync`. The distribution is
|
|
112
|
+
`rag-jev`, the import is `rag_jev`. PyPI/npm registry publication is pending; downloadable
|
|
113
|
+
packages are provided in the GitHub release.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
import asyncio
|
|
117
|
+
from rag_jev import ContextSelector, Document, Jev
|
|
118
|
+
|
|
119
|
+
async def main():
|
|
120
|
+
async with Jev() as provider: # reads TYPESAFE_API_KEY from the environment
|
|
121
|
+
selector = ContextSelector(provider, timeout_ms=5000, max_concurrency=8)
|
|
122
|
+
result = await selector.select(
|
|
123
|
+
query="What is the refund window?",
|
|
124
|
+
documents=[
|
|
125
|
+
Document(id="shipping", text="Orders ship within 5 business days."),
|
|
126
|
+
Document(id="refund", text="Request a refund within 30 days.",
|
|
127
|
+
metadata={"source": "refunds.md", "page": 2}),
|
|
128
|
+
],
|
|
129
|
+
min_relevance=0.20,
|
|
130
|
+
shadow=True,
|
|
131
|
+
)
|
|
132
|
+
# Feed result.documents to your existing generator.
|
|
133
|
+
# In shadow mode these are the originals; selected_ids shows what would change.
|
|
134
|
+
print(result.model_dump_json(indent=2))
|
|
135
|
+
|
|
136
|
+
asyncio.run(main())
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The library does not implicitly read `.env`; export the key or call
|
|
140
|
+
`dotenv.load_dotenv()` yourself. See [`examples/python_rag.py`](examples/python_rag.py).
|
|
141
|
+
Reuse one provider and selector per application event loop, then close the provider
|
|
142
|
+
at shutdown. The concurrency bound is shared across all requests using that selector.
|
|
143
|
+
|
|
144
|
+
Any retriever can supply `{id, text, metadata}` objects. For frameworks, map their
|
|
145
|
+
documents into this shape and use returned IDs to recover the original objects.
|
|
146
|
+
Include relevant titles/headings in `text`: metadata is preserved but **not sent
|
|
147
|
+
to Jev**. The query, passage text, and explicit relevance guidance are sent to TypeSafe.
|
|
148
|
+
|
|
149
|
+
## TypeScript
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
npm --prefix clients/typescript ci
|
|
153
|
+
npm --prefix clients/typescript run build
|
|
154
|
+
# With the service already running:
|
|
155
|
+
node examples/typescript-rag.mjs
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The [client README](clients/typescript/README.md) covers local packaging and usage
|
|
159
|
+
in another project. The client uses generated OpenAPI types, validates responses,
|
|
160
|
+
supports caller cancellation, and has no runtime dependencies. Use it server-side.
|
|
161
|
+
|
|
162
|
+
## Selection contract
|
|
163
|
+
|
|
164
|
+
`POST /v1/select` accepts the same options as `selector.select(...)`:
|
|
165
|
+
|
|
166
|
+
| Argument | Behavior |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| `query` | Required nonblank text, up to 8,000 characters |
|
|
169
|
+
| `documents` | Up to 256 documents with unique nonblank string IDs |
|
|
170
|
+
| `mode` | `filter` (default), `rerank`, or `filter_and_rerank` |
|
|
171
|
+
| `min_relevance` | Required for filtering, in `[0,1]`; rejected in rerank-only mode |
|
|
172
|
+
| `top_n` | Optional positive target after thresholding; pins and whole groups may exceed it |
|
|
173
|
+
| `max_context_tokens` | Optional passage-text token budget; preserves pins and whole groups, reports pinned overruns |
|
|
174
|
+
| `scoring_strategy` | `independent` (default) or `contextual` shared-candidate scoring |
|
|
175
|
+
| `retrieval_query` | Optional standalone query for conversational retrieval |
|
|
176
|
+
| `shadow` | Default `false`; when true, return unchanged context and proposed decisions |
|
|
177
|
+
| `relevance_guidance` | Optional domain-specific criteria, up to 2,000 characters |
|
|
178
|
+
|
|
179
|
+
Filtering retains scores **greater than or equal to** the threshold. Reranking
|
|
180
|
+
sorts descending; ties preserve input order. Reranking uses probability of usefulness,
|
|
181
|
+
not a graded relevance rubric. Empty inputs make no provider calls. If every chunk
|
|
182
|
+
fails the threshold, a successful request returns an empty selection.
|
|
183
|
+
|
|
184
|
+
Independent scoring uses one Jev request per passage with a yes/no (`Noul`) question.
|
|
185
|
+
Contextual scoring judges the candidate passages together and reports shared usage. The rubric includes
|
|
186
|
+
partial evidence, useful context, and facts correcting a false premise. It does not
|
|
187
|
+
require one passage to independently answer the whole query. A Noul has no separate
|
|
188
|
+
confidence field; uncertainty is reflected in its probability.
|
|
189
|
+
|
|
190
|
+
The response includes:
|
|
191
|
+
|
|
192
|
+
- `documents`: the actual context to send downstream.
|
|
193
|
+
- `selected_ids`: the proposed ordered selection, or `null` when selection was bypassed.
|
|
194
|
+
- `decisions`: one row per original chunk, in original order, with probability,
|
|
195
|
+
resolved model, observed tokens, selected/returned flags, and a policy reason.
|
|
196
|
+
- `status`: `applied`, `shadow`, or `bypassed`.
|
|
197
|
+
- `error_code`: a sanitized failure code on bypass.
|
|
198
|
+
- `models`, `prompt_version`, and `policy_version`: reproducibility information.
|
|
199
|
+
- `usage`, `elapsed_ms`, and original/selected/returned character counts.
|
|
200
|
+
|
|
201
|
+
Reasons are `retained`, `below_threshold`, `beyond_top_n`, `beyond_token_budget`, `pinned`, `group_retained`,
|
|
202
|
+
and `bypassed`. They are
|
|
203
|
+
policy explanations, not model-generated rationales. Shadow decisions distinguish
|
|
204
|
+
what **would be selected** from what **was actually returned**.
|
|
205
|
+
|
|
206
|
+
## Failures and limits
|
|
207
|
+
|
|
208
|
+
The total deadline includes queueing, network calls, and retries. The SDK may retry
|
|
209
|
+
once within that budget. On an upstream error, timeout, or invalid response, the
|
|
210
|
+
default library/service policy returns the **entire original context**, even when
|
|
211
|
+
`top_n` was requested. `status="bypassed"` makes that explicit. Partial rankings
|
|
212
|
+
are never applied. Outstanding tasks are canceled and awaited.
|
|
213
|
+
|
|
214
|
+
Use `ContextSelector(..., on_error="raise")` to raise `ProviderError` instead.
|
|
215
|
+
The service's equivalent is `RAG_JEV_ON_ERROR=raise`; failures become HTTP 502
|
|
216
|
+
(provider) or 504 (deadline). The CLI always raises on upstream failure and exits
|
|
217
|
+
nonzero, so evaluations cannot accidentally count passthrough as successful scoring.
|
|
218
|
+
Caller cancellation propagates rather than becoming passthrough.
|
|
219
|
+
|
|
220
|
+
On bypass, observed token usage can undercount provider billing: canceled or failed
|
|
221
|
+
calls may already have consumed tokens. `usage.complete=false` records this.
|
|
222
|
+
Core selection reports character counts. The workbench adds labeled token estimates and,
|
|
223
|
+
after optional generation, actual provider prompt/output usage plus configured-rate cost estimates.
|
|
224
|
+
|
|
225
|
+
Input limits: 60,000 characters per passage, 1,000,000 passage characters in total,
|
|
226
|
+
and 2 MB per HTTP body. These are local bounds, not promises about the provider's
|
|
227
|
+
token budget. Oversized or malformed input is rejected, never silently truncated.
|
|
228
|
+
Provider context-limit errors use normal failure handling.
|
|
229
|
+
|
|
230
|
+
`TYPESAFE_API_KEY` is required to start the live service. There is no silent demo
|
|
231
|
+
mode. `/healthz` checks service liveness, not provider availability.
|
|
232
|
+
|
|
233
|
+
| Environment variable | Default |
|
|
234
|
+
| --- | --- |
|
|
235
|
+
| `TYPESAFE_API_KEY` | Required for real calls |
|
|
236
|
+
| `RAG_JEV_MODEL` | `jev-latest`; pin a returned model version for repeatable evaluation |
|
|
237
|
+
| `RAG_JEV_TIMEOUT_MS` | `5000` |
|
|
238
|
+
| `RAG_JEV_MAX_CONCURRENCY` | `8` per selector / server process |
|
|
239
|
+
| `RAG_JEV_ON_ERROR` | `passthrough` for the service |
|
|
240
|
+
| `RAG_JEV_API_TOKEN` | Optional on localhost; required by CLI for external binds |
|
|
241
|
+
|
|
242
|
+
The service bearer token is separate from the provider key. When configured, send
|
|
243
|
+
`Authorization: Bearer <service-token>` to `/v1/select`. Keep the service behind
|
|
244
|
+
TLS/authenticated infrastructure if exposed beyond localhost. This MVP is a library
|
|
245
|
+
and single-service deployment, not a multitenant hosted SaaS. API access logs are
|
|
246
|
+
disabled by the CLI and input/response bodies are not logged by application code.
|
|
247
|
+
|
|
248
|
+
## Evaluate before enabling
|
|
249
|
+
|
|
250
|
+
Evaluation cases contain `id`, `query`, `documents`, `relevant_ids`, and an optional
|
|
251
|
+
`reference_answer`. Use representative positive, ambiguous, contradictory, and
|
|
252
|
+
unanswerable queries. Labels never enter the Jev request.
|
|
253
|
+
|
|
254
|
+
Offline demonstration:
|
|
255
|
+
|
|
256
|
+
```sh
|
|
257
|
+
uv run rag-jev eval examples/eval-cases.json \
|
|
258
|
+
--replay examples/fixture-traces.json --min-relevance 0.20
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Live scoring, then offline threshold adjustment:
|
|
262
|
+
|
|
263
|
+
```sh
|
|
264
|
+
mkdir -p artifacts
|
|
265
|
+
uv run rag-jev eval examples/eval-cases.json \
|
|
266
|
+
--min-relevance 0.20 --save-traces artifacts/live-traces.json \
|
|
267
|
+
--output artifacts/live-report.json
|
|
268
|
+
uv run rag-jev eval examples/eval-cases.json \
|
|
269
|
+
--replay artifacts/live-traces.json --min-relevance 0.35 \
|
|
270
|
+
--output artifacts/threshold-035.json
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The report compares baseline retrieval, filter, rerank, and filter-and-rerank.
|
|
274
|
+
Jev scoring happens once per passage; policy comparisons reuse those judgments.
|
|
275
|
+
Saved traces include an input fingerprint and prompt version, and mismatches fail.
|
|
276
|
+
Traces contain judgments and input hashes, not passage text.
|
|
277
|
+
|
|
278
|
+
Metrics include precision, evidence recall within retrieved candidates, binary nDCG,
|
|
279
|
+
relevant chunks dropped, total loss of useful evidence, empty-result correctness,
|
|
280
|
+
context characters, actual scoring token usage, and observed scoring p95 latency.
|
|
281
|
+
Summary values are query-level means. Recall/nDCG exclude no-relevant-document
|
|
282
|
+
queries; empty-result correctness measures those separately. Precision is zero for
|
|
283
|
+
empty outputs. A shared `top_n` cap applies to every policy, including baseline;
|
|
284
|
+
nDCG uses that fixed cutoff (or candidate count), padding shorter outputs with zeros.
|
|
285
|
+
|
|
286
|
+
**These metrics do not establish answer correctness.** The report explicitly says
|
|
287
|
+
`answer_quality="not_evaluated"` unless you pass an application callback to the Python
|
|
288
|
+
`evaluate()` function:
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from rag_jev.evaluation import evaluate
|
|
292
|
+
|
|
293
|
+
async def generate_and_grade(case, documents):
|
|
294
|
+
answer = await your_generator(case.query, documents)
|
|
295
|
+
return await your_grader(case.query, answer, case.reference_answer) # bool
|
|
296
|
+
|
|
297
|
+
report = await evaluate(cases, traces, min_relevance=0.20,
|
|
298
|
+
answer_evaluator=generate_and_grade)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The callback runs once per case per policy. Its inference cost/latency is not part
|
|
302
|
+
of the scoring metrics. Evaluate it separately with your existing generation stack.
|
|
303
|
+
Filtering cannot recover missing evidence, guarantee correct judgments, or act as
|
|
304
|
+
a prompt-injection security boundary.
|
|
305
|
+
|
|
306
|
+
## Development and verification
|
|
307
|
+
|
|
308
|
+
```sh
|
|
309
|
+
make setup
|
|
310
|
+
make check
|
|
311
|
+
uv build
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
`make check` runs lint/format checks, strict Python type checking, TypeScript build
|
|
315
|
+
and client tests, then Python tests including real local socket connections:
|
|
316
|
+
|
|
317
|
+
```text
|
|
318
|
+
TypeScript client → HTTP service → selector → official TypeSafe SDK → test HTTP endpoint
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
The controlled endpoint exercises provider responses, failures, and deadlines.
|
|
322
|
+
It does not verify live Jev quality. Real calls are explicitly opt-in:
|
|
323
|
+
|
|
324
|
+
```sh
|
|
325
|
+
RAG_JEV_LIVE=1 uv run pytest tests/test_live.py -q
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Generate updated HTTP/client contracts with `make schema` after changing models.
|
|
329
|
+
`uv.lock` and the TypeScript `package-lock.json` are checked in. Build distributions
|
|
330
|
+
with `uv build`; build and `npm pack` the TypeScript client for another application.
|
|
331
|
+
|
|
332
|
+
The core is in [`src/rag_jev`](src/rag_jev), examples in [`examples`](examples), and
|
|
333
|
+
contract/failure/integration tests in [`tests`](tests). No account system, database,
|
|
334
|
+
semantic cache, or scorer-provider switching layer is required for this MVP. The workbench
|
|
335
|
+
is bundled with the service. Native LangChain and Dify integrations are documented in
|
|
336
|
+
[the real-RAG guide](docs/REAL_RAG.md).
|
|
337
|
+
|
|
338
|
+
Official references: [TypeSafe SDK](https://docs.typesafe.ai/sdk/python),
|
|
339
|
+
[Noul](https://docs.typesafe.ai/primitives/noul),
|
|
340
|
+
[RAG passage filtering](https://docs.typesafe.ai/cookbooks/classifying_rag_passages).
|