Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Usage

GMI AgentBox SDK (gmi-agentbox-sdk) is a thin Python client for AgentBox.

Install

python -m pip install gmi-agentbox-sdk

Requires Python 3.9+. TLS uses certifi.

Authenticate

To create an AgentBox API key, open API keys, then click Create API key. Store the key securely; it authenticates requests for your organization.

export GMI_AGENTBOX_API_KEY="your-api-key"

GMI_AGENTBOX_API_KEY is required when constructing AgentBoxClient() with no api_key= argument.

Or pass it to the client:

from agentbox_sdk import AgentBoxClient

client = AgentBoxClient(api_key="your-api-key")

The default API root is production (used when GMI_AGENTBOX_BASE_URL is unset):

https://api.gmi-serving.com/v1/agents

Resource paths such as /deployments and /tasks are joined onto that root. Override the full root with GMI_AGENTBOX_BASE_URL or AgentBoxClient(base_url=...).

Quickstart

import time

from agentbox_sdk import AgentBoxClient, SandboxFailed, SandboxWaitTimeout

client = AgentBoxClient()

# Available values vary by organization. Use the catalogue to select an IDC
# and a SKU for the Sandbox runtime.
idcs = client.idcs.list(runtime="sandbox")
# Choose an `idc_id` returned above. This is the production example value.
idc_id = "us-central-iowa16"
products = client.products.list(idc_name=idc_id, runtime="sandbox")
# Choose an `instance_type` returned above.
instance_type = "gmi.sandbox.small"


def wait_for_template(agent, timeout=600, poll_interval=3):
    deadline = time.monotonic() + timeout
    while True:
        agent.refresh()
        status = agent.template_build_status
        if status == "ready" or status is None:
            return
        if status == "error":
            raise RuntimeError(agent.template_build_error or "Sandbox image build failed")
        if time.monotonic() >= deadline:
            raise TimeoutError(f"Sandbox image is still {status!r} after {timeout}s")
        print("...")
        time.sleep(poll_interval)

agent = None
sandbox = None
try:
    agent = client.agents.create(
        title="agentbox-demo",  # choose a unique name
        image_url="docker.io/library/alpine:3.20",
        idc=idc_id,
        instance_type=instance_type,
        runtime="sandbox",
    )
    print("Building Sandbox image; a new image can take several minutes.")
    wait_for_template(agent)
    sandbox = agent.launch(instance_type=instance_type)
    sandbox.wait_until_running()  # default timeout 300s
    print(sandbox.status, sandbox.endpoint_url)
    execution = sandbox.execute("echo hello")
    print(execution.status, execution.exit_code, execution.data.get("stdout"))
except SandboxFailed as exc:
    print("failed", exc.status, exc.message)
except SandboxWaitTimeout:
    print("still not running", sandbox.status, sandbox.last_error)
finally:
    if sandbox is not None:
        sandbox.delete()
    if agent is not None:
        agent.delete()

Use client.idcs.list(runtime="sandbox") and client.products.list(idc_name=..., runtime="sandbox") to discover available Sandbox data centers and SKUs.

Register an Agent

agent = client.agents.create(
    title="agentbox-demo",
    image_url="docker.io/library/alpine:3.20",
    idc=idc_id,
    runtime="sandbox",
    env=[{"name": "GMI_MODELS", "value": "llama", "secret": False}],
)
print(agent.slug, agent.launchable)
  • generated_api_key is plaintext only on create (and rare patch retries).
  • agent.launchable is false when the upstream template is missing.

Later:

agent = client.agents.get("agentbox-demo")
page = client.agents.list(page=1, page_size=20)
agent.update(title="renamed")
agent.delete()

Launch a Sandbox

sandbox = agent.launch(instance_type=instance_type)
# or: client.sandboxes.launch(agent.slug, instance_type=..., idc_name=agent.idc)

sandbox.wait_until_running(timeout=300, poll_interval=2.0)
sandbox.refresh()
print(sandbox.status, sandbox.last_error, sandbox.endpoint_url)

wait_until_running polls until running. It raises:

  • SandboxFailed if status is error, deleted, stopped, or stopping
  • SandboxWaitTimeout if the timeout elapses first

