Add multi-region sandbox support - #308
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds multi-region sandbox support to the vercel-sandbox Python SDK by introducing an ordered regions input (preferred region first, remaining as failovers) and mapping that to the backend’s region + failoverRegions fields across create, fork, get-or-create, update, and snapshot metadata.
Changes:
- Add
regionsas an ordered public input for sandbox create/fork/get-or-create/update flows, normalized internally. - Extend API request/response modeling to encode/decode
region+failoverRegions, and expose snapshot region availability as a tuple. - Update tests, an example, and add a news fragment documenting the new capability.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/vercel-sandbox/vercel/sandbox/sync.py | Exposes regions on sync convenience functions and forwards through to internal runtime. |
| src/vercel-sandbox/vercel/sandbox/_internal/sync_runtime.py | Wires regions through sync runtime operations and normalizes inputs before service/API calls. |
| src/vercel-sandbox/vercel/sandbox/_internal/state.py | Replaces single region state with ordered regions for sandboxes and snapshots. |
| src/vercel-sandbox/vercel/sandbox/_internal/service.py | Adds regions plumbing on service methods so API client can encode region/failovers. |
| src/vercel-sandbox/vercel/sandbox/_internal/runtime_common.py | Updates public handle accessors to expose regions on sandbox and snapshot handles. |
| src/vercel-sandbox/vercel/sandbox/_internal/models.py | Introduces RegionsInput and normalize_regions() validation/normalization helper. |
| src/vercel-sandbox/vercel/sandbox/_internal/async_runtime.py | Adds regions to async operations/updates and passes normalized tuples into service/API layers. |
| src/vercel-sandbox/vercel/sandbox/_internal/api_client.py | Implements request encoding (region/failoverRegions) and response decoding into ordered regions. |
| src/vercel-sandbox/vercel/sandbox/init.py | Adds regions to the public async surface functions and re-exports the input type for typing. |
| src/vercel-sandbox/tests/test_sandbox_public_flow.py | Updates mocks/assertions to validate request encoding and handle properties for multi-region. |
| src/vercel-sandbox/examples/sandbox_03_snapshot_restore.py | Demonstrates creating a sandbox with multiple regions and printing snapshot region availability. |
| changes/vercel-sandbox/multi-region.feature.md | Adds a changelog/news fragment for the new regions configuration feature. |
Suppressed comments (1)
src/vercel-sandbox/vercel/sandbox/_internal/runtime_common.py:463
- This change removes the
Sandbox.regionaccessor in favor ofSandbox.regions, which is a breaking API change for consumers. Consider keepingregionas a compatibility alias that returns the preferred region (regions[0]).
@property
def regions(self) -> tuple[str, ...]:
return self._payload.regions
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Expose ordered region preferences for sandbox creation, forks, and updates while keeping backend field mapping internal. Report sandbox and snapshot availability as immutable region tuples while sessions retain their actual landing region.
d3dfb20 to
4eb485f
Compare
I was wrong in my assumption that failover_regions was an ordered preference. Failover actually works by selecting the next closest region, so instead of having to explain that explicitly, let's just do the obvious thing here.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/vercel-sandbox/vercel/sandbox/_internal/models.py:34
normalize_failover_regionswill treat a single string like "sfo1" as an iterable of characters ("s", "f", "o", "1"), which will pass the current validation and produce an invalidfailoverRegionspayload. This should explicitly rejectstr/bytesinputs so accidental misuse fails fast with a clear error.
if regions is None:
return None
normalized = tuple(regions)
if any(not isinstance(region, str) or not region for region in normalized):
raise ValueError("failover_regions must contain non-empty strings")
This ports the multi-region sandbox support from vercel/sandbox#301 across create, fork, get-or-create, update, and snapshot metadata.
The Python API intentionally differs a little from the TypeScript SDK: instead of separateregionandfailoverRegionsvalues, it accepts one orderedregionsiterable. The first region is preferred and the rest are failovers, which keeps the placement preference together and maps back to the backend fields internally.Edit: I went back on this due to how the actual backend treats
regionandfailoverRegions. Failovers are not ordered by list-order, they are ordered by proximity to the primary region. This changes what I had originally understood, so we're going with two separate properties.