Runtime

LangGraph code execution tool in a sandbox

Bind Runtime's sandbox_tools(sbx) to the model and put them in a ToolNode; every tool call then runs in a Linux microVM.

Runtime gives each LangGraph thread a machine of its own that survives between turns, paused at no compute cost. The four tools are plain Python functions, so LangGraph's prebuilt ToolNode runs them as it runs any tool. Name the sandbox after the thread and the next turn gets the same files, memory and processes back. Each sandbox is a Firecracker microVM with its own Linux kernel, and 2 vCPU with 4 GiB costs $0.03125 an hour while the graph waits on the model. langgraph 1.2.12 was the current PyPI release on 25 September 2026; the tools ran in a LangGraph ToolNode with LangGraph 1.2 on 23 September 2026.

Install

Terminalpip install -U langgraph langchain langchain-anthropic withruntimenpx withruntime login

A graph with a sandbox tool node

The classic loop: the model node calls tools, tools_condition routes to the tool node while there are tool calls, and the result goes back to the model.

Pythonfrom langchain.chat_models import init_chat_modelfrom langchain_core.tools import toolfrom langgraph.graph import END, START, MessagesState, StateGraphfrom langgraph.prebuilt import ToolNode, tools_conditionfrom withruntime import Sandboxfrom withruntime.tools import sandbox_toolsdef build_graph(sbx: Sandbox):    tools = [tool(f) for f in sandbox_tools(sbx)]    model = init_chat_model("claude-sonnet-4-6", temperature=0).bind_tools(tools)    def call_model(state: MessagesState):        return {"messages": [model.invoke(state["messages"])]}    graph = StateGraph(MessagesState)    graph.add_node("model", call_model)    graph.add_node("tools", ToolNode(tools))    graph.add_edge(START, "model")    graph.add_conditional_edges("model", tools_condition)    graph.add_edge("tools", "model")    return graph.compile()with Sandbox.create(timeout_seconds=900) as sbx:    app = build_graph(sbx)    out = app.invoke({"messages": [("user", "Write fizzbuzz.py, run it for 1..15 and show the output.")]})    print(out["messages"][-1].content)

tools_condition ends the run at END when the model stops calling tools. By default the tool node hands a call with bad arguments back to the model as an error message. A command that fails is not an exception at all: it returns its exit_code and stderr, so the model reads the failure and tries again.

One sandbox per thread

A LangGraph checkpointer keeps a conversation's messages under a thread_id. Give its sandbox the same name, and Sandbox.get_or_create returns that sandbox on every turn, woken if it was paused:

Pythonfrom withruntime import Sandboxdef run_turn(thread_id: str, text: str) -> str:    sbx = Sandbox.get_or_create(f"thread-{thread_id}", idle_pause_seconds=600)    app = build_graph(sbx)  # from the example above    out = app.invoke({"messages": [("user", text)]}, {"configurable": {"thread_id": thread_id}})    return out["messages"][-1].content
  • Between turns the sandbox pauses after ten idle minutes. Files, memory and running processes are kept and compute billing stops; the next tool call wakes it, usually in about half a second (pause, wake, extend).
  • A dev server the agent started is still running when the user comes back, and sbx.previews.create(port) gives it a private HTTPS address.
  • When the thread ends, call sbx.stop().

Compile the graph with your checkpointer to keep the messages as well; the sandbox keeps the machine.

Why not an in-process Python sandbox?

LangChain's own langchain-sandbox ran model code in Pyodide, Python compiled to WebAssembly, under Deno. Its repository was archived on 14 January 2026, and its README now says: "These days we recommend accessing code execution either through sandbox APIs or LLM provider APIs."

Need in a LangGraph agent langchain-sandbox (archived) Runtime sandbox
Start time "A few seconds of latency" per run, per its README 351 ms median to the first Python result
Files the code writes "Currently not supported" Read, write and list, any size
Network httpx.AsyncClient, not requests Any library; allow lists or internet off, host-enforced
Languages Python under Pyodide Anything Ubuntu 24.04 runs, with sudo
State between turns A stateful mode Paused machine, 1 to 365 days

The 351 ms figure is the median of 20 runs on 24 September 2026, from the create request to the first Python result (speed).

Fan out with forks

A graph that tries several fixes in parallel can fork the prepared sandbox instead of reinstalling everything. sbx.fork(count=3) returns three running copies, each with the parent's files, memory and processes; build one graph per copy and run them as parallel branches. Each copy is billed as its own sandbox, and the snapshot the fork took is deleted when the fork finishes (snapshots and forks).

Keep keys and spending safe

  • The model key stays in the graph's process. Tools send the sandbox commands, never credentials.
  • max_cost_micros on the create refuses a sandbox whose first lease would cost more than that many microdollars.
  • A daily spending limit on the Runtime key caps what every thread's sandboxes can cost together in 24 hours; past it, a create or wake fails with spending_limit_reached (daily limits).
  • A read-only key lets a monitor list every thread's sandbox and its cost without the power to start one.
  • Network rules such as network={"internet": False} bind root inside the sandbox, because the host enforces them (turn off sandbox internet).
  • Secrets reach the sandbox as placeholders; the host adds the real value only on HTTPS requests to the hosts you name.

What it costs

Measured CPU at $0.025 per vCPU-hour, floor 50 millicores, plus $0.0075 per reserved GiB-hour while running. A paused thread pays only for parked storage (pricing). New accounts get 50 free sandbox hours, no card.

For the simpler prebuilt agent, see LangChain; for LangChain's planning harness, see Deep Agents. Why a microVM rather than a container is in run untrusted LLM code.

Sources

Checked 25 September 2026.

Facts on this page were checked on 25 September 2026.