verifiers 0.2.2.dev73__py3-none-any.whl → 0.2.2.dev74__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -332,8 +332,8 @@ def Progress(
332
332
 
333
333
  def _score_segments(traces: list[Trace], source: str) -> str | None:
334
334
  """`name mean` segments for every reward/metric key seen across `traces`,
335
- first-seen order (a trace records only the functions that ran for it, so keys
336
- can vary); None when no trace recorded anything."""
335
+ first-seen order (a trace carries only its own task's signals — unscored ones
336
+ seeded as `None` — so keys can vary); None when no trace recorded anything."""
337
337
  names: list[str] = []
338
338
  for trace in traces:
339
339
  names.extend(n for n in getattr(trace, source) if n not in names)
@@ -347,11 +347,13 @@ def _score_segments(traces: list[Trace], source: str) -> str | None:
347
347
 
348
348
 
349
349
  def _score(trace: Trace, source: str, name: str) -> float:
350
- """Rewards carry raw score + weight; the breakdown shows the raw score."""
350
+ """Rewards carry raw score + weight; the breakdown shows the raw score. A missing
351
+ or unscored (seeded `None`) entry reads as 0.0."""
351
352
  if source == "rewards":
352
353
  reward = trace.rewards.get(name)
353
354
  return reward.score if reward is not None else 0.0
354
- return trace.metrics.get(name, 0.0)
355
+ value = trace.metrics.get(name)
356
+ return value if value is not None else 0.0
355
357
 
356
358
 
357
359
  def _breakdown(scored: list[Trace], done: list[Trace]) -> Table | None:
@@ -398,7 +398,8 @@ class AgenticJudgeEnv(vf.Env[AgenticJudgeEnvConfig]):
398
398
  solution.record_metric(f"judge/{criterion.name}", scores[criterion.name])
399
399
  if self.config.score.task_weight != 1.0:
400
400
  for reward in solution.rewards.values():
401
- reward.weight *= self.config.score.task_weight
401
+ if reward is not None:
402
+ reward.weight *= self.config.score.task_weight
402
403
  total = sum(criterion.weight for criterion in criteria)
403
404
  reward = sum(c.weight * scores[c.name] for c in criteria) / total
404
405
  solution.record_reward("judge", reward, weight=self.config.score.judge_weight)
verifiers/v1/harness.py CHANGED
@@ -12,7 +12,12 @@ from verifiers.v1.errors import HarnessError, SandboxError, boundary
12
12
  from verifiers.v1.runtimes import ProgramResult, Runtime
13
13
  from verifiers.v1.task import TaskData
14
14
  from verifiers.v1.types import Messages
15
- from verifiers.v1.utils.decorators import discover_decorated, invoke_all
15
+ from verifiers.v1.utils.decorators import (
16
+ discover_decorated,
17
+ invoke_all,
18
+ seed,
19
+ unseed,
20
+ )
16
21
 
17
22
  if TYPE_CHECKING:
18
23
  # Annotation-only: `Trace` appears in signatures only, so this module stays
@@ -155,10 +160,12 @@ class Harness(ABC, Generic[ConfigT]):
155
160
  `runtime`) and can read what the harness left behind in the runtime."""
156
161
  available = {"task": trace.task.data, "trace": trace, "runtime": runtime}
157
162
  fns = discover_decorated(self, "metric")
163
+ seed(trace.metrics, (fn.__name__ for fn in fns))
158
164
  async with boundary(HarnessError, f"harness {self.config.id!r} metric"):
159
165
  results = await invoke_all(fns, available)
160
166
  for fn, result in zip(fns, results):
161
167
  if isinstance(result, Mapping):
168
+ unseed(trace.metrics, fn.__name__)
162
169
  trace.record_metrics(result)
163
170
  else:
164
171
  trace.record_metric(fn.__name__, result)
verifiers/v1/task.py CHANGED
@@ -23,7 +23,12 @@ from verifiers.v1.errors import TaskError, boundary
23
23
  from verifiers.v1.state import StateT
24
24
  from verifiers.v1.types import Messages, content_text
25
25
  from verifiers.v1.utils.artifacts import Artifact
26
- from verifiers.v1.utils.decorators import discover_decorated, invoke_all
26
+ from verifiers.v1.utils.decorators import (
27
+ discover_decorated,
28
+ invoke_all,
29
+ seed,
30
+ unseed,
31
+ )
27
32
  from verifiers.v1.utils.generic import concrete_type
28
33
 
29
34
  if TYPE_CHECKING:
@@ -180,31 +185,36 @@ class Task(Generic[DataT, StateT, ConfigT]):
180
185
  judge for judge in judges if not requires_runtime(judge.score)
181
186
  ]