endpoint_url is empty until the sandbox is running, and can still be empty after running. Do not assume an HTTP URL is always present.

Metadata and expiry

Both are sandbox-runtime only. metadata is your own labels. timeout_seconds is the lifetime in seconds from now. Omit it to keep the platform TTL and auto-renew. A positive value pins the expiry. Read the effective timestamp from sandbox.expires_at after refresh() or set_timeout().

sandbox = agent.launch(
    instance_type=instance_type,
    metadata={"session": "abc"},
    timeout_seconds=3600,
)
sandbox.refresh()
print(sandbox.metadata, sandbox.expires_at, sandbox.expiry_pinned)

sandbox.set_timeout(6 * 3600)
page = client.sandboxes.list(metadata={"session": "abc"})

set_timeout works while the sandbox is running or paused. It can shorten or extend the remaining lifetime, and it pins the sandbox so later reads do not auto-renew it.

pause() pauses a running sandbox. Disk files are kept (except /tmp, which is cleared on resume); memory and running processes are not. A paused sandbox stops consuming run time. The call returns when the pause is accepted; status is usually pausing. Poll with refresh() until it is paused. paused_expire_at is the deadline to resume before the sandbox and its disk are reclaimed.

sandbox.pause()
while sandbox.status != "paused":
    time.sleep(2)
    sandbox.refresh()
print(sandbox.paused_expire_at)

resume() wakes a paused sandbox. Omit timeout_seconds to keep the run time that was left at pause. Set it (>= 60) to reset that run time and pin the expiry. The call returns when the resume is accepted; status is usually resuming. Poll with wait_until_running(). Resume is a cold start: disk files come back except /tmp, processes are not restored, and the start command is not re-run.

sandbox.resume()
sandbox.wait_until_running()

sandbox.resume(timeout_seconds=3600)

Snapshots

A snapshot saves a running sandbox so new sandboxes of the same Agent can start from it. Creation is asynchronous: the snapshot starts creating; wait for ready. Launching from a snapshot inherits its sizing and IDC, so instance_type can be omitted.

snapshot = sandbox.create_snapshot(name="base")
snapshot.wait_until_ready(timeout=600)

clone = agent.launch(snapshot_id=snapshot.id)
clone.wait_until_running()

snapshots = agent.list_snapshots()  # newest first
snapshot.rename("base-v2")          # only when ready or error
snapshot.delete()                   # async; status becomes deleting

wait_until_ready() raises SnapshotFailed if the status becomes error, deleting or delete_failed, and SnapshotWaitTimeout if the timeout elapses first. A snapshot from client.sandboxes.create_snapshot() does not know its Agent; poll it with client.agents.list_snapshots() instead.

Run commands and transfer files

# Finished command
result = sandbox.execute("echo hello")
print(result.status, result.exit_code, result.data.get("stdout"))

# Start a long-running command, then cancel it.
execution = sandbox.execute("sleep 120", wait=False)
if execution.accepted:
    sandbox.cancel_execution(execution.id)

# File round trip
sandbox.upload_file(path="/home/user/input.txt", file=b"hello\n")
download = sandbox.download_file(path="/home/user/input.txt")
print(download.filename, download.content)

wait=False returns an accepted execution; retrieve its latest state with sandbox.get_execution(execution.id). wait_timeout_seconds accepts values from 1 through 25 and is omitted when wait=False. File paths must be absolute and cannot contain . or .. segments.

List and filter sandboxes

page = client.sandboxes.list()  # omits stopped and deleted
page = client.sandboxes.list(status=["running", "creating", "error"])
page = client.sandboxes.list(agent_id=agent.id, status="running")
page = client.sandboxes.list(metadata={"session": "abc"})
page = agent.sandboxes(metadata={"session": "abc"})

The default Sandbox list excludes stopped and deleted. Pass status when you need those.

Delete

sandbox.delete()  # one-way; the sandbox cannot be restarted
agent.delete()     # does not stop or delete remaining sandboxes

Always delete sandboxes you launched in tests. They bill the Console account.

Log stream

The SDK supports log streaming when the Sandbox provides the logs_stream capability:

if sandbox.capabilities.get("logs_stream"):
    for event in sandbox.stream():
        print(event.event, event.data)
        if event.event == "status" and isinstance(event.data, dict):
            break

When unavailable, this call raises an unsupported-runtime error. A backend can enable the capability without requiring an SDK update.

Template build logs

Sandbox Agents expose paged template build logs. Container Agents raise NotSupportedError (HTTP 501). GMI does not persist these logs.

import time

from agentbox_sdk import GoneError

builds = agent.list_builds()
build_id = builds[0].id
offset = 0
while True:
    try:
        page = agent.build_logs(build_id, offset=offset)
    except GoneError:
        print("logs no longer available")
        break
    for entry in page.entries:
        print(entry.level, entry.message)
    offset = page.next_offset
    if page.has_more:
        continue
    build = agent.get_build(build_id)
    if build.status in ("ready", "error"):
        try:
            final = agent.build_logs(build_id, offset=offset)
        except GoneError:
            print("logs no longer available")
            break
        for entry in final.entries:
            print(entry.level, entry.message)
        if build.status == "error" and build.failure is not None:
            raise RuntimeError(build.failure.message)
        break
    time.sleep(2)

has_more false only means nothing more is available right now. Keep polling from next_offset while the build is waiting or building, then read one last page after it reaches ready or error. 410 means the logs are gone, including for a successful build.

Eligibility and metrics

entitlement = client.eligibility()
print(entitlement.eligible, entitlement.data_centers)

if sandbox.capabilities.get("metrics"):
    batch = sandbox.metrics(start=start, end=end, kinds=["cpu", "memory"], step=60)
    series = sandbox.metrics_timeseries(kind="cpu", start=start, end=end)
    print(series.empty_reason, series.points)

kinds omitted means the API default (all 8 charts): gpu_util, gpu_mem, cpu, memory, disk_read, disk_write, net_rx, net_tx. start / end are unix seconds. Metrics are callable only when the Sandbox provides the metrics capability; otherwise the service reports that the runtime does not support them.

Concepts

Name Meaning
idc / idc_name Data center identifier, not a region label.
instance_type Billing SKU, not the product object name.

Errors

from agentbox_sdk import (
    AuthenticationError,
    SandboxFailed,
    SandboxWaitTimeout,
    NotFoundError,
    UnprocessableError,
)

try:
    client.agents.get("missing")
except NotFoundError as exc:
    print(exc.status_code, exc.message)

401 is AuthenticationError (for example, when the API key is invalid). 422 is UnprocessableError (locked field / template mismatch). 410 is GoneError (template build logs reclaimed). 501 is NotSupportedError (template build APIs on a container Agent) and is not retried as a server error. See Reference.

Metadata

Release files for gmi-agentbox-sdk 0.1.0b6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gmi-agentbox-sdk 0.1.0b6
File Size Uploaded Provenance Actions
gmi_agentbox_sdk-0.1.0b6.tar.gz 25.9 kB Without provenance Details for gmi_agentbox_sdk-0.1.0b6.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for gmi-agentbox-sdk 0.1.0b6
File Interpreter ABI Platform Actions
gmi_agentbox_sdk-0.1.0b6-py3-none-any.whl Python 3 none any Details for gmi_agentbox_sdk-0.1.0b6-py3-none-any.whl

Total release size: 47.0 kB

Release files / gmi_agentbox_sdk-0.1.0b6.tar.gz

Download URL gmi_agentbox_sdk-0.1.0b6.tar.gz
Size 25.9 kB
Tags Source
SHA-256 checksum
How to use checksums
571ac29879c8993736a376b01b9e7d14df1c8fe6bdafc0b102b8a5bf0601eed2
BLAKE2b-256 checksum
How to use checksums
27487dfbd86471189814eae7436292b3b35e04f7f36bc2fb230d4a8026aefd05
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / gmi_agentbox_sdk-0.1.0b6-py3-none-any.whl

Download URL gmi_agentbox_sdk-0.1.0b6-py3-none-any.whl
Size 21.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2381655a392e31f8e9559253513fd3fd28ecbaf7b44bfb51ab0a05d4c55926e5
BLAKE2b-256 checksum
How to use checksums
e57d6eaa8d04ccec6d9a134e08b71fbab4a14cefc95e47762ede2664919a5b5a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Security for gmi-agentbox-sdk 0.1.0b6

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page