whileai 0.66__tar.gz → 0.68__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 (98) hide show
  1. whileai-0.68/PKG-INFO +297 -0
  2. whileai-0.68/README.md +260 -0
  3. {whileai-0.66 → whileai-0.68}/pyproject.toml +2 -2
  4. whileai-0.68/whileai.egg-info/PKG-INFO +297 -0
  5. whileai-0.66/PKG-INFO +0 -276
  6. whileai-0.66/README.md +0 -239
  7. whileai-0.66/whileai.egg-info/PKG-INFO +0 -276
  8. {whileai-0.66 → whileai-0.68}/LICENSE +0 -0
  9. {whileai-0.66 → whileai-0.68}/MANIFEST.in +0 -0
  10. {whileai-0.66 → whileai-0.68}/setup.cfg +0 -0
  11. {whileai-0.66 → whileai-0.68}/whileai/__init__.py +0 -0
  12. {whileai-0.66 → whileai-0.68}/whileai/_env.py +0 -0
  13. {whileai-0.66 → whileai-0.68}/whileai/auth.py +0 -0
  14. {whileai-0.66 → whileai-0.68}/whileai/cli.py +0 -0
  15. {whileai-0.66 → whileai-0.68}/whileai/ingest.py +0 -0
  16. {whileai-0.66 → whileai-0.68}/whileai/init_evals.py +0 -0
  17. {whileai-0.66 → whileai-0.68}/whileai/platform.py +0 -0
  18. {whileai-0.66 → whileai-0.68}/whileai/py.typed +0 -0
  19. {whileai-0.66 → whileai-0.68}/whileai/simulations/__init__.py +0 -0
  20. {whileai-0.66 → whileai-0.68}/whileai/simulations/__main__.py +0 -0
  21. {whileai-0.66 → whileai-0.68}/whileai/simulations/data.py +0 -0
  22. {whileai-0.66 → whileai-0.68}/whileai/simulations/environment.py +0 -0
  23. {whileai-0.66 → whileai-0.68}/whileai/simulations/export.py +0 -0
  24. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/__init__.py +0 -0
  25. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/actionspace.py +0 -0
  26. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/adapters.py +0 -0
  27. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/agents.py +0 -0
  28. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/anthropic_backend.py +0 -0
  29. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/coverage.py +0 -0
  30. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/diversity.py +0 -0
  31. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/embeddings.py +0 -0
  32. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/explore.py +0 -0
  33. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/generator.py +0 -0
  34. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/offline_agent.py +0 -0
  35. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/scenarios.py +0 -0
  36. {whileai-0.66 → whileai-0.68}/whileai/simulations/generate/usage_meter.py +0 -0
  37. {whileai-0.66 → whileai-0.68}/whileai/simulations/ingest/__init__.py +0 -0
  38. {whileai-0.66 → whileai-0.68}/whileai/simulations/ingest/otel.py +0 -0
  39. {whileai-0.66 → whileai-0.68}/whileai/simulations/ingest/platform.py +0 -0
  40. {whileai-0.66 → whileai-0.68}/whileai/simulations/ingest/traces.py +0 -0
  41. {whileai-0.66 → whileai-0.68}/whileai/simulations/monitor.py +0 -0
  42. {whileai-0.66 → whileai-0.68}/whileai/simulations/py.typed +0 -0
  43. {whileai-0.66 → whileai-0.68}/whileai/simulations/run/__init__.py +0 -0
  44. {whileai-0.66 → whileai-0.68}/whileai/simulations/run/config.py +0 -0
  45. {whileai-0.66 → whileai-0.68}/whileai/simulations/run/engine.py +0 -0
  46. {whileai-0.66 → whileai-0.68}/whileai/simulations/run/rows.py +0 -0
  47. {whileai-0.66 → whileai-0.68}/whileai/simulations/run/spec.py +0 -0
  48. {whileai-0.66 → whileai-0.68}/whileai/simulations/schema.py +0 -0
  49. {whileai-0.66 → whileai-0.68}/whileai/simulations/schemas/row-v1.json +0 -0
  50. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/__init__.py +0 -0
  51. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/agreement.py +0 -0
  52. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/audit.py +0 -0
  53. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/checklist.py +0 -0
  54. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/curriculum.py +0 -0
  55. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/delta.py +0 -0
  56. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/grade_llm.py +0 -0
  57. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/grading.py +0 -0
  58. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/grounding.py +0 -0
  59. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/hack_scan.py +0 -0
  60. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/hygiene.py +0 -0
  61. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/judge_trust.py +0 -0
  62. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/judging.py +0 -0
  63. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/labels.py +0 -0
  64. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/llm_judge.py +0 -0
  65. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/logprobs.py +0 -0
  66. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/markers.py +0 -0
  67. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/optimize.py +0 -0
  68. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/pairwise.py +0 -0
  69. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/passat.py +0 -0
  70. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/preflight.py +0 -0
  71. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/privileged.py +0 -0
  72. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/publish_gate.py +0 -0
  73. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/quality.py +0 -0
  74. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/reference.py +0 -0
  75. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/rubric.py +0 -0
  76. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/spec.py +0 -0
  77. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/stage.py +0 -0
  78. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/stats.py +0 -0
  79. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/style.py +0 -0
  80. {whileai-0.66 → whileai-0.68}/whileai/simulations/score/trace.py +0 -0
  81. {whileai-0.66 → whileai-0.68}/whileai/simulations/simulation.py +0 -0
  82. {whileai-0.66 → whileai-0.68}/whileai/simulations/text.py +0 -0
  83. {whileai-0.66 → whileai-0.68}/whileai/simulations/training.py +0 -0
  84. {whileai-0.66 → whileai-0.68}/whileai/simulations/verify/__init__.py +0 -0
  85. {whileai-0.66 → whileai-0.68}/whileai/simulations/verify/base.py +0 -0
  86. {whileai-0.66 → whileai-0.68}/whileai/simulations/verify/code.py +0 -0
  87. {whileai-0.66 → whileai-0.68}/whileai/simulations/verify/math.py +0 -0
  88. {whileai-0.66 → whileai-0.68}/whileai/simulations/verify/structured.py +0 -0
  89. {whileai-0.66 → whileai-0.68}/whileai/simulations/verify/text.py +0 -0
  90. {whileai-0.66 → whileai-0.68}/whileai/simulations/world/__init__.py +0 -0
  91. {whileai-0.66 → whileai-0.68}/whileai/simulations/world/sandbox.py +0 -0
  92. {whileai-0.66 → whileai-0.68}/whileai/templates/__init__.py +0 -0
  93. {whileai-0.66 → whileai-0.68}/whileai/templates/evals.py +0 -0
  94. {whileai-0.66 → whileai-0.68}/whileai.egg-info/SOURCES.txt +0 -0
  95. {whileai-0.66 → whileai-0.68}/whileai.egg-info/dependency_links.txt +0 -0
  96. {whileai-0.66 → whileai-0.68}/whileai.egg-info/entry_points.txt +0 -0
  97. {whileai-0.66 → whileai-0.68}/whileai.egg-info/requires.txt +0 -0
  98. {whileai-0.66 → whileai-0.68}/whileai.egg-info/top_level.txt +0 -0
