Skip to content

provider: add uv SandboxProvider (PR #122 stage C)#135

Merged
Meirtz merged 1 commit into
masterfrom
provider/uv
Jul 2, 2026
Merged

provider: add uv SandboxProvider (PR #122 stage C)#135
Meirtz merged 1 commit into
masterfrom
provider/uv

Conversation

@Meirtz

@Meirtz Meirtz commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Third slice of the #122 split (stage map in the coordination comment; A = #133, B = #134). Cherry-picks the UvProvider from origin/abridge/gateway-tito (efedacf + the ruff hunk from 90f970a), plus fixes from the pre-PR adversarial review.

What's in

plugins/providers/uv — a lightweight SandboxProvider that skips Docker/Nix entirely: uv venv + uv pip install -e <project> (or reuse_venv=), then the runtime server runs as a local subprocess. For dev / eval / CI where container isolation isn't needed; registered as entry point uv.

Fixes on top of the port

  • stdout drain (the planned deadlock fix): nothing read the server's pipe after the health check — asyncio's flow control pauses reading at ~192KiB buffered, so a server that logged past that blocked mid-write and wedged every in-flight rollout. Now drained for the sandbox's lifetime into a bounded 8×64KiB tail, which also feeds the exited-before-health diagnostic.
  • CancelledError masking the crash diagnostic: the diagnostic path used wait_for, which cancels the drain task on timeout; delete()'s re-await then surfaced a bare CancelledError instead of the RuntimeError with rc + log tail (reproduced with a grandchild holding the pipe open). Switched to non-cancelling asyncio.wait.
  • Port-collision guard: the allocated port is only bound by the subprocess seconds later — concurrent create()s could collide (worst case: two rollouts silently sharing one runtime). Reserved in-process until the sandbox dies, mirroring DockerProvider's _inflight_ports.
  • Temp-root leak: a failed uv venv / uv pip install now removes its mkdtemp root (a retry loop leaked one partial venv per attempt).
  • Packaged-uv resolution: the default binary resolves via uv.find_uv_bin() — the wheel's binary isn't on PATH under systemd/cron/absolute-path launches, exactly where no system uv exists; a bare FileNotFoundError now carries install guidance.
  • README/docstring examples call provider.aclose() (the only thing that removes a materialized venv).

Verification

  • uv run pytest tests/ plugins/ — 496 passed (10 uv-provider tests: real subprocess servers via a fake-venv shim — flood/drain, grandchild-held pipe, port guard, temp cleanup, off-PATH resolution, plus the ported remote() round-trip)
  • uv run ruff check . / uv run pyright — clean / 0 errors
  • Adversarial review: 6 findings (2 major), all fixed above

🤖 Generated with Claude Code

Cherry-pick of efedacf from origin/abridge/gateway-tito (+ its ruff
import-order hunk from 90f970a): a lightweight SandboxProvider at
plugins/providers/uv that materializes a venv with uv and launches the
runtime server as a local subprocess — for dev / eval / CI where
container isolation isn't needed. Registered as entry point 'uv';
wired into pyright include/extraPaths (workspace membership via the
existing plugins/providers/* glob).

Fixes on top of the port (planned + adversarial review):

- stdout drain: the runtime's merged stdout/stderr is drained for the
  sandbox's lifetime into a bounded tail (8x64KiB). Previously nothing
  read the pipe after health — asyncio flow control pauses at ~192KiB
  buffered, so a server that logged past that blocked mid-write and
  wedged every in-flight rollout. The tail also feeds the
  exited-before-health diagnostic (was a one-shot read).
- that diagnostic path uses asyncio.wait, not wait_for — wait_for
  cancels the drain on timeout and delete()'s re-await then surfaced a
  bare CancelledError instead of the RuntimeError diagnostic (repro: a
  grandchild holding the pipe open past the 2s window).
- ports are reserved in-process until the sandbox dies (same
  _inflight_ports guard as DockerProvider) — the subprocess binds the
  number seconds after allocation, so concurrent creates could collide:
  worst case two rollouts silently sharing one runtime.
- a failed venv materialization removes its mkdtemp root (a retry loop
  leaked one partial venv per attempt).
- the default uv binary resolves via uv.find_uv_bin() — the packaged
  wheel's binary is not on PATH under systemd/cron/absolute-path
  launches; a bare FileNotFoundError also now carries install guidance.
- README/docstring examples close the provider (aclose is the only
  thing that removes a materialized venv).

Tests: flood server proving the drain (pre-fix it wedges mid-write),
grandchild-held pipe diagnostic, port-collision guard, temp-root
cleanup, packaged-uv resolution off-PATH.

Co-authored-by: FatPigeorz <wjhhhhhha0@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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