Skip to content

feat: emit typed provenance-stamped MetricFrame (evaluation-of-record) (#21) - #22

Merged
Polichinel merged 4 commits into
developmentfrom
feature/metricframe-emit
Jun 25, 2026
Merged

feat: emit typed provenance-stamped MetricFrame (evaluation-of-record) (#21)#22
Polichinel merged 4 commits into
developmentfrom
feature/metricframe-emit

Conversation

@Polichinel

Copy link
Copy Markdown
Collaborator

Closes the emit side of #21.

Why

views-reporting scrapes per-model metrics from WandB get_latest_run().summary at render time; in production this silently mis-selected runs (their C-48: 22/25 constituents rendered "not calculated"). The durable fix (views-reporting C-108 / ADR-018, views-frames ADR-020) is for views-evaluation to emit the evaluation-of-record as a typed, transportable, provenance-stamped MetricFrame that reporting consumes via an injected adapter — no WandB at render time.

What

  • MetricFrame (new) — a string-keyed value object on the axes (eval_type, target, metric, group_id, partition, level), reusing the views-frames substrate: FrameMetadata for generic provenance and conformance.assert_frame_envelope as the structural checker. Owns its serialization (save/load round-trip; views-frames IO is (time,unit)-only).
  • EvaluationReport.to_metric_frame(...) (new, Level-1 bridge) — flattens the nested results into per-group rows plus a group_id="mean" cross-group aggregate (the value views-reporting's _canonical_row matches). Maps schema names to the consumer-facing eval_type spelling. Purely additive: to_dict()/to_dataframe() unchanged.
  • Provenance split (ADR-020 / C-47): generic identity (model_id/run_id/data_version) goes in FrameMetadata; scoring_code_version (defaults to the installed package version — not a git SHA) and evaluation_timestamp stay in the MetricFrame's own metadata. All identity is injected, not inferred.
  • Optional dependency: views-frames is a frames extra; the core stays numpy/sklearn-only and importable without it (find_spec-gated export).

Contract / consumer alignment (verified against the repos)

  • Metric tokens are a subset of our catalog and exactly match views-reporting's canonical set; a C-41 drift guard test fails CI here if that ever diverges.
  • run_id is intentionally optional/None at emit time (the WandB run does not exist yet at the evaluation call site in pipeline-core).

Tests

30 tests (green/beige/red): emit for all 4 (task, pred_type) cells, assert_frame_envelope conformance, save/load round-trip incl. metadata, provenance split, NaN handling, fully-empty frame, fail-loud construction, and the canonical-token drift guard. Full suite: 279 passed, ruff clean. EvaluationReport CIC updated.

Out of scope (separate repos)

  • pipeline-core: inject model_id/run_id/data_version/partition/level into to_metric_frame(...).
  • views-reporting: the injected EvaluationSource adapter (their ADR-018 Phase 3).
  • The cross-repo schema_version round-trip contract test (views-frames C-46 open half) beyond the local one here.

🤖 Generated with Claude Code

xiaolong0728 and others added 4 commits May 18, 2026 11:32
release: v0.4.0 — EvaluationFrame architecture, metric catalog, Brier variants
#21)

Add EvaluationReport.to_metric_frame() and a MetricFrame value object so
views-reporting can consume a typed, transportable evaluation artifact instead
of scraping WandB at render time (views-reporting C-108/C-48; views-frames ADR-020).
This is the emit side only; pipeline-core injects provenance, reporting adds the
EvaluationSource adapter (separate repos).

- MetricFrame: string-keyed (eval_type, target, metric, group_id, partition, level)
  value object reusing the views-frames substrate (FrameMetadata +
  conformance.assert_frame_envelope). Owns its serialization (save/load round-trip).
- Provenance split per ADR-020/C-47: generic identity in FrameMetadata;
  scoring_code_version (defaults to installed package version) + evaluation_timestamp
  in MetricFrame's own metadata. All identity injected, not inferred.
- Row shape matches the consumer: per-group rows plus a group_id="mean" aggregate.
- views-frames is an optional 'frames' extra; core stays numpy/sklearn-only and
  importable without it (find_spec-gated export).
- 30 tests (green/beige/red): envelope conformance, round-trip, provenance split,
  fail-loud construction, and a C-41 canonical-token drift guard.
- Update EvaluationReport CIC for the new method + lazy-views-frames deviation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address findings from the PR review:
- save(): json.dumps now coerces numpy scalar types via a default handler, so an
  injected numpy timestamp/seed no longer crashes serialization (latent TypeError).
- MetricFrame._validate: reject non-1D identifier arrays (fail-loud on the public
  constructor; not reachable via to_metric_frame but MetricFrame is exported).
- to_metric_frame docstring: clarify the group_id="mean" denominator is the groups
  that reported each metric (= all groups in the normal uniform-metric-set emit path).
- Add regression tests: numpy-typed timestamp/seed round-trip; 2D identifier rejected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Satisfies the check-branch 'up-to-date with main' gate. main is content-identical
to development (no file delta); this only brings in the #19 merge node.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants