Skip to main content
Situation writers, coverage axes, agent adapters, model backends. 18 public names. import whileai.simulations as wai, then wai.name.

adapters

Framework adapters. Every runner returns {steps, final_text}.

AgentProfile

claude_code

connect

inspect

Read tools and system prompt off the agent; caller extras are merged in.

agents

OpenAI-compatible chat loop for hosted and local simulation backends.

hosted_model

The default simulation brain: hosted Qwen wearing these tools.

local_model

An agent that talks to an OpenAI-compatible endpoint (a served adapter, a local vLLM, any chat server) for simulate(agent=...). thinking is for reasoning bases such as Qwen3: False sends chat_template_kwargs={"enable_thinking": False} so the reply is the answer, not the reasoning, the way the hosted Qwen path already does; True asks for it; None (the default) sends nothing and leaves the server’s default. The same field goes to the simulated user when the agent’s own model plays it (the default) or user_model sits on the same endpoint, so the customer is asked not to reason either; a user_model on another endpoint keeps that server’s default. Either way <think> markup never reaches step["text"], final_text, or a user turn (step["user"] and the messages history): what the user model still emits as reasoning is stripped before it becomes speech, and a turn that was reasoning with no spoken line is retried, then dropped (#284). The run reports those under search["user_think"]: user_turns, stripped and unclosed as counts, stripped_share and unclosed_share as shares of the user turns, zeros when none. result_shapes pins what a tool returns: \{tool_name: example result dict\}. The sandbox fills the example on every call instead of inventing a record, so a policy branch that only exists for some tool results (a credit over $200 must be escalated) is reached on purpose rather than by luck. Field names and free text stay as written; ids, dates and people are re-drawn per call, and a number moves by up to about a third of itself (900.0 lands in roughly 600 to 1200, 90.0 in 60 to 120), so pick a template value whose whole range sits on the side of the threshold you want. An argument that shares a key with the template is echoed back (invoice_id in, same invoice_id out). To measure a branch, run the same pinned tasks under two shapes, one per side of the rule. Without it the situation writer drafts an example per tool (write_result_shapes) and the branch is exercised at random. fault_plans schedules faults per ask: \{message: \{tool_name: \{"mode": "timeout", "rate": 1.0\}\}\}, keyed by the exact user message, with mode one of timeout, malformed, stale or permission_denied and rate the chance the fault fires on a call. The plan may also carry world_state, stance, tone and texture, which are popped off and shape the world and the simulated user for that ask. simulate() writes these itself from fault_rate=; pass your own only to replay a known plan (tasks= does this for you). timeout is seconds per completion, LOCAL_MODEL_TIMEOUT (300) by default: a served model that scaled to zero takes two to three minutes to answer its first request, and a timeout under that drops every rollout of the first pass. When a call still times out the run says so in data.warnings with the fix (raise timeout=, or send one throwaway request first so the endpoint is warm). patience is a level name (PATIENCE_LEVELS) or a table {"second": p, "later": q}: the chance the person leaves at the agent’s second question and at every later one, fitted from your own traces (see PATIENCE_HAZARDS). user_temperature is the sampling temperature of every simulated-user line, follow-ups (USER_TURN_TEMPERATURE) and human-tool answers (HUMAN_TOOL_TEMPERATURE) alike; None keeps those two defaults. world_options is the mock world’s dials (a WorldOptions or the same fields as a dict: fault modes, hit counts, name pools, …); simulate(advanced={"world": {...}}) lands here. None is the defaults in defaults.py.

diversity

Sparse generic writer knobs and annealing helpers.

adaptive_allocator

Adaptive mix. Short remaining clock is messier; saturation walks more cards. Shares are explore / expand / verify. n_req and k are caps so expand and verify can actually run. Not a pinned n=1 k=1 policy.

allocator_slot_counts

Integer explore/expand/verify slots from mix shares.

generator

Model-driven scenario generation. Templates are bootstrap and offline fallback.

ModelSimulator

Invent what a person might send, ask, or discuss; it never grades.

write_scene_brief

One cheap LLM pass per simulate(). Private writer context. Empty on failure.

offline_agent

The free path, with something to catch.

World

The mock world for a callable agent, faults first. Built once; call reads the rollout being answered from current_rollout (thread-local, set by simulate before each rollout) so the row’s own faults and world_state apply. Outside a run it answers with no faults and no world state.

World.call

The world’s answer to one tool call. Scheduled faults apply first.

seeded_agent

A demo agent whose mistakes are on purpose and on the row. Honest by default: it picks the tool the ask names, calls it through world() (faults fire), and reports what came back. On rate of rollouts, drawn deterministically from seed, the prompt and the rollout index, it does one thing from behaviors (default SEEDED_BEHAVIORS): hedging, sycophancy, apology and boilerplate add the phrase style_report looks for; ignore_fault claims success although the tool faulted; leak quotes the row’s privileged context. Each row it answers carries seeded: what it did on purpose, [] when it behaved.

world

A World for a callable agent: world.call(tool, arguments). Pass the same tools you pass simulate. Inside a run the row’s scheduled faults fire on it; a callable that answers its tool calls through this sees them, and the row’s faults stop being a label with no effect.

scenarios

Coverage cells from the agent’s tools and policy, plus offline fallback wording.

build_dimensions

Coverage axes from this agent. Length and vagueness are writer-only.

novelty

Min cosine distance from a candidate embedding to every tested row.

open_ended_probes

Taxonomy-free probes. Wording rotates with seed.

policy_sections

Split policy text into short rule clauses used as coverage cells. Identity / system-prompt preambles are not clauses. A long unsplit paragraph is dropped rather than truncated mid-word into rule.

scenario_regions

Weighted target regions over a pairwise covering set of the dimensions. prefer_success defaults off in mode="rl" so fault cells survive for covering-grid RL data. Explicit True/False always wins.