Skip to content

feat: evaluate openadapt-flow on WAA (demonstrate-then-replay + hybrid-as-agent) with cost-guarded dry-run#265

Merged
abrichr merged 3 commits into
mainfrom
feat/openadapt-flow-waa-agent
Jul 14, 2026
Merged

feat: evaluate openadapt-flow on WAA (demonstrate-then-replay + hybrid-as-agent) with cost-guarded dry-run#265
abrichr merged 3 commits into
mainfrom
feat/openadapt-flow-waa-agent

Conversation

@abrichr

@abrichr abrichr commented Jul 14, 2026

Copy link
Copy Markdown
Member

What

Wires openadapt-flow (the demonstration compiler) into the existing WAA benchmark harness so we can evaluate it on WindowsAgentArena. Two eval modes, both scored by WAA's own task verifier (not self-report). Ships the code + a cost model. No Azure is provisioned and no money is spent — everything here is mock/dry-run testable locally; the live run is gated on the maintainer's explicit go.

Two eval modes

  1. Demonstrate-then-replay (openadapt_evals/flow/replay_runner.py) — the paradigm-correct eval for a demonstration compiler. Compile ONE demo into a bundle and replay it via openadapt-flow's WindowsBackend against the WAA in-guest /screenshot + /execute_windows server (the SAME contract WAA exposes — no adapter layer). Per-task metrics: WAA-verified success, replay self-report, structural rung fire rate, model calls (~0 on healthy replay), wall-clock, halt/heal events.
  2. Hybrid-as-agent (openadapt_evals/flow/hybrid_agent.py) — HybridFlowAgent implements BenchmarkAgent, so the existing evaluate_agent_on_benchmark runner drives it exactly like any agent. Compiled replay first; computer-use agent fallback only on a detected halt, gated by a shared SpendLedger. Directly comparable to a pure agent baseline on the same tasks / same verifier.

Cost model + hard guardrails

openadapt_evals/flow/cost.py (stdlib-only, no flow/VM/key needed): estimates $ for N tasks (Azure VM-hours + agent-arm token cost) and enforces a per-run $ cap, total $ ceiling, per-task token cap, and abort-on-repeated-billing-error — mirroring openadapt-flow's benchmark SpendLedger (the prior $40-70 uncapped-run incident is why these are mandatory).

scripts/eval_flow_on_waa.py is dry-run by default — prints the plan + cost and never provisions Azure/starts a VM/spends money. scripts/waa_cost_estimate.py gains a flow-aware --tasks/--mode/--dry-run path (legacy table unchanged with no args).

Estimated cost (Claude Sonnet 4, $0.19/hr VM)

Mode 10 tasks 154 tasks Pure-agent baseline (154)
replay ~$0.08 ~$0.54 (VM only, $0 tokens) ~$39
hybrid @30% fallback ~$6.15 (VM + $5.33 tokens) ~$39

(The $6.15 hybrid@154 case trips the default $5 total cap — the guard prints a WARNING and a --live run is refused until the cap is raised.)

