evalgate-ai 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.
- evalgate_ai-0.1.0/.gitattributes +1 -0
- evalgate_ai-0.1.0/.githooks/pre-push +12 -0
- evalgate_ai-0.1.0/.gitignore +14 -0
- evalgate_ai-0.1.0/CONTRIBUTING.md +50 -0
- evalgate_ai-0.1.0/LICENSE +21 -0
- evalgate_ai-0.1.0/PKG-INFO +194 -0
- evalgate_ai-0.1.0/README.md +165 -0
- evalgate_ai-0.1.0/docs/SPEC.md +1013 -0
- evalgate_ai-0.1.0/docs/ci.md +55 -0
- evalgate_ai-0.1.0/docs/report-format.md +64 -0
- evalgate_ai-0.1.0/examples/README.md +16 -0
- evalgate_ai-0.1.0/examples/cost-latency/baseline/cases.jsonl +6 -0
- evalgate_ai-0.1.0/examples/cost-latency/baseline/run.json +10 -0
- evalgate_ai-0.1.0/examples/cost-latency/candidate/cases.jsonl +6 -0
- evalgate_ai-0.1.0/examples/cost-latency/candidate/run.json +10 -0
- evalgate_ai-0.1.0/examples/cost-latency/evalgate.yaml +21 -0
- evalgate_ai-0.1.0/examples/quickstart/baseline/cases.jsonl +4 -0
- evalgate_ai-0.1.0/examples/quickstart/baseline/run.json +9 -0
- evalgate_ai-0.1.0/examples/quickstart/candidate/cases.jsonl +4 -0
- evalgate_ai-0.1.0/examples/quickstart/candidate/run.json +9 -0
- evalgate_ai-0.1.0/examples/quickstart/evalgate.yaml +31 -0
- evalgate_ai-0.1.0/examples/sdk_quickstart.py +52 -0
- evalgate_ai-0.1.0/examples/sdk_quickstart.yaml +14 -0
- evalgate_ai-0.1.0/pyproject.toml +68 -0
- evalgate_ai-0.1.0/src/evalgate/__init__.py +8 -0
- evalgate_ai-0.1.0/src/evalgate/_version.py +2 -0
- evalgate_ai-0.1.0/src/evalgate/api.py +31 -0
- evalgate_ai-0.1.0/src/evalgate/cli.py +116 -0
- evalgate_ai-0.1.0/src/evalgate/comparison/__init__.py +5 -0
- evalgate_ai-0.1.0/src/evalgate/comparison/engine.py +108 -0
- evalgate_ai-0.1.0/src/evalgate/engine.py +67 -0
- evalgate_ai-0.1.0/src/evalgate/io/__init__.py +19 -0
- evalgate_ai-0.1.0/src/evalgate/io/reader.py +110 -0
- evalgate_ai-0.1.0/src/evalgate/io/writer.py +139 -0
- evalgate_ai-0.1.0/src/evalgate/models/__init__.py +35 -0
- evalgate_ai-0.1.0/src/evalgate/models/policy.py +100 -0
- evalgate_ai-0.1.0/src/evalgate/models/report.py +113 -0
- evalgate_ai-0.1.0/src/evalgate/models/run.py +61 -0
- evalgate_ai-0.1.0/src/evalgate/policies/__init__.py +5 -0
- evalgate_ai-0.1.0/src/evalgate/policies/engine.py +93 -0
- evalgate_ai-0.1.0/src/evalgate/reporting/__init__.py +1 -0
- evalgate_ai-0.1.0/src/evalgate/reporting/html.py +234 -0
- evalgate_ai-0.1.0/src/evalgate/reporting/terminal.py +68 -0
- evalgate_ai-0.1.0/src/evalgate/validation.py +118 -0
- evalgate_ai-0.1.0/tests/conftest.py +55 -0
- evalgate_ai-0.1.0/tests/test_cli.py +96 -0
- evalgate_ai-0.1.0/tests/test_engine.py +183 -0
- evalgate_ai-0.1.0/tests/test_examples.py +35 -0
- evalgate_ai-0.1.0/tests/test_html_init.py +61 -0
- evalgate_ai-0.1.0/tests/test_io.py +87 -0
- evalgate_ai-0.1.0/tests/test_review_fixes.py +144 -0
- evalgate_ai-0.1.0/tests/test_sdk.py +112 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.githooks/* text eol=lf
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Local replacement for CI: lint, format, types and tests must pass before pushing.
|
|
3
|
+
# Enable once per clone: git config core.hooksPath .githooks
|
|
4
|
+
set -e
|
|
5
|
+
cd "$(git rev-parse --show-toplevel)"
|
|
6
|
+
# Use the project venv (created by `uv sync`); Scripts/ on Windows, bin/ elsewhere.
|
|
7
|
+
PATH="$PWD/.venv/Scripts:$PWD/.venv/bin:$PATH"
|
|
8
|
+
|
|
9
|
+
echo "pre-push: ruff check" && ruff check .
|
|
10
|
+
echo "pre-push: ruff format --check" && ruff format --check .
|
|
11
|
+
echo "pre-push: mypy" && mypy src
|
|
12
|
+
echo "pre-push: pytest" && pytest -q
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping. EvalGate aims to be a small, correct tool, and that shapes what gets merged.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv sync
|
|
9
|
+
git config core.hooksPath .githooks # enables the pre-push checks
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
There is no hosted CI. The `pre-push` hook runs the same checks a CI job would, and blocks the push if any fail:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
uv run ruff check .
|
|
16
|
+
uv run ruff format --check .
|
|
17
|
+
uv run mypy src
|
|
18
|
+
uv run pytest
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Ground rules
|
|
22
|
+
|
|
23
|
+
- **Fail closed.** Missing, malformed or incomparable input must give INVALID (exit 2), never PASS. Any change that touches validation or policy needs a test proving that bad input can't pass.
|
|
24
|
+
- **Explicit semantics.** Metrics declare a direction. Never divide by a zero baseline. No weighted overall scores.
|
|
25
|
+
- **Deterministic.** The same inputs, policy and version must produce byte-identical reports. Sort every list by stable keys, and keep timestamps out of results.
|
|
26
|
+
- **Offline core.** No network calls, LLM APIs or databases in the core.
|
|
27
|
+
- **Layering.** `comparison/` and `policies/` are pure functions over models. File I/O lives in `io/`. The CLI and SDK both go through `evalgate.api.compare`, so there is never a second code path.
|
|
28
|
+
- **Small.** Add a module or option only when real code needs it. New dependencies need a strong reason.
|
|
29
|
+
|
|
30
|
+
## Where things live
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
src/evalgate/
|
|
34
|
+
models/ Pydantic models: run, case, policy, report
|
|
35
|
+
io/ read run dirs and policies; write reports; EvalRun recorder
|
|
36
|
+
validation.py comparability checks -> diagnostics
|
|
37
|
+
comparison/ matching and metric deltas (pure)
|
|
38
|
+
policies/ rule evaluation (pure)
|
|
39
|
+
engine.py validate -> compare -> decide
|
|
40
|
+
api.py path-based entry point shared by CLI and SDK
|
|
41
|
+
reporting/ terminal and HTML renderers
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The product spec is [docs/SPEC.md](docs/SPEC.md), and the JSON report contract is [docs/report-format.md](docs/report-format.md). A change to the report shape must update that doc, and bump `report_version` if it breaks existing fields.
|
|
45
|
+
|
|
46
|
+
## Pull requests
|
|
47
|
+
|
|
48
|
+
- One topic per PR, with tests. A bug fix starts with a test that fails without the fix.
|
|
49
|
+
- Explain *why* in the description, especially any change to decision semantics.
|
|
50
|
+
- To release, bump `src/evalgate/_version.py`. It is the only place the version lives.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Josep Audenis
|
|
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,194 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: evalgate-ai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local-first AI regression testing and release decision engine.
|
|
5
|
+
Project-URL: Homepage, https://github.com/josep-audenis/evalgate
|
|
6
|
+
Project-URL: Repository, https://github.com/josep-audenis/evalgate
|
|
7
|
+
Project-URL: Issues, https://github.com/josep-audenis/evalgate/issues
|
|
8
|
+
Author: Josep Audenis
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ci,evaluation,llm,regression-testing,release-gate
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: pydantic>=2.6
|
|
25
|
+
Requires-Dist: pyyaml>=6
|
|
26
|
+
Requires-Dist: rich>=13
|
|
27
|
+
Requires-Dist: typer>=0.12
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# EvalGate
|
|
31
|
+
|
|
32
|
+
**Local-first regression testing and release decisions for AI applications.**
|
|
33
|
+
|
|
34
|
+
You changed a prompt, model, tool or retrieval step. Your eval pipeline produced scores for the old version and the new one. EvalGate answers three questions:
|
|
35
|
+
|
|
36
|
+
1. **What changed?**
|
|
37
|
+
2. **What got worse?**
|
|
38
|
+
3. **Is this version acceptable to release?**
|
|
39
|
+
|
|
40
|
+
It compares a *baseline* run against a *candidate* run case by case and applies your release policy. It returns an explainable **PASS / FAIL / INVALID**, with exit codes `0 / 1 / 2` for CI. It runs fully offline, with no LLM API key, account or database.
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
$ evalgate compare evals/baseline evals/candidate --policy evalgate.yaml
|
|
44
|
+
baseline-v1 -> candidate-v2 dataset regression-suite@v3 evaluator rubric-v2
|
|
45
|
+
cases: 4 matched, 4 compared, 0 baseline errors, 0 candidate errors
|
|
46
|
+
metric direction baseline candidate delta delta %
|
|
47
|
+
quality maximize 0.8 0.865 +0.065 +8.12%
|
|
48
|
+
...
|
|
49
|
+
FAIL checks.required.safety_valid: required check 'safety_valid' failed in 1 case(s): abuse-001/safety_valid
|
|
50
|
+
decision: FAIL
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Average quality went up, but one case started failing a safety check, so the candidate does not ship.
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install evalgate-ai
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The package is published as `evalgate-ai`. It imports as `evalgate`, and the command is `evalgate`. Requires Python 3.11 or newer.
|
|
62
|
+
|
|
63
|
+
## Quickstart
|
|
64
|
+
|
|
65
|
+
**1. Write each run as a directory.** Use `run.json` for identity and `cases.jsonl` with one JSON object per test case:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{"schema_version": "1.0", "run_id": "candidate-v2", "dataset_id": "regression-suite",
|
|
69
|
+
"dataset_version": "v3", "evaluator_version": "rubric-v2"}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{"case_id": "refund-001", "status": "ok", "scores": {"quality": 0.91},
|
|
74
|
+
"checks": {"safety_valid": true}, "metrics": {"latency_ms": 3000, "cost_usd": 0.012},
|
|
75
|
+
"metadata": {"category": "billing"}}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If the pipeline failed on a case, record `"status": "error"` instead of inventing scores. Or let the Python SDK below write these files for you.
|
|
79
|
+
|
|
80
|
+
**2. Create a policy:**
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
evalgate init # writes a commented evalgate.yaml
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
version: 1
|
|
88
|
+
metrics:
|
|
89
|
+
quality:
|
|
90
|
+
path: scores.quality
|
|
91
|
+
direction: maximize # explicit: EvalGate never assumes higher is better
|
|
92
|
+
max_mean_regression_abs: 0.02
|
|
93
|
+
latency:
|
|
94
|
+
path: metrics.latency_ms
|
|
95
|
+
direction: minimize
|
|
96
|
+
max_mean_regression_pct: 10
|
|
97
|
+
checks:
|
|
98
|
+
required: [safety_valid] # must pass in every candidate case
|
|
99
|
+
max_new_failures: 0 # nothing that passed before may fail now
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**3. Compare:**
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
evalgate validate evals/candidate
|
|
106
|
+
evalgate compare evals/baseline evals/candidate --policy evalgate.yaml \
|
|
107
|
+
--json report.json --report report.html
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- `--report` writes one standalone HTML file: no scripts and no external assets.
|
|
111
|
+
- `--json` writes a stable, documented report: see [report format](https://github.com/josep-audenis/evalgate/blob/main/docs/report-format.md).
|
|
112
|
+
- Neither report contains raw prompts or outputs.
|
|
113
|
+
|
|
114
|
+
## Python SDK
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from evalgate import EvalRun, compare
|
|
118
|
+
|
|
119
|
+
with EvalRun(
|
|
120
|
+
"candidate", dataset="support-suite", dataset_version="v3", evaluator_version="rubric-v2"
|
|
121
|
+
) as run: # writes evals/candidate/
|
|
122
|
+
for case in dataset:
|
|
123
|
+
try:
|
|
124
|
+
output = execute_agent(case)
|
|
125
|
+
except Exception as exc:
|
|
126
|
+
run.record_error(case["id"], repr(exc)) # never invent scores
|
|
127
|
+
continue
|
|
128
|
+
run.record(
|
|
129
|
+
case["id"],
|
|
130
|
+
scores={"quality": judge(output)},
|
|
131
|
+
checks={"safety_valid": is_safe(output)},
|
|
132
|
+
metrics={"latency_ms": 1234},
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
report = compare("evals/baseline", "evals/candidate", policy="evalgate.yaml")
|
|
136
|
+
print(report.summary())
|
|
137
|
+
report.to_json("report.json")
|
|
138
|
+
report.to_html("report.html")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- **Validation:** values are checked as you record them. Duplicate `case_id`s raise an error.
|
|
142
|
+
- **Crash safety:** `run.json` is written only when the `with` block exits cleanly, so an interrupted run never loads as complete.
|
|
143
|
+
- **Overwrites:** `EvalRun` won't replace an existing run unless you pass `overwrite=True`. Even then, the old run is replaced only when the new one finishes cleanly.
|
|
144
|
+
- **Same results as the CLI:** the SDK's `compare` is the same code path as `evalgate compare`, and the reports are identical.
|
|
145
|
+
|
|
146
|
+
## How decisions are made
|
|
147
|
+
|
|
148
|
+
- **INVALID (exit 2):** a trustworthy decision is impossible. Causes:
|
|
149
|
+
- missing or duplicate cases
|
|
150
|
+
- a dataset or evaluator version mismatch
|
|
151
|
+
- missing metrics or checks
|
|
152
|
+
- malformed files
|
|
153
|
+
- a percent threshold on a metric whose baseline mean is 0
|
|
154
|
+
|
|
155
|
+
Invalid input never produces a PASS.
|
|
156
|
+
- **FAIL (exit 1):** the comparison is valid, but at least one policy rule is violated.
|
|
157
|
+
- **PASS (exit 0):** the comparison is valid and every rule holds.
|
|
158
|
+
|
|
159
|
+
Further rules:
|
|
160
|
+
- Means are computed over cases that ran successfully in both runs.
|
|
161
|
+
- Cases with `status: "error"` count against `max_execution_errors` (default 0) and never count as successes.
|
|
162
|
+
- Every metric declares its direction and at least one threshold.
|
|
163
|
+
- The policy is strict. Unknown or duplicate keys, `true` where a number is expected, and a policy with no rules are all rejected.
|
|
164
|
+
- There is no weighted overall score. Each rule passes or fails on its own, so one regression can't hide behind an improvement elsewhere.
|
|
165
|
+
|
|
166
|
+
## Use in CI
|
|
167
|
+
|
|
168
|
+
Exit codes make EvalGate a drop-in CI step. See [CI integration](https://github.com/josep-audenis/evalgate/blob/main/docs/ci.md) for a GitHub Actions example.
|
|
169
|
+
|
|
170
|
+
## Examples
|
|
171
|
+
|
|
172
|
+
[examples/](https://github.com/josep-audenis/evalgate/tree/main/examples) has runnable scenarios:
|
|
173
|
+
- a safety regression hidden by better averages
|
|
174
|
+
- a quality gain that blows the cost and latency budget
|
|
175
|
+
- an SDK eval loop with a timed-out case
|
|
176
|
+
|
|
177
|
+
## Status and scope
|
|
178
|
+
|
|
179
|
+
Version 0.1.0 (alpha). EvalGate compares results your pipeline already produced. It does **not** run your application, call LLM judges, or host datasets.
|
|
180
|
+
|
|
181
|
+
Not yet included:
|
|
182
|
+
- statistical uncertainty (confidence intervals)
|
|
183
|
+
- repeated-run comparison
|
|
184
|
+
- adapters for other eval frameworks
|
|
185
|
+
|
|
186
|
+
Planned work is in the [spec](https://github.com/josep-audenis/evalgate/blob/main/docs/SPEC.md).
|
|
187
|
+
|
|
188
|
+
## Contributing
|
|
189
|
+
|
|
190
|
+
See [CONTRIBUTING.md](https://github.com/josep-audenis/evalgate/blob/main/CONTRIBUTING.md).
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
MIT
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# EvalGate
|
|
2
|
+
|
|
3
|
+
**Local-first regression testing and release decisions for AI applications.**
|
|
4
|
+
|
|
5
|
+
You changed a prompt, model, tool or retrieval step. Your eval pipeline produced scores for the old version and the new one. EvalGate answers three questions:
|
|
6
|
+
|
|
7
|
+
1. **What changed?**
|
|
8
|
+
2. **What got worse?**
|
|
9
|
+
3. **Is this version acceptable to release?**
|
|
10
|
+
|
|
11
|
+
It compares a *baseline* run against a *candidate* run case by case and applies your release policy. It returns an explainable **PASS / FAIL / INVALID**, with exit codes `0 / 1 / 2` for CI. It runs fully offline, with no LLM API key, account or database.
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
$ evalgate compare evals/baseline evals/candidate --policy evalgate.yaml
|
|
15
|
+
baseline-v1 -> candidate-v2 dataset regression-suite@v3 evaluator rubric-v2
|
|
16
|
+
cases: 4 matched, 4 compared, 0 baseline errors, 0 candidate errors
|
|
17
|
+
metric direction baseline candidate delta delta %
|
|
18
|
+
quality maximize 0.8 0.865 +0.065 +8.12%
|
|
19
|
+
...
|
|
20
|
+
FAIL checks.required.safety_valid: required check 'safety_valid' failed in 1 case(s): abuse-001/safety_valid
|
|
21
|
+
decision: FAIL
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Average quality went up, but one case started failing a safety check, so the candidate does not ship.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install evalgate-ai
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The package is published as `evalgate-ai`. It imports as `evalgate`, and the command is `evalgate`. Requires Python 3.11 or newer.
|
|
33
|
+
|
|
34
|
+
## Quickstart
|
|
35
|
+
|
|
36
|
+
**1. Write each run as a directory.** Use `run.json` for identity and `cases.jsonl` with one JSON object per test case:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{"schema_version": "1.0", "run_id": "candidate-v2", "dataset_id": "regression-suite",
|
|
40
|
+
"dataset_version": "v3", "evaluator_version": "rubric-v2"}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{"case_id": "refund-001", "status": "ok", "scores": {"quality": 0.91},
|
|
45
|
+
"checks": {"safety_valid": true}, "metrics": {"latency_ms": 3000, "cost_usd": 0.012},
|
|
46
|
+
"metadata": {"category": "billing"}}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If the pipeline failed on a case, record `"status": "error"` instead of inventing scores. Or let the Python SDK below write these files for you.
|
|
50
|
+
|
|
51
|
+
**2. Create a policy:**
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
evalgate init # writes a commented evalgate.yaml
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
version: 1
|
|
59
|
+
metrics:
|
|
60
|
+
quality:
|
|
61
|
+
path: scores.quality
|
|
62
|
+
direction: maximize # explicit: EvalGate never assumes higher is better
|
|
63
|
+
max_mean_regression_abs: 0.02
|
|
64
|
+
latency:
|
|
65
|
+
path: metrics.latency_ms
|
|
66
|
+
direction: minimize
|
|
67
|
+
max_mean_regression_pct: 10
|
|
68
|
+
checks:
|
|
69
|
+
required: [safety_valid] # must pass in every candidate case
|
|
70
|
+
max_new_failures: 0 # nothing that passed before may fail now
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**3. Compare:**
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
evalgate validate evals/candidate
|
|
77
|
+
evalgate compare evals/baseline evals/candidate --policy evalgate.yaml \
|
|
78
|
+
--json report.json --report report.html
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- `--report` writes one standalone HTML file: no scripts and no external assets.
|
|
82
|
+
- `--json` writes a stable, documented report: see [report format](https://github.com/josep-audenis/evalgate/blob/main/docs/report-format.md).
|
|
83
|
+
- Neither report contains raw prompts or outputs.
|
|
84
|
+
|
|
85
|
+
## Python SDK
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from evalgate import EvalRun, compare
|
|
89
|
+
|
|
90
|
+
with EvalRun(
|
|
91
|
+
"candidate", dataset="support-suite", dataset_version="v3", evaluator_version="rubric-v2"
|
|
92
|
+
) as run: # writes evals/candidate/
|
|
93
|
+
for case in dataset:
|
|
94
|
+
try:
|
|
95
|
+
output = execute_agent(case)
|
|
96
|
+
except Exception as exc:
|
|
97
|
+
run.record_error(case["id"], repr(exc)) # never invent scores
|
|
98
|
+
continue
|
|
99
|
+
run.record(
|
|
100
|
+
case["id"],
|
|
101
|
+
scores={"quality": judge(output)},
|
|
102
|
+
checks={"safety_valid": is_safe(output)},
|
|
103
|
+
metrics={"latency_ms": 1234},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
report = compare("evals/baseline", "evals/candidate", policy="evalgate.yaml")
|
|
107
|
+
print(report.summary())
|
|
108
|
+
report.to_json("report.json")
|
|
109
|
+
report.to_html("report.html")
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- **Validation:** values are checked as you record them. Duplicate `case_id`s raise an error.
|
|
113
|
+
- **Crash safety:** `run.json` is written only when the `with` block exits cleanly, so an interrupted run never loads as complete.
|
|
114
|
+
- **Overwrites:** `EvalRun` won't replace an existing run unless you pass `overwrite=True`. Even then, the old run is replaced only when the new one finishes cleanly.
|
|
115
|
+
- **Same results as the CLI:** the SDK's `compare` is the same code path as `evalgate compare`, and the reports are identical.
|
|
116
|
+
|
|
117
|
+
## How decisions are made
|
|
118
|
+
|
|
119
|
+
- **INVALID (exit 2):** a trustworthy decision is impossible. Causes:
|
|
120
|
+
- missing or duplicate cases
|
|
121
|
+
- a dataset or evaluator version mismatch
|
|
122
|
+
- missing metrics or checks
|
|
123
|
+
- malformed files
|
|
124
|
+
- a percent threshold on a metric whose baseline mean is 0
|
|
125
|
+
|
|
126
|
+
Invalid input never produces a PASS.
|
|
127
|
+
- **FAIL (exit 1):** the comparison is valid, but at least one policy rule is violated.
|
|
128
|
+
- **PASS (exit 0):** the comparison is valid and every rule holds.
|
|
129
|
+
|
|
130
|
+
Further rules:
|
|
131
|
+
- Means are computed over cases that ran successfully in both runs.
|
|
132
|
+
- Cases with `status: "error"` count against `max_execution_errors` (default 0) and never count as successes.
|
|
133
|
+
- Every metric declares its direction and at least one threshold.
|
|
134
|
+
- The policy is strict. Unknown or duplicate keys, `true` where a number is expected, and a policy with no rules are all rejected.
|
|
135
|
+
- There is no weighted overall score. Each rule passes or fails on its own, so one regression can't hide behind an improvement elsewhere.
|
|
136
|
+
|
|
137
|
+
## Use in CI
|
|
138
|
+
|
|
139
|
+
Exit codes make EvalGate a drop-in CI step. See [CI integration](https://github.com/josep-audenis/evalgate/blob/main/docs/ci.md) for a GitHub Actions example.
|
|
140
|
+
|
|
141
|
+
## Examples
|
|
142
|
+
|
|
143
|
+
[examples/](https://github.com/josep-audenis/evalgate/tree/main/examples) has runnable scenarios:
|
|
144
|
+
- a safety regression hidden by better averages
|
|
145
|
+
- a quality gain that blows the cost and latency budget
|
|
146
|
+
- an SDK eval loop with a timed-out case
|
|
147
|
+
|
|
148
|
+
## Status and scope
|
|
149
|
+
|
|
150
|
+
Version 0.1.0 (alpha). EvalGate compares results your pipeline already produced. It does **not** run your application, call LLM judges, or host datasets.
|
|
151
|
+
|
|
152
|
+
Not yet included:
|
|
153
|
+
- statistical uncertainty (confidence intervals)
|
|
154
|
+
- repeated-run comparison
|
|
155
|
+
- adapters for other eval frameworks
|
|
156
|
+
|
|
157
|
+
Planned work is in the [spec](https://github.com/josep-audenis/evalgate/blob/main/docs/SPEC.md).
|
|
158
|
+
|
|
159
|
+
## Contributing
|
|
160
|
+
|
|
161
|
+
See [CONTRIBUTING.md](https://github.com/josep-audenis/evalgate/blob/main/CONTRIBUTING.md).
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT
|