Skip to main content

Documentation Index

Fetch the complete documentation index at: https://budecosystem-b7b14df4.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The Python SDK exposes two namespaces for managing the code-interpreter sandbox that backs each agent’s bash and code_interpreter tools:
  • client.code_interpreter.templates — create, get, update, and delete custom Docker-based sandbox templates.
  • client.agents.add_code_interpreter / get_code_interpreter / remove_code_interpreter — attach, inspect, and detach a template on an agent (= prompt version).
Environments are managed implicitly. When you attach a template to an agent, the sandbox env is auto-provisioned by the backend; the returned AgentToolBinding.env_id is informational only.

Quick Start

from budai import BudClient

client = BudClient(api_key="your-key")

# 1. Build a custom template.
tpl = client.code_interpreter.templates.create(
    name="my-py-extra",
    commands=["RUN pip install --no-cache-dir pydantic-ai"],
    cpu_count=2,
    memory_mb=4096,
)
tpl.wait_until_ready(timeout=600)

# 2. Attach it to an agent. The env is created for you.
binding = client.agents.add_code_interpreter(
    "prm_abc",
    template_id=tpl.id,
    network_policy={"type": "filtered", "allow_out": ["pypi.org"], "deny_out": []},
)

Templates

Create Template

Submit a custom-template build; returns immediately with status="pending".
tpl = client.code_interpreter.templates.create(
    name="my-py-extra",
    commands=["RUN pip install --no-cache-dir pydantic-ai"],
    cpu_count=2,
    memory_mb=4096,
)
ParameterTypeDescription
namestrSlug-safe template id (also the sandbox alias).
commandslist[str]Raw Dockerfile instructions appended to the base. See Dockerfile Commands.
cpu_countintvCPU count.
memory_mbintMemory in MiB.
Returns a Template handle with .refresh() and .wait_until_ready() bound. Status is "pending" until the first workflow activity inserts the row.

Get Template

Fetch a single template row by id.
tpl = client.code_interpreter.templates.get("my-py-extra")
print(f"status={tpl.status} commands={tpl.commands}")
Raises NotFoundError if the template doesn’t exist or is in another project.

Update Template

Replace the template’s commands and rebuild the same sandbox image.
tpl = client.code_interpreter.templates.update(
    "my-py-extra",
    commands=[
        "RUN pip install --no-cache-dir pydantic-ai",
        "RUN pip install --no-cache-dir httpx",
    ],
)
tpl.wait_until_ready()
The same sandbox image is rebuilt; the previous image is overwritten. Already-running sandboxes keep their old snapshot until they’re killed or idled out — the next sandbox spawn picks up the new image.

Delete Template

Hard-delete the template row and its sandbox image.
client.code_interpreter.templates.delete("my-py-extra")
Idempotent — a 404 is silently absorbed. Raises if the template is still bound to an environment.

Wait Until Ready

Block until the template build completes; raise on failure.
try:
    tpl.wait_until_ready(timeout=600)
except BuildFailedError as exc:
    print(f"build failed: {exc.error_message}")
except TimeoutError:
    print("build did not finish in time")
ParameterTypeDefaultDescription
timeoutfloat600.0Total seconds to wait before raising TimeoutError.
poll_intervalfloat3.0Seconds between successive refresh() calls.
Returns self once status reaches "ready". Raises BuildFailedError when the server reports status="failed" — the captured stderr tail is on error_message.

Refresh

Re-fetch the row from the server; mutate self in place.
import time

while tpl.refresh().status == "building":
    time.sleep(5)
Returns self, so callers can chain. Tolerates 404 for a short grace window after create / update — the row may not exist yet because the first workflow activity hasn’t run.

Agent Bindings

Attach Code Interpreter

Attach (or update) the code-interpreter tool on an agent version.
binding = client.agents.add_code_interpreter(
    "prm_abc",
    template_id="my-py-extra",
    version=1,
    lifespan_seconds=1200,
    network_policy={
        "type": "filtered",
        "allow_out": ["pypi.org", "*.pythonhosted.org"],
        "deny_out": [],
    },
)
ParameterTypeDefaultDescription
agent_idstrThe agent (prompt) id.
template_idstrA builtin id (e.g. "python-4g") or a custom template id from create().
versionint1Agent version number.
lifespan_secondsint1200Sandbox idle-kill timeout.
network_policyNetworkPolicy | dictNoneEgress policy. See NetworkPolicy.
Attaching is upsert: calling the method again with different config replaces the previous binding. The agent (= prompt) must already exist. Returns an AgentToolBinding; binding.env_id is the auto-provisioned sandbox env id (informational only).

Get Binding

Fetch the agent’s current code-interpreter binding.
binding = client.agents.get_code_interpreter("prm_abc")
if binding is None:
    print("no binding")
else:
    print(f"env_id={binding.env_id}")
Returns an AgentToolBinding, or None if no binding exists for that agent version.

Remove Code Interpreter

Detach the code-interpreter tool from an agent version.
client.agents.remove_code_interpreter("prm_abc")
Idempotent — a 404 is silently absorbed.

Dockerfile Commands

commands is a list[str] of raw Dockerfile instructions appended verbatim to the platform’s base Dockerfile. The base ships the systemd
  • Jupyter setup that backs the MCP tools.

Allowed Instructions

RUN, ENV, WORKDIR, USER, ARG, LABEL — plus the rest of the Docker instruction set that doesn’t break the base. Server-side validation is deny-list, not allow-list, so harmless no-ops like EXPOSE and SHELL are silently accepted.

Rejected Instructions

The validator hard-rejects five instructions with explicit per-instruction error messages:
InstructionWhy it’s rejected
FROMReplaces the base image, killing the systemd + Jupyter setup.
COPY / ADDThe SDK doesn’t ship a build context. Use a heredoc inside RUN for small files, or RUN curl -fsSL … for already-hosted files.
CMD / ENTRYPOINTOverrides systemd as PID 1, so the Jupyter + FastAPI shim never starts.

Typo Catcher

Each line must start with a recognised Docker instruction keyword. A line like "pip install pydantic-ai" returns a 422 with a did you forget RUN? hint immediately, instead of failing 30 seconds into the sandbox build.

Structural Limits

  • ≤ 64 commands per template.
  • ≤ 4 KB per command line.
  • No NUL bytes, no bare CRs.

Response Objects

Template

class Template:
    id: str                         # Template id (also the sandbox alias)
    type: str                       # "custom" | "builtin"
    status: str                     # "pending" | "building" | "ready" | "failed"
    commands: list[str]             # Dockerfile instructions (empty for builtins)
    languages: list[str]            # Available kernel languages
    cpu_count: int                  # vCPU count
    memory_mb: int                  # Memory in MiB
    error_message: str | None       # Captured stderr tail when status == "failed"
    created_at: datetime | None
    updated_at: datetime | None
    project_id: UUID | None
Handles returned by create(), get(), and update() carry .refresh() and .wait_until_ready() bound to the originating client.

AgentToolBinding

class AgentToolBinding:
    agent_id: str
    version: int
    tool_name: Literal["code_interpreter"]
    env_id: str                     # Auto-provisioned sandbox env id (informational)
    template_id: str | None
    custom_template_id: str | None
    config: dict | None             # Full backend config blob

NetworkPolicy

class NetworkPolicy:
    type: Literal["disabled", "open", "filtered"] = "disabled"
    allow_out: list[str] = []
    deny_out: list[str] = []
  • "disabled" — block all egress (default).
  • "open" — unrestricted egress.
  • "filtered" — caller-defined allow / deny lists.
allow_out / deny_out accept IP literals, CIDR ranges, domain names, wildcard domains ("*.example.com"), and the case-insensitive sentinel "ALL_TRAFFIC" (which expands to 0.0.0.0/0). Allow takes precedence over deny.

BuildFailedError

class BuildFailedError(BudError):
    message: str                    # Formatted summary including error_message
    template_id: str | None
    error_message: str | None       # Captured stderr tail (≤ 2 KB)
Raised by Template.wait_until_ready() when the build workflow reports status="failed".

Async Client

The same surface exists on AsyncBudClient:
from budai import AsyncBudClient

async with AsyncBudClient() as client:
    tpl = await client.code_interpreter.templates.create(
        name="my-py-extra",
        commands=["RUN pip install --no-cache-dir pydantic-ai"],
        cpu_count=2,
        memory_mb=4096,
    )
    await tpl.wait_until_ready(timeout=600)
    binding = await client.agents.add_code_interpreter("prm_abc", template_id=tpl.id)

Error Handling

from budai.exceptions import BuildFailedError, NotFoundError, ValidationError

try:
    tpl = client.code_interpreter.templates.create(...)
    tpl.wait_until_ready(timeout=600)
except BuildFailedError as exc:
    print(f"Build failed: {exc.error_message}")
except ValidationError as exc:
    print(f"Invalid commands: {exc.errors}")
except NotFoundError:
    print("Template not found")
except TimeoutError:
    print("Build did not finish in time")

Limits

  • No file uploads — COPY / ADD are rejected. Heredoc in RUN or RUN curl are the documented workarounds.
  • No template versioning — edits are destructive. To preserve an old recipe, create a new template before editing the old one.
  • No template list endpoint in the SDK. Keep the id from create().
  • No environment CRUD in the SDK. agents.add_code_interpreter does it for you; project deletion cascades the cleanup.
  • Embedded credentials in RUN pip install --index-url=… are baked into the image. Treat any image you build as you would any artifact: if it contains secrets, do not share it.

Next Steps

Inference

Invoke agents that use your code-interpreter binding

Code Examples

Complete code examples and patterns