feat: record per-call model call metadata on v1 traces - #2061
Merged
Conversation
Every provider exchange behind a sampled turn lands on Trace.calls as an untyped ModelCall: the request body as actually sent upstream (model + sampling overrides applied), the native response object, provider response headers, wall-clock span, time-to-first-token for streamed turns, and — for a failed exchange — the error, coupled to the call that raised it. Each successful call links into the message graph via the id of the assistant node it committed (PendingTurn.commit now returns it), so per-call data joins the graph exactly even under branching (compaction, retokenized prefixes). Replayed/coalesced SDK retries record nothing — they are not provider exchanges. Closes RES-1085. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
time_to_first_token was only measurable on the streamed relay path; drop it until there's a consumer. The call's wire format is now a typed DialectName literal (chat/responses/anthropic, exported by v1.dialects) instead of the provider endpoint path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two per-call recording gaps from review: a provider stream dying mid-relay propagated out of the pump loop without landing on Trace.calls, and apply_overrides ran outside the guarded try, so a malformed native field escaped as an unshaped 500 with no record. Both now record the failed exchange (request=None when the overrides themselves failed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The endpoint path already identifies the format; drop DialectName and Dialect.name in favor of recording upstream_path on the call. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mikasenghaas
marked this pull request as ready for review
July 17, 2026 20:28
Contributor
ApprovabilityVerdict: Needs human review This PR introduces a new tracing feature with a schema version bump (1→2) and restructures where usage/finish_reason data is stored (from nodes to calls). The schema change and structural reorganization warrant human review to verify compatibility. You can customize Macroscope's approvability policy. Learn more. |
A completed provider exchange whose turn commit raises was dropped from Trace.calls on the non-streaming path (the streaming path already recorded it). Record it with the response and the failure before propagating, matching the stream path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A stream failure record now carries what the exchange already produced: provider headers on a mid-relay death, plus the assembled native payload when only the commit failed — matching the non-streaming path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wrap each path's whole exchange (overrides -> call -> commit) in a single try/finally whose finally appends the one ModelCall, instead of recording at every failure site. Error handlers just stash the error; a harness disconnect mid-stream now also records (the exchange happened), and a post-commit delivery failure keeps the success record. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review follow-ups from the single-record refactor: tracebacks are formatted from the exception object (the finally runs after handlers exit, where format_exc sees nothing), a cancelled exchange is recorded with its CancelledError instead of as a phantom error-free call, and the streaming span now starts after prepare_turn like the non-streaming one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kennethnym
reviewed
Jul 17, 2026
kennethnym
left a comment
There was a problem hiding this comment.
the ModelCall is really useful, i have some thoughts below.
some more thoughts:
- model id is stored per rollout under
Trace.agent.model. if in the future we support model switching on the fly, then the model id will have to move down a level (probably underModelCall) - maybe useful to have
ModelCall.idas well, pulled from provider's response?
mrmoxon
reviewed
Jul 17, 2026
Review asks: a failed call records the HTTP status it surfaced (ModelCall.status) and keeps the provider response headers when the failure carried an HTTP response — ProviderError now carries them from the eval client's three raise sites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
OverlongPromptError inherited ProviderError's 502 default, which record_call then stamped onto context-length records. Default it to the 400 the interception server surfaces, and let model_error keep the provider's real status when the failure carried one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Anthropic lifts output_config.effort onto the typed knob (where apply_overrides puts the eval's reasoning effort), and Responses keeps non-effort reasoning keys (e.g. summary) instead of dropping them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
No consumer reads MessageNode.finish_reason (prime-rl and research-environments have none; is_truncated was the only reader) and it is an exchange attribute, so it moves to ModelCall exclusively. is_truncated reads the last successful call; the v0 bridge records a minimal per-step call to keep it working. TRACE_VERSION bumps to 2 — nodes of previously persisted traces no longer validate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Provider usage is an exchange attribute: it moves to ModelCall exclusively (the v0 bridge records it on its per-step call). Branches keep their token accounting — Trace.branches attaches each branch's calls in path order, and Branch.usage/last_usage read those — so trace/branch num_* properties and the platform push payload are unchanged. Joins the v2 trace schema. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both max_tokens and max_completion_tokens can ride one wire request (an eval override on top of a harness's alias); the record keeps the canonical knob only, override winning. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Widen Usage.service_tier to a plain string (the ModdedChatCompletion pattern) instead of stripping unknown tiers before validation, so the new dialect tests' provisioned case round-trips. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The status only exists for failures, so it belongs on the failure record: Error gains status_code (populated on per-call records and rollout-level capture_error alike) and ModelCall.status goes away. No 502 fallback — a non-HTTP failure (commit error, cancellation) simply has none. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit cf18f49. Configure here.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
samsja
approved these changes
Jul 18, 2026
mikasenghaas
added a commit
to PrimeIntellect-ai/prime-rl
that referenced
this pull request
Jul 18, 2026
…plit (#3082) * feat: re-pin verifiers for per-call trace records Companion to PrimeIntellect-ai/verifiers#2061: Trace.calls per-call records, with finish_reason and usage moved off MessageNode onto ModelCall (trace schema v2). test_advantage's rollout builder attaches usage via a per-call record instead of the removed node field. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump verifiers pin to branch head Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump verifiers pin to the merged #2061 commit Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: export generation model/harness time split to wandb Bumps the verifiers pin to d5320edcb (per-call ModelCall records #2061 + model/harness generation-time split #2060). TimingMetrics gains generation/model and generation/harness (from timing.generation.{model, harness}.duration, stamped server-side by Rollout.split_generation and carried on the wire), emitted as timing/generation/{model,harness}/*. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
mikasenghaas
added a commit
that referenced
this pull request
Jul 29, 2026
One to_v<n> util per historical schema bump, chained by the migrate hook: v1->v2 lifts node-level usage/finish_reason into synthesized ModelCalls (#2061), v2->v3 nests the flat agent identity and top-level runtime into AgentInfo (#2106), v3->v4 wraps float rewards as Reward(score, weight=1) preserving reward sums (#2119), v4->v5 as before. Steps copy what they mutate, so validating the same dict twice is stable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mikasenghaas
added a commit
that referenced
this pull request
Jul 30, 2026
* fix: pin exclude-newer-package cutoffs as UTC timestamps Bare dates resolve to midnight in the machine's local timezone, so every timezone relocks uv.lock with different exclude-newer timestamps and the --locked pre-commit hooks fail for anyone outside the tz that produced the lock. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: align trace mutators on the record_* verb stamp -> record_run, capture_error -> record_error, matching the existing record_metric/record_reward/record_judge family and the trace's own 'record' vocabulary (to_record, record schema). Also drop the override warnings on record_metric/record_reward: overriding is defined behavior, last write wins. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: rename trace dump exclusions to EXCLUDE_FIELDS Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: an expired agent timeout is an agent error, not a truncation The rollout deadline expiring now records a HarnessError (ok=False, no scoring) instead of the clean harness_timeout stop that scored the partial trajectory: a timeout is the agent breaking its time budget, not a healthy run cut short. The stop-condition name is gone from the vocabulary; the legacy v0 bridge's timeout_reached maps through the generic truncation fallback. The concept is renamed harness timeout -> agent timeout throughout: TaskTimeout.harness -> TaskTimeout.agent, the rollout plumbing, and cap_remote_harness_timeout -> cap_remote_agent_timeout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: slim the Trace surface Drop the agent_name/trainable/runtime passthroughs (read trace.agent directly) and the duplicate error property (last_error is the one reader); align tool_messages with assistant_messages (nodes-based, branch-independent); require an explicit stop condition (the 'done' default was never used); correct the stop_condition docstring to the real vocabulary; tighten field docstrings and ordering. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: require agent and verifiers on Trace, default tools to empty agent: every producer sets a seat — the rollout its resolved config, the debug CLI its synthetic seat, and now the v0 bridge and validate CLI theirs — so the None guards at every consumer were dead weight. verifiers: stamped by default_factory at construction; stored records keep their serialized build. tools: the empty state was unreachable (dialects normalize [] to None to avoid clearing a recording), so None carried no signal over []. Bumps TRACE_VERSION to 5: old records without an agent no longer validate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: hoist TRACE_VERSION to the module top Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: tighten trace docstrings, require RunInfo.id Every consumer stamps a run id (the eval CLI its uuid, trainers their own), so the None default was unreachable. Docstrings across the trace models trimmed to the constraint they actually add; kept_tokens aligned with routed_experts' shape-first style. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: last trace.runtime accesses missed in the property removal Agent.interaction's two borrowed-runtime stamps and the dashboard's boot-vs-build stage probe still read the removed Trace.runtime passthrough; all three now read trace.agent.runtime. Verified live: eval with the rich dashboard renders and pushes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: migrate pre-v5 trace records on read A version-gated before-validator upgrades v4 records to the v5 shape (drop the explicit nulls v5 defaults now fill, seat records that predate the required agent, drop an id-less run stamp) so eval resume and replay keep reading old outputs. Current-version records validate strictly: a v5 record with tools=null is rejected. Delete the validator when v4 support is dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: restructure trace migration as chained per-version utils The migrate hook sits at the bottom of Trace and chains one _to_v<n> util per schema bump; the next bump adds _to_v6 and a new chain link. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: retroactive trace migrations back to v1 One to_v<n> util per historical schema bump, chained by the migrate hook: v1->v2 lifts node-level usage/finish_reason into synthesized ModelCalls (#2061), v2->v3 nests the flat agent identity and top-level runtime into AgentInfo (#2106), v3->v4 wraps float rewards as Reward(score, weight=1) preserving reward sums (#2119), v4->v5 as before. Steps copy what they mutate, so validating the same dict twice is stable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: drop trace record migrations, reset TRACE_VERSION to 1 The tightened schema restarts the version counter; pre-existing records are not loadable and old eval runs cannot be resumed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: migrate the standalone agent example and env docs off removed trace APIs * chore: drop the seat term from new comments, restore trimmed timing docstrings --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
eligotts
added a commit
that referenced
this pull request
Aug 6, 2026
Main already records these on ModelCall (#2061). They were reintroduced by merge conflict resolution and are unused on the node. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Closes RES-1085: per-call trace records for platform integration.
Trace.calls: list[ModelCall]— one lean typed record per real provider exchange, appended by the interception server on both the non-streaming and streaming (SSE relay) paths:node: index of the assistantMessageNodethis call committed (PendingTurn.commitnow returns it) — the link into the message graph. The call's conversation is not repeated on the record: it is exactly the linked node's root-to-self path, so linkage holds even under branching (compaction forks, retokenized prefixes)model: the model requested from the provider (equalsagent.modelby construction of the override; recorded per call because it's cheap and provable)sampling: the call's effective settings, translated by the dialect (Dialect.parse_sampling, next toparse_request/parse_response): a per-dialect whitelist (sampling_fields) of what shapes generation — decoding knobs, budgets/stops, reasoning effort, output contract — with native aliases mapped onto the canonicalSamplingConfigknobs (max_output_tokens→max_tokens,reasoning.effort→reasoning_effort) and dialect-specific keys (seed,tool_choice,response_format,parallel_tool_calls, …) riding as extras. Whitelisted, so payload / conversation state / tracking fields can never leak into the record by omission. Captures the eval-imposed knobs plus whatever the harness set that the eval left aloneendpoint: the provider path (/chat/completions//responses//v1/messages), i.e. which wire dialect the exchange spokefinish_reason+usage: exchange attributes, moved here fromMessageNodeexclusively.Trace.is_truncatedreads the last successful call;Trace.usageaggregates over calls; branches keep their token accounting (Trace.branchesattaches each branch's calls in path order,Branch.usage/last_usage/num_*read those), so the trace-levelnum_*_tokensprime-rl consumes and the platform push payload are unchanged; the v0 bridge records a per-step callerror: a failed exchange is recorded too — the error coupled to the call that raised it, with a real traceback for non-provider failures (cancellations included) and the upstream HTTP status onError.status_code(also populated on rollout-levelTrace.errorsnow)time: wall-clockTimeSpanof the exchange (node timestamps can't provide this: they are all stamped at commit)errorset, no node) and the successful retry (linked to its node). Deliberately not recorded: retries that replay or coalesce onto an earlier attempt (no provider exchange happened), aux routes (count_tokens), and judge calls (outside the agent's graph).callsis linear at ~250 B/call — the same 10-turn trace went 146 KB → 31 KB (calls121 KB → 2.5 KB, 8%).Example
Trace.callsfrom a 10-turn terminal-bench-2fix-gitrollout (first two and last call shown):[ { "node": 2, "model": "deepseek/deepseek-v4-flash", "sampling": { "temperature": 0.7, "parallel_tool_calls": false }, "endpoint": "/chat/completions", "finish_reason": "tool_calls", "time": { "start": 1784337093.274, "end": 1784337101.452 } }, { "node": 4, "model": "deepseek/deepseek-v4-flash", "sampling": { "temperature": 0.7, "parallel_tool_calls": false }, "endpoint": "/chat/completions", "finish_reason": "tool_calls", "time": { "start": 1784337128.306, "end": 1784337132.204 } }, { "node": 20, "model": "deepseek/deepseek-v4-flash", "sampling": { "temperature": 0.7, "parallel_tool_calls": false }, "endpoint": "/chat/completions", "finish_reason": "tool_calls", "time": { "start": 1784337382.416, "end": 1784337401.394 } } ]temperatureis the eval-injected knob;parallel_tool_callsis rlm's own request field the eval never touched — the per-callsamplingcaptures both.Verification
Linkage checked per call: successful calls point at a sampled assistant node, every sampled node is covered by exactly one call,
finish_reasonmatches the committed node,model/samplingpresent.deepseek/deepseek-v4-flash): reward 1.0, linkage OK.fix-git(rlm harness, prime VM runtime): 10-turn run — 10 calls, linkage OK, trace 31 KB withcallsat 2.5 KB (8%); earlier full runs: no-compaction (16 turns, reward 1.0) andsummarize_at_tokens=3000(40 calls, 87 nodes, 7 branches from compaction forks) — every call links to the right node on its branch.error(status_code=502) andnode=None; the retry linked to its committed node. Non-provider failures record a real traceback (formatted from the exception object — the record is written in afinally, where ambient exception state is gone).pytest tests/v1 -m "not e2e"green;test_single_turn(e2e) extended with per-call linkage assertions.Notes
OverlongPromptError.status_codenow defaults to 400 (the status the interception server surfaces for overlong prompts) instead of inheriting the 502;model_errorkeeps the provider's real status when the failure carried one.MessageNode.finish_reasonstays on the node although it is conceptually per-call (now also onModelCall): removing it would make previously persisted traces unreadable under strict validation — a trace-version-bump follow-up if wanted. Same reasoning keepsusageon the node (it anchors per-branch token accounting and the v0 bridge).prime-rl consumes the new field with its next verifiers re-pin; the
callsfield itself is additive.🤖 Generated with Claude Code
Note
Medium Risk
Touches core interception and trace schema (v2 bump) on every model turn; behavior is additive for consumers but changes where usage/truncation are sourced and how errors are recorded.
Overview
Adds
Trace.calls— oneModelCallper real provider exchange (non-stream and SSE relay), bumped toTRACE_VERSION = 2. Each record links to the committed assistant node vianode, capturesmodel, dialect-whitelistedsampling(parse_samplingon chat/Anthropic/Responses),endpoint,finish_reason,usage, wall-clocktime, and couplederror(withstatus_codeon trace errors). The interception server appends calls inrecord_callfrom afinallyon both paths;PendingTurn.commitnow returns the assistant node id.finish_reasonandusageare no longer written on newMessageNodecommits;Trace/Branchtoken accounting,is_truncated, and branchcallsall read fromcallsinstead. Dialects gainsampling_fieldswhitelists and format-specific alias normalization (e.g.max_completion_tokens, Anthropicoutput_config.effort, Responsesreasoning.effort). Anthropic responses use widenedModdedAnthropicMessagefor gatewayservice_tier;OverlongPromptErrordefaults to 400 with provider status preserved viamodel_error. The v0 bridge synthesizes minimalModelCallrows per trajectory step. E2Etest_single_turnasserts call↔sampled-node linkage.Reviewed by Cursor Bugbot for commit 5062742. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Summary
Closes RES-1085: per-call trace records for platform integration.
Trace.calls: list[ModelCall]— one lean typed record per real provider exchange, appended by the interception server on both the non-streaming and streaming (SSE relay) paths:node: index of the assistantMessageNodethis call committed (PendingTurn.commitnow returns it) — the link into the message graph. The call's conversation is not repeated on the record: it is exactly the linked node's root-to-self path, so linkage holds even under branching (compaction forks, retokenized prefixes)model: the model requested from the provider (equalsagent.modelby construction of the override; recorded per call because it's cheap and provable)sampling: the call's effective settings, translated by the dialect (Dialect.parse_sampling, next toparse_request/parse_response): a per-dialect whitelist (sampling_fields) of what shapes generation — decoding knobs, budgets/stops, reasoning effort, output contract — with native aliases mapped onto the canonicalSamplingConfigknobs (max_output_tokens→max_tokens,reasoning.effort→reasoning_effort) and dialect-specific keys (seed,tool_choice,response_format,parallel_tool_calls, …) riding as extras. Whitelisted, so payload / conversation state / tracking fields can never leak into the record by omission. Captures the eval-imposed knobs plus whatever the harness set that the eval left aloneendpoint: the provider path (/chat/completions//responses//v1/messages), i.e. which wire dialect the exchange spokefinish_reason+usage: exchange attributes, moved here fromMessageNodeexclusively.Trace.is_truncatedreads the last successful call;Trace.usageaggregates over calls; branches keep their token accounting (Trace.branchesattaches each branch's calls in path order,Branch.usage/last_usage/num_*read those), so the trace-levelnum_*_tokensprime-rl consumes and the platform push payload are unchanged; the v0 bridge records a per-step callerror: a failed exchange is recorded too — the error coupled to the call that raised it, with a real traceback for non-provider failures (cancellations included) and the upstream HTTP status onError.status_code(also populated on rollout-levelTrace.errorsnow)time: wall-clockTimeSpanof the exchange (node timestamps can't provide this: they are all stamped at commit)errorset, no node) and the successful retry (linked to its node). Deliberately not recorded: retries that replay or coalesce onto an earlier attempt (no provider exchange happened), aux routes (count_tokens), and judge calls (outside the agent's graph).callsis linear at ~250 B/call — the same 10-turn trace went 146 KB → 31 KB (calls121 KB → 2.5 KB, 8%).Example
Trace.callsfrom a 10-turn terminal-bench-2fix-gitrollout (first two and last call shown):[ { "node": 2, "model": "deepseek/deepseek-v4-flash", "sampling": { "temperature": 0.7, "parallel_tool_calls": false }, "endpoint": "/chat/completions", "finish_reason": "tool_calls", "time": { "start": 1784337093.274, "end": 1784337101.452 } }, { "node": 4, "model": "deepseek/deepseek-v4-flash", "sampling": { "temperature": 0.7, "parallel_tool_calls": false }, "endpoint": "/chat/completions", "finish_reason": "tool_calls", "time": { "start": 1784337128.306, "end": 1784337132.204 } }, { "node": 20, "model": "deepseek/deepseek-v4-flash", "sampling": { "temperature": 0.7, "parallel_tool_calls": false }, "endpoint": "/chat/completions", "finish_reason": "tool_calls", "time": { "start": 1784337382.416, "end": 1784337401.394 } } ]temperatureis the eval-injected knob;parallel_tool_callsis rlm's own request field the eval never touched — the per-callsamplingcaptures both.Verification
Linkage checked per call: successful calls point at a sampled assistant node, every sampled node is covered by exactly one call,
finish_reasonmatches the committed node,model/samplingpresent.deepseek/deepseek-v4-flash): reward 1.0, linkage OK.fix-git(rlm harness, prime VM runtime): 10-turn run — 10 calls, linkage OK, trace 31 KB withcallsat 2.5 KB (8%); earlier full runs: no-compaction (16 turns, reward 1.0) andsummarize_at_tokens=3000(40 calls, 87 nodes, 7 branches from compaction forks) — every call links to the right node on its branch.error(status_code=502) andnode=None; the retry linked to its committed node. Non-provider failures record a real traceback (formatted from the exception object — the record is written in afinally, where ambient exception state is gone).pytest tests/v1 -m "not e2e"green;test_single_turn(e2e) extended with per-call linkage assertions.Notes
OverlongPromptError.status_codenow defaults to 400 (the status the interception server surfaces for overlong prompts) instead of inheriting the 502;model_errorkeeps the provider's real status when the failure carried one.MessageNode.finish_reasonstays on the node although it is conceptually per-call (now also onModelCall): removing it would make previously persisted traces unreadable under strict validation — a trace-version-bump follow-up if wanted. Same reasoning keepsusageon the node (it anchors per-branch token accounting and the v0 bridge).prime-rl consumes the new field with its next verifiers re-pin; the
callsfield itself is additive.🤖 Generated with Claude Code
Changes since #2061 opened
ModelCall.statusfield toError.status_codefield in trace recording [5062742]ModelCallrecords [cf18f49]callsrecords metadata [a02c7f5]samplingfield in theModelCallmodel fromSamplingConfig | NonetoSampling | Nonewithin theverifiers.v1.tracemodule, including adding the correspondingSamplingimport fromverifiers.v1.types[ce55ead]callsfield in theBranchmodel within theverifiers.v1.tracemodule [ce55ead]