recall_guard.core
recall_guard.core
Public API for the core layer of the honest-model-ranking harness.
Re-exports the consumer-facing surface (Req 12.1) so that callers (the qualification notebook, future external scripts, and the harness layers themselves) can import every primitive from the package root without ever touching internal module paths::
from recall_guard.core import (
NvidiaLM, CompletionResult, TokenLogprob,
EvalRow, EvalSet, load_eval_set, load_cutoffs,
assert_cutoff_safe, CutoffViolation,
bootstrap_ci,
Manifest, write_manifest, read_manifest, compute_file_hash,
)
The __all__ list pins the documented names so from recall_guard.core import *
behaves predictably and so a typo in a re-exported name fails fast at import
time rather than at the first downstream lookup.
MultimodalVerdict
dataclass
Outcome of the separated-cluster check for one component.
separated is the gate: when it is true the component holds two clusters
with a genuine gap between them, there is no single location to estimate,
and a location estimator must not be run at all.
Source code in recall_guard/core/consensus.py
330 331 332 333 334 335 336 337 338 339 340 341 342 343 | |
Tail
Bases: StrEnum
Which tail an interval's confidence level refers to.
This must be declared rather than assumed: the draw count needed to certify an agreement target differs substantially between the two conventions, so a feasibility check evaluated against the wrong one is meaningless.
Source code in recall_guard/core/consensus.py
48 49 50 51 52 53 54 55 56 57 | |
CostEstimate
dataclass
What an ensemble would cost, computed without issuing anything.
Source code in recall_guard/core/ensemble.py
90 91 92 93 94 95 | |
EnsembleResult
dataclass
One ensemble's reduced answer plus the evidence behind it.
component_verdicts carries the separated-cluster check for every
component, not only the flagged ones. A verdict of separated=False with
masses near the threshold is a very different situation from one with no
mass on either side, and only the caller can judge which matters -- so the
result reports what the test saw rather than only its boolean conclusion. A
None verdict means the check did not run at all.
max_tokens and temperature record the settings the draws were taken
under. An ensemble is an audit artifact, and "under what generation settings"
belongs next to the draw-set digest: a consensus sampled at a different token
budget than production is not measuring the production decision.
sampled_at records when the draws were taken, and is None for a
result produced by replaying a stored draw set -- which is the honest
answer, because a replay was not sampled. It exists because the sampled
distribution moves between sessions as well as within one: the same prompt
against the same model id has been observed to shift a component's median
materially over two days. So a consensus has a shelf life, draws_sha256
pins which draws produced it but nothing else pins when, and a stored
corpus is not ground truth against which to judge a fresh ensemble.
Source code in recall_guard/core/ensemble.py
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 | |
EnsembleSpec
dataclass
Opt-in ensemble configuration.
Every default here is provisional: all of them were calibrated against a single measurement date at a crisis onset, chosen because it was the hard case. Whether they generalise to calmer regimes is unmeasured, which is why each threshold is a field rather than a literal.
max_tokens and temperature default to None, meaning the client's
own defaults. Set them to whatever production uses. An ensemble drawn at
a different token budget is not measuring the production decision -- and on a
reasoning model the budget is not a detail, because the chain of thought
consumes it and truncates the reply before the payload a caller parses.
Measured on one such model, dropping from a 2048-token production budget to
the 512-token client default took the parse rate from 95% to 48%.
draws is sized for agreement precision, not for component-split
detection; those are different numbers and the second is larger. See
:func:~recall_guard.core.consensus.smallest_detectable_split_n.
Source code in recall_guard/core/ensemble.py
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 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | |
smallest_certifiable_n
property
smallest_certifiable_n
Draws needed to certify agreement_target, or None if unset.
Unanimity is the best case, so this is a hard floor -- below it no observed agreement can clear the target, whatever the model returns.
LocationMode
Bases: StrEnum
Which location estimator to apply to an unflagged component.
Source code in recall_guard/core/ensemble.py
63 64 65 66 67 68 | |
MultimodalAction
Bases: StrEnum
What to do with a component that holds separated clusters.
Silently averaging across one is the single behaviour that must never be available: it launders a real disagreement into false precision, returning a value the model effectively never emitted.
Source code in recall_guard/core/ensemble.py
71 72 73 74 75 76 77 78 79 80 | |
ReferenceMode
Bases: StrEnum
Whether the optional reference draw varies per ensemble draw.
Source code in recall_guard/core/ensemble.py
83 84 85 86 87 | |
CutoffViolation
Bases: Exception
Raised when shortlisted models post-date the eval set's cutoff.
Source code in recall_guard/core/loader.py
44 45 | |
EvalRow
dataclass
One evaluation row: prompt + ground-truth direction + opaque metadata.
Source code in recall_guard/core/loader.py
48 49 50 51 52 53 54 | |
EvalSet
dataclass
A loaded JSONL eval set plus its cutoff header and content hash.
Source code in recall_guard/core/loader.py
57 58 59 60 61 62 63 | |
Manifest
dataclass
Per-run reproducibility manifest written to <out_dir>/manifest.json.
Fields mirror the design's core.manifest Service Interface verbatim.
The dataclass is frozen so callers cannot mutate a manifest after it has
been hashed/written, which keeps the persisted manifest.json faithful
to whatever the runner actually saw.
Source code in recall_guard/core/manifest.py
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | |
CompletionResult
dataclass
Single chat-completion response with logprobs.
Attributes:
| Name | Type | Description |
|---|---|---|
content |
str
|
Assistant message content. |
logprobs |
list[TokenLogprob]
|
Per-token logprob entries. |
raw_temperature_observed |
float | None
|
The temperature the API reported as honoured, when exposed. |
Source code in recall_guard/core/nvidia_lm.py
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | |
NvidiaLM
Thin HTTP client around the NVIDIA OpenAI-compatible chat endpoint.
Always sends logprobs=True and top_logprobs=20. The default
temperature is 0.0 (per Req 10.3) and can be overridden per call.
Source code in recall_guard/core/nvidia_lm.py
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 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 | |
generate
generate(prompt, temperature=0.0, max_tokens=512)
Send a single chat completion and return parsed logprobs.
Caps response length at max_tokens (default 512) so reasoning
models (gpt-oss-, nemotron-nano-) have enough budget to finish
their reasoning chain AND emit the final Direction: /
Confidence: lines. Non-reasoning models stop early on EOS so
the higher cap costs nothing for them.
Raises:
| Type | Description |
|---|---|
TimeoutError
|
If the underlying HTTP call times out. |
RuntimeError
|
If the response body lacks |
Source code in recall_guard/core/nvidia_lm.py
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 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | |
TokenLogprob
dataclass
Per-token logprob record returned by the NVIDIA OpenAI-compatible API.
Source code in recall_guard/core/nvidia_lm.py
63 64 65 66 67 68 69 | |
bootstrap_ci
bootstrap_ci(
samples,
statistic,
n_resamples=1000,
confidence=0.95,
seed=0,
)
Compute a percentile bootstrap CI for statistic over samples.
Args:
samples: Sequence of arbitrary objects (may be ints, floats, tuples,
dicts, dataclasses, etc.). Resampling uses index sampling, so the
element type does not need to be numpy-friendly.
statistic: Callable taking a resampled sequence and returning a float.
n_resamples: Number of bootstrap resamples (>=1). Default 1000 matches
the harness's Req 6.1 minimum.
confidence: Two-sided confidence level in (0, 1). Default 0.95.
seed: Seed for numpy.random.default_rng; same seed -> same output.
Returns:
Tuple (point, lo, hi):
- ``point = statistic(samples)`` (computed once on the original).
- ``lo``, ``hi`` are the lower / upper percentile bounds.
- Postcondition: ``lo <= point <= hi`` (clamped on tiny float drift).
Raises:
ValueError: if samples is empty, n_resamples < 1, or
confidence is outside (0, 1).
Source code in recall_guard/core/bootstrap.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 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 | |
detect_multimodal
detect_multimodal(
values,
*,
grid,
mass_min=0.25,
trough_steps=3,
density_ratio=10.0,
min_draws=8,
min_cluster_density=1.5,
)
Detect two clusters separated by a sparse gap.
Returns None when the check could not run -- never a silent "found
nothing". There are three such cases, and a caller who needs to tell them
apart should read the lattice adherence alongside this:
- no lattice was declared (a continuous quantity has none);
- fewer than
min_drawsdraws, which cannot evidence two clusters; - the clusters are too thinly populated for a gap between them to mean anything (see below).
The rule is defined directly on the lattice rather than by a classical unimodality test. Those assume a continuous distribution, and on heavily tied lattice data they measure tie mass instead of modality -- badly enough that on the measured corpus the most sharply converged component scores as more multimodal than the genuinely split one.
The sparsity guard matters. trough_steps counts lattice steps, not
draws, so on a lattice much finer than the sampling, empty runs occur
everywhere by chance; the density test is vacuous there because an empty gap
has no peak to compare against. Measured, a unimodal normal at a 0.001
lattice flagged on every single subsample.
What separates the two regimes is not how wide the gap is but how dense the
clusters are: a real cluster stacks many draws onto few lattice positions,
while a spurious one is a scatter of singletons whose gaps are ordinary
spacing. So each side of the split must average at least
min_cluster_density draws per occupied position.
Every threshold is a parameter because all of them were tuned against a single measurement date. Detects separated clusters only: two overlapping modes with no gap between them are invisible to it, a known and accepted false-negative class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mass_min
|
float
|
Minimum share of draws each cluster must hold. |
0.25
|
trough_steps
|
int
|
Minimum width of the gap, in lattice steps. |
3
|
density_ratio
|
float
|
How much denser the taller cluster peak must be than the busiest bin inside the gap. Note this binds only when the gap is non-empty; at realistic draw counts most detections win on a completely empty gap. |
10.0
|
min_draws
|
int
|
Below this many draws the check does not run. |
8
|
min_cluster_density
|
float
|
Minimum draws per occupied lattice position within each cluster. |
1.5
|
Source code in recall_guard/core/consensus.py
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | |
grid_adherence
grid_adherence(values, grid, *, tolerance=1e-09)
Fraction of values that actually lie on the declared lattice.
Reported so a caller who declares a lattice the data does not follow finds out, instead of silently receiving mis-snapped results. A continuous quantity scores near zero here at any lattice.
A non-finite draw counts as off-lattice rather than raising: this reports on data quality, so it has to survive the bad data it exists to describe.
Source code in recall_guard/core/consensus.py
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | |
lag_dependence
lag_dependence(labels, groups)
How much more alike draws are within a collection group than across them.
Returns None when there are fewer than two groups, or too few pairs to
compare -- never a misleading zero.
The statistic is the probability that two draws from the same group carry the same label, minus the probability for two draws from different groups. Zero means the grouping carries no information, which is what independence looks like; positive means draws collected together agree more than draws collected apart.
This exists because the reported agreement interval assumes independent draws, and that is precisely the assumption a serving stack violates -- batching, cache reuse, and node affinity all couple requests issued together. Positive dependence makes every interval narrower than its label, in the one direction that matters. Measuring it does not correct the interval; it makes the assumption falsifiable instead of merely disclaimed.
Depends only on the stored labels and group tags, never on arrival order, so it replays identically from a persisted draw set.
Source code in recall_guard/core/consensus.py
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | |
robust_location
robust_location(values, *, mode='median', trim=0.25)
Reduce values to one location, independent of their order.
mode is one of "mean", "median", or "trimmed".
Note what this cannot do: on a component whose draws form two separated clusters there is no single location to estimate, and no symmetric trim fraction escapes the gap between them -- trimming converges toward the median, not toward a mode. Callers must run the multimodality check first and skip this entirely for a flagged component.
Source code in recall_guard/core/consensus.py
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 | |
scale_floor
scale_floor(values, *, grid=None)
Robust scale estimate, floored at the lattice's resolution limit.
The floor exists because a concentrated lattice-valued component can drive the median absolute deviation to exactly zero, leaving the usual robust scale undefined. It is an identifiability floor, not an estimate of quantization noise: any true dispersion far below one lattice step produces observations on one or two lattice points and is indistinguishable from zero, so an estimate below that level carries no information.
Two caveats worth knowing before relying on it. It binds whenever
1.4826 * MAD < grid / sqrt(12) -- that is, for every MAD below
roughly 0.195 * grid, not only when the deviation is exactly zero. So on
a sharply concentrated component it can inflate a small but perfectly
well-defined estimate. And it only helps at all because the declared lattice
is coarser than the emitted one; declare the true lattice and the undefined
case returns.
Source code in recall_guard/core/consensus.py
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | |
smallest_certifiable_n
smallest_certifiable_n(
target,
*,
confidence=0.95,
tail=Tail.TWO_SIDED,
limit=100000,
)
Fewest unanimous draws whose interval's lower bound reaches target.
Unanimity is the best case, so this is a hard floor: below it no observed agreement can certify the target, and a configuration requesting fewer draws can never succeed no matter what the model returns. Surfacing it at construction turns a silently-unreachable setting into an error.
Source code in recall_guard/core/consensus.py
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | |
smallest_detectable_split_n
smallest_detectable_split_n(
*,
cluster_positions,
min_cluster_draws=8,
min_cluster_density=1.5,
)
Fewest draws at which :func:detect_multimodal can flag a split.
The companion to :func:smallest_certifiable_n, and the reason both exist:
agreement precision and split detection need different sample sizes, and
sizing for the first silently under-sizes for the second. Agreement is where
the reported confidence lives; component splits are where a silently wrong
answer lives.
This is a necessary condition, not a sufficient one -- exactly as its companion is a floor under unanimity rather than a promise. Below the value returned here the density guard cannot be satisfied at all, so a split of that shape is undetectable no matter how clean the data. Above it, detection becomes possible; whether it fires still depends on sampling noise in the trough and in the cluster masses.
Measured on a corpus whose split is unambiguous at full size, detection still missed ~3.6% of 64-draw bootstrap resamples -- the closer analogue to a fresh ensemble, which draws independently rather than from a fixed pool. Reaching 99% detection on that corpus took ~128 draws.
The figure tracks parsed draws, not the configured count. The test
only ever sees replies that survived transport, parsing, and the caller's
projection. A prompt with a 5% failure rate configured at draws=64 is
really operating at n=61, where the same measurement gives 5.5% rather than
3.6% -- so size against n_parsed, not against draws.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cluster_positions
|
int
|
How many lattice positions the two clusters together occupy. Wider clusters need more draws to reach the same density. |
required |
Source code in recall_guard/core/consensus.py
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 | |
snap_to_grid
snap_to_grid(value, grid)
Round value onto a lattice of step grid, half away from zero.
Deliberately does not divide by grid. That division is inexact in
binary in a value-dependent way -- 0.85 / 0.1 is exactly 8.5 while
0.95 / 0.1 is 9.499999999999998 -- so the tie direction ends up
depending on the value rather than on the rule, and the obvious spellings
disagree with one another. Working in integer lattice units avoids it.
Source code in recall_guard/core/consensus.py
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | |
wilson_interval
wilson_interval(
k,
n,
*,
confidence=0.95,
tail=Tail.TWO_SIDED,
continuity=False,
)
Score interval for a binomial proportion, or None when n is zero.
Inverting the score test rather than the Wald test keeps the interval inside
[0, 1] and, critically, non-degenerate at k == n. The Wald interval
collapses to zero width exactly there, which at high agreement is the
typical case -- it would report certainty from a couple of dozen draws.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
k
|
int
|
Successes observed. Must satisfy |
required |
n
|
int
|
Draws observed. |
required |
continuity
|
bool
|
Apply the Newcombe continuity correction, widening the interval. |
False
|
Source code in recall_guard/core/consensus.py
78 79 80 81 82 83 84 85 86 87 88 89 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 | |
canonical_draw_hash
canonical_draw_hash(contents)
SHA-256 over the draw set's reply text, independent of arrival order.
Covers reply text only. Logprob structures are excluded because their key ordering comes from the provider's JSON and is not stable across servers or library versions, and timing and thread identity are excluded because they are not properties of the answer.
Sorting before hashing is what makes the digest a property of the draw set; the tie-break rules elsewhere in this module recover a deterministic order from content alone, so nothing depends on how the draws arrived.
Source code in recall_guard/core/ensemble.py
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | |
estimate_cost
estimate_cost(
spec,
*,
max_retries,
has_reference,
seconds_per_request=None,
)
Worst-case request count and duration, without issuing any request.
The nominal draw count is the floor, not the worst case: each logical draw
can become max_retries + 1 requests, and a configured reference model
doubles the whole thing.
Source code in recall_guard/core/ensemble.py
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | |
generate_ensemble
generate_ensemble(
lm, prompt, spec, *, decide, components=None
)
Draw spec.draws replies to prompt and reduce them.
Raises:
| Type | Description |
|---|---|
ValueError
|
If a component holds separated clusters and the spec asks to raise. |
RuntimeError
|
If the request budget is exhausted, too few draws are usable, or transport failures exceed the configured share. Each of these is a refusal to report a confident answer computed from survivors. |
Source code in recall_guard/core/ensemble.py
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 | |
reduce_draws
reduce_draws(
draws,
spec,
*,
decide,
components=None,
waves=None,
n_requested=None,
fail_counts=None,
sampled_at=None,
)
Reduce a draw set to one answer. Pure: no I/O, no randomness, no clock.
Separated from execution so a stored draw set can be replayed into a
bit-identical result without contacting a model, which is what makes an
ensemble auditable after the fact. No clock is read here: sampled_at
stays None unless the caller passes through what execution recorded.
Source code in recall_guard/core/ensemble.py
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | |
assert_cutoff_safe
assert_cutoff_safe(eval_set, models, cutoffs)
Fail-fast guard: every shortlisted model has a cutoff <= eval cutoff.
Raises:
CutoffViolation: if any model is missing from cutoffs or, when
eval_set.cutoff_date is set, post-dates it.
Source code in recall_guard/core/loader.py
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | |
load_cutoffs
load_cutoffs(path)
Parse the cutoffs YAML registry into {model_id: cutoff_date}.
Source code in recall_guard/core/loader.py
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 238 239 240 | |
load_eval_set
load_eval_set(path)
Parse a JSONL eval file into an EvalSet.
The first line may optionally be a header object containing
{"_cutoff_date": "YYYY-MM-DD"}. All other lines must be row objects
matching the input contract (Req 2.1). Logs WARNING records for low-N
and class-imbalance conditions (Req 2.2, 2.3); never raises for those.
Returns the entire set as a single list with no train/dev split (Req 2.4).
Source code in recall_guard/core/loader.py
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 | |
compute_file_hash
compute_file_hash(path)
Return the sha256 hex digest of the bytes at path.
Reads in 8KB chunks via hashlib.sha256().update(chunk) so the function
can hash files larger than fit comfortably in memory. The chunked read is
semantically equivalent to hashlib.sha256(path.read_bytes()).hexdigest()
for any file size; the dedicated test exercises a >16KB payload to make
sure the chunk boundary does not corrupt the digest.
Source code in recall_guard/core/manifest.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | |
read_manifest
read_manifest(path)
Load manifest.json from path and reconstruct the dataclass.
Validates the top-level shape: the JSON object must have exactly the same
keys as Manifest's fields. Missing or extra keys raise ValueError
naming the offending key(s) so manifest drift is caught immediately rather
than silently dropped on round-trip.
Source code in recall_guard/core/manifest.py
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 | |
write_manifest
write_manifest(out_dir, manifest)
Serialise manifest to <out_dir>/manifest.json and return that path.
out_dir is created (with parents) if it does not yet exist; this lets
the runner pin the output directory at run start before any per-run
artifact has been produced. JSON is indented and key-sorted so the file is
diff-friendly across runs that differ only in metadata order.
Source code in recall_guard/core/manifest.py
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 | |