sandbox-agent/examples/docker-python/main.py
2026-02-25 02:18:16 -08:00

143 lines
4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Sandbox Agent Python + Docker example.
Starts a Docker container running sandbox-agent, connects to the sandbox-agent server, creates a session, sends a prompt, and
prints the streamed response.
Usage:
pip install -r requirements.txt
python main.py
"""
import json
import os
import signal
import subprocess
import sys
import time
import docker
import httpx
from client import SandboxConnection
from credentials import build_container_env, detect_agent
PORT = 3000
DOCKERFILE_DIR = os.path.join(os.path.dirname(__file__), "..", "shared")
IMAGE_NAME = "sandbox-agent-examples:latest"
def build_image(client: docker.DockerClient) -> str:
"""Build the shared example Docker image if it doesn't exist."""
try:
client.images.get(IMAGE_NAME)
return IMAGE_NAME
except docker.errors.ImageNotFound:
pass
print(f"Building {IMAGE_NAME} (first run only)...")
subprocess.run(
["docker", "build", "-t", IMAGE_NAME, DOCKERFILE_DIR],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
)
return IMAGE_NAME
def wait_for_health(base_url: str, timeout_s: float = 120) -> None:
deadline = time.monotonic() + timeout_s
last_err: str | None = None
while time.monotonic() < deadline:
try:
r = httpx.get(f"{base_url}/v1/health", timeout=5)
if r.status_code == 200 and r.json().get("status") == "ok":
return
last_err = f"health returned {r.status_code}"
except Exception as exc:
last_err = str(exc)
time.sleep(0.5)
raise RuntimeError(f"Timed out waiting for /v1/health: {last_err}")
def main() -> None:
agent = detect_agent()
print(f"Agent: {agent}")
client = docker.from_env()
image = build_image(client)
env = build_container_env()
print("Starting container...")
container = client.containers.run(
image,
command=[
"sh", "-c",
f"sandbox-agent install-agent {agent} && "
f"sandbox-agent server --no-token --host 0.0.0.0 --port {PORT}",
],
environment=env,
ports={f"{PORT}/tcp": PORT},
detach=True,
auto_remove=True,
)
def cleanup(*_args: object) -> None:
print("\nCleaning up...")
try:
container.stop(timeout=5)
except Exception:
pass
signal.signal(signal.SIGINT, cleanup)
signal.signal(signal.SIGTERM, cleanup)
try:
base_url = f"http://127.0.0.1:{PORT}"
print(f"Waiting for server at {base_url}...")
wait_for_health(base_url)
print("Server ready.")
print(f"Inspector: {base_url}/ui/")
# -- Session flow ----------------------------------------------------
conn = SandboxConnection(base_url, agent)
print("Connecting...")
init_result = conn.initialize()
agent_info = init_result.get("result", {}).get("agentInfo", {})
print(f"Connected to: {agent_info.get('title', agent)} {agent_info.get('version', '')}")
session_id = conn.new_session()
print(f"Session: {session_id}")
prompt_text = "Say hello and tell me what you are. Be brief (one sentence)."
print(f"\n> {prompt_text}")
response = conn.prompt(session_id, prompt_text)
if "error" in response:
err = response["error"]
print(f"Error: {err.get('message', err)}")
else:
print(f"Stop reason: {response.get('result', {}).get('stopReason', 'unknown')}")
# Give SSE events a moment to arrive.
time.sleep(1)
if conn.events:
for ev in conn.events:
if ev.get("method") == "session/update":
content = ev.get("params", {}).get("update", {}).get("content", {})
if content.get("text"):
print(content["text"], end="")
print()
conn.close()
print("\nDone.")
finally:
cleanup()
if __name__ == "__main__":
main()