SandboxesRun Python

Code interpreter API with Lizard Sandboxes

Lizard Sandboxes run LLM-generated code in Firecracker microVMs, each with its own kernel. This guide covers the CodeSandbox API in Lizard SDK: @lizard-build/sdk for TypeScript and JavaScript, and lizard-sdk for Python. The examples below create a sandbox on Lizard and return its output to your agent.

CodeSandbox isolates the code from your app and computer. It boots the interpreter template, which has Python and common data libraries. Python runs in a Jupyter kernel that keeps variables between calls, and each result carries the printed output, the value of the last expression, charts as image/png, and any error with its traceback.

Run code with state

import { CodeSandbox } from '@lizard-build/sdk';
 
const sandbox = await CodeSandbox.create({ projectId: 'proj_123', timeoutMs: 300_000 });
try {
  await sandbox.runCode('import pandas as pd\ndf = pd.DataFrame({"x": [1, 2, 3]})');
  const run = await sandbox.runCode('df.x.sum()');
  console.log(run.results[0]?.data); // "np.int64(6)"
  const bad = await sandbox.runCode('1/0');
  console.log(bad.error?.name); // "ZeroDivisionError"
} finally {
  await sandbox.kill();
}
from lizard import CodeSandbox
 
with CodeSandbox.create(project_id="proj_123", timeout_ms=300_000) as sandbox:
    sandbox.run_code('import pandas as pd\ndf = pd.DataFrame({"x": [1, 2, 3]})')
    run = sandbox.run_code("df.x.sum()")
    print(run.results[0].data)  # "np.int64(6)"
    print(sandbox.run_code("1/0").error.name)  # "ZeroDivisionError"

A code error is part of the result, not an exception. Request failures, such as a bad API key, throw.

What does runCode return?

runCode (Python: run_code) returns an Execution:

FieldContents
stdout, stderrEverything the code printed
resultsRich output: the last expression’s value, matplotlib charts and images, each with mime and data
errorname, message, and traceback when the code raised an exception
executionCountThe kernel’s cell counter (Python: execution_count)

Each result carries one MIME type, the richest one the output has, in this order: image/png, image/jpeg, image/svg+xml, text/html, text/markdown, application/json, text/latex, text/plain. Images come as base64. A number comes back as text/plain, but a pandas DataFrame comes back as text/html only.

Pass onStdout, onStderr, onResult or onError (Python: on_stdout and so on) to receive output while the code runs.

Use it as an LLM tool

Give the model one tool, for example run_python with a single string argument code. When the model calls it, run the code in the sandbox and send the text back as the tool result. The example keeps every text result, HTML included, so a DataFrame reaches the model too. Keep one sandbox for the whole conversation so the model’s variables survive between calls.

import { CodeSandbox } from '@lizard-build/sdk';
 
const sandbox = await CodeSandbox.create({ projectId: 'proj_123', timeoutMs: 600_000 });
 
// Call this when the model asks to run Python. Return the text to send back to it.
async function runPython(code: string): Promise<string> {
  const run = await sandbox.runCode(code, { timeoutMs: 30_000 });
  if (run.error) return `${run.error.name}: ${run.error.message}\n${run.error.traceback}`;
  const values = run.results.flatMap((r) =>
    r.type === 'result' && r.mime.startsWith('text/') ? [r.data] : [],
  );
  return [run.stdout, ...values].filter(Boolean).join('\n');
}
 
// When the conversation ends:
await sandbox.kill();
from lizard import CodeSandbox
 
sandbox = CodeSandbox.create(project_id="proj_123", timeout_ms=600_000)
 
def run_python(code: str) -> str:
    """Call this when the model asks to run Python. Return the text to send back to it."""
    run = sandbox.run_code(code, timeout_ms=30_000)
    if run.error:
        return f"{run.error.name}: {run.error.message}\n{run.error.traceback}"
    values = [r.data for r in run.results if r.mime.startswith("text/")]
    return "\n".join(filter(None, [run.stdout, *values]))
 
# When the conversation ends:
sandbox.kill()

The sandbox lifetime here is ten minutes. Raise it for longer conversations with setTimeout (Python: set_timeout), or set timeoutMs: 0 and kill the sandbox yourself.

Charts

When the code draws a matplotlib chart, results holds an item with mime set to image/png and the image as base64 in data:

import base64
from pathlib import Path
 
run = sandbox.run_code("import matplotlib.pyplot as plt\nplt.plot([1, 4, 9])\nplt.show()")
png = next(r.data for r in run.results if r.mime == "image/png")
Path("chart.png").write_bytes(base64.b64decode(png))

Contexts, timeouts, and languages

  • Contexts. createContext() (Python: create_context()) starts a separate kernel with its own variables and working directory; pass it as context to runCode. listContexts(), restartContext(ctx) and deleteContext(ctx) manage them. Without a context, each language uses its own default one.
  • Timeouts. timeoutMs (Python: timeout_ms) bounds one call and defaults to 60 seconds. A Python kernel that runs past it is interrupted and keeps its state; the result’s error.name is TimeoutError.
  • Other languages. JavaScript and bash run as a fresh process on each call, so no state carries over and results stays empty. JavaScript runs on the template’s Node 22, which has no npm.
  • Pause and snapshots. Kernels live inside the sandbox, so pause() and resume() keep their variables, and snapshot() and fork() copy them.

Run commands

process.exec starts a separate process for each command, so Python variables do not survive between calls. Use it for shell steps such as pip install, and save results you need later to files. See run a command and the SDK reference.

Untrusted code

Treat everything the model writes as untrusted. Do not pass lizardToken unless the code needs Lizard CLI. If you do, the sandbox gets a new key limited to its project and deleted with the sandbox, so code in it can change that project. Keep other secrets out of envs.

Storage and billing

Variables and files last only as long as the sandbox. Billing and storage rules are in storage and billing; sizes and prices are in limits.

Sandboxes overview · Quickstart · SDK reference · Persistent Volumes · Sandboxes product page

Updated