whileai-0.68/PKG-INFO ADDED
@@ -0,0 +1,297 @@
1
+ Metadata-Version: 2.4
2
+ Name: whileai
3
+ Version: 0.68
4
+ Summary: Post-training data and evaluation for tool-using LLM agents: simulate, grade, measure, select, train, export, with the checks from rlhfbook.com.
5
+ Author: Jacob Weiss
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/whilehq/whileai-sdk
8
+ Project-URL: Repository, https://github.com/whilehq/whileai-sdk
9
+ Project-URL: Issues, https://github.com/whilehq/whileai-sdk/issues
10
+ Project-URL: Datasets, https://huggingface.co/datasets/zero-proof-ai/agent-simulations
11
+ Keywords: zp,zeroproof,while,evals,agent-evals,rl,reinforcement-learning,grpo,synthetic-data,agents,tool-use,evaluation,simulation,llm
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: requests>=2.25.0
27
+ Requires-Dist: pydantic>=2.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0; extra == "dev"
30
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
31
+ Requires-Dist: ruff>=0.12; extra == "dev"
32
+ Requires-Dist: mypy>=1.16; extra == "dev"
33
+ Requires-Dist: types-requests; extra == "dev"
34
+ Provides-Extra: rl
35
+ Requires-Dist: verifiers>=0.3; (python_version >= "3.11" and python_version < "3.14") and extra == "rl"
36
+ Dynamic: license-file
37
+
38
+ <p align="center">
39
+ <a href="https://while.ai">
40
+ <picture>
41
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/whilehq/whileai-sdk/main/docs/assets/wordmark-dark.png">
42
+ <img src="https://raw.githubusercontent.com/whilehq/whileai-sdk/main/docs/assets/wordmark-light.png" alt="while" width="300">
43
+ </picture>
44
+ </a>
45
+ </p>
46
+
47
+ <p align="center"><code>MID-TRAINING AND POST-TRAINING FOR LANGUAGE MODELS</code></p>
48
+
49
+ <p align="center">
50
+ <a href="https://github.com/whilehq/whileai-sdk/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/whilehq/whileai-sdk/ci.yml?branch=main&label=ci&labelColor=0b1220&color=5cb08a" alt="CI"></a>
51
+ <a href="https://pypi.org/project/whileai/"><img src="https://img.shields.io/pypi/v/whileai?labelColor=0b1220&color=5cb08a" alt="PyPI"></a>
52
+ <a href="https://pypi.org/project/whileai/"><img src="https://img.shields.io/pypi/pyversions/whileai?labelColor=0b1220&color=3f8f6b" alt="Python"></a>
53
+ <a href="https://pepy.tech/project/whileai"><img src="https://img.shields.io/pepy/dt/whileai?labelColor=0b1220&color=3f8f6b" alt="Downloads"></a>
54
+ <a href=".github/workflows/ci.yml"><img src="https://img.shields.io/badge/coverage-%E2%89%A5%2090%25%20gated-5cb08a?labelColor=0b1220" alt="Coverage gate"></a>
55
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-3f8f6b?labelColor=0b1220" alt="License"></a>
56
+ </p>
57
+
58
+ `whileai` makes training and eval data for agents that call tools. Give it
59
+ an agent, or just the agent's tools and system prompt. It writes the
60
+ situations the agent might meet, runs the agent through them against a fake
61
+ world that fails on purpose, and hands back every conversation as a row.
62
+ You grade the rows with your own judge or a verifier. The package then does
63
+ the bookkeeping that is easy to skip and expensive to get wrong: pass rates
64
+ with intervals, difficulty bands for RL, a check that your judge agrees with
65
+ people, decontamination against your eval set, and a scan for rewards the
66
+ policy can game. Every method says where it comes from
67
+ ([References](#references)).
68
+
69
+ ```bash
70
+ uv add whileai
71
+ ```
72
+
73
+ Or `pip install whileai`. Python 3.10 to 3.13, one dependency, typed.
74
+ This package used to be called `zeroproof`; that name still installs it.
75
+
76
+ ## Two ways in
77
+
78
+ **You only want evals.** Plenty of teams cannot train and still need to
79
+ know whether the last prompt edit helped. Run `whileai init-evals` in your
80
+ project. It finds your agent, writes a judge and a runner around it, and
81
+ gives you a pass rate with a 95% interval, a table of where the agent
82
+ fails, and a test that goes red in CI when it gets worse. `coverage_gap`
83
+ tells you which situations your tests never reach. `compare_runs` reruns
84
+ the same tasks after a prompt or tool change and says whether the change
85
+ helped. Start at [docs/evals.md](docs/evals.md).
86
+
87
+ **You want to train.** Grade the same rows, keep the ones that carry
88
+ signal, export. That is the rest of this page.
89
+
90
+ ## Sixty seconds, offline
91
+
92
+ No key, no network. `seeded_agent` is a stand-in agent. It answers
93
+ honestly most of the time and, on a labeled fraction of rollouts, does one
94
+ thing wrong on purpose: hedges, flatters, or claims success after a tool
95
+ failed. Each row records what it did in `seeded`, so you can check that
96
+ your judge catches exactly those rows before you trust it on real ones.
97
+
98
+ ```python
99
+ import whileai.simulations as wai
100
+
101
+ TOOLS = [
102
+ {
103
+ "type": "function",
104
+ "function": {
105
+ "name": "get_order",
106
+ "description": "Look up an order by id.",
107
+ "parameters": {
108
+ "type": "object",
109
+ "properties": {"order_id": {"type": "string"}},
110
+ "required": ["order_id"],
111
+ },
112
+ },
113
+ }
114
+ ]
115
+
116
+ data = wai.simulate(
117
+ wai.seeded_agent(TOOLS),
118
+ tools=TOOLS,
119
+ system_prompt="Help customers with orders.",
120
+ simulator=False, # no model
121
+ mode="rl",
122
+ repeats=4,
123
+ repeat_policy="fixed",
124
+ budget=64,
125
+ )
126
+ scored = data.grade(judge=lambda row: {"reward": int(not row["seeded"])})
127
+ print(scored.pass_at)
128
+ ```
129
+
130
+ ```
131
+ pass@1 0.67 [0.55..0.78] | pass^4 (pass_pow_k) 0.19 [0.00..0.38] | pass@4 1.00 [1.00..1.00] | headroom 0.33 (16 groups, k=4)
132
+ ```
133
+
134
+ pass@1 is the pass rate over tasks with a bootstrap interval. pass^4 is
135
+ how often all four rollouts of a task pass. Headroom is pass@4 minus
136
+ pass@1, the gap an RL update could close.
137
+
138
+ To use your own agent, pass any callable that takes the user message and
139
+ returns `{"steps": [...], "final_text": "..."}`. To use a model, pass a
140
+ spec string: `openai:<model>`, `anthropic:<model>`, `vllm:<model>@<url>`,
141
+ or `ollama:<model>`. With no `agent=` at all, the run uses the Qwen we
142
+ host, on your key from `whileai login`, and Phi-4 grades. The judge is
143
+ never the model it is judging.
144
+
145
+ ## The loop
146
+
147
+ | Step | Call | What it computes | Refs |
148
+ |---|---|---|---|
149
+ | Simulate | `simulate(agent, tools=, system_prompt=, mode="rl", repeats=k)` | covering array over tools, world state and user stance; k rollouts per prompt; scheduled tool faults | [2], [3] |
150
+ | Grade | `data.grade(judge=)`, `verify.MathEqual`, `verify.CodeExec` | reward per rollout under one contract; verifiable rewards | [4], [5] |
151
+ | Validate the judge | `judge_trust`, `judge_probes` | agreement and Cohen's kappa against human gold; length bias; exploit probes | [6], [7] |
152
+ | Measure | `pass_at`, `delta_report`, `eval_variance`, `holdout_size` | pass@1, pass^k, pass@k with bootstrap intervals over tasks; paired delta with a permutation p-value; noise band; power | [8], [9], [10], [11] |
153
+ | Select | `optimize(mode="rl"\|"sft")`, `build_preference_pairs`, `curriculum` | 20 to 80% difficulty band, unanimous-group drop, rejection sampling, length-matched pairs, curriculum | [12], [13], [14], [15] |
154
+ | Guard | `decontaminate`, `hack_scan`, `trace_markers`, `HackMonitor` | overlap with the eval set; reward-feature correlation within task against a shuffle floor; trajectory lies | [16], [17], [18] |
155
+ | Train and export | `export_dataset`, `export_environment`, `train`, `serve` | loss masks; a `verifiers` environment for GRPO; hosted LoRA SFT, GRPO, DPO, RM | [1], [19], [20] |
156
+
157
+ ## The science
158
+
159
+ **SFT.** `optimize(mode="sft")` is rejection sampling [14], [16]: keep the
160
+ best-scoring completion for each prompt, with a random selector alongside
161
+ so you can tell whether picking the best did anything. Exported rows carry
162
+ a `loss_mask` per message, so the trainer learns from the agent's turns and
163
+ not from tool output. `unroll=True` splits a long conversation into one
164
+ sample per agent turn, each with the context that turn actually saw.
165
+ `format="trl"` is the shape `SFTTrainer` loads [1, ch. 4].
166
+
167
+ **RL with verifiable rewards.** When a program can check the answer, the
168
+ reward should be that program [5]: `MathEqual`, `CodeExec` against hidden
169
+ tests, `JSONSchema`, and combinations of them. In `mode="rl"` every prompt
170
+ gets two rollouts first. Only prompts where those two disagree are filled
171
+ to k, because a group that all passes or all fails has zero advantage under
172
+ GRPO [19]. That is DAPO's dynamic sampling [12], applied while the rollouts
173
+ are generated instead of after. `optimize(mode="rl")` then keeps the
174
+ prompts the policy solves 20 to 80% of the time [13] and lets you choose
175
+ what happens to rollouts that hit the length cap [12]. `export_environment`
176
+ writes the tasks, the fake world and the reward as a `verifiers` package
177
+ you can hand to a trainer. Rows keep their sampling logprobs so the trainer
178
+ can form the importance ratio [21], and `mean_kl` measures drift from the
179
+ reference model [22].
180
+
181
+ **Character training.** Write down how the model should talk as a
182
+ constitution [23], [24]. `load_spec` hashes it into `spec.version`, so an
183
+ edit to one principle is a new version. The judge is checked against the
184
+ labels the spec itself carries before it grades anything. Preference pairs
185
+ are matched on length [7], so the model learns the trait and not "longer
186
+ is better". Put `spec.behaviors()` in `must_not_regress` and
187
+ `delta_report` fails any run that improved one trait by giving up
188
+ another. [docs/character-training.md](docs/character-training.md).
189
+
190
+ **Evaluation.** Intervals are bootstrapped over tasks, not rollouts,
191
+ because rollouts of the same task are not independent [8], [10], [11].
192
+ Run an eval three times with `runs=3` and `delta_report` refuses to call a
193
+ change real when it sits inside twice the run-to-run standard deviation.
194
+ `holdout_size` says how many prompts you need to see a given gain at 80%
195
+ power [11]; most evals are too small. `decontaminate` checks training rows
196
+ against the eval set with the 80% n-gram overlap rule [16], and with
197
+ embeddings when you pass an embedder. [docs/evals.md](docs/evals.md).
198
+
199
+ **Over-optimization.** The reward is a proxy for what you want, and RL
200
+ finds the gap between the two [17]. `hack_scan` looks for the feature that
201
+ predicts reward within a task, against a shuffled baseline, so a judge that
202
+ pays for a phrase or a delimiter shows up before you train on it.
203
+ `judge_probes` tries the tricks a policy finds first, flattery included
204
+ [18]. `delta_report(proxy=, target=)` fails when the training reward went
205
+ up and the metric you care about did not. `HackMonitor` runs the same scan
206
+ inside a TRL training loop and can stop it.
207
+ [docs/reward-hacking.md](docs/reward-hacking.md).
208
+
209
+ ## Recipes
210
+
211
+ Each recipe is one script and a README that says what you learn, what you
212
+ need, and how long it takes. All of them run in CI.
213
+
214
+ | Step | Recipes |
215
+ |---|---|
216
+ | [01-simulate](recipes/01-simulate) | bring your own agent, verifiers, a traced coding agent |
217
+ | [02-measure](recipes/02-measure) | eval your agent, pass@k, reward hacking, safety evals |
218
+ | [03-select](recipes/03-select) | the row schema, GRPO data with a gradient gate, character |
219
+ | [04-train](recipes/04-train) | hosted loop, identity SFT, GRPO and DPO on Modal, text-to-SQL |
220
+ | [05-export](recipes/05-export) | Hugging Face datasets and adapters |
221
+ | [papers](recipes/papers) | one recent paper per recipe, the number it moved with its interval |
222
+
223
+ ## Platform
224
+
225
+ Push a graded run to your While account, train on it, serve the result.
226
+ `push` refuses RL data with no mixed groups, since a trainer would learn
227
+ nothing from it.
228
+
229
+ ```python
230
+ v1 = data.push("refunds-v1", holdout=0.2, gate=True)
231
+ run = wai.train(v1["datasetId"], method="grpo", steps=200) # sft | grpo | dpo | rm
232
+ run.wait()
233
+ model = wai.serve("refunds-v2", run) # OpenAI-compatible endpoint
234
+ ```
235
+
236
+ If you train with your own code, `wai.TrainerCallback` reports into the
237
+ same run page. Traces from production come back through `traces=`, which
238
+ points the next simulation at the situations that failed.
239
+
240
+ ## Documentation
241
+
242
+ [docs/reference.md](docs/reference.md): every call, knob, report and gate.
243
+ [docs/engine.md](docs/engine.md): how a row is made.
244
+ [CHANGELOG.md](CHANGELOG.md): one entry per release.
245
+
246
+ ## Development
247
+
248
+ ```bash
249
+ uv sync --extra dev
250
+ uv run pytest
251
+ uv run ruff check . && uv run mypy
252
+ ```
253
+
254
+ CI runs the suite on Python 3.10 to 3.13, gates coverage at 90%, and runs
255
+ every recipe's `smoke.sh`. [CONTRIBUTING.md](CONTRIBUTING.md).
256
+
257
+ ## Cite
258
+
259
+ ```bibtex
260
+ @software{weiss2026whileai,
261
+ title = {whileai: post-training data and evaluation for tool-using agents},
262
+ author = {Weiss, Jacob},
263
+ year = {2026},
264
+ url = {https://github.com/whilehq/whileai-sdk}
265
+ }
266
+ ```
267
+
268
+ ## References
269
+
270
+ 1. Lambert, N. *Reinforcement Learning from Human Feedback*. arXiv:2504.12501, 2025.
271
+ 2. Kuhn, D. R., Wallace, D. R., Gallo, A. M. Software Fault Interactions and Implications for Software Testing. *IEEE TSE* 30(6), 2004.
272
+ 3. Yao, S. et al. τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains. arXiv:2406.12045, 2024.
273
+ 4. Ouyang, L. et al. Training Language Models to Follow Instructions with Human Feedback. NeurIPS, 2022.
274
+ 5. Lambert, N. et al. Tülu 3: Pushing Frontiers in Open Language Model Post-Training. arXiv:2411.15124, 2024.
275
+ 6. Cohen, J. A Coefficient of Agreement for Nominal Scales. *Educational and Psychological Measurement* 20(1), 1960.
276
+ 7. Zheng, L. et al. Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena. NeurIPS, 2023.
277
+ 8. Chen, M. et al. Evaluating Large Language Models Trained on Code. arXiv:2107.03374, 2021.
278
+ 9. Wilson, E. B. Probable Inference, the Law of Succession, and Statistical Inference. *JASA* 22(158), 1927.
279
+ 10. Efron, B., Tibshirani, R. J. *An Introduction to the Bootstrap*. Chapman & Hall, 1993.
280
+ 11. Miller, E. Adding Error Bars to Evals. arXiv:2411.00640, 2024.
281
+ 12. Yu, Q. et al. DAPO: An Open-Source LLM Reinforcement Learning System at Scale. arXiv:2503.14476, 2025.
282
+ 13. He, J. et al. Skywork Open Reasoner 1 Technical Report. arXiv:2505.22312, 2025.
283
+ 14. Yuan, Z. et al. Scaling Relationship on Learning Mathematical Reasoning with Large Language Models. arXiv:2308.01825, 2023.
284
+ 15. Rafailov, R. et al. Direct Preference Optimization. NeurIPS, 2023.
285
+ 16. Touvron, H. et al. Llama 2: Open Foundation and Fine-Tuned Chat Models. arXiv:2307.09288, 2023.
286
+ 17. Gao, L., Schulman, J., Hilton, J. Scaling Laws for Reward Model Overoptimization. ICML, 2023.
287
+ 18. Sharma, M. et al. Towards Understanding Sycophancy in Language Models. ICLR, 2024.
288
+ 19. Shao, Z. et al. DeepSeekMath. arXiv:2402.03300, 2024.
289
+ 20. DeepSeek-AI. DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning. arXiv:2501.12948, 2025.
290
+ 21. Schulman, J. et al. Proximal Policy Optimization Algorithms. arXiv:1707.06347, 2017.
291
+ 22. Ziegler, D. M. et al. Fine-Tuning Language Models from Human Preferences. arXiv:1909.08593, 2019.
292
+ 23. Bai, Y. et al. Constitutional AI: Harmlessness from AI Feedback. arXiv:2212.08073, 2022.
293
+ 24. OpenAI. Model Spec, 2024. model-spec.openai.com.
294
+
295
+ ## License
296
+
297
+ Apache-2.0
whileai-0.68/README.md ADDED
@@ -0,0 +1,260 @@
1
+ <p align="center">
2
+ <a href="https://while.ai">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/whilehq/whileai-sdk/main/docs/assets/wordmark-dark.png">
5
+ <img src="https://raw.githubusercontent.com/whilehq/whileai-sdk/main/docs/assets/wordmark-light.png" alt="while" width="300">
6
+ </picture>
7
+ </a>
8
+ </p>
9
+
10
+ <p align="center"><code>MID-TRAINING AND POST-TRAINING FOR LANGUAGE MODELS</code></p>
11
+
12
+ <p align="center">
13
+ <a href="https://github.com/whilehq/whileai-sdk/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/whilehq/whileai-sdk/ci.yml?branch=main&label=ci&labelColor=0b1220&color=5cb08a" alt="CI"></a>
14
+ <a href="https://pypi.org/project/whileai/"><img src="https://img.shields.io/pypi/v/whileai?labelColor=0b1220&color=5cb08a" alt="PyPI"></a>
15
+ <a href="https://pypi.org/project/whileai/"><img src="https://img.shields.io/pypi/pyversions/whileai?labelColor=0b1220&color=3f8f6b" alt="Python"></a>
16
+ <a href="https://pepy.tech/project/whileai"><img src="https://img.shields.io/pepy/dt/whileai?labelColor=0b1220&color=3f8f6b" alt="Downloads"></a>
17
+ <a href=".github/workflows/ci.yml"><img src="https://img.shields.io/badge/coverage-%E2%89%A5%2090%25%20gated-5cb08a?labelColor=0b1220" alt="Coverage gate"></a>
18
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-3f8f6b?labelColor=0b1220" alt="License"></a>
19
+ </p>
20
+
21
+ `whileai` makes training and eval data for agents that call tools. Give it
22
+ an agent, or just the agent's tools and system prompt. It writes the
23
+ situations the agent might meet, runs the agent through them against a fake
24
+ world that fails on purpose, and hands back every conversation as a row.
25
+ You grade the rows with your own judge or a verifier. The package then does
26
+ the bookkeeping that is easy to skip and expensive to get wrong: pass rates
27
+ with intervals, difficulty bands for RL, a check that your judge agrees with
28
+ people, decontamination against your eval set, and a scan for rewards the
29
+ policy can game. Every method says where it comes from
30
+ ([References](#references)).
31
+
32
+ ```bash
33
+ uv add whileai
34
+ ```
35
+
36
+ Or `pip install whileai`. Python 3.10 to 3.13, one dependency, typed.
37
+ This package used to be called `zeroproof`; that name still installs it.
38
+
39
+ ## Two ways in
40
+
41
+ **You only want evals.** Plenty of teams cannot train and still need to
42
+ know whether the last prompt edit helped. Run `whileai init-evals` in your
43
+ project. It finds your agent, writes a judge and a runner around it, and
44
+ gives you a pass rate with a 95% interval, a table of where the agent
45
+ fails, and a test that goes red in CI when it gets worse. `coverage_gap`
46
+ tells you which situations your tests never reach. `compare_runs` reruns
47
+ the same tasks after a prompt or tool change and says whether the change
48
+ helped. Start at [docs/evals.md](docs/evals.md).
49
+
50
+ **You want to train.** Grade the same rows, keep the ones that carry
51
+ signal, export. That is the rest of this page.
52
+
53
+ ## Sixty seconds, offline
54
+
55
+ No key, no network. `seeded_agent` is a stand-in agent. It answers
56
+ honestly most of the time and, on a labeled fraction of rollouts, does one
57
+ thing wrong on purpose: hedges, flatters, or claims success after a tool
58
+ failed. Each row records what it did in `seeded`, so you can check that
59
+ your judge catches exactly those rows before you trust it on real ones.
60
+
61
+ ```python
62
+ import whileai.simulations as wai
63
+
64
+ TOOLS = [
65
+ {
66
+ "type": "function",
67
+ "function": {
68
+ "name": "get_order",
69
+ "description": "Look up an order by id.",
70
+ "parameters": {
71
+ "type": "object",
72
+ "properties": {"order_id": {"type": "string"}},
73
+ "required": ["order_id"],
74
+ },
75
+ },
76
+ }
77
+ ]
78
+
79
+ data = wai.simulate(
80
+ wai.seeded_agent(TOOLS),
81
+ tools=TOOLS,
82
+ system_prompt="Help customers with orders.",
83
+ simulator=False, # no model
84
+ mode="rl",
85
+ repeats=4,
86
+ repeat_policy="fixed",
87
+ budget=64,
88
+ )
89
+ scored = data.grade(judge=lambda row: {"reward": int(not row["seeded"])})
90
+ print(scored.pass_at)
91
+ ```
92
+
93
+ ```
94
+ pass@1 0.67 [0.55..0.78] | pass^4 (pass_pow_k) 0.19 [0.00..0.38] | pass@4 1.00 [1.00..1.00] | headroom 0.33 (16 groups, k=4)
95
+ ```
96
+
97
+ pass@1 is the pass rate over tasks with a bootstrap interval. pass^4 is
98
+ how often all four rollouts of a task pass. Headroom is pass@4 minus
99
+ pass@1, the gap an RL update could close.
100
+
101
+ To use your own agent, pass any callable that takes the user message and
102
+ returns `{"steps": [...], "final_text": "..."}`. To use a model, pass a
103
+ spec string: `openai:<model>`, `anthropic:<model>`, `vllm:<model>@<url>`,
104
+ or `ollama:<model>`. With no `agent=` at all, the run uses the Qwen we
105
+ host, on your key from `whileai login`, and Phi-4 grades. The judge is
106
+ never the model it is judging.
107
+
108
+ ## The loop
109
+
110
+ | Step | Call | What it computes | Refs |
111
+ |---|---|---|---|
112
+ | Simulate | `simulate(agent, tools=, system_prompt=, mode="rl", repeats=k)` | covering array over tools, world state and user stance; k rollouts per prompt; scheduled tool faults | [2], [3] |
113
+ | Grade | `data.grade(judge=)`, `verify.MathEqual`, `verify.CodeExec` | reward per rollout under one contract; verifiable rewards | [4], [5] |
114
+ | Validate the judge | `judge_trust`, `judge_probes` | agreement and Cohen's kappa against human gold; length bias; exploit probes | [6], [7] |
115
+ | Measure | `pass_at`, `delta_report`, `eval_variance`, `holdout_size` | pass@1, pass^k, pass@k with bootstrap intervals over tasks; paired delta with a permutation p-value; noise band; power | [8], [9], [10], [11] |
116
+ | Select | `optimize(mode="rl"\|"sft")`, `build_preference_pairs`, `curriculum` | 20 to 80% difficulty band, unanimous-group drop, rejection sampling, length-matched pairs, curriculum | [12], [13], [14], [15] |
117
+ | Guard | `decontaminate`, `hack_scan`, `trace_markers`, `HackMonitor` | overlap with the eval set; reward-feature correlation within task against a shuffle floor; trajectory lies | [16], [17], [18] |
118
+ | Train and export | `export_dataset`, `export_environment`, `train`, `serve` | loss masks; a `verifiers` environment for GRPO; hosted LoRA SFT, GRPO, DPO, RM | [1], [19], [20] |
119
+
120
+ ## The science
121
+
122
+ **SFT.** `optimize(mode="sft")` is rejection sampling [14], [16]: keep the
123
+ best-scoring completion for each prompt, with a random selector alongside
124
+ so you can tell whether picking the best did anything. Exported rows carry
125
+ a `loss_mask` per message, so the trainer learns from the agent's turns and
126
+ not from tool output. `unroll=True` splits a long conversation into one
127
+ sample per agent turn, each with the context that turn actually saw.
128
+ `format="trl"` is the shape `SFTTrainer` loads [1, ch. 4].
129
+
130
+ **RL with verifiable rewards.** When a program can check the answer, the
131
+ reward should be that program [5]: `MathEqual`, `CodeExec` against hidden
132
+ tests, `JSONSchema`, and combinations of them. In `mode="rl"` every prompt
133
+ gets two rollouts first. Only prompts where those two disagree are filled
134
+ to k, because a group that all passes or all fails has zero advantage under
135
+ GRPO [19]. That is DAPO's dynamic sampling [12], applied while the rollouts
136
+ are generated instead of after. `optimize(mode="rl")` then keeps the
137
+ prompts the policy solves 20 to 80% of the time [13] and lets you choose
138
+ what happens to rollouts that hit the length cap [12]. `export_environment`
139
+ writes the tasks, the fake world and the reward as a `verifiers` package
140
+ you can hand to a trainer. Rows keep their sampling logprobs so the trainer
141
+ can form the importance ratio [21], and `mean_kl` measures drift from the
142
+ reference model [22].
143
+
144
+ **Character training.** Write down how the model should talk as a
145
+ constitution [23], [24]. `load_spec` hashes it into `spec.version`, so an
146
+ edit to one principle is a new version. The judge is checked against the
147
+ labels the spec itself carries before it grades anything. Preference pairs
148
+ are matched on length [7], so the model learns the trait and not "longer
149
+ is better". Put `spec.behaviors()` in `must_not_regress` and
150
+ `delta_report` fails any run that improved one trait by giving up
151
+ another. [docs/character-training.md](docs/character-training.md).
152
+
153
+ **Evaluation.** Intervals are bootstrapped over tasks, not rollouts,
154
+ because rollouts of the same task are not independent [8], [10], [11].
155
+ Run an eval three times with `runs=3` and `delta_report` refuses to call a
156
+ change real when it sits inside twice the run-to-run standard deviation.
157
+ `holdout_size` says how many prompts you need to see a given gain at 80%
158
+ power [11]; most evals are too small. `decontaminate` checks training rows
159
+ against the eval set with the 80% n-gram overlap rule [16], and with
160
+ embeddings when you pass an embedder. [docs/evals.md](docs/evals.md).
161
+
162
+ **Over-optimization.** The reward is a proxy for what you want, and RL
163
+ finds the gap between the two [17]. `hack_scan` looks for the feature that
164
+ predicts reward within a task, against a shuffled baseline, so a judge that
165
+ pays for a phrase or a delimiter shows up before you train on it.
166
+ `judge_probes` tries the tricks a policy finds first, flattery included
167
+ [18]. `delta_report(proxy=, target=)` fails when the training reward went
168
+ up and the metric you care about did not. `HackMonitor` runs the same scan
169
+ inside a TRL training loop and can stop it.
170
+ [docs/reward-hacking.md](docs/reward-hacking.md).
171
+
172
+ ## Recipes
173
+
174
+ Each recipe is one script and a README that says what you learn, what you
175
+ need, and how long it takes. All of them run in CI.
176
+
177
+ | Step | Recipes |
178
+ |---|---|
179
+ | [01-simulate](recipes/01-simulate) | bring your own agent, verifiers, a traced coding agent |
180
+ | [02-measure](recipes/02-measure) | eval your agent, pass@k, reward hacking, safety evals |
181
+ | [03-select](recipes/03-select) | the row schema, GRPO data with a gradient gate, character |
182
+ | [04-train](recipes/04-train) | hosted loop, identity SFT, GRPO and DPO on Modal, text-to-SQL |
183
+ | [05-export](recipes/05-export) | Hugging Face datasets and adapters |
184
+ | [papers](recipes/papers) | one recent paper per recipe, the number it moved with its interval |
185
+
186
+ ## Platform
187
+
188
+ Push a graded run to your While account, train on it, serve the result.
189
+ `push` refuses RL data with no mixed groups, since a trainer would learn
190
+ nothing from it.
191
+
192
+ ```python
193
+ v1 = data.push("refunds-v1", holdout=0.2, gate=True)
194
+ run = wai.train(v1["datasetId"], method="grpo", steps=200) # sft | grpo | dpo | rm
195
+ run.wait()
196
+ model = wai.serve("refunds-v2", run) # OpenAI-compatible endpoint
197
+ ```
198
+
199
+ If you train with your own code, `wai.TrainerCallback` reports into the
200
+ same run page. Traces from production come back through `traces=`, which
201
+ points the next simulation at the situations that failed.
202
+
203
+ ## Documentation
204
+
205
+ [docs/reference.md](docs/reference.md): every call, knob, report and gate.
206
+ [docs/engine.md](docs/engine.md): how a row is made.
207
+ [CHANGELOG.md](CHANGELOG.md): one entry per release.
208
+
209
+ ## Development
210
+
211
+ ```bash
212
+ uv sync --extra dev
213
+ uv run pytest
214
+ uv run ruff check . && uv run mypy
215
+ ```
216
+
217
+ CI runs the suite on Python 3.10 to 3.13, gates coverage at 90%, and runs
218
+ every recipe's `smoke.sh`. [CONTRIBUTING.md](CONTRIBUTING.md).
219
+
220
+ ## Cite
221
+
222
+ ```bibtex
223
+ @software{weiss2026whileai,
224
+ title = {whileai: post-training data and evaluation for tool-using agents},
225
+ author = {Weiss, Jacob},
226
+ year = {2026},
227
+ url = {https://github.com/whilehq/whileai-sdk}
228
+ }
229
+ ```
230
+
231
+ ## References
232
+
233
+ 1. Lambert, N. *Reinforcement Learning from Human Feedback*. arXiv:2504.12501, 2025.
234
+ 2. Kuhn, D. R., Wallace, D. R., Gallo, A. M. Software Fault Interactions and Implications for Software Testing. *IEEE TSE* 30(6), 2004.
235
+ 3. Yao, S. et al. τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains. arXiv:2406.12045, 2024.
236
+ 4. Ouyang, L. et al. Training Language Models to Follow Instructions with Human Feedback. NeurIPS, 2022.
237
+ 5. Lambert, N. et al. Tülu 3: Pushing Frontiers in Open Language Model Post-Training. arXiv:2411.15124, 2024.
238
+ 6. Cohen, J. A Coefficient of Agreement for Nominal Scales. *Educational and Psychological Measurement* 20(1), 1960.
239
+ 7. Zheng, L. et al. Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena. NeurIPS, 2023.
240
+ 8. Chen, M. et al. Evaluating Large Language Models Trained on Code. arXiv:2107.03374, 2021.
241
+ 9. Wilson, E. B. Probable Inference, the Law of Succession, and Statistical Inference. *JASA* 22(158), 1927.
242
+ 10. Efron, B., Tibshirani, R. J. *An Introduction to the Bootstrap*. Chapman & Hall, 1993.
243
+ 11. Miller, E. Adding Error Bars to Evals. arXiv:2411.00640, 2024.
244
+ 12. Yu, Q. et al. DAPO: An Open-Source LLM Reinforcement Learning System at Scale. arXiv:2503.14476, 2025.
245
+ 13. He, J. et al. Skywork Open Reasoner 1 Technical Report. arXiv:2505.22312, 2025.
246
+ 14. Yuan, Z. et al. Scaling Relationship on Learning Mathematical Reasoning with Large Language Models. arXiv:2308.01825, 2023.
247
+ 15. Rafailov, R. et al. Direct Preference Optimization. NeurIPS, 2023.
248
+ 16. Touvron, H. et al. Llama 2: Open Foundation and Fine-Tuned Chat Models. arXiv:2307.09288, 2023.
249
+ 17. Gao, L., Schulman, J., Hilton, J. Scaling Laws for Reward Model Overoptimization. ICML, 2023.
250
+ 18. Sharma, M. et al. Towards Understanding Sycophancy in Language Models. ICLR, 2024.
251
+ 19. Shao, Z. et al. DeepSeekMath. arXiv:2402.03300, 2024.
252
+ 20. DeepSeek-AI. DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning. arXiv:2501.12948, 2025.
253
+ 21. Schulman, J. et al. Proximal Policy Optimization Algorithms. arXiv:1707.06347, 2017.
254
+ 22. Ziegler, D. M. et al. Fine-Tuning Language Models from Human Preferences. arXiv:1909.08593, 2019.
255
+ 23. Bai, Y. et al. Constitutional AI: Harmlessness from AI Feedback. arXiv:2212.08073, 2022.
256
+ 24. OpenAI. Model Spec, 2024. model-spec.openai.com.
257
+
258
+ ## License
259
+
260
+ Apache-2.0
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "whileai"
7
- version = "0.66"
7
+ version = "0.68"
8
8
  description = "Post-training data and evaluation for tool-using LLM agents: simulate, grade, measure, select, train, export, with the checks from rlhfbook.com."
9
9
  readme = "README.md"
10
10
  license = {text = "Apache-2.0"}
11
11
  requires-python = ">=3.10"
12
12
  dependencies = ["requests>=2.25.0", "pydantic>=2.0"]
13
- authors = [{name = "While"}]
13
+ authors = [{name = "Jacob Weiss"}]
14
14
  keywords = [
15
15
  "zp", "zeroproof", "while", "evals", "agent-evals",
16
16
  "rl", "reinforcement-learning", "grpo", "synthetic-data", "agents",