Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b705a04
feat(v1): name an env, and the cohort an episode was planned in
mikasenghaas Aug 4, 2026
c2765a1
chore(v1): trim the new docstrings, make EnvInfo.name optional
mikasenghaas Aug 4, 2026
507c4fe
fix(v1): a resumed run rejoins the group it left
mikasenghaas Aug 4, 2026
98cdf64
revert(v1): don't mint groups in the eval CLI
mikasenghaas Aug 4, 2026
50f2297
feat(v1): the train run records the policy it came from, and Episode.…
mikasenghaas Aug 4, 2026
d3be61d
feat(v1): an eval run records the policy version it measures
mikasenghaas Aug 4, 2026
c12e77b
feat(v1): online eval belongs to the training run
mikasenghaas Aug 4, 2026
70490aa
chore(v1): export EnvInfo
mikasenghaas Aug 4, 2026
8c318d0
feat(v1): a run records the policy span, and derives staleness from it
mikasenghaas Aug 4, 2026
def0d5a
chore(v1): drop Episode.to_record, plainer wording on the policy span
mikasenghaas Aug 4, 2026
54eef2c
docs(v1): say what off_policy_steps measures for each kind
mikasenghaas Aug 4, 2026
260b6f1
feat(v1)!: what an episode is to its run is a nested, discriminated m…
mikasenghaas Aug 4, 2026
66dccbf
chore(v1): drop the GroupInfo docstring
mikasenghaas Aug 4, 2026
9576a5a
fix(v1): an eval's off-policy is its drift, not its distance from a step
mikasenghaas Aug 4, 2026
eddc781
refactor(v1): the run types live with the episode they describe
mikasenghaas Aug 4, 2026
a79e674
chore(v1): docstrings in the file's own register
mikasenghaas Aug 4, 2026
9313f4b
refactor(v1)!: the policy span belongs to the episode's metadata
mikasenghaas Aug 5, 2026
3a38df5
revert(v1): drop GroupInfo from the episode
mikasenghaas Aug 5, 2026
306a95f
fix(v1): keep raw node tensors out of the written episode
mikasenghaas Aug 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion verifiers/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from verifiers.v1.configs.taskset import TasksetConfig
from verifiers.v1.env import Env
from verifiers.v1.envs.single_agent import SingleAgentEnv, SingleAgentEnvConfig
from verifiers.v1.episode import Episode, WireEpisode
from verifiers.v1.episode import Episode, GroupInfo, WireEpisode
from verifiers.v1.errors import (
EnvError,
HarnessError,
Expand Down Expand Up @@ -194,6 +194,7 @@
"WireTrace",
"Reward",
"Episode",
"GroupInfo",
"WireEpisode",
"TRACE_VERSION",
"AgentInfo",
Expand Down
13 changes: 10 additions & 3 deletions verifiers/v1/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
_declared_agent_configs,
default_agent_harness,
)
from verifiers.v1.episode import EnvInfo, Episode
from verifiers.v1.episode import EnvInfo, Episode, GroupInfo
from verifiers.v1.errors import EnvError, boundary
from verifiers.v1.harness import Harness, HarnessConfig
from verifiers.v1.interception import (
Expand Down Expand Up @@ -56,6 +56,8 @@ class RunSlot:
traces: list[Trace] = field(default_factory=list)
episode: Episode | None = None
done: bool = False
group: GroupInfo | None = None
"""The cohort this slot was planned in, stamped onto its episode when it lands."""

@classmethod
def finished(cls, episode: Episode) -> "RunSlot":
Expand All @@ -64,6 +66,7 @@ def finished(cls, episode: Episode) -> "RunSlot":
traces=list(episode.traces),
episode=episode,
done=True,
group=episode.group,
)


Expand Down Expand Up @@ -294,10 +297,13 @@ async def run_episode(
return episode

def slots(self, task: Task, n: int = 1) -> list[RunSlot]:
"""Plan `n` independent episodes of `task` (`-r n`): nothing couples them."""
"""Plan `n` independent episodes of `task` (`-r n`). They run independently, but they
are one cohort — the attempts a consumer compares with each other — so they share a
`GroupInfo`, which lands on each episode."""
if n < 1:
raise ValueError("a task needs at least one rollout (n >= 1)")
return [RunSlot(task) for _ in range(n)]
group = GroupInfo(size=n)
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Resolved
return [RunSlot(task, group=group) for _ in range(n)]

async def run_slot(
self,
Expand Down Expand Up @@ -330,6 +336,7 @@ def discard(trace: Trace) -> None:
)

episode = await run_episode_with_retry(attempt, self.config.retries)
episode.group = slot.group
slot.traces = list(episode.traces)
slot.episode = episode
slot.done = True
Expand Down
21 changes: 21 additions & 0 deletions verifiers/v1/episode.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ class EnvInfo(BaseModel):
id: str = ""
"""`EnvConfig.env_id`, e.g. `agentic-judge+gsm8k-v1`."""

name: str = ""
"""What the caller knows this env by, when that differs from `id` — the key it was
configured under. A run over several envs keys its metrics by this; a run over one
can leave it empty."""


class GroupInfo(BaseModel):
"""The cohort an episode belongs to: the episodes planned together from one task, which a
consumer compares against each other (pass@k over the group, a GRPO baseline within it).

Its `id` is per cohort, not per task: the same task planned again later is a new group, so
two rounds of it never merge into one comparison."""

id: str = Field(default_factory=lambda: uuid.uuid4().hex)

size: int = 1
"""How many episodes were planned in it — the `k` of `-r k`, before any of them fail."""


class Episode(BaseModel, Generic[DataT, StateT, AgentConfigT]):
"""The artifact Env.run produces. Contains multiple agents' traces."""
Expand All @@ -26,6 +44,9 @@ class Episode(BaseModel, Generic[DataT, StateT, AgentConfigT]):

env: EnvInfo = Field(default_factory=EnvInfo)
"""The env that produced this episode."""
group: GroupInfo | None = None
"""The cohort it was planned in, when the producer planned one — several episodes of the
same task, meant to be compared with each other."""
run: RunInfo | None = None
"""The run this episode belongs to (eval or train), consumer-stamped. It lives here rather than
on each trace because the episode is what a consumer dispatches, and an episode that produced
Expand Down
Loading