Skip to content

Python SDK

The barista_sdk package is the importable client for pausable compute sessions — the same /v1 API the CLI uses, as three lines of Python. It depends only on httpx. (Import name barista_sdk; the distribution is barista-sdk — top-level barista belongs to the contract package.)

pip install barista-sdk        # not yet on PyPI — until then, from this repo:
pip install -e sdk/python

Hello world

from barista_sdk import Session

s = Session.create(name="hello", image="busybox:latest", digest="sha256:…")
print(s.exec(["echo", "hi"]).stdout)

Auth and endpoint resolve argument → environment: pass Client(api_key=…, base_url=…) or set BARISTA_API_KEY and BARISTA_URL (default http://127.0.0.1:8099). A missing key fails at Client() construction, not at the first request. Images must be digest-pinned (sha256:…) — the node refuses tag-only refs; barista create (the CLI) can resolve a digest via your local Docker if you need one.

The session handle

from barista_sdk import Client, Session, list_sessions

client = Client(api_key="bk_…", base_url="https://beta.barista.sh")

s = Session.create(name="agent1", image="node:20-slim", digest="sha256:…",
                   command=["sh", "-c", "sleep infinity"], idle_pause_s=300, client=client)
s = Session.connect("agent1", client=client)     # existing session; creates nothing

exit_code, stdout, stderr = s.exec(["cat", "/etc/hostname"], workdir="/work")
s.pause()                                        # freeze, keeping memory
s.resume()                                       # continue where it froze
for event in s.events():                         # SSE tail of node events
    print(event["type"], event["message"])
s.delete()

list_sessions(client=client)                     # [{"name": …, "status": …}, …]

Bounded output and completeness

The gateway bounds captured stdout and stderr. Use exec_capture() whenever a prefix would be unsafe—for example, when stdout contains an archive or generated file:

capture = s.exec_capture(["cat", "/work/result.tar"])
if capture.output_truncated is not False:
    # True means cut; None means an older server did not report completeness.
    raise RuntimeError(f"incomplete output: {capture.output_truncation}")
consume(capture.stdout)

When truncated, output_truncation preserves the server's limit plus captured and total byte counts for each stream. Those counts cannot be reconstructed from the UTF-8 strings. Existing s.exec() remains exactly the three-value ExecResult tuple; if it receives a response known to be truncated it emits OutputTruncatedWarning rather than silently presenting the prefix as complete.

Errors are typed, mirroring the gateway's contract — branch on type, not status code:

Exception When
NotFound 404 — no such session
AuthError 401/403, or no API key at all at Client() construction
NotReady 409 — mid-boot / mid-wake; retryable, carries .retry_after (s)
APIError anything else ≥ 400; carries .status_code

Coming from E2B

barista_sdk.e2b exposes E2B's Sandbox shape on Barista sessions — switching is an import-line change for the supported subset:

-from e2b import Sandbox
+from barista_sdk.e2b import Sandbox

-sbx = Sandbox(template="base")
+sbx = Sandbox(template="base")  # a `barista template` name — or image= + digest=

 result = sbx.commands.run("echo hi")   # polls while the sandbox boots — just works
 print(result.stdout, result.exit_code)

 sbx.pause()
 sbx = Sandbox.connect(sbx.sandbox_id)  # resumes — memory preserved
 sbx.kill()

Honest differences (the full table lives in the barista_sdk.e2b module docstring): timeout= maps to idle-pause — the sandbox pauses keeping memory instead of being killed; run_code is a one-shot interpreter exec with no persistent Jupyter kernel; a non-zero exit comes back in the result instead of raising; template= resolves a registered barista template server-side; in-place set_timeout() raises a loud NotImplementedError rather than silently approximating (set the timeout at construction).