whileai 0.52__tar.gz → 0.53__tar.gz

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.
Files changed (88) hide show
  1. {whileai-0.52/whileai.egg-info → whileai-0.53}/PKG-INFO +1 -1
  2. {whileai-0.52 → whileai-0.53}/pyproject.toml +1 -1
  3. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/passat.py +20 -1
  4. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/privileged.py +15 -0
  5. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/rubric.py +28 -2
  6. {whileai-0.52 → whileai-0.53/whileai.egg-info}/PKG-INFO +1 -1
  7. {whileai-0.52 → whileai-0.53}/LICENSE +0 -0
  8. {whileai-0.52 → whileai-0.53}/MANIFEST.in +0 -0
  9. {whileai-0.52 → whileai-0.53}/README.md +0 -0
  10. {whileai-0.52 → whileai-0.53}/setup.cfg +0 -0
  11. {whileai-0.52 → whileai-0.53}/whileai/__init__.py +0 -0
  12. {whileai-0.52 → whileai-0.53}/whileai/_env.py +0 -0
  13. {whileai-0.52 → whileai-0.53}/whileai/auth.py +0 -0
  14. {whileai-0.52 → whileai-0.53}/whileai/cli.py +0 -0
  15. {whileai-0.52 → whileai-0.53}/whileai/ingest.py +0 -0
  16. {whileai-0.52 → whileai-0.53}/whileai/py.typed +0 -0
  17. {whileai-0.52 → whileai-0.53}/whileai/simulations/__init__.py +0 -0
  18. {whileai-0.52 → whileai-0.53}/whileai/simulations/__main__.py +0 -0
  19. {whileai-0.52 → whileai-0.53}/whileai/simulations/data.py +0 -0
  20. {whileai-0.52 → whileai-0.53}/whileai/simulations/environment.py +0 -0
  21. {whileai-0.52 → whileai-0.53}/whileai/simulations/export.py +0 -0
  22. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/__init__.py +0 -0
  23. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/actionspace.py +0 -0
  24. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/adapters.py +0 -0
  25. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/agents.py +0 -0
  26. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/coverage.py +0 -0
  27. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/diversity.py +0 -0
  28. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/embeddings.py +0 -0
  29. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/explore.py +0 -0
  30. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/generator.py +0 -0
  31. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/offline_agent.py +0 -0
  32. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/scenarios.py +0 -0
  33. {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/usage_meter.py +0 -0
  34. {whileai-0.52 → whileai-0.53}/whileai/simulations/ingest/__init__.py +0 -0
  35. {whileai-0.52 → whileai-0.53}/whileai/simulations/ingest/otel.py +0 -0
  36. {whileai-0.52 → whileai-0.53}/whileai/simulations/ingest/platform.py +0 -0
  37. {whileai-0.52 → whileai-0.53}/whileai/simulations/ingest/traces.py +0 -0
  38. {whileai-0.52 → whileai-0.53}/whileai/simulations/monitor.py +0 -0
  39. {whileai-0.52 → whileai-0.53}/whileai/simulations/py.typed +0 -0
  40. {whileai-0.52 → whileai-0.53}/whileai/simulations/run/__init__.py +0 -0
  41. {whileai-0.52 → whileai-0.53}/whileai/simulations/run/config.py +0 -0
  42. {whileai-0.52 → whileai-0.53}/whileai/simulations/run/engine.py +0 -0
  43. {whileai-0.52 → whileai-0.53}/whileai/simulations/run/rows.py +0 -0
  44. {whileai-0.52 → whileai-0.53}/whileai/simulations/run/spec.py +0 -0
  45. {whileai-0.52 → whileai-0.53}/whileai/simulations/schema.py +0 -0
  46. {whileai-0.52 → whileai-0.53}/whileai/simulations/schemas/row-v1.json +0 -0
  47. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/__init__.py +0 -0
  48. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/agreement.py +0 -0
  49. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/checklist.py +0 -0
  50. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/curriculum.py +0 -0
  51. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/delta.py +0 -0
  52. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/grade_llm.py +0 -0
  53. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/grading.py +0 -0
  54. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/grounding.py +0 -0
  55. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/hack_scan.py +0 -0
  56. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/hygiene.py +0 -0
  57. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/judge_trust.py +0 -0
  58. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/judging.py +0 -0
  59. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/labels.py +0 -0
  60. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/llm_judge.py +0 -0
  61. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/logprobs.py +0 -0
  62. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/markers.py +0 -0
  63. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/optimize.py +0 -0
  64. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/pairwise.py +0 -0
  65. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/preflight.py +0 -0
  66. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/publish_gate.py +0 -0
  67. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/quality.py +0 -0
  68. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/reference.py +0 -0
  69. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/spec.py +0 -0
  70. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/stage.py +0 -0
  71. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/stats.py +0 -0
  72. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/style.py +0 -0
  73. {whileai-0.52 → whileai-0.53}/whileai/simulations/score/trace.py +0 -0
  74. {whileai-0.52 → whileai-0.53}/whileai/simulations/simulation.py +0 -0
  75. {whileai-0.52 → whileai-0.53}/whileai/simulations/training.py +0 -0
  76. {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/__init__.py +0 -0
  77. {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/base.py +0 -0
  78. {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/code.py +0 -0
  79. {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/math.py +0 -0
  80. {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/structured.py +0 -0
  81. {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/text.py +0 -0
  82. {whileai-0.52 → whileai-0.53}/whileai/simulations/world/__init__.py +0 -0
  83. {whileai-0.52 → whileai-0.53}/whileai/simulations/world/sandbox.py +0 -0
  84. {whileai-0.52 → whileai-0.53}/whileai.egg-info/SOURCES.txt +0 -0
  85. {whileai-0.52 → whileai-0.53}/whileai.egg-info/dependency_links.txt +0 -0
  86. {whileai-0.52 → whileai-0.53}/whileai.egg-info/entry_points.txt +0 -0
  87. {whileai-0.52 → whileai-0.53}/whileai.egg-info/requires.txt +0 -0
  88. {whileai-0.52 → whileai-0.53}/whileai.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: whileai
3
- Version: 0.52
3
+ Version: 0.53
4
4
  Summary: While Python SDK: trace ingestion and verification client, plus agent simulations grounded in your tools and system prompt.
5
5
  Author: While
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "whileai"
7
- version = "0.52"
7
+ version = "0.53"
8
8
  description = "While Python SDK: trace ingestion and verification client, plus agent simulations grounded in your tools and system prompt."
9
9
  readme = "README.md"
10
10
  license = {text = "Apache-2.0"}
@@ -195,6 +195,25 @@ class PassAt:
195
195
  return f"{head} {tail})"
196
196
 
197
197
 
198
+ def _nothing_to_score(rows: Sequence[dict]) -> str:
199
+ """Why no row carried a binary reward.
200
+
201
+ "grade first" is right when nothing has been judged, and wrong -- it
202
+ sends the user back to the step that already ran -- when grading did
203
+ happen and every row failed. A cold hosted judge does exactly that: all
204
+ the concurrent calls time out together and the whole set reads as
205
+ ungraded.
206
+ """
207
+ judged = [r for r in rows if isinstance(r, dict) and r.get("judge_status")]
208
+ failed = [r for r in judged if str(r.get("judge_status")) != "ok"]
209
+ if not judged or len(failed) != len(judged):
210
+ return "no binary rewards; grade first"
211
+ statuses = "/".join(sorted({str(r.get("judge_status")) for r in failed}))
212
+ reason = next((str(r.get("reason") or "").strip() for r in failed if r.get("reason")), "")
213
+ tail = f": {reason[:120]}" if reason else ""
214
+ return f"the judge failed on all {len(failed)} rows ({statuses}){tail}; re-run the judge"
215
+
216
+
198
217
  def pass_at(
199
218
  rows: Sequence[dict] | Any,
200
219
  *,
@@ -249,7 +268,7 @@ def pass_at(
249
268
  pass_at_k=None,
250
269
  n_groups=0,
251
270
  n_rows=0,
252
- note="no binary rewards; grade first",
271
+ note=_nothing_to_score(row_list),
253
272
  config=run_config(row_list, n_tasks=0, k=int(k or 1)),
254
273
  )
255
274
 
@@ -63,9 +63,15 @@ def leak_report(rows: Sequence[dict], *, min_len: int = 12) -> dict[str, Any]:
63
63
  the block, so the result is vacuous), ``leaked`` (up to 20 rows:
64
64
  ``scenario_id``, ``rollout_index``, ``field``, ``needle``) and
65
65
  ``summary``. Does not mutate ``rows``.
66
+
67
+ Pass ``data.trajectories``, not ``data.rows()``: the export scrubs
68
+ ``privileged`` at any depth, so exported rows carry nothing to check
69
+ and the report is vacuous. When it can tell the rows came through the
70
+ export, ``summary`` says so and names the accessor to use instead.
66
71
  """
67
72
  n_rows = 0
68
73
  n_checked = 0
74
+ exported = False
69
75
  leaked: list[dict[str, Any]] = []
70
76
  for row in rows:
71
77
  if not isinstance(row, dict):
@@ -73,6 +79,10 @@ def leak_report(rows: Sequence[dict], *, min_len: int = 12) -> dict[str, Any]:
73
79
  n_rows += 1
74
80
  needles = _needles(row.get("privileged"), min_len=min_len)
75
81
  if not needles:
82
+ # ``export_row`` always writes ``scenario_id`` (``""`` when the
83
+ # row has none), so a row that has the key but no privileged
84
+ # block was scrubbed on the way out rather than never filled.
85
+ exported = exported or "scenario_id" in row
76
86
  continue
77
87
  n_checked += 1
78
88
  hay = _norm(assistant_text(row))
@@ -94,6 +104,11 @@ def leak_report(rows: Sequence[dict], *, min_len: int = 12) -> dict[str, Any]:
94
104
  f"checked 0 of {n_rows} rows: none carried privileged context, "
95
105
  "so this says nothing about leaks"
96
106
  )
107
+ if exported:
108
+ summary += (
109
+ ". These came through the export (rows(), save(), push()), which scrubs "
110
+ "privileged at any depth -- pass data.trajectories, which keeps it"
111
+ )
97
112
  elif not n_leaked:
98
113
  summary = f"checked {n_checked} of {n_rows} rows: no reply quoted its privileged context"
99
114
  else:
@@ -26,12 +26,19 @@ import concurrent.futures
26
26
  import hashlib
27
27
  import json
28
28
  import re
29
+ import threading
29
30
  from collections.abc import Callable, Mapping, Sequence
30
31
  from dataclasses import dataclass, field
31
32
  from typing import Any, Literal
32
33
 
33
34
  from ..generate.agents import complete, parse_backend_spec
34
- from .grade_llm import JUDGE_TEMPERATURE, _render_payload, judge_spec, judge_version
35
+ from .grade_llm import (
36
+ JUDGE_TEMPERATURE,
37
+ _render_payload,
38
+ judge_spec,
39
+ judge_version,
40
+ warm_judge,
41
+ )
35
42
 
36
43
  Kind = Literal["hard", "principle", "pitfall"]
37
44
  KINDS: tuple[str, ...] = ("hard", "principle", "pitfall")
@@ -375,10 +382,28 @@ def rubric_judge(
375
382
  ``markers`` (``rubric:<slug>`` = 1.0 met / 0.0 not, and for a pitfall
376
383
  1.0 clean / 0.0 exhibited), ``criteria`` (the raw verdicts),
377
384
  ``rubric_version`` and the score breakdown. The judge's name folds the
378
- rubric version in when one is fixed."""
385
+ rubric version in when one is fixed.
386
+
387
+ The hosted judge scales to zero, so the first row through warms it once
388
+ (``warm_judge``, a 600s budget) while the rest of the fan-out waits.
389
+ Without that, ``run_judge``'s eight concurrent calls all raced a
390
+ container that was still loading its weights and every row came back
391
+ ``invalid_result`` with a ``TimeoutError``. Warm-up failure is not
392
+ fatal: the rows are judged anyway and report the real error."""
379
393
  resolved = judge_spec(spec=spec)
380
394
  url, model = parse_backend_spec(resolved)
381
395
  system = str(prompt or "").strip() or RUBRIC_JUDGE_SYSTEM
396
+ warm_lock = threading.Lock()
397
+ warmed: list[dict] = []
398
+
399
+ def ensure_warm() -> None:
400
+ # once per judge, and the other workers block here rather than
401
+ # opening their own request against a cold server
402
+ if warmed:
403
+ return
404
+ with warm_lock:
405
+ if not warmed:
406
+ warmed.append(warm_judge(resolved, api_key=api_key))
382
407
 
383
408
  def judge(row: dict) -> dict[str, Any]:
384
409
  use = rubric or rubric_of(row)
@@ -391,6 +416,7 @@ def rubric_judge(
391
416
  },
392
417
  default=str,
393
418
  )
419
+ ensure_warm()
394
420
  try:
395
421
  reply = complete(
396
422
  url,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: whileai
3
- Version: 0.52
3
+ Version: 0.53
4
4
  Summary: While Python SDK: trace ingestion and verification client, plus agent simulations grounded in your tools and system prompt.
5
5
  Author: While
6
6
  License: Apache-2.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes