muteval architecture¶
A one-page map of the codebase for contributors. muteval mutation-tests an eval suite: it degrades the system under test, reruns your existing evals against each degraded version, and reports which regressions the evals missed.
The flow of a run¶
MutEvalConfig (system + cases + run + evals)
│
▼
select_mutants ──▶ mutators.py apply operators to the System
│ (prompt / context / tools / model)
▼
runner.py: for each mutant → config.invoke(system, case) → output
│ → grade output with every eval
▼
verdict per mutant (killed / survived / inert / errored), with a validity gate
│
▼
report.py: score (+ Wilson CI) · ranked survivors · suggested fix per survivor
Core modules (src/muteval/)¶
| File | Responsibility |
|---|---|
system.py |
System — the mutation target (prompt + context + tools + model). as_system promotes a bare prompt string for back-compat. |
config.py |
MutEvalConfig (what the user supplies) + load_config (executes a Python config file — a trust boundary). |
evals.py |
EvalOutcome (passed + score + threshold + margin) and coerce_outcome. Evals may return bool or EvalOutcome. |
checks.py |
Framework-free eval factories (contains, regex_matches, is_json, llm_judge, grounded, …). The judge speaks any OpenAI-compatible endpoint via base_url. |
mutators.py |
The mutation operators (prompt / context / tool / model) + the OPERATORS registry + register_operator. |
runner.py |
The engine: baseline gate → generate mutants → grade → score. Owns the fail-closed validity states. |
runners.py |
Built-in run helpers so users don't write one: openai_run, callable_run (--target), http_run (--endpoint). |
report.py |
Terminal report, HTML report, probe report card, JSON, badge, secret redaction. |
stats.py |
Dependency-free Wilson / Jeffreys intervals, Spearman, AUC, Krippendorff α, ICC. |
severity.py |
Ranks each mutant (invert/corrupt = high, drop/weaken = medium, cosmetic = low). |
suggest.py |
The fix: line under each survivor (operator-aware starter eval). |
doctor.py |
muteval check — layered, cheapest-first validation of a config + baseline. |
cache.py |
Optional sqlite memoization of outputs + eval outcomes (--cache). |
cli.py |
The command line: run / init / check / probe / results / show / report / label / list. |
Adapters (src/muteval/adapters/)¶
Point muteval at another framework's suite. base.py is the contract;
promptfoo.py, deepeval.py, ragas.py implement it. Each lives behind an
optional extra so the core stays dependency-free.
Probes (src/muteval/probes/)¶
The eval-quality "report card" (muteval probe) — separate lenses on suite
quality: judge_reliability, discrimination, statistical_adequacy,
redundancy, threshold_calibration, human_agreement (+ a judge_bias panel
for pairwise judges). Registered via register_probe. Dependency direction is
one-way: probes → core, never core → probes.
Extension points¶
- Operator:
register_operator(name, fn)—fn(target) -> list[Mutant]. - Probe:
register_probe(name, fn)—fn(config) -> ProbeResult. - Adapter: implement
adapters/base.py. - Custom pipeline: just pass your own
run(system, case)andevalsin a Python config — muteval never constrains what they call.
See docs/PLUGINS.md for the full API and
tests/test_plugin_contract.py for a worked third-party example.
Tests & CI gates (tests/)¶
pytest— unit + property (Hypothesis) + edge + determinism tests.mypy— type gate (config inpyproject.toml).test_ci_coverage.py(slow) — Monte-Carlo interval coverage.test_stats_reference.py— cross-checks the hand-rolled stats against statsmodels/scipy/sklearn/pingouin ([verify]extra).test_eval_quality.py— enforces the central claim (score rises with suite coverage across four domains).- CI matrix: Python 3.9–3.13 × ubuntu/macos/windows, 90% coverage gate.
Conventions¶
Apache-2.0. Core dependency-free; integrations behind extras. Every operator and
public function gets a test. A Mutant.description must be human-actionable — it
is what users read in the survivor report.