evalbox
Run a suite of checks against a language model and fail the build when it regresses.
A model that is right nine times out of ten is not the same as a model that is right. evalbox runs each case several times, measures how often it holds up, and lets you say how often it has to. A case with a threshold of 0.9 that comes back 8/10 is a failure, and the report says which assertion broke and on which attempt.
No agent, no framework, no provider client. You give it a callable; it gives you a verdict and an exit code.
Install
pip install -e ".[schema]"
Python 3.10 or later. One dependency, PyYAML. The JSON Schema check pulls in
jsonschema as an optional extra; every other check uses the standard library.
A suite
name: invoice extraction
defaults:
repeat: 10
threshold: 0.9
cases:
- name: reads the total from a clean invoice
prompt: |
Return JSON with the keys supplier, total and currency.
Invoice: Nordvik Bygg AB, 12 450 SEK including VAT.
checks:
- json_valid
- json_field: { path: supplier, equals: "Nordvik Bygg AB" }
- json_field: { path: total, equals: 12450 }
- max_latency_ms: 2500
- name: says so instead of guessing when the total is missing
prompt: |
Return JSON with the keys supplier, total and currency.
Invoice: Nordvik Bygg AB, amount illegible.
tags: [safety]
checks:
- json_valid
- json_field: { path: total, equals: null }
The model
Anything callable that takes the prompt and returns a string, or a Response when you
want cost reported:
from evalbox import Response
def answer(prompt: str) -> Response:
completion = client.responses.create(model="...", input=prompt)
return Response(text=completion.output_text, cost_usd=price_of(completion))
Run it
evalbox examples/suite.yaml --model examples.fake_model:answer --workers 8
The example ships with a stand-in model so the command works with no API key. It is wrong in the way a real one is wrong, which is the point:
case pass p50 p95 cost note
-------------------------------------------- --------- ------ ------ ------- ------------------------------------
reads the total from a clean invoice ok 10/10 119 ms 170 ms $0.0040
keeps the shape when the amount is written i FAIL 5/10 118 ms 235 ms $0.0040 json_schema: total: '12450' is not o
says so instead of guessing when the total i ok 9/10 148 ms 218 ms $0.0040
FAIL 2/3 cases, 30 calls, $0.0120, 0.7 s
Exit code 0 when every case clears its threshold, 1 when one does not, 2 when the suite
or the model could not be loaded. --json report.json writes the same run as data, so a
build can keep it and compare.
Checks
| check | what it asserts |
|---|---|
equals |
the whole answer, trimmed unless you say strip: false |
contains, not_contains |
a substring is present, or is not |
regex |
a pattern matches, with flags: ims |
json_valid |
the answer parses as JSON |
json_schema |
the parsed answer satisfies a JSON Schema |
json_field |
a dotted path exists, and equals a value if you give one |
length |
the answer is between min and max characters |
max_latency_ms |
the call came back in time |
max_cost_usd |
the call stayed under a price, and fails when the model reports none |
Two decisions worth knowing about
The suite is validated before the first call. Checks are built when the file is read, so a misspelled check name or a broken regex fails in the first second rather than after the run has spent ten minutes and real money.
A model that raises fails one attempt, not the run. The exception is recorded as that attempt's reason and the suite carries on, because a provider that drops one call in fifty is exactly the thing you are trying to measure.
Tests
pytest -q
ruff check .
MIT licensed.
Metadata
Release files for evalbox 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| evalbox-0.1.0.tar.gz | 17.9 kB | Without provenance | Details for evalbox-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| evalbox-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for evalbox-0.1.0-py3-none-any.whl |
Total release size: 32.5 kB
Release files / evalbox-0.1.0.tar.gz
| Download URL | evalbox-0.1.0.tar.gz |
|---|---|
| Size | 17.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
96d4774fa5dc878c921cc6523513992848e18526d474ccc82c76a53404536c3b
|
|
BLAKE2b-256 checksum How to use checksums |
6a3afc8a651c66e00f0af4b1fd9ecd1c75f2820347946f02dc3649a42d1b4d67
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / evalbox-0.1.0-py3-none-any.whl
| Download URL | evalbox-0.1.0-py3-none-any.whl |
|---|---|
| Size | 14.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
903a94e3ae1d6f04bdc8b5f33c0fd1aacf44c6a1cde72c3facb93ebf6659162b
|
|
BLAKE2b-256 checksum How to use checksums |
65e85fdaf41f02863958cc50bf957db8bf7e67cfa691192c08e58073f8a3cb04
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Security for evalbox 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.