foley.eval#
Tier-1 retrieval evaluation — metrics, a frozen golden set, and the nDCG gate.
This subpackage answers “did we retrieve the right sound?” with TREC-style
ranking metrics (nDCG@10 / Recall@k / mAP@10 / MRR@10) over a
frozen golden set, and ships the PR gate: a pytest check that blocks a change
whose nDCG@10 regresses by more than 0.02 on the Ring-0 golden set. Every
part is pure numpy/stdlib and deterministic — no ranx/numba, no CLAP, no
downloads — so the gate runs on every index/embedder/prompt PR in foley’s CI.
Tier-2 fit-judging (#10b) — “does this clip match the intent?” — lives alongside
in fit (the judge-based fit harness), reliability
(Krippendorff’s α + judge-vs-human calibration), and fidelity
(set-level FAD/KAD generation fidelity). Tier-2 is nightly/pre-release + cost-gated —
NOT a per-PR gate — and never touches the retrieval ranking. All numpy imports are
function-local so import foley stays dol-only.
Functions
|
Normalized DCG at |
|
Fraction of ALL relevant docs (grade ≥ |
|
Fraction of the top- |
|
Average precision (trec_eval |
|
Reciprocal rank of the first relevant doc in the top- |
|
Macro-average of per-query metric values (0.0 for an empty list). |
|
Turn an ordered |
|
Score a |
|
Load and validate the frozen golden set from |
|
Flatten the golden set into TREC qrels: |
|
Build the ephemeral Ring-0 eval library (stem ids + injected caption vectors). |
|
Score the golden set's queries against the Ring-0 library — the gate input. |
|
Load the committed baseline dict from |
|
Write a fresh baseline from |
|
True if the baseline's fixture stamps no longer match the fixtures on disk. |
|
Run the Tier-2 fit eval over a stratified golden sample → a |
|
Fraction of judge-accepted candidates that are actually gold-relevant. |
|
|
|
The harmonic mean of |
|
Deterministic seeded stratified draw over |
|
Krippendorff's α over |
|
Raw pairwise percent agreement, reported ALONGSIDE α (report 08 §2.3). |
|
Map an α (or κ) to Krippendorff's benchmark band. |
|
Compute judge-vs-human agreement and the promotion verdict (report 08 §2.3). |
|
Fréchet distance between two embedding sets (the FAD formula). |
|
Kernel Audio Distance — the unbiased RBF-MMD² between two embedding sets. |
|
Embed two wav sets through an injected embedder and compute FAD or KAD. |
Classes
|
Per-query + mean Tier-1 metrics over a golden set (JSON-friendly). |
|
One frozen |
|
Deterministic hashing bag-of-words text embedder (L2-normalized). |
|
Tier-2 fit metrics over a stratified golden sample (JSON-friendly, mirrors |
|
A judge-vs-human calibration record (surfaced as |
|
Mandatory provenance for a FAD/KAD score — it is meaningless without it. |
|
A stamped set-level fidelity score (attached to |
- class foley.eval.AlphaResult(alpha, level, n_units, n_raters, percent_agreement, band, promoted)[source]#
Bases:
objectA judge-vs-human calibration record (surfaced as
FitReport.calibration).Codifies the promotion rule in one legible object: a model judge is
promotedto unattended use only once its agreement with the human raters reaches theALPHA_RELIABLEband.
- class foley.eval.FidelityResult(metric, value, stamp)[source]#
Bases:
objectA stamped set-level fidelity score (attached to
FitReport.fidelity).
- class foley.eval.FidelityStamp(embedding, toolkit, version, n_ref, n_gen)[source]#
Bases:
objectMandatory provenance for a FAD/KAD score — it is meaningless without it.
FAD/KAD are not comparable across embeddings/toolkits/versions, so a bare number is uninterpretable; this stamp makes the basis of comparison legible.
- class foley.eval.FitReport(per_item=<factory>, fit_precision=0.0, fit_recall=0.0, fit_f1=0.0, fit_score=0.0, auto_accept_rate=0.0, n_accepted=0, n_confirmed=0, strata=<factory>, calibration=None, fidelity=<factory>, judge_model='', embedder_model_id='', seed=0, k=10, schema_version=1)[source]#
Bases:
objectTier-2 fit metrics over a stratified golden sample (JSON-friendly, mirrors
RetrievalReport).The fit numbers are co-emitted with the judge-vs-human
calibration(anAlphaResultdict) so the §5 human-calibration guardrail is legible: afit_precisionis only trusted where α certifies the judge.fidelityholds stamped FAD/KAD results for release-level backend comparison. Both default empty on the pure-fake path (no human labels, no generated-set wavs).
- class foley.eval.GoldenItem(id, context, expected_events, answer_clip_ids, grade, negatives=<factory>, labeler='llm+human', schema_version=1)[source]#
Bases:
objectOne frozen
(context → expected sounds)judgment (report 08 §1.3).- id#
Unique item id (e.g.
gld_0001).
- context#
The narrative paragraph (the future SELECT input).
- expected_events#
One or more
{query, ucs_catid, layer, diegetic, salience, ...}dicts;queryis the string fed tosearch.
- answer_clip_ids#
{ucs_catid: [clip_id, ...]}— the acceptable clips.
- grade#
{clip_id: grade}(2 ideal / 1 acceptable / 0 wrong).
- negatives#
Free-text distractors (unused by scoring; documentation).
- labeler#
Provenance of the labels (
llm+humanetc.).
- schema_version#
The GoldenItem schema version.
- class foley.eval.HashingBowEmbedder(*, dim=64)[source]#
Bases:
objectDeterministic hashing bag-of-words text embedder (L2-normalized).
Two texts sharing a token share a dimension, so cosine similarity tracks lexical overlap — plausible and reproducible (a stable
hashlibhash, never the salted builtinhash). Suitable only for the eval gate / offline retrieval regression; real retrieval uses CLAP.
- class foley.eval.RetrievalReport(per_query=<factory>, mean=<factory>, ranks=<factory>, k=10)[source]#
Bases:
objectPer-query + mean Tier-1 metrics over a golden set (JSON-friendly).
- foley.eval.average_precision_at_k(qrels_q, run_q, k=10, *, rel_lvl=1)[source]#
Average precision (trec_eval
map: divide by TOTAL relevant, notmin(R,k)).- Return type:
- foley.eval.build_eval_library(*, embedder=None, manifest_path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/corpus.json'))[source]#
Build the ephemeral Ring-0 eval library (stem ids + injected caption vectors).
Each manifest clip becomes a
SoundRecordwith idring0:<stem>and a vector =embedder.embed_text(caption + tags)— so both the vector and BM25 legs carry the caption bag-of-words and the answer clip lands at integer rank 1 (deterministic, cross-platform).- Parameters:
embedder – A text embedder (default:
HashingBowEmbedder).manifest_path – The Ring-0
manifest.json(default: the bundled fixture).
- Returns:
A populated in-memory
SoundLibrary.
- foley.eval.build_run(candidates)[source]#
Turn an ordered
search()result into a run row with distinct scores.Assigns each candidate a strictly-descending integer score (
len - rank) in the order returned, so the run carries **no score ties** and_ranked_ids()reproduces the search ranking exactly — the determinism the gate relies on. The doc id iscandidate.sound.id.- Parameters:
candidates – An ordered
list[Candidate]fromfoley.index.library.SoundLibrary.search()(best first).- Return type:
- Returns:
{clip_id: score}with distinct descending scores.
- foley.eval.calibrate_judge_vs_human(human_grades, judge_grades, *, level='ordinal')[source]#
Compute judge-vs-human agreement and the promotion verdict (report 08 §2.3).
Stacks the human rating row(s) and the model-judge row into one reliability matrix and returns an
AlphaResult;promotedisTrueiff α reaches the reliable band — the model judge may then be trusted unattended on that slice.- Parameters:
human_grades – A 1-D per-unit human grade sequence, or a 2-D
(humans × units)matrix (nan= missing).judge_grades – The model judge’s 1-D per-unit grades (
nan= missing).level (
str) – The measurement level (default'ordinal').
- Return type:
- Returns:
An
AlphaResult.
- foley.eval.evaluate_run(run, qrels, *, k=10, metrics=('ndcg@10', 'recall@10', 'map@10', 'mrr@10'), rel_lvl=1)[source]#
Score a
runagainstqrels, returning aRetrievalReport.Only query ids present in
qrelsare scored (a run row without a ground-truth judgment is ignored).ranksrecords, per query, the 1-based rank of the highest-graded answer (for the failure diff).- Return type:
- foley.eval.fit_f1(matches, relevants)[source]#
The harmonic mean of
fit_precision()andfit_recall().- Return type:
- foley.eval.fit_precision(matches, relevants)[source]#
Fraction of judge-accepted candidates that are actually gold-relevant.
TP / (TP + FP)where a candidate is a positive iff the fit-judge saidmatchand gold-relevant iff its golden grade ≥ 1. This measures the JUDGE’s auto-accept purity (a low value ⇒ the judge waves through wrong clips) — NOT the ranker. Pure, closed-form, and the decisive correctness oracle (the Ring-0 + fake-judge plumbing makes the end-to-end value tautological, so the metric math is tested in isolation).
- foley.eval.fit_recall(matches, relevants)[source]#
TP / (TP + FN)— of the gold-relevant candidates, how many the judge confirmed.- Return type:
- foley.eval.frechet_distance(x_ref, x_gen)[source]#
Fréchet distance between two embedding sets (the FAD formula).
‖μ_r − μ_g‖² + Tr(Σ_r + Σ_g − 2(Σ_r Σ_g)^½)— lower = the generated set’s embedding distribution is closer to the reference. Feed PANNs Wavegram-Logmel embeddings for “FAD-P”, or CLAP for a domain-matched FAD.- Parameters:
x_ref – Reference embeddings
(n_ref, d).x_gen – Generated embeddings
(n_gen, d).
- Return type:
- Returns:
The FAD as a
float(≥ 0up to numerical round-off;~0for identical distributions).
- foley.eval.generation_fidelity(ref_wavs, gen_wavs, *, embedder, sr=48000, metric='fad', toolkit='foley-numpy', version='1')[source]#
Embed two wav sets through an injected embedder and compute FAD or KAD.
The real fidelity path is a zero-call-site-change embedder swap: a fake / hashing embedder in CI, PANNs / CLAP (
foley[fit]) in prod. The embedder’smodel_idis stamped into the result.- Parameters:
ref_wavs – An iterable of reference waveforms (1-D arrays).
gen_wavs – An iterable of generated waveforms (1-D arrays).
embedder – An object with
embed_audio(wav, sr) -> vectorandmodel_id.sr (
int) – The sample rate passed toembed_audio.metric (
str) –'fad'(default) or'kad'.toolkit (
str) – Provenance label for the stamp (default'foley-numpy').version (
str) – Provenance version for the stamp.
- Return type:
- Returns:
A
FidelityResultcarrying the score and itsFidelityStamp.
- foley.eval.is_stale(baseline, *, seed_path, manifest_path)[source]#
True if the baseline’s fixture stamps no longer match the fixtures on disk.
- Return type:
- foley.eval.kernel_audio_distance(x_ref, x_gen, *, bandwidth=None)[source]#
Kernel Audio Distance — the unbiased RBF-MMD² between two embedding sets.
Distribution-free and small-sample-convergent; preferred over FAD for the small reference/generated sets of early-stage eval. The diagonal (same-sample) kernel terms are dropped (the unbiased estimator), so
KAD(X, X) ≈ 0and can be slightly negative.
- foley.eval.krippendorff_alpha(reliability_data, *, level='ordinal', value_domain=None)[source]#
Krippendorff’s α over
(raters × units)ratings (nan= missing).α = 1 − D_o/D_e— 1 is perfect agreement, ~0 is chance, negative is systematic disagreement. Ordinal is the default (foley’s 0–3 grades); missing ratings are tolerated (units with < 2 ratings are dropped, no imputation). Pure numpy.- Parameters:
reliability_data – A 2-D array-like
(raters × units);nan= missing.level (
str) –'ordinal'(default) |'nominal'|'interval'.value_domain – Optional explicit value set (for a label unobserved by some rater).
- Return type:
- Returns:
α as a bare
float(1.0when there is no expected disagreement,D_e = 0).
- foley.eval.load_baseline(path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/baseline.json'))[source]#
Load the committed baseline dict from
path.- Return type:
- foley.eval.load_golden(path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/seed.json'))[source]#
Load and validate the frozen golden set from
path(JSON list).- Return type:
- foley.eval.mean_over_queries(values)[source]#
Macro-average of per-query metric values (0.0 for an empty list).
- Return type:
- foley.eval.mrr_at_k(qrels_q, run_q, k=10, *, rel_lvl=1)[source]#
Reciprocal rank of the first relevant doc in the top-
k(0.0 if none).- Return type:
- foley.eval.ndcg_at_k(qrels_q, run_q, k=10)[source]#
Normalized DCG at
kwith linear gains (0.0 when no graded answer).- Return type:
- foley.eval.percent_agreement(reliability_data)[source]#
Raw pairwise percent agreement, reported ALONGSIDE α (report 08 §2.3).
On foley’s skewed ‘most candidates irrelevant’ label distribution a chance-corrected α can look pessimistic, so the uncorrected agreement is co-reported for context.
- Parameters:
reliability_data – A 2-D array-like
(raters × units);nan= missing.- Return type:
- Returns:
The fraction of same-unit rater pairs that agree (
0.0if no pairs).
- foley.eval.precision_at_k(qrels_q, run_q, k=10, *, rel_lvl=1)[source]#
Fraction of the top-
kthat is relevant (denominator is literalk).- Return type:
- foley.eval.recall_at_k(qrels_q, run_q, k=10, *, rel_lvl=1)[source]#
Fraction of ALL relevant docs (grade ≥
rel_lvl) retrieved in the top-k.- Return type:
- foley.eval.run_fit_eval(*, golden=None, golden_path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/seed.json'), sample=None, strata_keys=('family', 'diegetic'), fit_judge=None, embedder=None, level=VerifyLevel.judge, seed=0, k=10, llm=None)[source]#
Run the Tier-2 fit eval over a stratified golden sample → a
FitReport.Composes the Tier-1 golden builders + the #7 SELECT loop, applies the authoritative fit-judge as an independent audit, and aggregates fit-precision/recall/F1 + fit-score
auto-accept-rate, with a per-stratum breakdown. Report-only unless
min_fit_precisionis supplied.
- Parameters:
golden – A pre-loaded golden list (default: load
golden_path).golden_path – The golden JSON (default: the bundled Ring-0 seed set).
sample (
Optional[int]) – The stratified sample cap (default: the whole set — the cost gate).strata_keys – The stratification axes (default
('family', 'diegetic')).fit_judge – The injected authoritative
Judge(default:foley.agent.verify._default_fit_judge()forllm— the hermetic fake unlessllm/$FOLEY_LLMopts in).embedder – The Ring-0 text/audio embedder (default:
HashingBowEmbedder).level (
str|VerifyLevel) – The verify rung the fit-judge audits at —'listen'or'judge'(defaultVerifyLevel.judge).'clap'is rejected: it never invokes the fit-judge (it only re-runs the retrieval clap gate), so it cannot measure fit.seed (
int) – The sampling RNG seed.k (
int) – The retrieval shortlist depth per event.
- Return type:
- Returns:
A
FitReport. Gating is the caller’s job viaFitReport.gate().- Raises:
ValueError – If
levelis'clap'(fit judging requires a listen/judge rung).
- foley.eval.run_ring0_retrieval_eval(*, k=10, golden_path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/seed.json'), embedder=None)[source]#
Score the golden set’s queries against the Ring-0 library — the gate input.
Runs every golden
expected_events[].querythrough the realSoundLibrary.search()path (vector ⊕ BM25 ⊕ RRF) and evaluates the resulting runs against the golden qrels.- Parameters:
k (
int) – Retrieval cutoff (and the metric@k).golden_path – The golden set JSON.
embedder – The eval embedder (default:
HashingBowEmbedder).
- Return type:
- Returns:
A
RetrievalReport(per-query + mean nDCG@10 / recall / mAP / MRR).
- foley.eval.stratified_sample(units, *, strata_keys, sample, seed)[source]#
Deterministic seeded stratified draw over
units— the cost gate.Round-robins across strata (so an easy family cannot dominate a capped sample);
sample=Nonekeeps the full set. Each unit is(GoldenItem, event_index, event_dict).
- foley.eval.to_qrels(golden)[source]#
Flatten the golden set into TREC qrels:
{query_id: {clip_id: grade}}.One qrels row per
(item, event)—query_id = f"{item.id}::{event_idx}"— future-proofing multi-event items (seed items are single-event today). Each clip inanswer_clip_idscarries itsgrade.
- foley.eval.write_baseline(report, *, path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/baseline.json'), metric='ndcg@10', tolerance=0.02, seed_path, manifest_path, embedder_model_id='foley-eval/hashing-bow-v1', dim=64, rrf_k=60, updated_at, n_items, revision='gld-v1')[source]#
Write a fresh baseline from
report(the--update-baselineaction).Records the mean metric value plus sha256 stamps of the seed + manifest so a later fixture edit is detected.
updated_atis passed in (not read from the clock) so the caller controls reproducibility.revisionlabels the golden-set generation (bumped when the frozen set is regrown).- Return type:
- Returns:
The baseline dict that was written.
Modules
The committed nDCG baseline — the PR gate's SSOT + staleness stamps. |
|
A deterministic, numpy-only text embedder for the retrieval eval gate. |
|
Set-level generation-fidelity — Fréchet Audio Distance (FAD) + Kernel Audio Distance (KAD). |
|
Tier-2 fit evaluation — "does the accepted clip actually match the intent?" (report 08 §2/§5). |
|
The frozen golden set + the deterministic Ring-0 retrieval harness. |
|
Inter-rater reliability — Krippendorff's α + the judge-vs-human calibration guardrail. |
|
Tier-1 retrieval metrics + run assembly — pure stdlib, CI-safe, ranx-exact. |