Skip to content

th-4c3e2d: Smooth Operator engine-parity benchmark (engine × model axis)#246

Open
brentrager wants to merge 6 commits into
mainfrom
th-4c3e2d-engine-parity-bench
Open

th-4c3e2d: Smooth Operator engine-parity benchmark (engine × model axis)#246
brentrager wants to merge 6 commits into
mainfrom
th-4c3e2d-engine-parity-bench

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Problem

No way to measure whether the 5 polyglot smooth-operator engines (rust/go/ts/python/dotnet) behave at parity. The mature smooth-bench harness was deleted in the microVM/daemon rewrite.

Solution

Resurrect the Aider-Polyglot slice of smooth-bench onto current main (the rest — SWE-bench/replay/research/tmux/tui scorers — intentionally omitted; resurrect later if needed), and add an engine × model axis:

  • crates/smooth-bench/src/engine.rsEngine enum with a pure boot_command(repo, port) mirroring operator-serve.sh's per-engine cases + env contract; ProcessBooter spawns each LocalServer, waits for the port, kills-on-drop.
  • run_engine_matrix (parameterised on an EngineBooter trait) loops engine×model, points the sweep at each booted engine, tags each Score with engine+model, emits JSON-lines + summary.
  • CLI: smooth-bench score --engine <...> (repeatable, default all) --model <id> (repeatable, default deepseek-v4-flash).

The slice compiled clean on first build against current main (only fixups: a ?Sized relaxation + a CommandExt import). smooth-operator now resolves to published smooai-smooth-operator-core v1.7.0.

Verification

  • cargo test -p smooai-smooth-bench110 passed. New tests: engine→boot-command mapping for all 5, distinct default ports, engine×model matrix aggregation via a FakeBooter+canned TaskRunner (no live LLM).
  • fmt/clippy/build clean; workspace pre-commit passed at commit time.
  • Caveat: live end-to-end scoring needs SMOOAI_GATEWAY_KEY + the smooth-operator repo + per-engine toolchains. Harness builds + unit-tests green without it.

Pearl th-4c3e2d (depends on th-3f46fd / PR #245).

🤖 Generated with Claude Code

Restore the deleted `smooth-bench` crate — the focused Aider-Polyglot
slice only (single-task runner, curated sweep, WS chat driver, scoring,
auto-approve) — against current main. Deps now resolve to the published
`smooai-smooth-operator-core` engine plus in-tree cast/code/pearls; the
slice compiled with only two mechanical fixups (`run_sweep` relaxed to
`?Sized`, `CommandExt` import). The SWE-bench/replay/research/cleanup/
TUI-driver scorers were dropped.

Add the engine-parity benchmark: `smooth-bench score` runs the curated
aider-polyglot suite through each of the five smooth-operator LocalServer
implementations (rust/go/ts/python/dotnet), scoring per engine and per
model.

- `--engine` (repeatable, default all) + `--model` (repeatable, default
  deepseek-v4-flash).
- Each engine booted the way `scripts/operator-serve.sh` does (uniform
  env contract + per-engine bind var), sweep driven over the canonical
  WS protocol, torn down before the next cell.
- Per-cell results carry engine+model and emit as JSON-lines + summary.
- Matrix runner is parameterised on an `EngineBooter` trait; the
  engine×model aggregation and engine→boot-command mapping are unit-
  tested without a live LLM. A real scoring run needs SMOOAI_GATEWAY_KEY.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Az1X4F5Ny3quF3pSBbLmz3
@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5d052b9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@smooai/smooth Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@brentrager
brentrager enabled auto-merge (squash) July 22, 2026 23:21
brentrager and others added 5 commits July 22, 2026 19:00
…in ensure_server

BigSmoothClient::ensure_server probed GET {url}/health and, on failure,
spawned 'th up' + waited 10s. The polyglot smooth-operator LocalServers
serve /ws but no /health, so the bench never reached the WS connect
(~65s = 5 retries). Short-circuit on a live TCP socket at the target
addr; the /health+autostart path still runs for th code when nothing
is listening. Layer 2 (canonical-protocol driver rewrite) tracked in
th-7232fb.

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

The bench's default drive path assumed the deleted microVM "create a pearl +
dispatch a teammate" model, and the legacy direct path spoke the retired
bespoke /ws handshake — so no engine ever completed a scored turn.

Replace both with a new `canonical_driver` speaking the schema-driven protocol
every engine now uses (create_conversation_session -> immediate_response
.sessionId -> send_message -> drain to eventual_response), modeled on the
daemon's OperatorTurnDriver::drive_once. It parses stream_chunk tool-result
events into the BenchResult tool-calls and best-effort-scans the terminal event
for cost (polyglot servers don't surface it -> $0, noted).

Boot the engine PER TASK with its workspace pointed at the task's scratch
work_dir: rust via SMOOTH_WORKSPACE; go/ts/python/dotnet (no workspace env)
with cwd = work_dir. Go runs a prebuilt binary (go run can't launch from a
foreign cwd); the rest use an absolute path / --project.

Retire chat_driver + the smooth-code headless dependency.

Verified live (deepseek-v4-flash / claude-haiku on the daemon): the go engine
boots per task, the canonical turn runs, tests execute, and a real scored table
is produced. The rust daemon engine additionally edits solution files in the
task workspace (read_file/edit_file/bash tool results parsed) — a real 5/6
scored table.

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

CI windows-latest failed: `cannot find unix in os` + `no method named
process_group`. The new-process-group call (so kill-on-drop reaps
`go run`/`dotnet run` children) is unix-only. Gate the import and the
call; the bench only ever RUNS on unix, but the crate must COMPILE on
windows for CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Az1X4F5Ny3quF3pSBbLmz3
The compile fix landed (tests now RUN on windows), exposing the next
failure: boot_command_mapping_matches_operator_serve asserts exact
unix-style path strings, but repo.join(..) yields backslashes on
windows. The bench only ever RUNS on unix (engines need unix
toolchains); the crate just has to compile+test green on windows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Az1X4F5Ny3quF3pSBbLmz3
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