Runtime

How to add a code interpreter to a chatbot

Give each conversation its own sandbox with a stateful interpreter, run the model's code there, and show the text, tables and charts.

On Runtime a conversation's interpreter costs $0.03125 an hour while it waits for the next message. Runtime bills the CPU the code uses, not the CPUs the sandbox holds, so a chat that runs a cell every few minutes pays for the seconds of work and the memory. A new sandbox ran its first Python command 351 ms after the request at the median on 24 September 2026 (speed), so it can start when the conversation does.

The short answer

One sandbox per conversation, found again by name on every turn. The interpreter keeps variables between cells, and the sandbox pauses itself when the chat goes quiet.

TypeScriptimport { Sandbox } from "withruntime";export async function runCell(conversationId: string, code: string) {  const sbx = await Sandbox.getOrCreate(`chat-${conversationId}`, {    vcpu: 1,    memoryMiB: 2048,    idlePauseSeconds: 600, // pause after ten quiet minutes    network: { internet: false },  });  const cell = await sbx.interpreter.run(code, { timeoutMs: 60_000 });  return {    status: cell.status, // "ok", "error", "timeout" ...    stdout: cell.stdout,    error: cell.error?.value,    results: cell.results.map((result) => result.data), // text/plain, image/png, tables  };}
Pythonfrom withruntime import Sandboxdef run_cell(conversation_id: str, code: str) -> dict:    sbx = Sandbox.get_or_create(        f"chat-{conversation_id}",        vcpu=1,        memory_mib=2048,        idle_pause_seconds=600,  # pause after ten quiet minutes        network={"internet": False},    )    cell = sbx.interpreter.run(code, timeout_ms=60_000)    return {        "status": cell["status"],        "stdout": cell["stdout"],        "error": (cell["error"] or {}).get("value"),        "results": [result["data"] for result in cell["results"]],    }

Expose runCell to the model as a tool, for example run_python(code), and pass what it returns back as the tool result. The first call in a conversation creates the sandbox; every later call gets the same one, woken if it paused.

What comes back

Each cell returns its status, its printed output, an error with its traceback if it raised, and a list of results. Each result is a bundle keyed by MIME type:

You run You get back
A pandas data frame such as df A table (application/vnd.runtime.table+json)
A matplotlib chart A PNG image (base64 in image/png)
print(...) stdout
An exception status: "error" and error with name, value, traceback
A cell that runs too long status: "timeout"
display of a file (.csv, .html) The file as a result

Render the PNG in your chat UI as an image, the table as a table, and send the text to the model so it can read its own output.

Let users upload files

A user's CSV goes into the sandbox as a file. The next cell reads it:

TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.getOrCreate("chat-42", { idlePauseSeconds: 600 });await sbx.files.write("/workspace/sales.csv", "month,revenue\nJan,120\nFeb,135\n");const cell = await sbx.interpreter.run(  "import pandas as pd\ndf = pd.read_csv('sales.csv')\ndf.describe()",);console.log(cell.results[0]?.data["text/plain"]);
Pythonfrom withruntime import Sandboxsbx = Sandbox.get_or_create("chat-42", idle_pause_seconds=600)sbx.files.write("/workspace/sales.csv", "month,revenue\nJan,120\nFeb,135\n")cell = sbx.interpreter.run("import pandas as pd\ndf = pd.read_csv('sales.csv')\ndf.describe()")print(cell["results"][0]["data"]["text/plain"])

/workspace is the working directory, so relative paths land there. pandas, NumPy and matplotlib are in the default image (the sandbox environment). Large files go up in checked chunks; there is no size limit beyond the disk.

What a chatbot interpreter needs

Need How Runtime covers it
State between turns Variables persist between cells; a paused sandbox keeps its memory and processes
Several languages Python, JavaScript, TypeScript, R, Java, Bash and Go
Charts and tables matplotlib and R plots as PNG; pandas and R data frames as tables
One user cannot reach another A Firecracker microVM with its own kernel for every conversation
Code that sends data out network: { internet: false }, enforced on the host, root included
An infinite loop timeoutMs ends the cell and reports timeout
Quiet conversations idlePauseSeconds pauses; the next call wakes it, usually in about half a second
A runaway bill maxCostMicros per create and a daily spending limit per key
Many chats at once 100 sandboxes at once on a paid account to start, eight on the free trial

R, Java and Go install the first time a sandbox uses them, about 35 to 90 seconds once; bake them into a custom image with apt so every conversation starts with them.

When the model needs a package

Leave the internet off by default. To let a cell install from PyPI, allow only the registry, then close it again:

TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.getOrCreate("chat-42", { network: { internet: false } });await sbx.network.set({ internet: true, allow: ["pypi.org", "*.pythonhosted.org"] });await sbx.exec("pip install --quiet tabulate", { check: true, timeoutMs: 120_000 });await sbx.network.set({ internet: false });

A rule applies at once, to connections already open too (turn off sandbox internet).

What it costs

A conversation's sandbox of 1 vCPU and 2 GiB is billed while it runs: measured CPU at $0.025 per vCPU-hour, with a floor of a twentieth of a vCPU, and memory at $0.0075 per GiB-hour (pricing). Take 10,000 conversations a month, each keeping its sandbox running for 5 minutes and using 20 CPU-seconds of work:

TextCPU:    10,000 × 20 s / 3,600 × $0.025          = $1.39Memory: 10,000 × 300 s / 3,600 × 2 GiB × $0.0075 = $12.50Total:                                             $13.89

That is about $0.0014 a conversation. 20 CPU-seconds over 300 seconds is above the floor, so the floor adds nothing. Stop the sandbox when the conversation closes; a sandbox you keep paused instead is billed as paused storage at $0.08 per GB per 30-day month. New accounts get 50 free sandbox hours, no card.

Start

Terminalnpx withruntime sandbox run --trial -- python3 -c 'print(6 * 7)'

The first run prints a link to approve in your browser; there is no API key to copy. Then use the JavaScript or Python SDK as above.

Related: what a code interpreter is, run untrusted LLM code safely, a data analysis agent, pause and resume a sandbox.

Facts on this page were checked on 25 September 2026.