182
187
 
188
+ seed(trace.metrics, (fn.__name__ for fn in metrics))
189
+ seed(trace.rewards, (fn.__name__ for fn in rewards))
190
+ seed(trace.rewards, (judge.reward_name for judge in judges))
191
+
183
192
  metric_results = await invoke_all(metrics, available)
184
193
  for fn, result in zip(metrics, metric_results):
185
194
  if isinstance(result, Mapping):
195
+ unseed(trace.metrics, fn.__name__)
186
196
  trace.record_metrics(result)
187
197
  else:
188
198
  trace.record_metric(fn.__name__, result)
189
199
  reward_results = await invoke_all(rewards, available)
190
200
  for fn, result in zip(rewards, reward_results):
191
201
  weight = getattr(fn, "_vf_weight", 1.0)
192
- items = (
193
- result.items()
194
- if isinstance(result, Mapping)
195
- else [(fn.__name__, result)]
196
- )
202
+ if isinstance(result, Mapping):
203
+ unseed(trace.rewards, fn.__name__)
204
+ items = result.items()
205
+ else:
206
+ items = [(fn.__name__, result)]
197
207
  for key, value in items:
198
208
  trace.record_reward(key, value, weight)
199
209
  judge_results = await invoke_all(
200
210
  [judge.score for judge in judges], available
201
211
  )
202
212
  for judge, result in zip(judges, judge_results):
203
- items = (
204
- result.items()
205
- if isinstance(result, Mapping)
206
- else [(judge.reward_name, result)]
207
- )
213
+ if isinstance(result, Mapping):
214
+ unseed(trace.rewards, judge.reward_name)
215
+ items = result.items()
216
+ else:
217
+ items = [(judge.reward_name, result)]
208
218
  for key, value in items:
209
219
  trace.record_reward(key, value, judge.config.weight)
210
220
 
verifiers/v1/trace.py CHANGED
@@ -319,10 +319,11 @@ class Trace(BaseModel, Generic[DataT, StateT, AgentConfigT]):
319
319
  calls: list[ModelCall] = Field(default_factory=list)
320
320
  """Every model call; automatically recorded at intercept time + linked into `nodes`."""
321
321
 
322
- rewards: dict[str, Reward] = Field(default_factory=dict)
323
- """Named, weighted rewards"""
324
- metrics: dict[str, float] = Field(default_factory=dict)
325
- """Unweighted, named metrics"""
322
+ rewards: dict[str, Reward | None] = Field(default_factory=dict)
323
+ """Named, weighted rewards; `None` means scoring didn't run (e.g. because of a
324
+ preceding error)."""
325
+ metrics: dict[str, float | None] = Field(default_factory=dict)
326
+ """Unweighted, named metrics; `None` as in `rewards`."""
326
327
  info: dict[str, Any] = Field(default_factory=dict)
327
328
  """Scratch space for task-specific metadata."""
328
329
  state: StateT = Field(default_factory=State, exclude=True)
@@ -346,7 +347,7 @@ class Trace(BaseModel, Generic[DataT, StateT, AgentConfigT]):
346
347
 
347
348
  @property
348
349
  def reward(self) -> float:
349
- return sum(r.value for r in self.rewards.values())
350
+ return sum(r.value for r in self.rewards.values() if r is not None)
350
351
 
351
352
  @property
352
353
  def has_error(self) -> bool:
@@ -2,7 +2,7 @@
2
2
 
3
3
  import asyncio
4
4
  import inspect
5
- from collections.abc import Callable
5
+ from collections.abc import Callable, Iterable
6
6
  from typing import Any, TypeVar, overload
7
7
 
8
8
  F = TypeVar("F", bound=Callable[..., Any])
