Evaluation Records¶
Reference
Surface: Captured evaluation records and evidence bindings.
Stability: Public core contract for captured evaluation.
Use this page when: Implementing or reviewing captured evidence.
Captured comparisons use neutral evaluation record contracts. A record keeps its stable ID, input, expected value, output, score provenance, metadata, and error state. Baseline and subject records must use the same complete schedule.
The public request contract is invarlock/evaluation-request-v2. Deterministic
and recorded-score captured comparisons use directory pack
invarlock/evidence-pack-v2 and an external
invarlock/evidence-verification-receipt-v3. The pack has one manifest signature,
not a second signed record envelope. verify checks recipient-owned policy,
complete-run and normalized-request digests, and signer identity. report is a
deterministic presentation step and does not confer verification authority.
The captured comparison binds bindings.baseline, bindings.subject, and
bindings.policy. Metric results use baseline_mean and subject_mean; absolute
policy bounds use subject_minimum and subject_maximum. The captured SDK helper
is compare_runs(baseline=..., subject=..., policy=...), with no alternate role
keywords or wire-field aliases. These captured fields do not rename
native historical reports or third-party exports. Native pack v1 and receipt
v1/v2 contracts are unchanged. Selecting comparison.metric: judge instead
publishes judge evidence with its own measurement and recipient trust contracts;
see judge measurements.
For the exact JSON schemas, see the files under contracts/ and their synced
copies under src/invarlock/_data/contracts/.
Record contracts¶
| Schema | Wire identifier | Meaning |
|---|---|---|
evaluator_export.schema.json |
invarlock/evaluator-export-v1 |
Source-specific native payload with explicit evaluator version and run ID |
evaluation_run.schema.json |
invarlock/evaluation-run-v1 |
Complete canonical run with records and score provenance |
evaluation_case_set.schema.json |
invarlock/evaluation-case-set-v1 |
Independently reviewed IDs, inputs, expected values and metadata |
comparison_policy.schema.json |
invarlock/comparison-policy-v1 |
Metric, slice, sample, interval and subject-bound requirements |
multi_metric_comparison.schema.json |
invarlock/multi-metric-comparison-v1 |
Deterministically replayed paired results and three-way decision |
normalized_captured_request.schema.json |
invarlock/evaluation-request-v2 |
Path-free portable request identity stored inside the captured pack |
Runs require format, source (name, version), run_id, artifact_digest,
source_digest, score_provenance, and records. Every record has id, input,
expected, output, scores, metadata, error, and context. Null output or
an explicit error is retained, not silently dropped. Likelihood scoring can use
a null output when valid reference-continuation facts are present. Matching IDs
must bind matching input, reference and metadata. Missing required scorer facts
produce insufficient_evidence, rather than a pass computed over survivors. Duplicate
IDs or changed paired facts are integration errors.
artifact_digest attributes the run to a supplied artifact identity; it does not
prove inference. source_digest binds raw imported bytes when applicable and is
part of the complete run. Canonical run_digest covers the whole normalized run,
including provenance and all records, not just its model or schedule. Do not
substitute a raw export checksum for a complete-run pin.
load_run supports invarlock, jsonl, evaluator-native-json, evaluator-json,
inspect-json,
lm-eval-samples, promptfoo-jsonl, and langfuse-json.
These adapters normalize recorded outputs, never register a
runtime provider or import execution authority. Original upstream bytes and
their source_digest remain unchanged. A canonical invarlock run supplies its
own metadata; external adapters require explicit source, run ID and artifact
or hosted service identity, with approved score provenance when recorded metrics
are used. External captured request sources accept artifact_digest: null
with the complete service_identity descriptor. Canonical adapter: invarlock
runs carry their own descriptor and reject source identity overrides. The
external parser also binds the original file bytes through source_digest.
Dedicated evaluator exports¶
For an existing pipeline, save its native cases or results as JSON and use
adapter: evaluator-native-json in the captured request. Dedicated mappings
cover all 19 maintained ecosystems. Your pipeline keeps its current evaluator environment;
evaluation, verification and reporting run in a separate core InvarLock
environment without the upstream SDK. No common export envelope or SDK-side
InvarLock installation is required; the file follows its evaluator's documented JSON profile.
For example, use Ragas's own explicit serializer after retaining your
SingleTurnSample objects and their planned IDs:
import json
rows = [{"id": case_id, "sample": sample.model_dump(mode="json")}
for case_id, sample in captured_samples]
if [row["id"] for row in rows] != planned_ids:
raise ValueError("capture differs from the complete planned schedule")
with open("subject-native.json", "x", encoding="utf-8") as stream:
json.dump(rows, stream, ensure_ascii=False, allow_nan=False)
captured_samples and the independently frozen planned_ids belong to your
workflow. Export the baseline in the same way, preserving failed cases. Declare
the raw file, actual SDK version, run ID and evaluated model identity in each
captured request source:
path: subject-native.json
adapter: evaluator-native-json
source: {name: ragas, version: "0.4.3"}
run_id: subject-campaign
artifact_digest: "<actual model artifact digest>"
Use the version actually captured. For hosted models, declare
artifact_digest: null and the complete service descriptor.
Pin an independently reviewed expected_case_set_digest in policy to reject
missing or changed scheduled cases, and an approved expected_run_digest on
each source when required. Raw-file digests bind the imported bytes; the source
name and version remain explicit attribution rather than proof of execution.
When the evaluator SDK and InvarLock dependency requirements are compatible, the shared Python exporter also accepts native SDK objects directly:
from importlib.metadata import version
from invarlock.engine import export_evaluator_result, evaluator_input_capabilities
run = export_evaluator_result(
"ragas",
[{"id": case_id, "sample": sample} for case_id, sample in captured_samples],
"subject-export.json",
expected_ids=planned_ids,
source_version=version("ragas"),
run_id="subject-campaign",
artifact_digest=model_artifact_digest,
)
print(evaluator_input_capabilities(run))
This convenience API checks complete case membership before writing, refuses
an existing destination, and returns a validated canonical run. Its output is
an invarlock/evaluator-export-v1 envelope consumed with
adapter: evaluator-json, using the same source/run/model declarations. For
hosted models pass artifact_digest=None, service_identity=identity.
Both adapters use the same source-specific mapping and downstream scorers.
Keep pinned MLflow 3.14.0 in a separate environment: it requires
cryptography<49, while core InvarLock requires cryptography>=50. Export the
original prediction table as JSON and use evaluator-native-json; no core
dependency change is needed. SDK serializer smoke tests run from source with
minimal test dependencies and do not establish SDK/core co-installation.
The native shape recipes
cover Harness, Inspect, Promptfoo, DeepEval, Ragas, LightEval, Hugging Face
Evaluate, Pydantic Evals, AutoEvals, OpenEvals, MLflow, Garak, OpenAI Evals,
Phoenix Evals, Langfuse, Opik, Azure AI Evaluation, Evidently and TruLens.
With compatible co-installation, Inspect EvalLog and Langfuse ExperimentResult
objects can be passed directly to the Python exporter. The raw-file route uses
Inspect's JSON log or bare public Langfuse ExperimentResult fields, including
item_results; it does not require a Langfuse-specific InvarLock envelope. Original outputs can be captured before
upstream grading. Scalar SDK wrappers
accept an optional metric_result; no extra evaluator call is required to use
InvarLock's scorers. Reference fields may be omitted for reference-free judge
tasks where the native profile permits them; exact match and NLL still require
a reference. Structured outputs remain JSON for compatible policies; text-only
scorers do not stringify them. Aggregate-only reports still need the original per-case
prediction table or attempts.
Per-case string metadata supplies policy slices. Optional
metadata.invarlock_scores selects named finite numeric observations; native
per-case numeric metrics are also retained where the profile defines them.
Recorded-score policies require explicit score_provenance at export/import
and matching accepted_provenance in policy. Numeric metrics do not become
likelihoods or judge calls. Preserve actual continuation facts under
metadata.invarlock_likelihood for NLL, and supply the separate complete judge
measurement contract for judging. See scorer selection
and policy requirements.
Langfuse experiments¶
langfuse-json imports the explicit invarlock/langfuse-export-v1 capture
produced from a Langfuse Python experiment. It does not parse arbitrary dashboard
exports. The maintained exporter uses langfuse==4.14.1; the installed parser,
verification and reporting need no Langfuse SDK, account or network access.
Preserve a stable item ID and the complete independently selected case list. The exporter rejects missing or duplicate results instead of accepting only successful tasks. Structured inputs require an explicit text projection for judge scoring. Exact match needs original answers and references; NLL requires explicit continuation measurements; judging requires a recipe and complete judge measurements. Numeric Langfuse scores alone do not establish those facts.
Follow the Langfuse handoff example for capture, source identities, failed records and scorer-specific requirements.
Hosted service identity¶
A canonical run may include service_identity only with artifact_digest: null.
Local artifact runs retain their existing digest contract and need no new field.
The service descriptor is closed and requires every field below; unavailable
observed model or exposed revision values are explicitly null.
| Field | Accepted value and meaning |
|---|---|
kind |
Exactly hosted_service |
provider, service, deployment, requested_model |
Nonempty strings, at most 512 characters each, describing the requested endpoint |
observed_model, exposed_revision |
Nonempty strings of at most 512 characters, or null; source-reported observations |
configuration |
JSON object with at most 100 properties containing relevant non-secret settings |
configuration_digest |
invarlock.engine.digest(configuration) |
harness |
Closed object with nonempty name and version (at most 512 characters each), plus SHA-256 source_digest |
observation_window |
Closed object with started_at and ended_at, ordered UTC timestamps using YYYY-MM-DDTHH:MM:SS[.microseconds]Z |
The complete descriptor is limited to 1 MiB. Unknown fields, an inconsistent
configuration digest, invalid timestamps or a reversed window are rejected.
Identity strings reject ASCII control characters and all-whitespace values. Fractional seconds, when
present, contain one through six digits; the start may equal the end.
invarlock.engine.make_run and capture_evaluator_run accept
artifact_digest=None, service_identity=identity. The configuration digest is
computed over the configuration object; the service identity digest is computed
over the complete descriptor. run_digest additionally binds all records and
run provenance. These are different identities and cannot substitute for one
another. The public invarlock.engine.validate_service_identity(identity)
validates the descriptor. invarlock.engine.evaluated_subject_digest(run) returns
the hosted descriptor digest or, for a local run, its explicit artifact digest;
it does not fall back from a malformed hosted identity. Neither helper
authorizes the subject or replaces a complete-run pin.
For example, given a reviewed descriptor and explicitly mapped records:
from invarlock.engine import capture_evaluator_run, digest
identity["configuration_digest"] = digest(identity["configuration"])
run = capture_evaluator_run(
rows,
source={"name": "service-capture", "version": "1"},
run_id="subject-campaign",
artifact_digest=None,
service_identity=identity,
)
The variables identity and rows must contain the complete descriptor and
actual captured facts. Canonical source bytes and complete-run anchors authenticate
what was supplied; a service label, revision string or digest of the descriptor
does not identify immutable model weights or attest execution. A hosted run
uses the captured request and verification path. It does not register a native
runtime provider or require provider access during offline replay. See the
requalification guide for
campaign preparation and interpretation.
Capture and explicit input projections¶
invarlock.engine.capture_evaluator_run accepts explicitly mapped per-case rows
from any evaluator, with its actual source name/version, run ID and supplied
artifact digest, or artifact_digest=None with a hosted service_identity.
evaluator_input_capabilities reports usable counts and
unavailable IDs for exact_match, normalized_nll_per_utf8_byte and judge.
Availability describes input facts; it grants neither qualification nor proof
of an upstream scorer execution.
For a structured input, input_projection may select an existing string with
{"kind": "json-pointer", "pointer": "/input/question"}. Pointers must be
rooted at /input or /context and select a string. The capture retains the original input and
context plus the exact projection binding. Paired runs must agree on the
original input and projection configuration as well as the selected text.
Objects and arrays are never implicitly converted to text.
External adapter sources may declare the same projection in a captured request.
Canonical adapter: invarlock runs already contain their bindings and reject
source overrides, including a second projection.
Set comparison.metric to exact_match, normalized_nll_per_utf8_byte or
judge to select a scorer explicitly. Exact match and NLL must agree with the
single metric in the comparison policy. Omit this selector for an ordinary
multi-metric comparison. Judge uses a separate recipe and retained-call path.
Captured reference likelihoods¶
A record may additionally retain likelihood with these closed fields:
| Field | Meaning |
|---|---|
basis |
Exactly reference_continuation |
logprob_sum |
Finite, zero or negative natural-log probability sum of the reference continuation |
token_count |
Positive integer count of measured reference tokens |
utf8_byte_count |
Positive integer equal to the reference string's UTF-8 byte length |
input_digest, reference_digest |
Canonical digests of the original input and reference |
artifact_digest, source |
Exact evaluated artifact and evaluator identity from the run; artifact is null for hosted runs |
service_identity_digest |
Required for hosted likelihoods: canonical digest of the complete run service descriptor; absent for local artifacts |
configuration_digest, tokenizer_digest |
Pins for the likelihood measurement configuration and tokenizer |
These facts must originate from an actual likelihood measurement. Generated
answer probabilities and summary losses have different semantics. With an input
projection, input_digest still identifies the preserved original input.
The NLL policy metric uses direction: lower, unit: nats_per_utf8_byte,
aggregation: mean and ratio_max in place of maximum_regression. Its
configuration requires configuration_digest, baseline_tokenizer_digest and
subject_tokenizer_digest. For hosted NLL, the policy has one shared
configuration_digest: both service descriptors and each likelihood row must
match it. Different service configurations are not admitted by this profile;
separate model, observation-window and tokenizer identities may still differ.
The score per record is -logprob_sum / utf8_byte_count;
the comparison is the ratio of subject and baseline means, with the same paired
resampling method as native NLL. Interval width is measured in ratio units.
Reference and configuration bindings and the declared tokenizer identities are
checked before scoring. Replay does not execute the model or tokenizer, so their
measurement claims remain source assertions. The
retained Mistral 7B sentinel
contains fresh generation and reference-likelihood measurements across all 19
maintained evaluator profiles, both import routes and two model artifacts. The
Harness likelihood reference
is a smaller six-pair, same-model CPU control. Each reference qualifies its
recorded profile; synthetic contract tests and exact-match results alone do not
establish likelihood measurement support.
Judge selection uses the separate judge recipe and retained measurement contract
through the same captured request. It does not reinterpret a recorded scalar
score as a native judge trial. prompt.reference_mode: per_case explicitly adds
the frozen string reference to each judge request as a separate field. Omitted
or none preserves the reference-free behavior. The reference never becomes part
of the evaluated model input. See judge measurements.
Policy and identity¶
Policies contain 1..16 metrics and up to 16 metadata slices in addition to the
overall scope. Each metric selects a unique name, kind, closed configuration,
direction (higher/lower), unit, mean aggregation, minimum count, maximum
regression and maximum interval width; NLL replaces maximum regression with
ratio_max. Optional absolute limits on the subject mean are
subject_minimum and subject_maximum.
Built-in captured kinds are exact_match, normalized_match,
numeric_tolerance, json_exact, json_fields, token_f1, and
normalized_nll_per_utf8_byte. Exact match and normalized NLL share native
scorer semantics while retaining captured-input assurance. normalized_match and token_f1 pin
unicode_version; a recipient with an incompatible Unicode environment refuses
replay without issuing a receipt. recorded instead selects a score_key and
exact accepted_provenance (kind, source, version, unit, rubric_digest).
The verifier authenticates attributed values and paired arithmetic; it does not
reproduce or authenticate the original scoring process.
An independent planned case set can be pinned in policy as
expected_case_set_digest. Use freeze_case_set, case_set_digest, and
validate_run_case_set to require the reviewed schedule, not merely agreement
between two possibly incomplete captures.
normalize_captured_request strips physical source/output paths and binds each
adapter, complete run digest, explicit expected_run_digest when authored,
source overrides, and policy digest. captured_request_digest hashes those
canonical portable bytes. An explicit run pin is semantic and is not dropped
even when equal to the derived run digest. Relocating a request tree changes no
portable identity; changing a source, provenance, policy or pin can change it.
Use these SDK helpers, not a handwritten YAML/JSON hash recipe.
Pack boundary¶
Captured pack v2 has exactly manifest.json, checksums.sha256, request.json,
inputs/policy.json, records/baseline.json, records/subject.json, and
reports/evaluation.report.json. Signed packs also have
manifest.signature.json; authentication: unsigned_local packs have no
signature and a null signing-key fingerprint. The signature uses the existing
invarlock/evidence-pack-signature-v1 envelope over the canonical manifest.
There is no second signed comparison envelope, embedded verification receipt,
or implicit legacy migration.
The normalized request, policy, records and report are canonical JSON with a
final LF. The checksum ledger uses fixed payload paths. comparison_id hashes
the canonical object containing kind: captured, the normalized request digest,
both run digests and the policy digest; it identifies the comparison inputs.
Extra files/directories, path traversal, symlinks, duplicate keys, non-finite
numbers and byte-limit violations are rejected. Payloads must be regular files;
verification checks a stable snapshot and validates their digests.
Publication is atomic and no-clobber. Keep receipts and rendered outputs outside
the immutable pack. See capacity,
reports and receipt scopes, and the
signed trust-profile flow.