""" Blaxel-backed sandbox example for manual validation. This example mirrors the other cloud extension runners. It supports: - Standard agent run (non-streaming and streaming). - PTY interactive session demo (agent-driven). - Blaxel Drive mount demo (persistent storage). Prerequisites: uv sync --extra blaxel export OPENAI_API_KEY=... export BL_API_KEY=... export BL_WORKSPACE=... Run: # Basic agent run uv run python examples/sandbox/extensions/blaxel_runner.py --stream # With a specific image and region uv run python examples/sandbox/extensions/blaxel_runner.py \\ --image blaxel/py-app --region us-pdx-1 --stream # PTY terminal demo (agent-driven interactive Python session) uv run python examples/sandbox/extensions/blaxel_runner.py --demo pty # Drive mount demo (requires an existing drive, defaults region to us-was-1) uv run python examples/sandbox/extensions/blaxel_runner.py \\ --demo drive --drive-name my-drive """ from __future__ import annotations import argparse import asyncio import os import sys import uuid from pathlib import Path from openai.types.responses import ResponseTextDeltaEvent from agents import ModelSettings, Runner, set_tracing_disabled from agents.run import RunConfig from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig from agents.sandbox.capabilities import Shell from agents.sandbox.entries import File from agents.sandbox.manifest import Environment if __package__ is None or __package__ == "": sys.path.insert(0, str(Path(__file__).resolve().parents[3])) from examples.sandbox.misc.example_support import text_manifest, tool_call_name from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability try: from agents.extensions.sandbox import ( DEFAULT_BLAXEL_WORKSPACE_ROOT, BlaxelDriveMountStrategy, BlaxelSandboxClient, BlaxelSandboxClientOptions, ) from agents.extensions.sandbox.blaxel import BlaxelDriveMount except Exception as exc: raise SystemExit( "Blaxel sandbox examples require the optional repo extra.\n" "Install it with: uv sync --extra blaxel" ) from exc DEFAULT_MODEL = "gpt-5.6-sol" DEFAULT_QUESTION = "Summarize this cloud sandbox workspace in 2 sentences." DEFAULT_PTY_QUESTION = ( "Start an interactive Python session with `tty=true`. In that same session, compute " "`5 + 5`, then add 5 more to the previous result. Briefly report the outputs and " "confirm that you stayed in one Python process." ) def _build_manifest() -> Manifest: """Build a small demo manifest for the default agent run.""" manifest = text_manifest( { "README.md": ( "# Blaxel Demo Workspace\n\nThis workspace validates the Blaxel sandbox backend.\n" ), "project/status.md": ( "# Project Status\n\n" "- Backend: Blaxel cloud sandbox\n" "- Region: auto-selected\n" "- Features: exec, file I/O, PTY, drives, preview URLs\n" ), "project/tasks.md": ( "# Tasks\n\n" "1. Inspect the workspace files.\n" "2. List all features mentioned in status.md.\n" "3. Summarize in 2-3 sentences.\n" ), } ) return Manifest( root=DEFAULT_BLAXEL_WORKSPACE_ROOT, entries=manifest.entries, environment=Environment( value={"DEMO_ENV": "blaxel-agent-demo"}, ), ) def _require_env(name: str) -> str: value = os.environ.get(name) if value: return value raise SystemExit(f"{name} must be set before running this example.") def _stream_event_banner(event_name: str, raw_item: object) -> str | None: _ = raw_item if event_name == "tool_called": return "[tool call]" if event_name == "tool_output": return "[tool output]" return None def _raw_item_call_id(raw_item: object) -> str | None: if isinstance(raw_item, dict): call_id = raw_item.get("call_id") or raw_item.get("id") else: call_id = getattr(raw_item, "call_id", None) or getattr(raw_item, "id", None) return call_id if isinstance(call_id, str) and call_id else None # --------------------------------------------------------------------------- # PTY demo (agent-driven) # --------------------------------------------------------------------------- async def _run_pty_demo( *, model: str, question: str, image: str | None, region: str | None, ) -> None: """Demonstrate PTY interaction: start an interactive Python process and continue it.""" agent = SandboxAgent( name="Blaxel PTY Demo", model=model, instructions=( "Complete the task by interacting with the sandbox through the shell capability. " "Keep the final answer concise. " "Preserve process state when the task depends on it. If you start an interactive " "program, continue using that same process instead of launching a second one." ), default_manifest=Manifest( root=DEFAULT_BLAXEL_WORKSPACE_ROOT, entries=text_manifest( { "README.md": ( "# Blaxel PTY Agent Example\n\n" "This workspace is used by the Blaxel PTY demo.\n" ), } ).entries, ), capabilities=[Shell()], model_settings=ModelSettings(tool_choice="required"), ) client = BlaxelSandboxClient() run_config = RunConfig( sandbox=SandboxRunConfig( client=client, options=BlaxelSandboxClientOptions( name=f"blaxel-demo-pty-{uuid.uuid4().hex[:8]}", image=image, region=region, ), ), workflow_name="Blaxel PTY sandbox example", ) try: result = Runner.run_streamed(agent, question, run_config=run_config) saw_text_delta = False saw_any_text = False tool_names_by_call_id: dict[str, str] = {} async for event in result.stream_events(): if event.type == "raw_response_event" and isinstance( event.data, ResponseTextDeltaEvent ): if not saw_text_delta: print("assistant> ", end="", flush=True) saw_text_delta = True print(event.data.delta, end="", flush=True) saw_any_text = True continue if event.type != "run_item_stream_event": continue raw_item = event.item.raw_item banner = _stream_event_banner(event.name, raw_item) if banner is None: continue if saw_text_delta: print() saw_text_delta = False if event.name == "tool_called": t_name = tool_call_name(raw_item) call_id = _raw_item_call_id(raw_item) if call_id is not None and t_name: tool_names_by_call_id[call_id] = t_name if t_name: banner = f"{banner} {t_name}" elif event.name == "tool_output": call_id = _raw_item_call_id(raw_item) output_tool_name = tool_names_by_call_id.get(call_id or "") if output_tool_name: banner = f"{banner} {output_tool_name}" print(banner) if saw_text_delta: print() if not saw_any_text: print(result.final_output) finally: await client.close() # --------------------------------------------------------------------------- # Drive demo # --------------------------------------------------------------------------- async def _run_drive_demo( *, model: str, question: str | None, image: str | None, region: str | None, drive_name: str | None, stream: bool, ) -> None: """Mount a Blaxel Drive and write a file to it.""" if not drive_name: print("Usage: --demo drive --drive-name ") print() print("You need an existing Blaxel Drive. Create one at:") print(" https://app.blaxel.ai or via the Blaxel CLI.") return # Blaxel drives must be in the same region as the sandbox. effective_region = region or os.environ.get("BL_REGION") or "us-was-1" mount_path = "/mnt/demo-drive" manifest = Manifest( root=DEFAULT_BLAXEL_WORKSPACE_ROOT, entries={ "README.md": File( content=(b"# Blaxel Drive Demo\n\nThe drive is mounted at /mnt/demo-drive.\n") ), "drive": BlaxelDriveMount( drive_name=drive_name, drive_mount_path=mount_path, mount_strategy=BlaxelDriveMountStrategy(), ), }, ) marker = f"demo-{uuid.uuid4().hex[:8]}" agent = SandboxAgent( name="Blaxel Drive Demo", model=model, instructions=( "Execute the exact shell commands the user gives you. " "Do not explore, do not run any other commands. " "Report the stdout and stderr of each command you ran. " "You must run the exact commands from the user message using the shell tool. " "Do not substitute, rewrite, or add any commands. Just execute and report output." ), default_manifest=manifest, capabilities=[Shell()], model_settings=ModelSettings(tool_choice="required"), ) client = BlaxelSandboxClient() run_config = RunConfig( sandbox=SandboxRunConfig( client=client, options=BlaxelSandboxClientOptions( name=f"blaxel-demo-drive-{uuid.uuid4().hex[:8]}", image=image, region=effective_region, ), ), workflow_name="Blaxel drive demo", ) effective_question = question or ( f"Run: echo 'drive persistence ok ({marker})' > {mount_path}/{marker}.txt && " f"cat {mount_path}/{marker}.txt && ls {mount_path}" ) if not stream: result = await Runner.run(agent, effective_question, run_config=run_config) print(result.final_output) else: stream_result = Runner.run_streamed(agent, effective_question, run_config=run_config) saw_text_delta = False async for event in stream_result.stream_events(): if event.type == "raw_response_event" and isinstance( event.data, ResponseTextDeltaEvent ): if not saw_text_delta: print("assistant> ", end="", flush=True) saw_text_delta = True print(event.data.delta, end="", flush=True) if saw_text_delta: print() await client.close() # --------------------------------------------------------------------------- # Standard agent run (streaming / non-streaming) # --------------------------------------------------------------------------- async def main( *, model: str, question: str | None, image: str | None, region: str | None, memory: int | None, ttl: str | None, pause_on_exit: bool, stream: bool, demo: str | None, drive_name: str | None, ) -> None: _require_env("OPENAI_API_KEY") # Handle dedicated demos. if demo == "pty": await _run_pty_demo( model=model, question=question or DEFAULT_PTY_QUESTION, image=image, region=region, ) return if demo == "drive": await _run_drive_demo( model=model, question=question, image=image, region=region, drive_name=drive_name, stream=stream, ) return manifest = _build_manifest() agent = SandboxAgent( name="Blaxel Sandbox Assistant", model=model, instructions=( "Answer questions about the sandbox workspace. Inspect the files before answering " "and keep the response concise. " "Do not invent files or statuses that are not present in the workspace. Cite the " "file names you inspected. Also run `echo $DEMO_ENV` to confirm environment " "variables are set." ), default_manifest=manifest, capabilities=[WorkspaceShellCapability()], model_settings=ModelSettings(tool_choice="required"), ) run_config = RunConfig( sandbox=SandboxRunConfig( client=BlaxelSandboxClient(), options=BlaxelSandboxClientOptions( name=f"blaxel-demo-agent-{uuid.uuid4().hex[:8]}", image=image, region=region, memory=memory, ttl=ttl, labels={"purpose": "agent-demo", "source": "blaxel-runner"}, pause_on_exit=pause_on_exit, ), ), workflow_name="Blaxel sandbox example", ) effective_question = question or DEFAULT_QUESTION if not stream: result = await Runner.run(agent, effective_question, run_config=run_config) print(result.final_output) return stream_result = Runner.run_streamed(agent, effective_question, run_config=run_config) saw_text_delta = False async for event in stream_result.stream_events(): if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent): if not saw_text_delta: print("assistant> ", end="", flush=True) saw_text_delta = True print(event.data.delta, end="", flush=True) if saw_text_delta: print() if __name__ == "__main__": set_tracing_disabled(True) parser = argparse.ArgumentParser( description="Blaxel sandbox demo -- showcases sandbox features.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=( "demos:\n" " agent Run a sandboxed agent (default)\n" " pty Agent-driven PTY interactive terminal\n" " drive Mount a Blaxel Drive (requires --drive-name)\n" ), ) parser.add_argument( "--demo", choices=["agent", "pty", "drive"], default="agent", help="Which demo to run (default: agent).", ) parser.add_argument("--model", default=DEFAULT_MODEL, help="Model name.") parser.add_argument("--question", default=None, help="Override the default prompt.") parser.add_argument("--stream", action="store_true", help="Stream response.") parser.add_argument("--image", default=None, help="Sandbox image.") parser.add_argument("--region", default=None, help="Sandbox region.") parser.add_argument("--memory", type=int, default=None, help="Memory in MB.") parser.add_argument("--ttl", default=None, help="Sandbox TTL (e.g. '1h').") parser.add_argument("--pause-on-exit", action="store_true", help="Pause on exit.") parser.add_argument("--drive-name", default=None, help="Drive name for drive demo.") args = parser.parse_args() asyncio.run( main( model=args.model, question=args.question, image=args.image, region=args.region, memory=args.memory, ttl=args.ttl, pause_on_exit=args.pause_on_exit, stream=args.stream, demo=args.demo, drive_name=args.drive_name, ) )