Skip to content

Commit 7a652c0

Browse files
committed
refactor(sprites→fly): rename SDK abstraction to use Fly as vendor
Reviewer feedback: every other provider in agents-python uses the vendor name as the package directory (e.g. \`vercel/sandbox.py\`, \`e2b/sandbox.py\`). Sprites is the *product*, not the vendor — the vendor is Fly. Aligning with the convention so future Fly extensions (DB, volumes, etc.) have a coherent namespace and so the SDK reads consistently with peer providers. Renames at the SDK abstraction layer only: - \`agents.extensions.sandbox.sprites/\` → \`agents.extensions.sandbox.flyio/\` - \`SpritesSandbox*\` classes → \`FlySandbox*\` - \`Sprites{PlatformContext,UrlAccess,Checkpoints}\` → \`Fly*\` - \`DEFAULT_SPRITES_*\` constants → \`DEFAULT_FLY_SANDBOX_*\` - Polymorphic discriminators: \`type: Literal["sprites"]\` → \`Literal["fly"]\` (capability discriminators \`sprites_*\` → \`fly_*\` to match) - \`backend_id\` and error contexts: \`"sprites"\` → \`"fly"\` - Test file: \`test_sandbox_sprites.py\` → \`test_sandbox_fly.py\` - Example runner: \`sprites_runner.py\` → \`fly_runner.py\` - Docs ref dir: \`docs/ref/extensions/sandbox/sprites/\` → \`flyio/\` Intentionally NOT renamed (real-platform layer): - \`sprites-py\` PyPI dependency (Fly's own package; we don't own the name) - \`[sprites]\` optional extra - \`SPRITES_API_TOKEN\` / \`SPRITES_API_URL\` env vars (literal platform credentials) - \`import sprites\` statements in implementation - \`/.sprite/llm.txt\` literal path - \`sprite-env\` CLI references in system-prompt text and docs - Internal \`_SPRITE_*\` constants that describe platform behavior Docs framing now describes the SDK as "Fly's sandbox product, powered by Sprites" — the user-facing API uses \`FlySandboxClient\` while authentication still flows through Sprites credentials, matching the platform reality. Compat-guard parametrize entries updated for the new module path, class names, and discriminator strings. 119 unit tests pass; mypy/ruff clean repo-wide; live smoke-tested against a real sprite.
1 parent c8e501c commit 7a652c0

12 files changed

Lines changed: 308 additions & 304 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# `Sandbox`
2+
3+
::: agents.extensions.sandbox.flyio.sandbox

docs/ref/extensions/sandbox/sprites/sandbox.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

docs/sandbox/clients.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ For provider-specific setup notes and links for the checked-in extension example
9696
| `E2BSandboxClient` | `openai-agents[e2b]` | [E2B runner](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/extensions/e2b_runner.py) |
9797
| `ModalSandboxClient` | `openai-agents[modal]` | [Modal runner](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/extensions/modal_runner.py) |
9898
| `RunloopSandboxClient` | `openai-agents[runloop]` | [Runloop runner](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/extensions/runloop/runner.py) |
99-
| `SpritesSandboxClient` | `openai-agents[sprites]` | [Sprites runner](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/extensions/sprites_runner.py) |
99+
| `FlySandboxClient` | `openai-agents[sprites]` | [Fly runner](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/extensions/fly_runner.py) |
100100
| `VercelSandboxClient` | `openai-agents[vercel]` | [Vercel runner](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/extensions/vercel_runner.py) |
101101

102102
</div>
@@ -114,7 +114,7 @@ Hosted sandbox clients expose provider-specific mount strategies. Choose the bac
114114
| `DaytonaSandboxClient` | Supports rclone-backed cloud storage mounts with `DaytonaCloudBucketMountStrategy`; use it with `S3Mount`, `GCSMount`, `R2Mount`, `AzureBlobMount`, and `BoxMount`. |
115115
| `E2BSandboxClient` | Supports rclone-backed cloud storage mounts with `E2BCloudBucketMountStrategy`; use it with `S3Mount`, `GCSMount`, `R2Mount`, `AzureBlobMount`, and `BoxMount`. |
116116
| `RunloopSandboxClient` | Supports rclone-backed cloud storage mounts with `RunloopCloudBucketMountStrategy`; use it with `S3Mount`, `GCSMount`, `R2Mount`, `AzureBlobMount`, and `BoxMount`. |
117-
| `SpritesSandboxClient` | No hosted-specific mount strategy is currently exposed. Use manifest files, repos, or other workspace inputs instead. Sprites exposes at most one external HTTP port per sprite (declared as a service in the sprite image); other ports must be reverse-proxied inside the VM. |
117+
| `FlySandboxClient` | No hosted-specific mount strategy is currently exposed. Use manifest files, repos, or other workspace inputs instead. Sprites exposes at most one external HTTP port per sprite (declared as a service in the sprite image); other ports must be reverse-proxied inside the VM. |
118118
| `VercelSandboxClient` | No hosted-specific mount strategy is currently exposed. Use manifest files, repos, or other workspace inputs instead. |
119119

120120
</div>
@@ -132,7 +132,7 @@ The table below summarizes which remote storage entries each backend can mount d
132132
| `DaytonaSandboxClient` |||||| - |
133133
| `E2BSandboxClient` |||||| - |
134134
| `RunloopSandboxClient` |||||| - |
135-
| `SpritesSandboxClient` | - | - | - | - | - | - |
135+
| `FlySandboxClient` | - | - | - | - | - | - |
136136
| `VercelSandboxClient` | - | - | - | - | - | - |
137137

138138
</div>

examples/sandbox/extensions/README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ They intentionally keep the flow simple:
77

88
1. Build a tiny manifest in memory.
99
2. Create a `SandboxAgent` that inspects that workspace through one shell tool.
10-
3. Run the agent against E2B, Modal, Daytona, Cloudflare, Runloop, Blaxel, Sprites, or Vercel.
10+
3. Run the agent against E2B, Modal, Daytona, Cloudflare, Runloop, Blaxel, Fly, or Vercel.
1111

1212
All of these examples require `OPENAI_API_KEY`, because they call the model through the normal
1313
`Runner` path. Each cloud backend also needs its own provider credentials.
@@ -328,7 +328,11 @@ the default home and working directory become `/root`, so the example also uses
328328
`/root` as its manifest workspace root. If you configure root launch in your
329329
own code, either rely on that root-mode default or explicitly choose a
330330
`manifest.root` under `/root`.
331-
## Sprites
331+
## Fly
332+
333+
Fly's sandbox product is powered by [Sprites](https://sprites.dev/), Fly's
334+
sandbox-platform credential and runtime. The SDK exposes it as
335+
`FlySandboxClient`; users authenticate with a Sprites API token.
332336

333337
### Setup
334338

@@ -351,7 +355,7 @@ export SPRITES_API_TOKEN=...
351355
### Run
352356

353357
```bash
354-
uv run python examples/sandbox/extensions/sprites_runner.py --stream
358+
uv run python examples/sandbox/extensions/fly_runner.py --stream
355359
```
356360

357361
Useful flags:
@@ -361,12 +365,12 @@ Useful flags:
361365
- `--skip-snapshot-check` — skip the tar workspace persistence verification.
362366
- `--question "..."` — override the default prompt.
363367

364-
The Sprites client resolves the API token from `SPRITES_API_TOKEN` (override via
365-
`SpritesSandboxClient(token=...)`) and supports exec, filesystem read/write,
366-
PTY-mode interactive exec, and tar-based workspace snapshots. Sprites exposes
367-
at most one external HTTP port per sprite — declare it as a service with
368-
`--http-port` in the sprite image, then reference it via
369-
`SpritesSandboxClientOptions(exposed_ports=(<port>,))`.
368+
The Fly client resolves the Sprites API token from `SPRITES_API_TOKEN` (override
369+
via `FlySandboxClient(token=...)`) and supports exec, filesystem read/write,
370+
PTY-mode interactive exec, and tar-based workspace snapshots. The Fly sandbox
371+
exposes at most one external HTTP port per sprite — declare it as a service
372+
with `--http-port` in the sprite image, then reference it via
373+
`FlySandboxClientOptions(exposed_ports=(<port>,))`.
370374

371375
## Blaxel
372376

examples/sandbox/extensions/sprites_runner.py renamed to examples/sandbox/extensions/fly_runner.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333

3434
try:
3535
from agents.extensions.sandbox import (
36-
SpritesSandboxClient,
37-
SpritesSandboxClientOptions,
36+
FlySandboxClient,
37+
FlySandboxClientOptions,
3838
)
3939
except Exception as exc: # pragma: no cover - import path depends on optional extras
4040
raise SystemExit(
@@ -92,8 +92,8 @@ async def _verify_stop_resume(*, sprite_name: str | None) -> None:
9292
against the existing sprite (no create/delete on the API).
9393
"""
9494

95-
client = SpritesSandboxClient()
96-
options = SpritesSandboxClientOptions(sprite_name=sprite_name)
95+
client = FlySandboxClient()
96+
options = FlySandboxClientOptions(sprite_name=sprite_name)
9797

9898
with tempfile.TemporaryDirectory(prefix="sprites-snapshot-example-") as snapshot_dir:
9999
sandbox = await client.create(
@@ -157,10 +157,10 @@ async def main(
157157
model_settings=ModelSettings(tool_choice="required"),
158158
)
159159

160-
client = SpritesSandboxClient()
160+
client = FlySandboxClient()
161161
sandbox = await client.create(
162162
manifest=manifest,
163-
options=SpritesSandboxClientOptions(sprite_name=sprite_name),
163+
options=FlySandboxClientOptions(sprite_name=sprite_name),
164164
)
165165

166166
run_config = RunConfig(

src/agents/extensions/sandbox/__init__.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -110,23 +110,23 @@
110110
_HAS_VERCEL = False
111111

112112
try:
113-
from .sprites import (
114-
DEFAULT_SPRITES_API_URL as DEFAULT_SPRITES_API_URL,
115-
DEFAULT_SPRITES_CONTEXT_PATH as DEFAULT_SPRITES_CONTEXT_PATH,
116-
DEFAULT_SPRITES_WAIT_FOR_RUNNING_TIMEOUT_S as DEFAULT_SPRITES_WAIT_FOR_RUNNING_TIMEOUT_S,
117-
DEFAULT_SPRITES_WORKSPACE_ROOT as DEFAULT_SPRITES_WORKSPACE_ROOT,
118-
SpritesCheckpoints as SpritesCheckpoints,
119-
SpritesPlatformContext as SpritesPlatformContext,
120-
SpritesSandboxClient as SpritesSandboxClient,
121-
SpritesSandboxClientOptions as SpritesSandboxClientOptions,
122-
SpritesSandboxSession as SpritesSandboxSession,
123-
SpritesSandboxSessionState as SpritesSandboxSessionState,
124-
SpritesUrlAccess as SpritesUrlAccess,
125-
)
126-
127-
_HAS_SPRITES = True
113+
from .flyio import (
114+
DEFAULT_FLY_SANDBOX_API_URL as DEFAULT_FLY_SANDBOX_API_URL,
115+
DEFAULT_FLY_SANDBOX_CONTEXT_PATH as DEFAULT_FLY_SANDBOX_CONTEXT_PATH,
116+
DEFAULT_FLY_SANDBOX_WAIT_FOR_RUNNING_TIMEOUT_S as DEFAULT_FLY_SANDBOX_WAIT_FOR_RUNNING_TIMEOUT_S, # noqa: E501
117+
DEFAULT_FLY_SANDBOX_WORKSPACE_ROOT as DEFAULT_FLY_SANDBOX_WORKSPACE_ROOT,
118+
FlyCheckpoints as FlyCheckpoints,
119+
FlyPlatformContext as FlyPlatformContext,
120+
FlySandboxClient as FlySandboxClient,
121+
FlySandboxClientOptions as FlySandboxClientOptions,
122+
FlySandboxSession as FlySandboxSession,
123+
FlySandboxSessionState as FlySandboxSessionState,
124+
FlyUrlAccess as FlyUrlAccess,
125+
)
126+
127+
_HAS_FLY = True
128128
except Exception: # pragma: no cover
129-
_HAS_SPRITES = False
129+
_HAS_FLY = False
130130

131131
__all__: list[str] = []
132132

@@ -227,19 +227,19 @@
227227
]
228228
)
229229

230-
if _HAS_SPRITES:
230+
if _HAS_FLY:
231231
__all__.extend(
232232
[
233-
"DEFAULT_SPRITES_API_URL",
234-
"DEFAULT_SPRITES_CONTEXT_PATH",
235-
"DEFAULT_SPRITES_WAIT_FOR_RUNNING_TIMEOUT_S",
236-
"DEFAULT_SPRITES_WORKSPACE_ROOT",
237-
"SpritesCheckpoints",
238-
"SpritesPlatformContext",
239-
"SpritesSandboxClient",
240-
"SpritesSandboxClientOptions",
241-
"SpritesSandboxSession",
242-
"SpritesSandboxSessionState",
243-
"SpritesUrlAccess",
233+
"DEFAULT_FLY_SANDBOX_API_URL",
234+
"DEFAULT_FLY_SANDBOX_CONTEXT_PATH",
235+
"DEFAULT_FLY_SANDBOX_WAIT_FOR_RUNNING_TIMEOUT_S",
236+
"DEFAULT_FLY_SANDBOX_WORKSPACE_ROOT",
237+
"FlyCheckpoints",
238+
"FlyPlatformContext",
239+
"FlySandboxClient",
240+
"FlySandboxClientOptions",
241+
"FlySandboxSession",
242+
"FlySandboxSessionState",
243+
"FlyUrlAccess",
244244
]
245245
)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from __future__ import annotations
2+
3+
from .capabilities import (
4+
DEFAULT_FLY_SANDBOX_CONTEXT_PATH,
5+
FlyCheckpoints,
6+
FlyPlatformContext,
7+
FlyUrlAccess,
8+
UrlVisibility,
9+
clear_platform_context_cache,
10+
)
11+
from .sandbox import (
12+
DEFAULT_FLY_SANDBOX_API_URL,
13+
DEFAULT_FLY_SANDBOX_WAIT_FOR_RUNNING_TIMEOUT_S,
14+
DEFAULT_FLY_SANDBOX_WORKSPACE_ROOT,
15+
FlySandboxClient,
16+
FlySandboxClientOptions,
17+
FlySandboxSession,
18+
FlySandboxSessionState,
19+
)
20+
21+
__all__ = [
22+
"DEFAULT_FLY_SANDBOX_API_URL",
23+
"DEFAULT_FLY_SANDBOX_CONTEXT_PATH",
24+
"DEFAULT_FLY_SANDBOX_WAIT_FOR_RUNNING_TIMEOUT_S",
25+
"DEFAULT_FLY_SANDBOX_WORKSPACE_ROOT",
26+
"FlyCheckpoints",
27+
"FlyPlatformContext",
28+
"FlySandboxClient",
29+
"FlySandboxClientOptions",
30+
"FlySandboxSession",
31+
"FlySandboxSessionState",
32+
"FlyUrlAccess",
33+
"UrlVisibility",
34+
"clear_platform_context_cache",
35+
]

src/agents/extensions/sandbox/sprites/capabilities.py renamed to src/agents/extensions/sandbox/flyio/capabilities.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from ....sandbox.session.base_sandbox_session import BaseSandboxSession
1212
from ....tool import Tool, function_tool
1313

14-
DEFAULT_SPRITES_CONTEXT_PATH = "/.sprite/llm.txt"
14+
DEFAULT_FLY_SANDBOX_CONTEXT_PATH = "/.sprite/llm.txt"
1515

1616
UrlVisibility = Literal["public", "sprite"]
1717
"""Sprites URL visibility values. ``"sprite"`` restricts the URL to organization
@@ -46,7 +46,7 @@ def clear_platform_context_cache(sprite_name: str | None = None, path: str | Non
4646
_PLATFORM_CONTEXT_CACHE.pop((sprite_name, path), None)
4747

4848

49-
class SpritesPlatformContext(Capability):
49+
class FlyPlatformContext(Capability):
5050
"""Inject the sprite's ``/.sprite/llm.txt`` platform-context file into the agent's instructions.
5151
5252
Sprites bundle an LLM-facing document at ``/.sprite/llm.txt`` describing
@@ -66,7 +66,7 @@ class SpritesPlatformContext(Capability):
6666
capabilities=[
6767
WorkspaceShellCapability(),
6868
Filesystem(),
69-
SpritesPlatformContext(),
69+
FlyPlatformContext(),
7070
],
7171
)
7272
@@ -75,8 +75,8 @@ class SpritesPlatformContext(Capability):
7575
manifest root.
7676
"""
7777

78-
type: Literal["sprites_platform_context"] = "sprites_platform_context"
79-
path: str = DEFAULT_SPRITES_CONTEXT_PATH
78+
type: Literal["fly_platform_context"] = "fly_platform_context"
79+
path: str = DEFAULT_FLY_SANDBOX_CONTEXT_PATH
8080
"""Sprite-side path of the context file. Defaults to ``/.sprite/llm.txt``."""
8181

8282
timeout_s: float = 5.0
@@ -120,11 +120,11 @@ async def instructions(self, manifest: Manifest) -> str | None:
120120

121121

122122
def _resolve_sprite_handle(session: BaseSandboxSession | None) -> Any | None:
123-
"""Return the underlying ``sprites.Sprite`` from a SpritesSandboxSession, or None.
123+
"""Return the underlying ``sprites.Sprite`` from a FlySandboxSession, or None.
124124
125125
Capabilities are bound to the runtime ``SandboxSession`` wrapper, not the
126126
inner backend session — so we dig through ``_inner`` to reach the
127-
SpritesSandboxSession's ``_sprite`` attribute.
127+
FlySandboxSession's ``_sprite`` attribute.
128128
"""
129129

130130
if session is None:
@@ -145,15 +145,15 @@ def _resolve_sprite_name(session: BaseSandboxSession | None) -> str | None:
145145
return name if isinstance(name, str) and name else None
146146

147147

148-
class SpritesUrlAccess(Capability):
148+
class FlyUrlAccess(Capability):
149149
"""Expose a tool that lets the agent toggle the sprite's public URL visibility.
150150
151151
Sprite URL access is a *host-platform* setting, not something the in-VM
152152
``sprite-env`` CLI can change — the in-VM API socket only exposes
153153
services/checkpoints. Without this capability, an agent asked to "make the
154154
URL public" tends to thrash between unauthenticated commands. This
155155
capability wraps ``Sprite.update_url_settings`` (which already has the
156-
application's API token via ``SpritesSandboxClient``) so the model can
156+
application's API token via ``FlySandboxClient``) so the model can
157157
flip visibility in one call.
158158
159159
Going ``public`` is gated by ``allow_public`` (default ``False``). The
@@ -167,13 +167,13 @@ class SpritesUrlAccess(Capability):
167167
capabilities=[
168168
WorkspaceShellCapability(),
169169
Filesystem(),
170-
SpritesPlatformContext(),
171-
SpritesUrlAccess(allow_public=True),
170+
FlyPlatformContext(),
171+
FlyUrlAccess(allow_public=True),
172172
],
173173
)
174174
"""
175175

176-
type: Literal["sprites_url_access"] = "sprites_url_access"
176+
type: Literal["fly_url_access"] = "fly_url_access"
177177
allow_public: bool = False
178178
"""When ``False`` (default), the tool refuses ``visibility="public"``."""
179179

@@ -226,7 +226,7 @@ async def _apply_visibility(self, visibility: str) -> str:
226226
return f"sprite URL visibility is now {visibility!r}"
227227

228228

229-
class SpritesCheckpoints(Capability):
229+
class FlyCheckpoints(Capability):
230230
"""Expose tools to create, list, and (optionally) restore native sprite checkpoints.
231231
232232
Sprite checkpoints are point-in-time snapshots of the writable filesystem
@@ -245,12 +245,12 @@ class SpritesCheckpoints(Capability):
245245
...,
246246
capabilities=[
247247
...,
248-
SpritesCheckpoints(allow_restore=True),
248+
FlyCheckpoints(allow_restore=True),
249249
],
250250
)
251251
"""
252252

253-
type: Literal["sprites_checkpoints"] = "sprites_checkpoints"
253+
type: Literal["fly_checkpoints"] = "fly_checkpoints"
254254
allow_restore: bool = False
255255
"""When ``False`` (default), the restore tool is omitted entirely."""
256256

@@ -386,10 +386,10 @@ def _do_restore() -> list[str]:
386386

387387

388388
__all__ = [
389-
"DEFAULT_SPRITES_CONTEXT_PATH",
390-
"SpritesCheckpoints",
391-
"SpritesPlatformContext",
392-
"SpritesUrlAccess",
389+
"DEFAULT_FLY_SANDBOX_CONTEXT_PATH",
390+
"FlyCheckpoints",
391+
"FlyPlatformContext",
392+
"FlyUrlAccess",
393393
"UrlVisibility",
394394
"clear_platform_context_cache",
395395
]

0 commit comments

Comments
 (0)