Sandboxes

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.

a stateful session
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 .html
Sessions survive pause and resume
The interpreter lives inside the sandbox, so pausing snapshots it along with everything else. Resume - hours later, or automatically on the next call with auto-resume - and your dataframes are exactly where you left them. Nothing to reload, no session to rebuild.

Rich 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:

ParameterTypeDescription
textstringPlain-text rendering. Always present.
htmlstringHTML rendering, when the value has one - pandas DataFrames arrive as ready-to-render tables.
pngstring (base64)The rendered image, for matplotlib figures.
chartobjectParsed chart data (type, title, axis labels, series values) for bar, line, pie and scatter figures - re-render the chart natively instead of embedding pixels.
jsonobjectStructured 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.

png + chart data
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.

streaming
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_code

Isolated 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:

contexts
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 down

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

structured errors
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_seconds the running code is interrupted and the response has interrupted: true - but the context's variables survive, so the next call continues where you were.
  • restarted: true means 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.
  • truncated lists 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.

Reading with an agent? This page is also plain markdown at /docs/sandboxes/code-interpreter.md, and the full docs index lives at /docs/llms.txt.