Code interpreter
The code-interpreter template turns run_code into a stateful session, like a notebook: every call executes in a persistent Python interpreter, so variables, imports and dataframes carry over between calls, and results come back rich - text, HTML tables, PNG charts and parsed chart data - instead of flat stdout. It ships Python 3.12 with pandas, NumPy and matplotlib preinstalled, on top of everything the python-3.12 base has.
from orkestr import Sandbox
sbx = Sandbox.create(template="code-interpreter")
# Each run_code call executes in the SAME interpreter: variables,
# imports and figures persist between calls.
sbx.run_code("import pandas as pd; df = pd.read_csv('sales.csv')")
execution = sbx.run_code("df.describe()")
print(execution.text) # text rendering of the last expression
table = execution.results[0] # the same result also carries .htmlRich results
Each execution returns a results list - one item per displayed value (the final expression, plus anything explicitly displayed). Every item carries all the representations the interpreter produced, and type names the richest one:
| Parameter | Type | Description |
|---|---|---|
| text | string | Plain-text rendering. Always present. |
| html | string | HTML rendering, when the value has one - pandas DataFrames arrive as ready-to-render tables. |
| png | string (base64) | The rendered image, for matplotlib figures. |
| chart | object | Parsed chart data (type, title, axis labels, series values) for bar, line, pie and scatter figures - re-render the chart natively instead of embedding pixels. |
| json | object | Structured application/json output, when emitted. |
Charts, twice
A matplotlib figure comes back as both the picture and the numbers: the PNG for showing, the chart data for anything programmatic - feeding a charting library, asserting on values in a test, or letting an agent read the result without vision.
execution = sbx.run_code("""
import matplotlib.pyplot as plt
df.groupby('region')['sales'].sum().plot.bar(title='Sales by region')
plt.show()
""")
result = execution.results[0]
with open("chart.png", "wb") as fh:
fh.write(result.png_bytes()) # the rendered PNG
print(result.chart)
# {'type': 'bar', 'title': 'Sales by region',
# 'x_tick_labels': ['fsn1', 'hel1', 'rbx'],
# 'elements': [{'label': 'fsn1', 'value': 202.5}, ...]}Streaming
For long cells - model training, big joins - stream the output as it happens instead of waiting for the cell to finish: run_code_stream / runCodeStream yields one event per output burst and per result, ending with a done event that carries the same execution summary a buffered call returns.
for event in sbx.run_code_stream("train(df, epochs=50)", timeout_seconds=600):
if event.kind == "stream":
print(event.text, end="") # output as it happens
elif event.kind == "result":
handle(event.result) # rich results as they appear
elif event.kind == "done":
summary = event.execution # same shape as buffered run_codeIsolated contexts
A sandbox starts with one default context. Create more when you need interpreters that cannot see each other's variables - one per user, one per conversation thread, one per experiment:
ctx = sbx.create_code_context()
ctx.run_code("x = 1")
sbx.run_code("print(x)") # NameError - default context has no x
sbx.run_code("print(x)", context_id=ctx.id) # 1
ctx.restart() # same id, fresh namespace
ctx.remove() # shut the interpreter downErrors and timeouts
A raising cell is a normal response, not a transport error: the HTTP call succeeds and the exception arrives as structured data, exactly like a notebook shows a traceback.
execution = sbx.run_code("1 / 0")
execution.error.name # "ZeroDivisionError"
execution.error.value # "division by zero"
execution.error.traceback # list of traceback lines
# The exception did not reset anything - state up to the raise is kept:
sbx.run_code("print(df.shape)") # still works- Timeouts interrupt, they do not destroy. When a call exceeds
timeout_secondsthe running code is interrupted and the response hasinterrupted: true- but the context's variables survive, so the next call continues where you were. restarted: truemeans the context's interpreter had died (for example, out of memory) and was restarted transparently for this call - its previous variables are gone. Check it when you depend on long-lived state.truncatedlists any outputs cut by size caps (very large stdout or results). Empty means nothing was dropped.
When to use exec instead
run_code is for Python that thinks in sessions. Shell commands, builds, servers and one-shot scripts belong to exec - and both work in the same sandbox. On templates without the interpreter runtime, run_code raises InterpreterUnavailable (HTTP 409 with code: "interpreter_unavailable") - create the sandbox from code-interpreter or a custom template built on it.
See the REST API reference for the raw endpoints, and Base images for what the template ships.