Eval harness¶
Most of the harness is reached through grk eval — the eval harness
guide covers what it measures, what it deliberately does
not, and how to reproduce any report it writes.
The two modules below have no CLI verb and are called directly from Python. That is deliberate in both cases: one writes a corpus, which the read-only eval surface is not the place for, and the other answers a question about a report after the fact rather than during a run.
Significance¶
significant is decided by the percentile confidence interval and never by
p_value_two_sided; the two are computed independently. That p-value is a
Monte Carlo achieved significance level over the observed deltas rather than a
null-centred p-value, and it carries a (r + 1) / (B + 1) finite-sample
correction — so it has a floor and can never be reported as exactly zero.
The unit of resampling is a query. Pairing is checked by query id, so a missing or reordered query raises rather than silently becoming an unpaired comparison, and the generator is seeded per call so a result does not depend on call order.
groundkit.evals.significance ¶
Deterministic paired-bootstrap comparisons for retrieval experiments.
The unit of resampling is a query, not an individual retrieved hit. Callers must therefore provide one scalar score per query for both systems. Pairing by query id is checked explicitly so a missing or reordered query cannot turn into an unpaired comparison by accident.
BootstrapResult ¶
Bases: BaseModel
Machine-readable result of one paired-bootstrap comparison.
compare_report_stages ¶
compare_report_stages(
report: EvalReport,
*,
baseline: StageName,
candidate: StageName,
metric: MetricName,
resamples: int = 10000,
seed: int = 7,
confidence_level: float = 0.95,
) -> BootstrapResult
Compare two stages from the same eval artifact.
Source code in src/groundkit/evals/significance.py
paired_bootstrap ¶
paired_bootstrap(
baseline_scores: Mapping[str, float],
candidate_scores: Mapping[str, float],
*,
baseline_name: str,
candidate_name: str,
metric: MetricName,
resamples: int = 10000,
seed: int = 7,
confidence_level: float = 0.95,
) -> BootstrapResult
Compare two systems by resampling paired query-level score deltas.
The confidence interval uses linear interpolation between ordered
bootstrap means, and is what :attr:BootstrapResult.significant reads.
:attr:BootstrapResult.p_value_two_sided is twice the smaller empirical
tail proportion around zero, carrying a (r + 1) / (B + 1)
finite-sample correction so it is never reported as exactly zero. It is
an achieved significance level computed from resamples of the observed
deltas, not from a null-centred distribution -- see that field's own note.
The two are computed independently: correcting one cannot move the other.
Source code in src/groundkit/evals/significance.py
BEIR adaptation¶
BEIR relevance is per document and a groundkit judgment is a verbatim quote,
so each adapted gold quote is the whole document text — document-level
relevance expressed in the span vocabulary, rather than span annotations BEIR
does not provide. A score computed over the adapted set is therefore not
comparable to a published BEIR number, which ranks documents where grk eval
ranks chunks; the guide's warning on that
point states what
comparing them would require.
A BEIR corpus is an untrusted third-party download, and every identifier that becomes part of a path is treated as such — validated as a single path component, containment-checked independently, and rejected before anything is written rather than after.
groundkit.evals.beir ¶
Adapt BEIR datasets to groundkit's quote-anchored evaluation format.
Placed in :mod:groundkit.evals rather than a top-level datasets package
because it imports :mod:groundkit.evals.corpus to reuse the harness's own
judgment loader. GK-021 (tests/test_deterministic_core.py) bars every
module outside groundkit.evals from importing the harness, so that the
harness stays droppable from a plain library install; an adapter that exists
only to feed the harness belongs inside that boundary rather than being an
exception to it.
Validation order is the load-bearing property here. Every identifier is checked -- against both this module's path-safety class and the harness's own stricter contract -- before a single byte is written, so a dataset the harness would later reject leaves nothing behind on disk. An earlier shape of this module wrote the corpus first and re-validated afterwards, which turned a rejected dataset into a half-populated output directory.
BeirAdaptationReport ¶
Bases: BaseModel
Counts and paths produced by :func:adapt_beir_dataset.
adapt_beir_dataset ¶
adapt_beir_dataset(
source_dir: Path,
output_dir: Path,
*,
split: str = "test",
) -> BeirAdaptationReport
Convert a BEIR directory without downloading or filtering its data.
BEIR qrels are document-level. Each emitted gold quote is therefore the complete adapted document text. groundkit resolves the quote at offset zero and treats every overlapping chunk as relevant, preserving the qrel meaning without pretending that BEIR provides span-level annotations.
The source directory is untrusted input -- a BEIR corpus is a third-party
download, and a mirror or hand-off can supply any _id it likes -- so
every identifier is validated as a path component before it reaches a
path join, and every resulting path is containment-checked against
output_dir as a second, independent barrier.
Raises:
| Type | Description |
|---|---|
EvalError
|
The source files are missing or malformed, an identifier is unsafe as a path component or violates the harness's own contract, the qrels reference an unknown document or query, the split contains no positive relevance rows, or the output corpus directory already holds files (which would silently mix two datasets -- remove it and re-run). |
Source code in src/groundkit/evals/beir.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | |