verifiers 0.2.2.dev112__py3-none-any.whl → 0.3.1.dev4__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.
verifiers/v1/episode.py CHANGED
@@ -1,14 +1,14 @@
1
1
  """The episode — one run's traces plus their shared standing, whole."""
2
2
 
3
3
  import uuid
4
- from typing import Generic
4
+ from typing import Any, Generic
5
5
 
6
6
  from pydantic import BaseModel, Field
7
7
 
8
8
  from verifiers.v1.configs.agent import WireAgentConfig
9
9
  from verifiers.v1.state import State, StateT
10
10
  from verifiers.v1.task import DataT, WireTaskData
11
- from verifiers.v1.trace import AgentConfigT, Error, Trace
11
+ from verifiers.v1.trace import EXCLUDE_FIELDS, AgentConfigT, Error, Trace
12
12
  from verifiers.v1.types import Usage
13
13
 
14
14
 
@@ -72,6 +72,14 @@ class Episode(BaseModel, Generic[DataT, StateT, AgentConfigT]):
72
72
  grouped.setdefault(trace.agent.name, []).append(trace)
73
73
  return grouped
74
74
 
75
+ def to_record(self) -> dict[str, Any]:
76
+ """JSON record without raw trace tensors, which remain on the msgpack wire."""
77
+ return self.model_dump(
78
+ mode="json",
79
+ exclude={"traces": {"__all__": EXCLUDE_FIELDS}},
80
+ exclude_none=True,
81
+ )
82
+
75
83
  @classmethod
76
84
  def of(cls, trace: Trace, env: str = "") -> "Episode":
77
85
  """The single-agent record: one trace as its own episode."""
@@ -1,8 +1,8 @@
1
1
  """Push a finished eval run to the Prime Intellect platform (`--no-push` to skip).
2
2
 
3
- Converts each v1 `Trace` to the platform's (v0) eval-sample schema and uploads the
4
- run over the `/evaluations/` API (create -> push samples -> finalize) — the same
5
- contract as `prime eval push`, done inline at the end of a run. Auth + base URL
3
+ Uploads one sample per v1 `Episode` over the `/evaluations/` API (create -> push
4
+ samples -> finalize). Each sample keeps the complete native Episode as its source
5
+ of truth and includes a flat summary for older Platform consumers. Auth + base URL
6
6
  come from `$PRIME_API_KEY` / `~/.prime/config.json`.
7
7
  """
8
8
 
@@ -27,6 +27,17 @@ DEFAULT_FRONTEND_URL = "https://app.primeintellect.ai"
27
27
  _MAX_SAMPLES_PAYLOAD_BYTES = 25 * 1024 * 1024
28
28
 
29
29
 
30
+ def json_bytes(value: Any) -> int:
31
+ return len(
32
+ json.dumps(
33
+ value,
34
+ ensure_ascii=False,
35
+ separators=(",", ":"),
36
+ allow_nan=False,
37
+ ).encode("utf-8")
38
+ )
39
+
40
+
30
41
  @dataclass
31
42
  class PushState:
32
43
  """Mutable upload status shared with the dashboard."""
@@ -98,7 +109,7 @@ def trace_to_sample(
98
109
  return sample
99
110
 
100
111
 
101
- def _creds() -> tuple[str | None, str, str, str | None]:
112
+ def credentials() -> tuple[str | None, str, str, str | None]:
102
113
  """(api_key, api_base, frontend_url, team_id) from env vars / `~/.prime/config.json`."""
103
114
  cfg = load_prime_config()
104
115
  api_key = os.getenv("PRIME_API_KEY") or cfg.get("api_key")
@@ -118,7 +129,7 @@ def _creds() -> tuple[str | None, str, str, str | None]:
118
129
  return api_key, base, frontend, team_id
119
130
 
120
131
 
121
- def _run_metrics(episodes: list[Episode], traces: list[Trace]) -> dict[str, Any]:
132
+ def run_metrics(episodes: list[Episode], traces: list[Trace]) -> dict[str, Any]:
122
133
  """Run-level aggregates as v0's `GenerateMetadata`. Rewards/metrics aggregate
123
134
  over the trainable traces only — fixed agents (a judge, a modeled user) often
124
135
  carry no rewards and would dilute every mean with structural zeros — falling
@@ -149,23 +160,47 @@ def _run_metrics(episodes: list[Episode], traces: list[Trace]) -> dict[str, Any]
149
160
  }
150
161
 
151
162
 
152
- def _build_samples(episodes: list[Episode]) -> list[dict[str, Any]]:
153
- """One platform sample per trace, with one `rollout_number` per EPISODE: a
154
- multi-agent rollout's seats are the same attempt at the task, not attempts
155
- 1..n. The grouping is the episode envelope — every trace in one episode shares
156
- its `rollout_number` and `episode_id`."""
163
+ def build_samples(episodes: list[Episode]) -> list[dict[str, Any]]:
164
+ """One Platform sample per Episode, with a legacy-compatible trace summary.
165
+
166
+ The native Episode in `info.native_wrapper` is authoritative and contains every
167
+ trace. One trainable trace (or the first trace) supplies only the flat summary
168
+ used by older consumers. `native_trace_index` identifies that summary trace.
169
+ """
157
170
  counts: dict[int, int] = {}
158
- episode_numbers: dict[str, int] = {}
159
171
  samples = []
160
172
  for episode in episodes:
161
- # One rollout_number per episode: all its seats are the same attempt.
162
- number = episode_numbers.get(episode.id)
163
- for trace in episode.traces:
164
- if number is None:
165
- idx = trace.task.data.idx
166
- counts[idx] = number = counts.get(idx, 0) + 1
167
- episode_numbers[episode.id] = number
168
- samples.append(trace_to_sample(trace, number, episode.id))
173
+ if not episode.traces:
174
+ continue
175
+ summary_trace_index = next(
176
+ (
177
+ index
178
+ for index, candidate in enumerate(episode.traces)
179
+ if candidate.agent.trainable
180
+ ),
181
+ 0,
182
+ )
183
+ summary_trace = episode.traces[summary_trace_index]
184
+ idx = summary_trace.task.data.idx
185
+ counts[idx] = number = counts.get(idx, 0) + 1
186
+ sample = trace_to_sample(summary_trace, number, episode.id)
187
+ sample["sample_id"] = episode.id
188
+ sample["info"] = {
189
+ **(sample["info"] or {}),
190
+ "native_wrapper": episode.to_record(),
191
+ "native_trace_index": summary_trace_index,
192
+ }
193
+ if len(b'{"samples":[]}') + json_bytes(sample) <= _MAX_SAMPLES_PAYLOAD_BYTES:
194
+ samples.append(sample)
195
+ continue
196
+ logger.warning(
197
+ "Episode %s exceeds the Platform sample limit; uploading projected traces",
198
+ episode.id,
199
+ )
200
+ samples.extend(
201
+ trace_to_sample(candidate, number, episode.id)
202
+ for candidate in episode.traces
203
+ )
169
204
  return samples
170
205
 
171
206
 
@@ -186,7 +221,7 @@ def push_traces(
186
221
  state.done = True
187
222
  return url
188
223
 
189
- api_key, base, frontend, team_id = _creds()
224
+ api_key, base, frontend, team_id = credentials()
190
225
  if not api_key:
191
226
  logger.warning(
192
227
  "--push: no PRIME_API_KEY (set it or run `prime login`); skipping upload"
@@ -195,8 +230,7 @@ def push_traces(
195
230
 
196
231
  traces = [trace for episode in episodes for trace in episode.traces]
197
232
  env_name = (config.env.taskset.id) or config.legacy.id
198
- metrics = _run_metrics(episodes, traces)
199
- samples = _build_samples(episodes)
233
+ metrics = run_metrics(episodes, traces)
200
234
  num_examples = len({t.task.data.idx for t in traces})
201
235
  metadata = {
202
236
  "framework": "verifiers",
@@ -213,18 +247,12 @@ def push_traces(
213
247
  # The run is done and its results saved; a network blip here must not crash it
214
248
  # — log and skip the upload instead.
215
249
  try:
250
+ samples = build_samples(episodes)
216
251
  batches: list[list[dict[str, Any]]] = []
217
252
  batch: list[dict[str, Any]] = []
218
253
  payload_bytes = len(b'{"samples":[]}')
219
254
  for i, sample in enumerate(samples):
220
- sample_bytes = len(
221
- json.dumps(
222
- sample,
223
- ensure_ascii=False,
224
- separators=(",", ":"),
225
- allow_nan=False,
226
- ).encode("utf-8")
227
- )
255
+ sample_bytes = json_bytes(sample)
228
256
  sample_payload_bytes = len(b'{"samples":[]}') + sample_bytes
229
257
  if sample_payload_bytes > _MAX_SAMPLES_PAYLOAD_BYTES:
230
258
  raise ValueError(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: verifiers
3
- Version: 0.2.2.dev112
3
+ Version: 0.3.1.dev4
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
@@ -170,7 +170,7 @@ verifiers/utils/version_utils.py,sha256=39gV8-mNF9GspAgKloLo8oy7_MVluMrwGPrntQv2
170
170
  verifiers/v1/__init__.py,sha256=y-2SJbNv_3Hbg-a6eTpmygk8w2L1qtcJtkaJOsiCf1o,7585
171
171
  verifiers/v1/agent.py,sha256=MQVKGjUhrP8uzRXAq94uJHkEsdHFziPxQHZ7TOD324I,31306
172
172
  verifiers/v1/env.py,sha256=g6fpoG-Z9lM3vTKrrU-QRJY3S88_y9zsPHwBvfs5vdY,17865
173
- verifiers/v1/episode.py,sha256=YtuRTV_PpwDglXFeN6UNg8IswpP1HseFLCYtascGMNI,3218
173
+ verifiers/v1/episode.py,sha256=tok3urx9zDbGjUJYBSIzAe0H1Gra4qrrTmnc2MzbHxY,3529
174
174
  verifiers/v1/errors.py,sha256=kQeEPX06TwAIuiz7MlWFYirrpKbx5STi6A5b_dtQQoA,6885
175
175
  verifiers/v1/graph.py,sha256=psE4gww42TuSy_B4rJfGauycGTzPes6Z2SKFAzPYg7M,29427
176
176
  verifiers/v1/harness.py,sha256=45mocvRaVAgk_wfpf0QCcHVAKemB1WyIO96BhDJif70,13923
@@ -341,12 +341,12 @@ verifiers/v1/utils/interrupt.py,sha256=F-KKhc5ndPJJfhd3SuMqyqhXhA32FhCRy5KWFJpEo
341
341
  verifiers/v1/utils/loaders.py,sha256=NSIBdNU3JVeyddL-dM4dJt6khCHyjCgzJuFtLrgiwD0,9989
342
342
  verifiers/v1/utils/logging.py,sha256=OcMHA6NsYux3oIzjPuI95rDWmFBHNcHDjZeNIhXTX-Y,2084
343
343
  verifiers/v1/utils/memory.py,sha256=ZkIvGk6uITAH5sKon65LifKPbvZr8mJ__PVc23FZpOQ,1835
344
- verifiers/v1/utils/platform.py,sha256=56Ixmk1SER6q5LvzyYcA0hgzGpzhxKZL8Hiqp4c_XVc,11576
344
+ verifiers/v1/utils/platform.py,sha256=WGMlduI-EbJUykvN8jhygTnaUYPlrrEcil89CCQ3JpE,12326
345
345
  verifiers/v1/utils/retries.py,sha256=Y2ZgrAjn-qNkRKZP_RVNL_7EK0iaRcZeCa404lpGYi4,5417
346
346
  verifiers/v1/utils/score.py,sha256=-R5Cog14r_tJ6Q_oOfGr5VPAm2CCUhofYZB6B9lm4wM,5787
347
347
  verifiers/v1/utils/version.py,sha256=-obEo_-l9-D8FLef4hYxncOe-uJpxrM1g2Hig_37Sgs,1607
348
- verifiers-0.2.2.dev112.dist-info/METADATA,sha256=iMTLFf3Gx0UQPvCAme9XMLjXfxD8sLzq5XDKbQngAuc,4649
349
- verifiers-0.2.2.dev112.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
350
- verifiers-0.2.2.dev112.dist-info/entry_points.txt,sha256=v6v0QT9vVExnfn4br42MostI_o3f3ZbWYlTormz-U3g,515
351
- verifiers-0.2.2.dev112.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
352
- verifiers-0.2.2.dev112.dist-info/RECORD,,
348
+ verifiers-0.3.1.dev4.dist-info/METADATA,sha256=t2ua9TX2bLpe6cvHvXqL_Pj-KGwzrt8HNdxYTbnz4XA,4647
349
+ verifiers-0.3.1.dev4.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
350
+ verifiers-0.3.1.dev4.dist-info/entry_points.txt,sha256=v6v0QT9vVExnfn4br42MostI_o3f3ZbWYlTormz-U3g,515
351
+ verifiers-0.3.1.dev4.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
352
+ verifiers-0.3.1.dev4.dist-info/RECORD,,