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.
Files changed (124) hide show
  1. rag_jev-0.2.0/.gitignore +15 -0
  2. rag_jev-0.2.0/CHANGELOG.md +23 -0
  3. rag_jev-0.2.0/CONTRIBUTING.md +28 -0
  4. rag_jev-0.2.0/LICENSE +21 -0
  5. rag_jev-0.2.0/MVP_SCOPE.md +28 -0
  6. rag_jev-0.2.0/PKG-INFO +340 -0
  7. rag_jev-0.2.0/README.md +319 -0
  8. rag_jev-0.2.0/SECURITY.md +25 -0
  9. rag_jev-0.2.0/THIRD_PARTY_NOTICES.md +24 -0
  10. rag_jev-0.2.0/benchmarks/public/README.md +83 -0
  11. rag_jev-0.2.0/benchmarks/public/RESEARCH.md +108 -0
  12. rag_jev-0.2.0/benchmarks/public/RESEARCH_RESULTS.md +28 -0
  13. rag_jev-0.2.0/benchmarks/public/RESULTS.md +50 -0
  14. rag_jev-0.2.0/benchmarks/public/__init__.py +1 -0
  15. rag_jev-0.2.0/benchmarks/public/common.py +94 -0
  16. rag_jev-0.2.0/benchmarks/public/download.py +32 -0
  17. rag_jev-0.2.0/benchmarks/public/frozen-policy.json +229 -0
  18. rag_jev-0.2.0/benchmarks/public/prepare.py +127 -0
  19. rag_jev-0.2.0/benchmarks/public/protocol.json +63 -0
  20. rag_jev-0.2.0/benchmarks/public/publish_report.py +226 -0
  21. rag_jev-0.2.0/benchmarks/public/report.py +115 -0
  22. rag_jev-0.2.0/benchmarks/public/research-v2-frozen.json +10 -0
  23. rag_jev-0.2.0/benchmarks/public/research-v2-protocol.json +74 -0
  24. rag_jev-0.2.0/benchmarks/public/research.py +496 -0
  25. rag_jev-0.2.0/benchmarks/public/research_publish.py +261 -0
  26. rag_jev-0.2.0/benchmarks/public/research_verify.py +124 -0
  27. rag_jev-0.2.0/benchmarks/public/run.py +288 -0
  28. rag_jev-0.2.0/benchmarks/research/README.md +208 -0
  29. rag_jev-0.2.0/benchmarks/research/RESULTS.md +48 -0
  30. rag_jev-0.2.0/benchmarks/research/RETRIEVAL_RESULTS.md +25 -0
  31. rag_jev-0.2.0/benchmarks/research/__init__.py +1 -0
  32. rag_jev-0.2.0/benchmarks/research/design.py +276 -0
  33. rag_jev-0.2.0/benchmarks/research/fetch_sources.py +47 -0
  34. rag_jev-0.2.0/benchmarks/research/frozen.json +28 -0
  35. rag_jev-0.2.0/benchmarks/research/metric-sources.json +12 -0
  36. rag_jev-0.2.0/benchmarks/research/neural.py +69 -0
  37. rag_jev-0.2.0/benchmarks/research/prior-exposure.json +646 -0
  38. rag_jev-0.2.0/benchmarks/research/protocol.json +186 -0
  39. rag_jev-0.2.0/benchmarks/research/publish.py +370 -0
  40. rag_jev-0.2.0/benchmarks/research/publish_retrieval.py +101 -0
  41. rag_jev-0.2.0/benchmarks/research/reranker-source.json +5 -0
  42. rag_jev-0.2.0/benchmarks/research/retrieval-protocol.json +48 -0
  43. rag_jev-0.2.0/benchmarks/research/retrieval.py +344 -0
  44. rag_jev-0.2.0/benchmarks/research/runner.py +300 -0
  45. rag_jev-0.2.0/benchmarks/research/source-verification.json +9 -0
  46. rag_jev-0.2.0/benchmarks/research/statistics.py +282 -0
  47. rag_jev-0.2.0/benchmarks/research/verification.json +16 -0
  48. rag_jev-0.2.0/benchmarks/research/verify.py +245 -0
  49. rag_jev-0.2.0/benchmarks/research/verify_retrieval.py +56 -0
  50. rag_jev-0.2.0/benchmarks/robustness/README.md +34 -0
  51. rag_jev-0.2.0/benchmarks/robustness/cases.json +371 -0
  52. rag_jev-0.2.0/benchmarks/robustness/protocol.json +38 -0
  53. rag_jev-0.2.0/benchmarks/robustness/results.json +679 -0
  54. rag_jev-0.2.0/benchmarks/robustness/run.py +224 -0
  55. rag_jev-0.2.0/docs/BENCHMARK_DEMO.md +45 -0
  56. rag_jev-0.2.0/docs/CALIBRATION.md +68 -0
  57. rag_jev-0.2.0/docs/HUMAN_REVIEW.md +28 -0
  58. rag_jev-0.2.0/docs/REAL_RAG.md +257 -0
  59. rag_jev-0.2.0/docs/RELEASING.md +32 -0
  60. rag_jev-0.2.0/docs/RESEARCH_DEMO.md +33 -0
  61. rag_jev-0.2.0/examples/eval-cases.json +80 -0
  62. rag_jev-0.2.0/examples/fixture-traces.json +80 -0
  63. rag_jev-0.2.0/examples/knowledge/refunds.md +7 -0
  64. rag_jev-0.2.0/examples/knowledge/shipping.md +5 -0
  65. rag_jev-0.2.0/examples/knowledge/subscriptions.md +5 -0
  66. rag_jev-0.2.0/examples/local_rag.py +124 -0
  67. rag_jev-0.2.0/examples/python_rag.py +28 -0
  68. rag_jev-0.2.0/examples/request.json +29 -0
  69. rag_jev-0.2.0/examples/typescript-rag.mjs +11 -0
  70. rag_jev-0.2.0/integrations/dify/.difyignore +6 -0
  71. rag_jev-0.2.0/integrations/dify/_assets/icon.svg +1 -0
  72. rag_jev-0.2.0/integrations/dify/main.py +6 -0
  73. rag_jev-0.2.0/integrations/dify/manifest.yaml +25 -0
  74. rag_jev-0.2.0/integrations/dify/models/rerank/_position.yaml +1 -0
  75. rag_jev-0.2.0/integrations/dify/models/rerank/jev.yaml +5 -0
  76. rag_jev-0.2.0/integrations/dify/models/rerank/rerank.py +93 -0
  77. rag_jev-0.2.0/integrations/dify/provider/rag_jev.py +7 -0
  78. rag_jev-0.2.0/integrations/dify/provider/rag_jev.yaml +52 -0
  79. rag_jev-0.2.0/integrations/dify/pyproject.toml +5 -0
  80. rag_jev-0.2.0/integrations/dify/requirements.txt +2 -0
  81. rag_jev-0.2.0/integrations/dify/uv.lock +796 -0
  82. rag_jev-0.2.0/pyproject.toml +66 -0
  83. rag_jev-0.2.0/src/rag_jev/__init__.py +15 -0
  84. rag_jev-0.2.0/src/rag_jev/calibration.py +353 -0
  85. rag_jev-0.2.0/src/rag_jev/cli.py +206 -0
  86. rag_jev-0.2.0/src/rag_jev/demo.py +76 -0
  87. rag_jev-0.2.0/src/rag_jev/evaluation.py +275 -0
  88. rag_jev-0.2.0/src/rag_jev/generation.py +223 -0
  89. rag_jev-0.2.0/src/rag_jev/integrations/__init__.py +1 -0
  90. rag_jev-0.2.0/src/rag_jev/integrations/langchain.py +124 -0
  91. rag_jev-0.2.0/src/rag_jev/models.py +142 -0
  92. rag_jev-0.2.0/src/rag_jev/provider.py +167 -0
  93. rag_jev-0.2.0/src/rag_jev/py.typed +0 -0
  94. rag_jev-0.2.0/src/rag_jev/review.py +57 -0
  95. rag_jev-0.2.0/src/rag_jev/selector.py +256 -0
  96. rag_jev-0.2.0/src/rag_jev/server.py +253 -0
  97. rag_jev-0.2.0/src/rag_jev/static/app.js +481 -0
  98. rag_jev-0.2.0/src/rag_jev/static/benchmark-results.svg +2115 -0
  99. rag_jev-0.2.0/src/rag_jev/static/benchmarks.html +38 -0
  100. rag_jev-0.2.0/src/rag_jev/static/fonts.css +1 -0
  101. rag_jev-0.2.0/src/rag_jev/static/index.html +236 -0
  102. rag_jev-0.2.0/src/rag_jev/static/replays/MUSIQUE_LICENSE +396 -0
  103. rag_jev-0.2.0/src/rag_jev/static/replays/catalog.json +14 -0
  104. rag_jev-0.2.0/src/rag_jev/static/replays/improvement.json +406 -0
  105. rag_jev-0.2.0/src/rag_jev/static/replays/regression.json +407 -0
  106. rag_jev-0.2.0/src/rag_jev/static/research-v2-protocol.json +74 -0
  107. rag_jev-0.2.0/src/rag_jev/static/research-v2-results.json +206 -0
  108. rag_jev-0.2.0/src/rag_jev/static/research-v2-results.svg +2401 -0
  109. rag_jev-0.2.0/src/rag_jev/static/research-v2.html +19 -0
  110. rag_jev-0.2.0/src/rag_jev/static/research-v3-protocol.json +186 -0
  111. rag_jev-0.2.0/src/rag_jev/static/research-v3-results.json +641 -0
  112. rag_jev-0.2.0/src/rag_jev/static/research-v3-results.svg +2323 -0
  113. rag_jev-0.2.0/src/rag_jev/static/research-v3-verification.json +16 -0
  114. rag_jev-0.2.0/src/rag_jev/static/research-v3.html +20 -0
  115. rag_jev-0.2.0/src/rag_jev/static/retrieval-v1-protocol.json +48 -0
  116. rag_jev-0.2.0/src/rag_jev/static/retrieval-v1-results.json +46 -0
  117. rag_jev-0.2.0/src/rag_jev/static/retrieval-v1-verification.json +15 -0
  118. rag_jev-0.2.0/src/rag_jev/static/retrieval-v1.html +12 -0
  119. rag_jev-0.2.0/src/rag_jev/static/review.html +16 -0
  120. rag_jev-0.2.0/src/rag_jev/static/review.js +51 -0
  121. rag_jev-0.2.0/src/rag_jev/static/style.css +538 -0
  122. rag_jev-0.2.0/src/rag_jev/tokens.py +16 -0
  123. rag_jev-0.2.0/src/rag_jev/workbench.py +281 -0
  124. rag_jev-0.2.0/uv.lock +3541 -0
@@ -0,0 +1,15 @@
1
+ .env
2
+ .env.*
3
+ !.env.example
4
+ .venv/
5
+ __pycache__/
6
+ .pytest_cache/
7
+ .mypy_cache/
8
+ .ruff_cache/
9
+ *.egg-info/
10
+ dist/
11
+ node_modules/
12
+ artifacts/
13
+ .DS_Store
14
+ .agentstack/
15
+ .serena/
@@ -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).