Runnable now vs. needs Azure + $

  • Runnable now (mock/dry-run, this PR's tests): cost model, guardrails, both runners with injected fakes, and --dry-run plan/estimate. 30 new tests, zero Azure, zero VM, zero flow dependency.
  • Needs Azure + $ + revived waa-pool: the actual replay/hybrid execution — oa-vm pool-create / pool-wait, compiled bundles per task, then --live. Gated behind --live + a reachable server; refuses on missing bundles before any network call.

Dependency note

openadapt-flow is added as an optional [flow] extra (on PyPI, 0.20.0). All replay/hybrid code lazy-imports it. openadapt_flow.backends.win_agent (PR #95, still open) is not required for the WAA path — WAA already exposes the same in-guest server contract that WindowsBackend targets. Verified at API level against the real flow package (WindowsBackend server_url, RunReport fields) without a VM.

Tests

tests/test_flow_waa_cost.py, tests/test_flow_replay_runner.py, tests/test_flow_hybrid_agent.py, tests/test_eval_flow_on_waa_dryrun.py — 30 passing, fully mocked. Existing WAA/mock/cost suites (113 tests) still green; ruff clean on all new/modified files.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ

abrichr and others added 3 commits July 14, 2026 00:10
…d-as-agent) with cost-guarded dry-run

Wire openadapt-flow (the demonstration compiler) into the WAA benchmark
harness with two eval modes, both scored by WAA's own task verifier:

- demonstrate-then-replay (openadapt_evals/flow/replay_runner.py): compile
  ONE demo into a bundle and replay it via openadapt-flow's WindowsBackend
  against the WAA in-guest /screenshot + /execute_windows server (~0 model
  calls). Emits per-task WAA-verified success, structural rung fire rate,
  model calls, wall-clock, and halt/heal events.
- hybrid-as-agent (openadapt_evals/flow/hybrid_agent.py): HybridFlowAgent
  implements BenchmarkAgent so the existing runner can drive it -- compiled
  replay first, computer-use agent fallback only on a detected halt, gated by
  a SpendLedger. Directly comparable to a pure agent baseline on the same
  tasks.

Cost model + hard guardrails (openadapt_evals/flow/cost.py, stdlib-only):
per-run $ cap, total $ ceiling, per-task token cap, abort-on-repeated-billing
-error (mirrors openadapt-flow's SpendLedger; the prior $40-70 uncapped-run
incident is why these are mandatory). scripts/eval_flow_on_waa.py is dry-run
by default -- prints the plan + cost for N tasks and never provisions Azure,
starts a VM, or spends money. waa_cost_estimate.py gains a flow-aware
--tasks/--mode/--dry-run path.

openadapt-flow is an optional [flow] extra; all replay/hybrid code lazy-imports
it, so the cost model and dry-run work with zero flow dependency and the whole
integration is mock/dry-run testable locally (30 new tests, no Azure, no VM).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ
…he hybrid agent's paid fallback

The observation handed to the first fallback act() predates the compiled
replay's VM mutations (replay drove the WAA server directly, bypassing the
adapter). A no-op WAIT forces the runner to fetch current state before the
paid computer-use agent takes its first real step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ
…low harness

Adds a LOCAL Parallels environment so the demonstrate-then-replay eval mode runs
at $0 against the Parallels Win11 VM (native Apple-Silicon virt) with the SAME
runner + metrics as WAA -- for de-risking before spending Azure $.

- openadapt_evals/flow/parallels_env.py: ParallelsSession (snapshot -> run ->
  revert, never stops/deletes the VM), reuses openadapt-flow's ParallelsVM
  (prlctl wrapper) + win_agent server (PR #95) via launch_agent; once up it
  exposes the SAME /screenshot + /execute_windows contract WAA does, so the
  identical WindowsBackend replay path works unchanged. Parallels supplies the
  ENVIRONMENT but not WAA's tasks/verifiers -- built-in trivial Notepad/
  Calculator tasks + ground-truth in-guest verifiers are supplied here.
  Opt-in gated (OPENADAPT_PARALLELS=1), skipped by default.
- scripts/eval_flow_on_waa.py: --env {waa,parallels}. Parallels dry-run shows
  $0 (local), the opt-in gate status, and the snapshot-safe note; --live is
  gated on the opt-in var and refuses otherwise.

Local-$0 (Parallels) vs cloud-$ (WAA/Azure) is labelled everywhere. Heavy
imports (openadapt_flow) stay lazy and the VM + replay are injectable, so the
whole path is mock/dry-run testable (10 new tests) with no prlctl, no VM, no
mutation. Note: launch_agent/win_agent ship in openadapt-flow PR #95 (still
open) -- a live local run depends on that; the dry-run + tests do not.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ
@abrichr abrichr merged commit b95e27d into main Jul 14, 2026
1 check passed
@abrichr abrichr deleted the feat/openadapt-flow-waa-agent branch July 14, 2026 04:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant