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.
- {whileai-0.52/whileai.egg-info → whileai-0.53}/PKG-INFO +1 -1
- {whileai-0.52 → whileai-0.53}/pyproject.toml +1 -1
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/passat.py +20 -1
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/privileged.py +15 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/rubric.py +28 -2
- {whileai-0.52 → whileai-0.53/whileai.egg-info}/PKG-INFO +1 -1
- {whileai-0.52 → whileai-0.53}/LICENSE +0 -0
- {whileai-0.52 → whileai-0.53}/MANIFEST.in +0 -0
- {whileai-0.52 → whileai-0.53}/README.md +0 -0
- {whileai-0.52 → whileai-0.53}/setup.cfg +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/__init__.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/_env.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/auth.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/cli.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/ingest.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/py.typed +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/__init__.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/__main__.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/data.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/environment.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/export.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/__init__.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/actionspace.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/adapters.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/agents.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/coverage.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/diversity.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/embeddings.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/explore.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/generator.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/offline_agent.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/scenarios.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/generate/usage_meter.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/ingest/__init__.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/ingest/otel.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/ingest/platform.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/ingest/traces.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/monitor.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/py.typed +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/run/__init__.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/run/config.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/run/engine.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/run/rows.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/run/spec.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/schema.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/schemas/row-v1.json +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/__init__.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/agreement.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/checklist.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/curriculum.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/delta.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/grade_llm.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/grading.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/grounding.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/hack_scan.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/hygiene.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/judge_trust.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/judging.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/labels.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/llm_judge.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/logprobs.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/markers.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/optimize.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/pairwise.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/preflight.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/publish_gate.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/quality.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/reference.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/spec.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/stage.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/stats.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/style.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/score/trace.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/simulation.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/training.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/__init__.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/base.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/code.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/math.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/structured.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/verify/text.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/world/__init__.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai/simulations/world/sandbox.py +0 -0
- {whileai-0.52 → whileai-0.53}/whileai.egg-info/SOURCES.txt +0 -0
- {whileai-0.52 → whileai-0.53}/whileai.egg-info/dependency_links.txt +0 -0
- {whileai-0.52 → whileai-0.53}/whileai.egg-info/entry_points.txt +0 -0
- {whileai-0.52 → whileai-0.53}/whileai.egg-info/requires.txt +0 -0
- {whileai-0.52 → whileai-0.53}/whileai.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "whileai"
|
|
7
|
-
version = "0.
|
|
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=
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|