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.)
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).