evalproof 0.2.0__py3-none-any.whl

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.
evalproof/systems.py ADDED
@@ -0,0 +1,154 @@
1
+ """Systems under test: an LLM prompt, a Python function, or an HTTP endpoint.
2
+
3
+ A system failure is recorded on the case, not raised: one timeout should not throw
4
+ away an hour of judging. Errored cases score as failures and are listed in reports.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import importlib
10
+ import string
11
+ import sys
12
+ import time
13
+ from collections.abc import Callable
14
+ from typing import Any, Protocol
15
+
16
+ import httpx
17
+
18
+ from evalproof.cache import CachedProvider, ResponseCache
19
+ from evalproof.config import ConfigError, TargetConfig
20
+ from evalproof.models import Case, SystemOutput
21
+ from evalproof.providers import Message, Provider, ProviderError, make_provider, with_retries
22
+
23
+
24
+ class System(Protocol):
25
+ name: str
26
+
27
+ def run(self, case: Case) -> SystemOutput: ...
28
+
29
+
30
+ def render(template: str, variables: dict[str, Any]) -> str:
31
+ """``str.format`` with a clear error naming the missing field."""
32
+ missing = [
33
+ field
34
+ for _, field, _, _ in string.Formatter().parse(template)
35
+ if field and field.split(".")[0].split("[")[0] not in variables
36
+ ]
37
+ if missing:
38
+ raise ConfigError(f"prompt template references unknown fields: {sorted(set(missing))}")
39
+ return template.format_map(variables)
40
+
41
+
42
+ class LLMSystem:
43
+ def __init__(self, cfg: TargetConfig, provider: Provider) -> None:
44
+ self.cfg = cfg
45
+ self.provider = provider
46
+ self.name = cfg.label or cfg.target
47
+
48
+ def messages(self, case: Case) -> list[Message]:
49
+ msgs: list[Message] = []
50
+ if self.cfg.system_prompt:
51
+ msgs.append(Message(role="system", content=self.cfg.system_prompt))
52
+ msgs.append(
53
+ Message(role="user", content=render(self.cfg.prompt_template, case.render_vars()))
54
+ )
55
+ return msgs
56
+
57
+ def run(self, case: Case) -> SystemOutput:
58
+ try:
59
+ resp = self.provider.chat(self.messages(case), self.cfg.params)
60
+ except ProviderError as exc:
61
+ return SystemOutput(output="", error=str(exc))
62
+ return SystemOutput(
63
+ output=resp.text,
64
+ latency_ms=resp.latency_ms,
65
+ tokens_in=resp.tokens_in,
66
+ tokens_out=resp.tokens_out,
67
+ cost_usd=self.cfg.pricing.cost(resp.tokens_in, resp.tokens_out),
68
+ cached=resp.cached,
69
+ )
70
+
71
+
72
+ def _coerce(result: Any) -> tuple[str, list[str]]:
73
+ if isinstance(result, str):
74
+ return result, []
75
+ if isinstance(result, dict) and "output" in result:
76
+ return str(result["output"]), [str(x) for x in result.get("retrieved") or []]
77
+ raise TypeError("a system must return a string or a dict with an 'output' key")
78
+
79
+
80
+ class PythonSystem:
81
+ """``python:package.module:function`` — the function receives the case input.
82
+
83
+ ``search_path`` (the suite's directory) is put on ``sys.path`` so a suite can ship
84
+ its system next to it and still run from any working directory.
85
+ """
86
+
87
+ def __init__(self, spec: str, search_path: str | None = None) -> None:
88
+ self.name = spec
89
+ if search_path and search_path not in sys.path:
90
+ sys.path.insert(0, search_path)
91
+ try:
92
+ _, module_name, func_name = spec.split(":", 2)
93
+ except ValueError as exc:
94
+ raise ConfigError(
95
+ f"python target must be 'python:<module>:<function>', got {spec!r}"
96
+ ) from exc
97
+ module = importlib.import_module(module_name)
98
+ func = getattr(module, func_name, None)
99
+ if not callable(func):
100
+ raise ConfigError(f"{module_name}.{func_name} is not callable")
101
+ self.func: Callable[[Any], Any] = func
102
+
103
+ def run(self, case: Case) -> SystemOutput:
104
+ start = time.perf_counter()
105
+ try:
106
+ output, retrieved = _coerce(self.func(case.input))
107
+ except Exception as exc:
108
+ return SystemOutput(output="", error=f"{type(exc).__name__}: {exc}")
109
+ return SystemOutput(
110
+ output=output, retrieved=retrieved, latency_ms=(time.perf_counter() - start) * 1000
111
+ )
112
+
113
+
114
+ class HttpSystem:
115
+ """POSTs ``{"id", "input"}``; expects JSON ``{"output", "retrieved"?}`` or plain text."""
116
+
117
+ def __init__(self, cfg: TargetConfig, client: httpx.Client | None = None) -> None:
118
+ self.cfg = cfg
119
+ self.name = cfg.label or cfg.target
120
+ self._client = client or httpx.Client(timeout=cfg.timeout_s)
121
+
122
+ def run(self, case: Case) -> SystemOutput:
123
+ def call() -> httpx.Response:
124
+ resp = self._client.post(self.cfg.target, json={"id": case.id, "input": case.input})
125
+ resp.raise_for_status()
126
+ return resp
127
+
128
+ start = time.perf_counter()
129
+ try:
130
+ resp = with_retries(call)
131
+ ctype = resp.headers.get("content-type", "")
132
+ output, retrieved = _coerce(resp.json() if "json" in ctype else resp.text)
133
+ except (ProviderError, TypeError, ValueError) as exc:
134
+ return SystemOutput(output="", error=str(exc))
135
+ return SystemOutput(
136
+ output=output, retrieved=retrieved, latency_ms=(time.perf_counter() - start) * 1000
137
+ )
138
+
139
+
140
+ def make_system(
141
+ cfg: TargetConfig,
142
+ cache: ResponseCache | None = None,
143
+ client: httpx.Client | None = None,
144
+ search_path: str | None = None,
145
+ ) -> System:
146
+ kind = cfg.kind
147
+ if kind == "python":
148
+ return PythonSystem(cfg.target, search_path=search_path)
149
+ if kind == "http":
150
+ return HttpSystem(cfg, client=client)
151
+ provider: Provider = make_provider(cfg, client=client)
152
+ if cache is not None:
153
+ provider = CachedProvider(provider, cache, base_url=cfg.base_url)
154
+ return LLMSystem(cfg, provider)
@@ -0,0 +1,279 @@
1
+ Metadata-Version: 2.5
2
+ Name: evalproof
3
+ Version: 0.2.0
4
+ Summary: Treat LLM evaluation as measurement: calibrate the judge, correct the pass rate, gate CI on significant regressions.
5
+ Project-URL: Homepage, https://github.com/technobasant/evalproof
6
+ Project-URL: Documentation, https://github.com/technobasant/evalproof#readme
7
+ Project-URL: Source, https://github.com/technobasant/evalproof
8
+ Project-URL: Issues, https://github.com/technobasant/evalproof/issues
9
+ Project-URL: Changelog, https://github.com/technobasant/evalproof/blob/main/CHANGELOG.md
10
+ Project-URL: Live demo, https://evalproof.streamlit.app
11
+ Project-URL: Write-up, https://basantbhattarai.com.np/writing/eval-score-drop-twelve-answers/
12
+ Author: Basant Bhattarai
13
+ License-Expression: Apache-2.0
14
+ License-File: LICENSE
15
+ License-File: NOTICE
16
+ Keywords: calibration,ci,evaluation,llm,llm-as-a-judge,statistics
17
+ Classifier: Development Status :: 4 - Beta
18
+ Classifier: Environment :: Console
19
+ Classifier: Intended Audience :: Developers
20
+ Classifier: Intended Audience :: Science/Research
21
+ Classifier: Operating System :: OS Independent
22
+ Classifier: Programming Language :: Python :: 3
23
+ Classifier: Programming Language :: Python :: 3 :: Only
24
+ Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Programming Language :: Python :: 3.14
28
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
29
+ Classifier: Topic :: Software Development :: Quality Assurance
30
+ Classifier: Topic :: Software Development :: Testing
31
+ Classifier: Typing :: Typed
32
+ Requires-Python: >=3.11
33
+ Requires-Dist: httpx>=0.27
34
+ Requires-Dist: numpy>=1.26
35
+ Requires-Dist: pydantic>=2.7
36
+ Requires-Dist: pyyaml>=6.0
37
+ Description-Content-Type: text/markdown
38
+
39
+ # evalproof
40
+
41
+ [![PyPI](https://img.shields.io/pypi/v/evalproof)](https://pypi.org/project/evalproof/)
42
+ [![Python](https://img.shields.io/pypi/pyversions/evalproof)](https://pypi.org/project/evalproof/)
43
+ [![ci](https://github.com/technobasant/evalproof/actions/workflows/ci.yml/badge.svg)](https://github.com/technobasant/evalproof/actions/workflows/ci.yml)
44
+ [![licence](https://img.shields.io/badge/licence-Apache--2.0-blue)](https://github.com/technobasant/evalproof/blob/main/LICENSE)
45
+ [![Open in Streamlit](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://evalproof.streamlit.app)
46
+
47
+ **Treat LLM evaluation as measurement.** Calibrate the judge, correct its pass rate, and
48
+ gate CI on regressions that are real rather than noise.
49
+
50
+ **Try it in the browser: [evalproof.streamlit.app](https://evalproof.streamlit.app).** Check whether
51
+ a drop between two runs is real, or replay the run below and read the twelve answers that
52
+ changed. It runs this repository's code; after a quiet spell it takes about half a minute to wake.
53
+
54
+ An LLM judge is a classifier with error rates. If it passes bad answers at rate FPR and
55
+ good answers at rate TPR, its pass rate converges to `FPR + (TPR − FPR) · p`, not to the
56
+ true rate `p`. That is classify-and-count, and it is biased the way any classifier's count
57
+ is — the effect is worked through with a seeded simulation in
58
+ [*The classifier was 95% accurate. The trend it counted was a third too flat*](https://basantbhattarai.com.np/writing/count-prevalence-not-classifications/)
59
+ ([lab](https://github.com/technobasant/prevalence-shift)).
60
+
61
+ evalproof:
62
+
63
+ 1. **measures the judge** against a small gold-labelled sample — confusion matrix, TPR,
64
+ FPR, Cohen's κ;
65
+ 2. **corrects its pass rate** with adjusted classify-and-count and prediction-powered
66
+ inference, each with a confidence interval;
67
+ 3. **compares runs on paired cases** — exact McNemar for pass/fail, paired bootstrap for
68
+ scores;
69
+ 4. **gates CI** only when a drop is both larger than your tolerance *and* statistically
70
+ significant, plus latency and cost budgets, with distinct exit codes.
71
+
72
+ It is local-first: Ollama is the default provider, Anthropic and OpenAI are optional, and
73
+ every model call is cached by content so re-runs are free and reproducible.
74
+
75
+ ## Install
76
+
77
+ ```bash
78
+ pip install evalproof # or: uv add evalproof
79
+ uv tool install evalproof # just the CLI
80
+ ```
81
+
82
+ Python 3.11 to 3.14. `compare`, `calibrate` and `report` work on run files and need no
83
+ model. `run` needs a system to evaluate: a Python function, an HTTP endpoint, a local
84
+ Ollama model, or an Anthropic or OpenAI key.
85
+
86
+ ## What it found on a real run
87
+
88
+ A local `qwen3:1.7b` support assistant for a fictional product, judged by `qwen3:8b`, 83
89
+ questions, one Apple M5. Full method and every table in [MEASUREMENTS.md](https://github.com/technobasant/evalproof/blob/main/MEASUREMENTS.md).
90
+
91
+ | Question | Measured |
92
+ |---|---|
93
+ | How good is the 8B judge? | TPR 92.6%, FPR 6.7%, Cohen's κ 0.78 against the answer key |
94
+ | Did swapping the grounded prompt for a "friendly" one regress quality? | −4.8 pts on the judge, 8 cases worse vs 4 better, McNemar p = 0.39 — **gate warns, does not fail**; a delta-only gate would have failed it |
95
+ | What did regress? | p95 latency, 1,469 → 4,422 ms; mean answer 19 → 77 tokens. A 2,500 ms budget fails the change with exit 3 |
96
+ | Does correcting the judge's rate help? | On average yes (offset 4.8 → ~3.8 pts), but with 30 labels the interval doubles; at 83 cases the judge's bias is smaller than its own noise |
97
+ | Was the gold trustworthy? | No: of 13 judge–key disagreements the key was wrong 7 times, the judge 4. Most of the "bias" corrected was the gold's |
98
+ | Is pairwise judging safer? | No: 42% of verdicts flipped with the order of the two answers, and the judge preferred the candidate 42 to 6 — mostly where both answers were right and the candidate's was five times longer |
99
+ | Is it reproducible? | 20/20 identical outputs and verdicts with the cache off; a warm-cache re-run took 0.05 s instead of 363 s |
100
+
101
+ ## Five-minute quickstart
102
+
103
+ The offline example lives in the repository:
104
+
105
+ ```bash
106
+ git clone https://github.com/technobasant/evalproof && cd evalproof
107
+ uv sync
108
+
109
+ # Offline: no model, no network. Four versions of a ticket router.
110
+ for v in baseline regressed noisy refactored; do
111
+ uv run evalproof run examples/deterministic/suite.yaml \
112
+ --system "python:router:${v}_route" --out "runs/$v.json" --quiet
113
+ done
114
+ uv run evalproof compare runs/baseline.json runs/regressed.json \
115
+ --suite examples/deterministic/suite.yaml --gate; echo "exit $?" # 1: real regression
116
+ uv run evalproof compare runs/baseline.json runs/noisy.json \
117
+ --suite examples/deterministic/suite.yaml --gate; echo "exit $?" # 0: 2 of 60 is noise
118
+ ```
119
+
120
+ With Ollama installed, the LLM example is
121
+ [`examples/support-qa`](https://github.com/technobasant/evalproof/blob/main/examples/support-qa) (`ollama pull qwen3:1.7b` and
122
+ `ollama pull qwen3:8b`; about 6–10 minutes per run on an Apple M5).
123
+
124
+ The browser app in [`app/`](https://github.com/technobasant/evalproof/blob/main/app) runs locally too:
125
+ `uv run --with-requirements app/requirements.txt streamlit run app/streamlit_app.py`.
126
+
127
+ ## Concepts
128
+
129
+ **Suite** — one YAML file: a JSONL golden set, the system under test, scorers, an optional
130
+ judge, calibration settings and a gate.
131
+
132
+ ```yaml
133
+ name: support-qa
134
+ dataset: cases.jsonl # {"id", "input", "reference", ...extra fields}
135
+ system:
136
+ target: ollama:qwen3:1.7b # or anthropic:…, openai:…, python:module:func, https://…
137
+ system_prompt_file: prompt.md
138
+ prompt_template: "{question}"
139
+ params: {temperature: 0, seed: 0, think: false}
140
+ judge:
141
+ target: ollama:qwen3:8b
142
+ scorers:
143
+ - {name: answer_key, type: regex, params: {any_of_field: accept, none_of_field: reject}}
144
+ - {name: judge, type: llm_judge, params: {rubric_file: rubric.md}}
145
+ calibration: {judge_scorer: judge, gold_scorer: answer_key, gold_sample: 30}
146
+ gate: {metric: judge, max_drop: 0.03, alpha: 0.05, min_cases: 40, max_p95_latency_ms: 2500}
147
+ ```
148
+
149
+ **Run** — `evalproof run suite.yaml` writes one self-describing JSON file: every case's
150
+ output, latency, tokens and scores, plus the dataset hash, seed, Ollama version and model
151
+ digests.
152
+
153
+ **Scorers** — `exact_match`, `contains`, `regex` (accept/reject answer keys),
154
+ `json_schema`, `numeric_tolerance`, `recall_at_k`, `mrr`, `ndcg`, and `llm_judge`: a strict
155
+ JSON verdict, one retry on an unparseable reply, and an explicit *undecided* state that is
156
+ counted, never scored as a pass. `evalproof pairwise` judges two runs head to head in both
157
+ orders and reports order-dependent verdicts as ties.
158
+
159
+ **Calibrate** — `evalproof calibrate run.json --judge-scorer judge --labels gold.jsonl`
160
+ (human labels) or `--gold-scorer answer_key --gold-sample 30`:
161
+
162
+ | Estimate | What it is |
163
+ |---|---|
164
+ | raw judge pass rate | classify-and-count, Wilson interval |
165
+ | gold labels only | the labelled sample alone |
166
+ | ACC | `(q − FPR) / (TPR − FPR)`, bootstrap interval over both the judged set and the gold sample |
167
+ | PPI | judge mean on unlabelled cases + mean (label − judge) on the gold sample, normal interval |
168
+
169
+ **Compare and gate** — `evalproof compare baseline.json candidate.json --suite suite.yaml --gate`
170
+
171
+ | Exit | Meaning |
172
+ |---|---|
173
+ | 0 | pass — possibly with warnings (a big drop that is not significant; a significant drop within tolerance) |
174
+ | 1 | quality regression: drop ≥ `max_drop` **and** p < `alpha` |
175
+ | 3 | budget breach: candidate p95 latency or cost over the limit |
176
+ | 4 | cannot gate: fewer than `min_cases` paired cases, or the runs used different data |
177
+ | 2 | usage or configuration error |
178
+
179
+ ## From Python
180
+
181
+ The CLI is a thin layer over the same functions:
182
+
183
+ ```python
184
+ from evalproof import GateConfig, calibrate, compare_runs, decide, load_run
185
+
186
+ baseline = load_run("runs/baseline.json")
187
+ candidate = load_run("runs/candidate.json")
188
+
189
+ cmp = compare_runs(baseline, candidate) # paired cases: exact McNemar, bootstrap intervals
190
+ decision = decide(cmp, GateConfig(metric="judge", max_drop=0.03))
191
+ print(decision.status, decision.exit_code, decision.warnings)
192
+
193
+ report = calibrate(baseline, "judge", gold_scorer="answer_key", gold_sample=30)
194
+ for est in report.estimates: # raw judge rate, gold only, ACC, PPI
195
+ print(est.method, est.point, est.low, est.high)
196
+ ```
197
+
198
+ The names in `evalproof.__all__` are the supported API. Other modules are internal and may
199
+ change between minor versions; see the [changelog](https://github.com/technobasant/evalproof/blob/main/CHANGELOG.md).
200
+
201
+ ## Architecture
202
+
203
+ The architecture diagram is in the [README on GitHub](https://github.com/technobasant/evalproof#architecture).
204
+
205
+ ## Decisions
206
+
207
+ | Decision | Alternative | Why | ADR |
208
+ |---|---|---|---|
209
+ | Treat the judge as a classifier to be calibrated | Trust the judge's pass rate | Its count is biased by its own error rates, and the bias moves with the true rate | [0001](https://github.com/technobasant/evalproof/blob/main/docs/adr/0001-judge-is-a-classifier.md) |
210
+ | Local Ollama by default, HTTP-only providers, content-addressed cache | Hosted APIs and vendor SDKs | Reproducible, free to re-run, tied to a model digest; no SDK dependency | [0002](https://github.com/technobasant/evalproof/blob/main/docs/adr/0002-local-first-providers.md) |
211
+ | Gate on "large AND significant", on paired cases | "Fail if the score dropped by X" | On a few dozen cases, X-point drops are mostly noise, and a noisy gate gets switched off | [0003](https://github.com/technobasant/evalproof/blob/main/docs/adr/0003-gate-on-significance-not-delta.md) |
212
+ | Gate one metric; report the rest | Gate every metric | Testing many metrics at α = 0.05 each inflates false failures | 0003 |
213
+ | Pointwise verdicts for gating; pairwise as a diagnostic | Pairwise preference for gating | Measured: 42% of pairwise verdicts flipped with answer order, with a strong length preference | [MEASUREMENTS §6](https://github.com/technobasant/evalproof/blob/main/MEASUREMENTS.md) |
214
+ | numpy and the standard library for statistics | SciPy, statsmodels | Small enough to read and to test against hand-worked values | — |
215
+
216
+ ## In CI
217
+
218
+ The composite action runs `compare --gate`, appends the report to the job summary, and
219
+ fails the job on exit codes 1, 3 or 4:
220
+
221
+ ```yaml
222
+ - uses: technobasant/evalproof@v0.2.0
223
+ with:
224
+ baseline: eval/baseline.json # produced on main, e.g. as a build artifact
225
+ candidate: eval/candidate.json # produced on this branch
226
+ suite: eval/suite.yaml
227
+ ```
228
+
229
+ This repository's own CI runs the offline example on every commit and asserts that the
230
+ gate returns 1 for the real regression and 0 for the noisy and refactored candidates.
231
+
232
+ ## How it relates to other tools
233
+
234
+ [promptfoo](https://www.promptfoo.dev/docs/intro/) (open-source, local evals of prompts,
235
+ models and RAG pipelines with deterministic and model-graded assertions, a GitHub Action,
236
+ and red teaming), [DeepEval](https://deepeval.com/docs/getting-started) (pytest-style
237
+ LLM tests with research-backed metrics such as G-Eval), [Ragas](https://docs.ragas.io/en/stable/)
238
+ (LLM-driven metrics and experiments for LLM and RAG applications) and
239
+ [Inspect](https://inspect.aisi.org.uk/) (the UK AI Security Institute's framework for
240
+ model and agent evaluations, with sandboxed tools and a log viewer) are broad evaluation
241
+ frameworks.
242
+
243
+ evalproof is deliberately narrow: it is the measurement layer — how much a judge's verdict
244
+ is worth, and whether a difference between two runs is real. Its run file is plain,
245
+ documented JSON, so it can sit beside an existing harness: produce the per-case verdicts
246
+ however you like, then calibrate and gate here.
247
+
248
+ ## Limits
249
+
250
+ * The measurements cover one machine, two small local models, one fictional domain and
251
+ 83 cases. They show the method; they are not a benchmark of those models.
252
+ * Calibration is only as good as its gold labels — [MEASUREMENTS §4](https://github.com/technobasant/evalproof/blob/main/MEASUREMENTS.md)
253
+ shows a regex answer key being wrong more often than the judge. Use human labels when
254
+ the decision matters.
255
+ * ACC needs both classes in the gold sample and a judge better than chance; PPI needs the
256
+ gold sample to be random. evalproof reports when either does not hold rather than
257
+ printing a number.
258
+ * The percentile-bootstrap p-value for continuous scores is an approximation; pass/fail
259
+ metrics use the exact McNemar test.
260
+ * Cost tracking needs a price table in the suite for hosted models.
261
+
262
+ ## Roadmap
263
+
264
+ * Stratified gold sampling (by tag) and per-tag calibration.
265
+ * Multiple-comparison correction when more than one metric is gated.
266
+ * A reasoning judge (`think: true`) measured against the same answer key.
267
+ * Importers for other harnesses' per-case results.
268
+
269
+ ## Project
270
+
271
+ [Changelog](https://github.com/technobasant/evalproof/blob/main/CHANGELOG.md) · [Contributing](https://github.com/technobasant/evalproof/blob/main/CONTRIBUTING.md) ·
272
+ [Security policy](https://github.com/technobasant/evalproof/blob/main/SECURITY.md) · [Code of conduct](https://github.com/technobasant/evalproof/blob/main/CODE_OF_CONDUCT.md) ·
273
+ [How to cite](https://github.com/technobasant/evalproof/blob/main/CITATION.cff). Releases are published to PyPI from GitHub Actions with
274
+ [trusted publishing](https://docs.pypi.org/trusted-publishers/), and each file carries a
275
+ signed provenance attestation.
276
+
277
+ ## Licence
278
+
279
+ Apache-2.0. See [LICENSE](https://github.com/technobasant/evalproof/blob/main/LICENSE) and [NOTICE](https://github.com/technobasant/evalproof/blob/main/NOTICE).
@@ -0,0 +1,30 @@
1
+ evalproof/__init__.py,sha256=EXJgFmkOEU6CCP8mhb1k1B-r9Lt2v1zttIxP13LuiiE,1953
2
+ evalproof/_version.py,sha256=8LOhs9njNsWQUbKWbELE0YghKdN7GOQChQgEnFWM4Ps,121
3
+ evalproof/cache.py,sha256=hzJB7jV6hCzT6F_GZDB8NpVXh4dgiwtc-8Ni56WPAs8,3066
4
+ evalproof/calibration.py,sha256=oRqg7qSnKKd2gwk7boNSYwsarYDLRlNKWb5Mwe3E2TY,9552
5
+ evalproof/cli.py,sha256=qlLgxkhZysXOgVzP9FxUWzgoyTYRwu1rD7xiXKFiD00,9831
6
+ evalproof/compare.py,sha256=iyNddsgjPLYJ1IC3KJyxs989tsYheukoDPMnsFD2wEI,5297
7
+ evalproof/config.py,sha256=1R0Dxgegx_knJSGbFylfBi3pQK-aUPw0_S6E5-A8SEA,5329
8
+ evalproof/dataset.py,sha256=5bvBaPajQxNnaUFAWwgEL6OnRRsQiT0WsYI7FPW3w1E,2346
9
+ evalproof/gate.py,sha256=R-cm0TQaQWcnY8LkJOJy4hQNT2imK1MNV0_QnC51jt4,4036
10
+ evalproof/models.py,sha256=g3AyW9rJRKuKrE1dNrZWvwADqhY3xxl4VEv01XCfR7k,3332
11
+ evalproof/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ evalproof/report.py,sha256=q2fVojAQTlKiBfgxSbTl7uBEvsJkjvvaxO1zxj83IfA,5831
13
+ evalproof/runner.py,sha256=6QCvComHEa02Ql28qnQiOqm0SUzsu8f7qn1caOH3Bt8,4724
14
+ evalproof/stats.py,sha256=Xwt5k7g29uC7_l2rs-Vyw14XKYBr9Mn_APXy75ewXDE,4992
15
+ evalproof/systems.py,sha256=pvAV02QVRnPsVZxRMivJDW-mlhPtAt1qcLCS3jnjNEI,5631
16
+ evalproof/providers/__init__.py,sha256=YIptAL2kK6LU7rcuyUV2LrCv9KjpDjYwXCLz2aUzdis,1785
17
+ evalproof/providers/anthropic.py,sha256=jogTjgxQCad3D-E-DAyFnfzgODB2pmago2Ujy3rCGpw,2623
18
+ evalproof/providers/base.py,sha256=XNRiu5S1vyfwU1EzmaAPmL6v-vsHvOBteJ3jnRqv1Lk,2136
19
+ evalproof/providers/ollama.py,sha256=MklL_Ex4vGeDc-fyV3NxscZwk7cRFgKDr3Zx7Tpd1Dk,2306
20
+ evalproof/providers/openai.py,sha256=2mviallmw4BJOblE596z0_djOgVxvo-4MpZGwn7e3kA,2626
21
+ evalproof/scorers/__init__.py,sha256=s-aCK5o39XASypzT-Z2cE3H61Asg3g9mqHiktyzFCSw,2205
22
+ evalproof/scorers/deterministic.py,sha256=4u7wfl9WG4EQKbAErDCrSwTyzfCzd11ck8JS3btBzRk,8393
23
+ evalproof/scorers/judge.py,sha256=crRCZI7XFOV-U_3Wxo0XZVYlsA-In1mCm_FnXJwxc4c,6365
24
+ evalproof/scorers/retrieval.py,sha256=QAEKPPfdZWpR23g9ieNzsIIxv6Vt4QTY-pkC4JIyhYc,2291
25
+ evalproof-0.2.0.dist-info/METADATA,sha256=eCcYpx36acVv13QinExbSu_w0Yg9v6cBgZkGz9C7_nE,15308
26
+ evalproof-0.2.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
27
+ evalproof-0.2.0.dist-info/entry_points.txt,sha256=q81031vTHHlprNogYBcd0RNWW9UgchMn1kHCMDzZ_24,49
28
+ evalproof-0.2.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
29
+ evalproof-0.2.0.dist-info/licenses/NOTICE,sha256=QKLv_qIUn1TOjv_xERUUCCiWJBxtuUJxTV--1WVCLjw,121
30
+ evalproof-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ evalproof = evalproof.cli:main