drums-verifiers
Drums environments for the verifiers library and prime-rl: isolated desktops, OSWorld tasks and audited rewards for computer-use RL.
Website · Docs · Benchmarks · Console
What it is
Drums runs many isolated Linux desktops from one running operating system: a host daemon, drumsd, restores a checkpointed desktop per environment, forks a running environment into copies of the same state, and resets it in milliseconds instead of booting a VM. This package connects that runtime to the verifiers v1 API, so prime-rl (and, through adapters, verl and SkyRL) can run computer-use rollouts on it.
One rollout is one Drums environment running one task. The model acts through computer_* tools and gets a
screenshot back after every action. The reward is the task's own checker, run by drumsd outside the desktop, returned
together with the episode's validity verdict and a reward audit. A reward that cannot be trusted never reaches the
trainer as a reward.
The repository has three parts:
| Part | Package | What it does |
|---|---|---|
| The environment | drums_verifiers, drums_chat |
DrumsTaskset and DrumsEnv for verifiers and prime-rl: OSWorld tasks, the computer_* tool server, fork groups for GRPO, the drop rules for invalid episodes, and a chat harness with a visual-context window |
| The task factory | drums_tasks |
Seeded, parametrized tasks across LibreOffice Calc, Writer and Impress, Chrome, GIMP, VS Code, files and multi-app workflows, each with a programmatic checker, a redundant checker, a scripted solution and near-miss solutions, plus the validator that proves every task on a live host |
| Trainer adapters | drums_adapters |
A verl tool and agent loop, and a SkyRL environment, one desktop per trajectory |
Quickstart
Install
Python 3.11 or later. The package pulls verifiers (0.4.1 or later, the version prime-rl requires) and the Drums SDK
(drums-sdk, import name drums) from PyPI.
python3 -m venv .venv && . .venv/bin/activate
pip install "drums-verifiers[test]"
To work from a checkout instead: pip install -e ".[test]" in the repository.
Extras: test (pytest), verl, skyrl, and factory (the task generator's pins; it needs its own venv, see
Development).
Run a rollout without a Drums host
The test suite ships a fake drumsd (tests/fake_drumsd.py) and a scripted stand-in for the model
(examples/mock_policy.py), so the whole path (verifiers, the environment, the tool server, the SDK) runs on a laptop.
From a clone of this repository, in the venv above:
echo t0k > /tmp/drums-token
python tests/fake_drumsd.py 7070 t0k &
python examples/mock_policy.py --port 8011 &
export DRUMS_URL=http://127.0.0.1:7070
export DRUMS_TOKEN_FILE=/tmp/drums-token
export DRUMS_RECORD=/tmp/drums-records.jsonl
export MOCK_KEY=x
vf-run drums-verifiers -m mock -n 1 -r 2 --client.base-url http://127.0.0.1:8011/v1 --client.api-key-var MOCK_KEY
Two rollouts run (one probe, one scripted solve), and DRUMS_RECORD gets one line per scored trace:
Output
env drums-verifiers · drums-chat harness · subprocess runtime
model mock via http://127.0.0.1:8011/v1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2/2 · 10s · reward 0.50 · err 0.00
rewards task_success 0.50
metrics drums_valid 1.00 · drums_actions 4.00
╭ [success ] task name=e0df059f agent=agent · 3fd18077 · subp… reward=0.00 · 8s
╰ [success ] task name=e0df059f agent=agent · 6c14bc89 · subp… reward=1.00 · 10s
{"env": "s102", "valid": true, "verdict": "valid", "score": 0.0, "reward": 0.0, "dropped": null, "trusted": null, "turns": 5}
{"env": "s101", "valid": true, "verdict": "valid", "score": 1.0, "reward": 1.0, "dropped": null, "trusted": null, "turns": 7}
(records trimmed to the fields that matter; trusted is null because the fake drumsd sends no audit)
Run against a Drums host
Needs a Drums host: a drumsd (v0.2.0 or later) that the env workers can reach, a tunnel is fine. The token is the
host's /etc/drums/token, or an API key from the console for a hosted Drums.
export DRUMS_URL=http://127.0.0.1:7070
export DRUMS_TOKEN_FILE=/etc/drums/token
Then the same vf-run command with a real model endpoint that serves tool calls (-m and --client.base-url), or
prime-rl:
[[orchestrator.train.source]]
name = "drums"
env.taskset.id = "drums-verifiers"
env.taskset.task_ids = ["e0df059f-28a6-4169-924f-b9623e7184cc"] # OSWorld ids, served by drumsd's pinned checkout
env.agent.max_turns = 15
# optional: GRPO from one reached state. One episode = one parent env, `prefix`, then N forked children
env.fork_n = 8
env.prefix = ["pyautogui.click(960, 540)"]
With fork_n = N, the N child traces of an episode form the comparison group: use group_size = 1 with grpo. With
fork_n = 0 (the default) use group_size as usual. A complete config is in
examples/rl-drums-fork.toml; uv run rl @ examples/rl-drums-fork.toml --dry-run
validates it on any machine.
How it works
flowchart LR
T[prime-rl orchestrator] -->|episode| E[DrumsEnv]
E -->|create / fork / close| D[drumsd]
D --> X[(desktop)]
E -->|task + first screenshot| A[agent: drums-chat harness]
A -->|computer_* tool call| S[tool server]
S -->|step| D
D -->|screenshot| S --> A
A -->|final reply| R[DrumsTask.task_success]
R -->|verify| D
D -->|score + verdict + audit| R
R -->|valid: reward = score| T
R -->|invalid: trace dropped, no reward| T
DrumsEnv.runcreates the environment with the task (drumsd runs the task's own setup), waits until every gate of the episode has passed, and opens the conversation with the instruction and the first screenshot. Withfork_n, it playsprefixon the parent, lets the desktop settle, forks N children and runs one agent per child.- The agent acts through MCP tools (
tools.py). Each tool is one action in the desktop, a pause, and a screenshot. Three tool styles:drums(one tool per action, pixel coordinates),scalecua(the ScaleCUA agent's singlecomputertool and prompt, for ScaleCUA checkpoints) andhybrid(the GUI tools plus shell, file and app tools on the same desktop). DrumsTask.task_successcallsenv.verify(): drumsd runs the task's checker outside the desktop and returns the score, the gate verdict and the reward audit. The environment is closed after scoring, and fork states are deleted.
Task format
A task is an OSWorld task JSON: id, instruction, config (setup steps drumsd runs before the episode) and an
evaluator. OSWorld's own tasks are served by id from drumsd's pinned OSWorld checkout (task_ids), or read from a
directory of <id>.json files (tasks_dir).
The task factory (drums_tasks) generates tasks in the same format with a drums_verifier evaluator: a function in
the lab verifier drums_t6.py, its options, a redundant checker, and a scope manifest (the sha256 of every seeded file
under the task's roots, plus the globs the task is allowed to change). 37 families across seven apps; the default
build is 1,640 tasks with 4,285 near-miss solutions. Everything is a function of (seed, family, index):
python -m drums_tasks.build --seed 1 --out /tmp/ts --check
writes tasks.jsonl (what an environment is created with; no solutions inside), solutions.jsonl (the scripted
oracle, the near misses and the scope probe of every task), drums_t6.py (installed as root into
/etc/drums/verifiers/ on the host) and index.json (counts and the sha256 of each file). --check builds twice and
fails on any byte difference. Only tasks.jsonl and the verifier go to a host; the solutions are for the validator
(python -m drums_tasks.validate), which forks every task into no-op, oracle, near misses and scope probe on a live
host and accepts a task only when the checker separates them. The generator is deterministic, so anyone with the code
and a seed can rebuild a task set and its solutions; treat the seed of an evaluation set accordingly.
How to add a family: CONTRIBUTING.md.
Verifier contract
The reward never reaches the trainer without its verdict:
| What happened | Trace | prime-rl |
|---|---|---|
| Every gate passed, the checker ran | reward = score (0 or 1) |
trained |
| A gate failed (isolation, mounts, network, cgroup limits, state hash, processes alive after restore), at the start or mid-episode (drumsd quarantines the environment) | no reward; dropped = "drums_invalid:<verdict>:<gates>" |
skipped by iter_trainable_traces, left out of eval means |
| The checker failed, or drumsd or the desktop failed mid-episode | error (ok = False) |
skipped |
Every scored trace carries trace.info["drums"]: environment and fork parent, verdict and failed gates, score,
checker error, action counts, and the reward audit drumsd attached:
trustedisTrueonly for a valid episode with a score and no audit flag. An older drumsd sends no audit, sotrustedisNone(unknown), neverTrueby default. The metricdrums_reward_trustedis recorded when it is known.audit_flagsname what went wrong:nondeterministic(the same checker gave another answer on a recheck),redundant_disagree(the task's independent second checker disagreed),scope_violation(a file outside the task's allowed globs changed),checker_crash,checker_timeout,scope_unchecked.- Redundant checkers: every factory task names a second checker (
check_alt) that reads the same state through a different route and parser (the desktop's own tools instead of the file API; raw OOXML instead of openpyxl, python-docx and python-pptx; PyMuPDF instead of pypdf). drumsd runs both and compares them.
The audit never changes score or valid; this package drops invalid episodes and leaves the use of trusted and
the flags to the trainer. DRUMS_RECORD=path.jsonl writes one line per scored trace (environment, parent, verdict,
failed gates, score, reward handed out, audit flags, trusted, drop reason, timings) for offline audit.
Vendored code: SCALE-CUA
src/drums_verifiers/scalecua.py carries the ScaleCUA OSWorld agent format (system prompt, the single computer
tool, tool call to pyautogui), copied from
THUDM/SCALE-CUA at commit 3929e2fe (osworld_eval/mm_agents/scalecua-osworld/utils.py
and main.py), Apache License 2.0, Copyright the SCALE-CUA authors. One modification, recorded in the file as section
4(b) of the licence requires: the system prompt no longer gives the model the image's sudo password. In a Drums
environment the desktop user has no privilege escalation and an agent is never handed a credential.
Development
python3 -m venv .venv && . .venv/bin/activate
pip install -e '.[test]'
ruff check .
python -m pytest tests
pytest tests runs the unit tests and an end-to-end vf-run run against the fake drumsd and the mock policy: single
environments, fork groups, invalid children that are dropped, a mid-episode environment failure, the reuse guard,
quarantine. Nothing in tests/ talks to a real drumsd. CI (ci.yml) runs exactly these
three commands on Ubuntu with Python 3.11 and 3.12.
The task factory's generator pins pillow==11.0.0 (drumsd's checker venv), which conflicts with prime-rl's renderers,
so it lives in its own venv; the factory tests also need the checker venv's parsers:
python3 -m venv .venv-factory
.venv-factory/bin/pip install -r t6/requirements-factory.txt pymupdf==1.28.2 pypdf==6.20.1 pandas==2.2.3 pytest pytest-asyncio
.venv-factory/bin/pip install -e . --no-deps
.venv-factory/bin/python -m pytest tests/test_factory.py
Validation on a live host (drums_tasks.validate), the acceptance run on a fresh host with a real model
(acceptance/), the replication pipeline (t6/) and the GPU setup for prime-rl (gpu/) need a Drums host; their
scripts document their own inputs.
Benchmarks
Measurements of Drums against other runtimes, with their conditions (paired or unpaired, replicated or not, scripted policy or model), are at bench.drums.run. This repository makes no performance claim of its own.
Security
Report vulnerabilities through GitHub's private vulnerability reporting, see SECURITY.md. The
environment never puts the drumsd URL or token into task data or traces; the token comes from DRUMS_TOKEN_FILE
(default /etc/drums/token) or DRUMS_TOKEN. What the desktop isolates and what it does not is documented in
SECURITY.md and in the docs.
Contributing
CONTRIBUTING.md: set-up, tests, how to add a task family, and what a pull request needs (a Developer Certificate of Origin sign-off, tests, a validation run for a new family). Code of conduct: CODE_OF_CONDUCT.md.
Licence
Apache License 2.0, see LICENSE. src/drums_verifiers/scalecua.py is derived from Apache-2.0 code of the
SCALE-CUA authors, as described above.
Status
Beta. Tested against verifiers 60290d43 (the commit prime-rl a566e5d8 pins), drumsd v0.2.0 and drums-sdk 0.2.5.
- Stable: the taskset and environment configuration, the drop rules, the record format, the three tool styles.
- Working, may change: the task factory's families and checker kinds (
GENERATOR_VERSIONchanges when a build's bytes change), the verl and SkyRL adapters. - Not verified: running this package through the Drums Cloud gateway rather than against a drumsd host directly.
Metadata
Release files for drums-verifiers 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| drums_verifiers-0.2.1.tar.gz | 134.9 kB | Without provenance | Details for drums_verifiers-0.2.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| drums_verifiers-0.2.1-py3-none-any.whl | Python 3 | none | any | Details for drums_verifiers-0.2.1-py3-none-any.whl |
Total release size: 223.7 kB
Release files / drums_verifiers-0.2.1.tar.gz
| Download URL | drums_verifiers-0.2.1.tar.gz |
|---|---|
| Size | 134.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9ab0d5a4643c45275d64ff969b09f5d3fd86f4e576c0fd12606718928eb94e98
|
|
BLAKE2b-256 checksum How to use checksums |
df15022f92c0906a72b0cacbc1b8000e2f45b1ece257df110cc70f24fde0b76c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Release files / drums_verifiers-0.2.1-py3-none-any.whl
| Download URL | drums_verifiers-0.2.1-py3-none-any.whl |
|---|---|
| Size | 88.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b372c29c043f45761b5cbbbc91e4a46733387ff6a9bf7591c6c47d8d40951278
|
|
BLAKE2b-256 checksum How to use checksums |
3a5572cd300b0e2d5e41a3e21552c784e29e2e01e98a4246d62c224c8aece5ed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Security for drums-verifiers 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.