@@ -38,6 +38,22 @@ async def invoke_all(
38
38
  return await asyncio.gather(*(invoke(fn, available) for fn in fns))
39
39
 
40
40
 
41
+ def seed(scores: dict[str, Any], names: Iterable[str]) -> None:
42
+ """Mark every expected scoring key as unscored (`None`) before invocation, so a
43
+ trace records which signals should have run even when scoring fails midway.
44
+ Overwrites: a re-scoring attempt resets its own names, so stale values from a
45
+ previous attempt never read as fresh."""
46
+ for name in names:
47
+ scores[name] = None
48
+
49
+
50
+ def unseed(scores: dict[str, Any], name: str) -> None:
51
+ """Drop a still-unscored seed — a handler returning keyed scores records under
52
+ its result's keys, not its function name."""
53
+ if scores.get(name) is None:
54
+ scores.pop(name, None)
55
+
56
+
41
57
  def mark(attr: str, **extra: Any) -> Callable[[F], F]:
42
58
  def decorator(f: F) -> F:
43
59
  setattr(f, attr, True)
@@ -93,7 +93,8 @@ def trace_to_sample(
93
93
  # Flatten sub-rewards to top-level keys the way v0 does (raw scores, as v0's
94
94
  # per-function outputs were); env metrics stay nested.
95
95
  for name, reward in trace.rewards.items():
96
- sample.setdefault(name, reward.score)
96
+ if reward is not None:
97
+ sample.setdefault(name, reward.score)
97
98
  return sample
98
99
 
99
100
 
@@ -128,8 +129,15 @@ def _run_metrics(episodes: list[Episode], traces: list[Trace]) -> dict[str, Any]
128
129
  sums: dict[str, float] = {}
129
130
  counts: dict[str, int] = {}
130
131
  for trace in scored:
131
- scores = {name: reward.score for name, reward in trace.rewards.items()}
132
- for name, value in {**scores, **trace.metrics}.items():
132
+ scores = {
133
+ name: reward.score
134
+ for name, reward in trace.rewards.items()
135
+ if reward is not None
136
+ }
137
+ metrics = {
138
+ name: value for name, value in trace.metrics.items() if value is not None
139
+ }
140
+ for name, value in {**scores, **metrics}.items():
133
141
  sums[name] = sums.get(name, 0.0) + value
134
142
  counts[name] = counts.get(name, 0) + 1
135
143
  n = len(scored)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: verifiers
3
- Version: 0.2.2.dev73
3
+ Version: 0.2.2.dev74
4
4
  Summary: Verifiers: Environments for LLM Reinforcement Learning
5
5
  Project-URL: Homepage, https://github.com/primeintellect-ai/verifiers
6
6
  Project-URL: Documentation, https://github.com/primeintellect-ai/verifiers
@@ -173,15 +173,15 @@ verifiers/v1/env.py,sha256=g6fpoG-Z9lM3vTKrrU-QRJY3S88_y9zsPHwBvfs5vdY,17865
173
173
  verifiers/v1/episode.py,sha256=YtuRTV_PpwDglXFeN6UNg8IswpP1HseFLCYtascGMNI,3218
174
174
  verifiers/v1/errors.py,sha256=kQeEPX06TwAIuiz7MlWFYirrpKbx5STi6A5b_dtQQoA,6885
175
175
  verifiers/v1/graph.py,sha256=aBAzh1Ibp3klKNYjvzNyJSbjV8Oi5TJ791dN7o6ex1E,29070
176
- verifiers/v1/harness.py,sha256=i3pYHmdVPD7RtfZ-MAHlctaoLO8FxazCPmNy7mtPoSk,10954
176
+ verifiers/v1/harness.py,sha256=eKuqvYbQjnCMavkiYWZQDHJgH08m6SJ4PuevPuShjS0,11097
177
177
  verifiers/v1/judge.py,sha256=Pvr0C41ah1qkNSZ0WXdkDvBMfv5YSfY1REHncwlJ8cg,9346
178
178
  verifiers/v1/legacy.py,sha256=eLsFiwihOMh4zS_u1V7DXF22r9beoG4k2JGwNEcSPfg,22872
179
179
  verifiers/v1/rollout.py,sha256=s1f1VZAAAI89j3y9ceq_E4mvX0F7cFQAnyx27SZsjvA,17881
180
180
  verifiers/v1/session.py,sha256=tNAraQfiJKpWqgBFNLyPhfHiw0JhzzkyOfRcKWGnR2Q,6939
181
181
  verifiers/v1/state.py,sha256=EckF2bWp-vV4b1jYJ9sLI5xrfGuI5spIgYYwW926toI,595
182
- verifiers/v1/task.py,sha256=D5PKxu6WlqCjv7hE311aaNvBA3QWVWDuPIEwDsq-J7g,9075
182
+ verifiers/v1/task.py,sha256=jwMKiKlMtksTd8j2dcDlFtb6LC8BRE-N5jkNXlMp2jc,9457
183
183
  verifiers/v1/taskset.py,sha256=fp2E0IEhL_Ybj9cegZwljfTmW27p_30zTWHFKw_kXE4,4374
184
- verifiers/v1/trace.py,sha256=_bGHOrE6fRLGm0JYc0PrrrO3lyr-qqOF5Y1bHfViwR0,19347
184
+ verifiers/v1/trace.py,sha256=Z4uK-lYAbl-6CHFhTo-gadHnbi63LC0sJtUERC_m5ss,19477
185
185
  verifiers/v1/types.py,sha256=1PxamJspmoTc3OlFZAwH6o_2i_Tg9ZNvHxttIHWIxq8,8948
186
186
  verifiers/v1/acp/__init__.py,sha256=9SYCFtzGUM_wH98ldpVLcFhoq2M999jbG0tU5ODY70U,2297
187
187
  verifiers/v1/acp/_runner.py,sha256=BcYaNZHuawzCgxOVdhiF6PY_B1HMxIA1MwHy0mOzhSk,7740
@@ -196,7 +196,7 @@ verifiers/v1/cli/serve.py,sha256=_mdJaHVVe4pXzcpAot0ugcmiN3ra2gS4GrGmLghnX5M,286
196
196
  verifiers/v1/cli/validate.py,sha256=7ax6FqIzNBSYjd3JXK4v7Vbq34Rozyh1OvGpYnGqlSg,10266
197
197
  verifiers/v1/cli/dashboard/__init__.py,sha256=v-baMxQuWxOCsbU7-p_jj2Q9BUnTN-TWi1q2hK6rU2s,198
198
198
  verifiers/v1/cli/dashboard/base.py,sha256=kUP93zJSIVLptSbnWX6MOy8kGg63fpeAzPqakCpPyDc,3547
199
- verifiers/v1/cli/dashboard/eval.py,sha256=nQ24ptQ_6ylmrB9VoZkgga1CLp_7sXZueC_MC4O9Dow,33452
199
+ verifiers/v1/cli/dashboard/eval.py,sha256=4j2YCPyzNHDT7d8MwqYR3C906lzwDVAd3FxgFYFgsw0,33588
200
200
  verifiers/v1/cli/dashboard/replay.py,sha256=CvRVaf0dUum5v0IzPQbFPPikBQmTrkMeaunGfVMTWHc,2755
201
201
  verifiers/v1/cli/dashboard/validate.py,sha256=rLsQ_31DjTIZpC_VO7zSG41ncUEBxqCx8mL9nTnGiMc,3650
202
202
  verifiers/v1/cli/eval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -234,7 +234,7 @@ verifiers/v1/dialects/chat.py,sha256=R_otqi5N0snB1QVKIYloNeLHR9kc7OmI06HYg1ir_Lw
234
234
  verifiers/v1/dialects/responses.py,sha256=ismnxUikPcPUIm80FUTPBbj-Cn1e0yGAsISMEseAWRc,13679
235
235
  verifiers/v1/envs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
236
236
  verifiers/v1/envs/agentic_judge/__init__.py,sha256=6vwtMCQ_cuvuubCF5-nrE5W7gGgzE-LIPKorkM5DXdw,309
237
- verifiers/v1/envs/agentic_judge/env.py,sha256=jreQPnbp8pJQSyZW0Me2IW-JipYsczqgohfM4-JCz9c,16968
237
+ verifiers/v1/envs/agentic_judge/env.py,sha256=6QrfuIlg-9MkcqIn5_6F3qJ6ddLJNaeYsZRJC9EzGkw,17011
238
238
  verifiers/v1/envs/best_of_n/__init__.py,sha256=mubASiwXMNIhnQFDccNAc7yZ0OFsOVK8hH8WIx9b2A4,119
239
239
  verifiers/v1/envs/best_of_n/env.py,sha256=KEUJBCVqblnXP_lEwm3mjGdaikBMkecz8UjOFPBhxc0,1987
240
240
  verifiers/v1/envs/single_agent/__init__.py,sha256=r0edAc_6gtHBhPzeBGVcdHZZqSdCPZnLMje6eVgyz5U,138
@@ -324,7 +324,7 @@ verifiers/v1/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
324
324
  verifiers/v1/utils/aio.py,sha256=0yNFOqB2oO6Qktc6NI_ZSAGuo1aHsIK_wuT1BtmoZ3I,1532
325
325
  verifiers/v1/utils/artifacts.py,sha256=vOzbPSuMirUecyylrhvLZ_vAcTd6JTJBztGPDQ0ANNk,6706
326
326
  verifiers/v1/utils/compile.py,sha256=Oh4hDVRNsql_BSOMh4kDEuze9_ZF17Q7uzBhhU70UZA,5663
327
- verifiers/v1/utils/decorators.py,sha256=9CcCwgZlRjVGPqT5G1CFJUHP4OIMuBGkUJZkcI6dnbk,3865
327
+ verifiers/v1/utils/decorators.py,sha256=DG_K3HMiWqRHsY1bA7FJD3uTHUVuLw6SUJV-PnNRifA,4538
328
328
  verifiers/v1/utils/format.py,sha256=NfQo9M5KMzWCZpQaet5YtsJx56vCKAZPfbjZZJLJhhM,2187
329
329
  verifiers/v1/utils/generic.py,sha256=2VIN9RapMO9pVkFHB1UKUHck1vlTJ1pjfad2NzxuuXA,2055
330
330
  verifiers/v1/utils/git.py,sha256=MTRinOvG4EddfjdahHkFZUihHmhJ37n5ROtgjzjE1NA,8366
@@ -334,12 +334,12 @@ verifiers/v1/utils/interrupt.py,sha256=F-KKhc5ndPJJfhd3SuMqyqhXhA32FhCRy5KWFJpEo
334
334
  verifiers/v1/utils/loaders.py,sha256=NSIBdNU3JVeyddL-dM4dJt6khCHyjCgzJuFtLrgiwD0,9989
335
335
  verifiers/v1/utils/logging.py,sha256=OcMHA6NsYux3oIzjPuI95rDWmFBHNcHDjZeNIhXTX-Y,2084
336
336
  verifiers/v1/utils/memory.py,sha256=ZkIvGk6uITAH5sKon65LifKPbvZr8mJ__PVc23FZpOQ,1835
337
- verifiers/v1/utils/platform.py,sha256=MWZmvyj0iyzNIO0xRhlkCXtFF1plq6EJi8rWlvrFtvI,11363
337
+ verifiers/v1/utils/platform.py,sha256=56Ixmk1SER6q5LvzyYcA0hgzGpzhxKZL8Hiqp4c_XVc,11576
338
338
  verifiers/v1/utils/retries.py,sha256=Y2ZgrAjn-qNkRKZP_RVNL_7EK0iaRcZeCa404lpGYi4,5417
339
339
  verifiers/v1/utils/score.py,sha256=-R5Cog14r_tJ6Q_oOfGr5VPAm2CCUhofYZB6B9lm4wM,5787
340
340
  verifiers/v1/utils/version.py,sha256=-obEo_-l9-D8FLef4hYxncOe-uJpxrM1g2Hig_37Sgs,1607
341
- verifiers-0.2.2.dev73.dist-info/METADATA,sha256=DU0GJRGJc9sAtCc2vIZa5E-O-4ubaCtIXJiCZQHs2rc,4545
342
- verifiers-0.2.2.dev73.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
343
- verifiers-0.2.2.dev73.dist-info/entry_points.txt,sha256=dF82JUYEFslR1AOW8LZfuBWeZeQoyX4wCRrduklg8-I,551
344
- verifiers-0.2.2.dev73.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
345
- verifiers-0.2.2.dev73.dist-info/RECORD,,
341
+ verifiers-0.2.2.dev74.dist-info/METADATA,sha256=mYHTNMUxzf9tA02Dz787WOdGwU54MPGEJBgTSFG6aSs,4545
342
+ verifiers-0.2.2.dev74.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
343
+ verifiers-0.2.2.dev74.dist-info/entry_points.txt,sha256=dF82JUYEFslR1AOW8LZfuBWeZeQoyX4wCRrduklg8-I,551
344
+ verifiers-0.2.2.dev74.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
345
+ verifiers-0.2.2.dev74.dist-info/RECORD,,