Skip to content

feat: push eval runs to the platform with uv run eval --push#1947

Draft
mikasenghaas wants to merge 14 commits into
mainfrom
feat/eval-push
Draft

feat: push eval runs to the platform with uv run eval --push#1947
mikasenghaas wants to merge 14 commits into
mainfrom
feat/eval-push

Conversation

@mikasenghaas

@mikasenghaas mikasenghaas commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Add a --push flag to uv run eval (off by default) that uploads a finished run to the Prime Intellect platform's private Evaluations tab at the end of the eval.
  • New verifiers/v1/samples.py: trace_to_sample, the canonical per-trace → platform (v0) eval-sample converter. Extracted so it is the single source of truth for the "old format" mapping and can be shared with the prime-rl monitor (see companion PR).
  • New verifiers/v1/push.py: a tiny sync client (push_traces) that does the /evaluations/ flow — resolve env by name (get-or-create) → create evaluation → push samples → finalize — then logs the evaluation id. Auth + base URL reuse the existing CLI plumbing ($PRIME_API_KEY / ~/.prime/config.json, team-scoped). No new dependencies; no dependency on prime/prime-evals (would be circular).
  • Hooked in at the single convergence point in verifiers/v1/cli/eval/main.py where all three execution paths (v1 in-process, --server, and v0 legacy --id) have produced list[Trace], so one hook covers every run type.

Platform record shape

Matched to what the platform's Evaluations UI reads, verified by round-tripping real pushes against api.primeintellect.ai:

  • Model + counts: model, num_examples (distinct examples uploaded — config.num_tasks is None on a full run), rollouts_per_example, and the avg_reward/avg_metrics/avg_error triple go in the evaluation metadata, which the overview reads.
  • Sub-rewards are flattened onto each sample under their reward-function names; env metrics stay in the nested metrics field / info.
  • num_steps and the per-branch reward are not emitted (stray custom state / redundant).

Crash safety

The upload runs after the eval has finished and saved results, so push_traces wraps the HTTP flow in a try/except that logs and returns None — a network blip or platform 4xx/5xx never crashes the run (and on the non-rich path never swallows the final per-trace JSON output).

Companion PR

prime-rl's monitor pushes training-run samples through its own hand-rolled copy of this conversion. The companion PR switches it to verifiers.v1.samples.trace_to_sample so both producers stay aligned on one canonical record shape:
PrimeIntellect-ai/prime-rl#2974

Verification

Live smoke runs against api.primeintellect.ai (deepseek/deepseek-v4-flash), all COMPLETED; each with metadata.model and num_examples populated:

Without --push, behavior is unchanged. ruff check, ruff format, and tests/v1/test_configs.py pass.

Convert each in-memory v1 Trace to the platform's (v0) eval-sample format and
upload the finished run over the /evaluations/ API (create -> push samples ->
finalize), the same contract `prime eval push` uploads a saved run through.
Off by default; gated behind --push. Covers v1, server, and v0 legacy runs
(all three converge on list[Trace]).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread verifiers/v1/push.py Fixed
Comment thread verifiers/v1/push.py Outdated
Comment thread verifiers/v1/cli/eval/main.py
mikasenghaas and others added 2 commits July 7, 2026 16:05
Move the Trace -> platform (v0) eval-sample conversion into a dependency-light
`verifiers/v1/samples.py` so it can be shared with the prime-rl monitor's sample
upload (companion PR), keeping one canonical "old format" mapping. Bake in a
non-finite-float guard (`drop_non_finite`) so NaN/Inf rewards/metrics can't emit
invalid JSON on the eval push or land as bad sample data in either producer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Non-finite rewards/metrics only arise from custom numpy aggregations over empty
inputs (rare, author-specific) and would fail the push loudly rather than corrupt
data. Keep the shared converter single-purpose.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread verifiers/v1/push.py Outdated
Comment thread verifiers/v1/samples.py
mikasenghaas and others added 3 commits July 7, 2026 23:21
Conservative test additions: the converter is exercised end-to-end by
eval --push and the prime monitor's sample upload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
push_traces uploaded only the mean reward as the run's metrics, dropping
every env-recorded metric and the error rate - the platform summary no
longer matched the eval dashboard. Aggregate per-key means over the
traces that recorded each metric and the errored fraction, matching
v0's avg_reward/avg_metrics/avg_error accumulators.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/push.py Outdated
mikasenghaas and others added 4 commits July 8, 2026 01:51
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- push: emit run metrics in the platform's canonical {reward, metrics, error}
  shape (was avg_reward/avg_metrics/avg_error); the metrics breakdown now
  includes the per-reward-function values, not just env metrics.
- push: put model + counts in evaluation metadata so the overview resolves the
  model (was Unknown Model) instead of only setting model_name.
- samples: drop num_steps (rendered as stray custom state) and carry the
  per-reward-function / metric breakdown in info.reward_signals, the recognized
  nested key, since the platform folds unknown top-level sample fields into info.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…promote sample columns)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/push.py Outdated
mikasenghaas and others added 2 commits July 8, 2026 03:06
The platform promotes a numeric sample field to a per-rollout reward column
only when its name ends in reward / reward_func / score (the v0 reward-function
naming); anything else folds into the sample's info. v1 reward functions (e.g.
'solved') lack that suffix, so they were being buried in info.

trace_to_sample now flattens each sub-reward under a reward_column_name() key
(appending _reward_func when a v1 name lacks a recognized suffix), and
push_traces declares the same names in metadata.avg_metrics so the overview
lines up. Env metrics stay in the nested metrics field / info, as they do for v0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The run has already finished and results are saved by the time push_traces
runs, so a network blip or platform 4xx/5xx (raise_for_status) / JSON decode
error must not propagate. Wrap the HTTP flow in try/except that logs and
returns None; on the non-rich path this also preserves the final per-trace
JSON output, which prints after the push call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/push.py Outdated
mikasenghaas and others added 2 commits July 8, 2026 03:24
- Remove the per-branch `reward` from each trajectory entry (redundant with the
  sample-level reward; not needed by consumers).
- Drop `reward_column_name` / `_REWARD_COLUMN_SUFFIXES`: flatten sub-rewards under
  their raw function names instead of appending `_reward_func`, and aggregate the
  same raw names in `metadata.avg_metrics`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
config.num_tasks is None for a full run, leaving num_examples blank in the
uploaded evaluation metadata. Use the number of distinct example ids among the
pushed traces so num_examples * rollouts_per_example matches the samples.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/push.py
"avg_error": sum(t.has_error for t in traces) / n if n else 0.0,
}

env_name = config.taskset.id or config.id

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium v1/push.py:77

env_name is set to config.taskset.id or config.id and sent to the platform as the environment name in the /environmentshub/resolve call. When the id is a hub-style plugin id like org/name@version, the resolve request uses that raw plugin id instead of the canonical package name, so the run gets attached to the wrong environment and won't match what prime eval push and other producers upload. Consider normalizing env_name with the same env_name(...) transformation used elsewhere in the codebase before sending it to the API.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @verifiers/v1/push.py around line 77:

`env_name` is set to `config.taskset.id or config.id` and sent to the platform as the environment name in the `/environmentshub/resolve` call. When the id is a hub-style plugin id like `org/name@version`, the resolve request uses that raw plugin id instead of the canonical package name, so the run gets attached to the wrong environment and won't match what `prime eval push` and other producers upload. Consider normalizing `env_name` with the same `env_name(...)` transformation used elsewhere in the codebase before sending it to the API.

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