Together AI function calling: run the model's code in a sandbox
Give a Together model a run_python tool backed by a stateful sandbox interpreter, and return each cell's output as a tool message.
A five-minute coding session costs about $0.0014 in a Runtime sandbox,
against the $0.03 per session Together charges for its Code Interpreter,
checked 25 September 2026. Runtime bills the CPU the code uses and the memory
it holds, so a session that mostly waits for the model costs little, and its
interpreter runs seven languages where Together's runs Python. The examples
call zai-org/GLM-5.3, which Together's serverless catalogue marked for
function calling on 25 September 2026.
One interpreter per conversation
Together's Code Interpreter keeps "all packages, variables, and memory" for a
session reused by its session_id. The Runtime equivalent is a sandbox found
again by name: Sandbox.getOrCreate returns the conversation's sandbox, woken
if it paused, and its interpreter still holds every variable.
TypeScriptimport Together from "together-ai";import type { ChatCompletionMessageParam } from "together-ai/resources/chat/completions";import { Sandbox } from "withruntime";const together = new Together(); // TOGETHER_API_KEY, on your serverasync function runCell(conversationId: string, code: string): Promise<string> { const sbx = await Sandbox.getOrCreate(`chat-${conversationId}`, { vcpu: 1, memoryMiB: 2048, idlePauseSeconds: 600, network: { internet: false }, }); const cell = await sbx.interpreter.run(code, { timeoutMs: 60_000 }); return JSON.stringify({ status: cell.status, stdout: cell.stdout, stderr: cell.stderr, error: cell.error?.value ?? null, result: cell.results.find((r) => r.main)?.data["text/plain"] ?? null, });}const tools = [ { type: "function" as const, function: { name: "run_python", description: "Run a Python cell in this conversation's notebook. Variables and imports persist " + "between calls. No internet. Returns status, stdout, stderr, error and the last value.", parameters: { type: "object", properties: { code: { type: "string" } }, required: ["code"], }, }, },];export async function chat(conversationId: string, messages: ChatCompletionMessageParam[]) { for (let turn = 0; turn < 8; turn++) { const response = await together.chat.completions.create({ model: "zai-org/GLM-5.3", messages, tools, }); const message = response.choices[0]?.message; const toolCalls = message?.tool_calls ?? []; if (toolCalls.length === 0) return message?.content ?? ""; messages.push({ role: "assistant", content: "", tool_calls: toolCalls }); for (const call of toolCalls) { const { code } = JSON.parse(call.function.arguments) as { code: string }; messages.push({ role: "tool", tool_call_id: call.id, content: await runCell(conversationId, code), }); } } return "stopped after 8 turns";}console.log( await chat("42", [ { role: "user", content: "Load a 1,000-row random DataFrame and describe it." }, ]),);Pythonimport jsonfrom together import Togetherfrom withruntime import Sandboxclient = Together() # TOGETHER_API_KEY, on your serverdef run_cell(conversation_id: str, code: str) -> str: sbx = Sandbox.get_or_create( f"chat-{conversation_id}", vcpu=1, memory_mib=2048, idle_pause_seconds=600, network={"internet": False}, ) cell = sbx.interpreter.run(code, timeout_ms=60_000) main = next((r["data"].get("text/plain") for r in cell["results"] if r["main"]), None) return json.dumps( { "status": cell["status"], "stdout": cell["stdout"], "stderr": cell["stderr"], "error": (cell["error"] or {}).get("value"), "result": main, } )tools = [ { "type": "function", "function": { "name": "run_python", "description": "Run a Python cell in this conversation's notebook. Variables and imports persist " "between calls. No internet. Returns status, stdout, stderr, error and the last value.", "parameters": {"type": "object", "properties": {"code": {"type": "string"}}, "required": ["code"]}, }, }]def chat(conversation_id: str, messages: list) -> str: for _ in range(8): message = client.chat.completions.create(model="zai-org/GLM-5.3", messages=messages, tools=tools).choices[0].message if not message.tool_calls: return message.content or "" messages.append({"role": "assistant", "content": "", "tool_calls": [c.model_dump() for c in message.tool_calls]}) for call in message.tool_calls: code = json.loads(call.function.arguments)["code"] messages.append({"role": "tool", "tool_call_id": call.id, "content": run_cell(conversation_id, code)}) return "stopped after 8 turns"print(chat("42", [{"role": "user", "content": "Load a 1,000-row random DataFrame and describe it."}]))The first call in a conversation creates its sandbox; later calls reuse it,
from any process. After ten quiet minutes it pauses with its memory kept, and
the next cell wakes it, usually in about half a second
(pause and resume). Stop it with
sbx.stop() when the conversation closes. A cell that runs too long returns
status: "timeout" instead of hanging the chat.
Together Code Interpreter compared
| Question | Together Code Interpreter (TCI) | Runtime sandbox interpreter |
|---|---|---|
| How the model reaches it | Your code calls code_interpreter / codeInterpreter.execute |
Your code calls sbx.interpreter.run |
| Languages | "Currently only supports Python" | Python, JavaScript, TypeScript, R, Java, Bash and Go |
| Session life | 60 minutes | Runs, then pauses when idle; kept 1 to 365 days while paused |
| Packages | Pre-installed data libraries, !pip install |
Default image, pip behind an allow list, or a custom image |
| Output | stdout, stderr, errors, display data | stdout, stderr, error with traceback, PNG charts, tables |
| Network control | Not described on the TCI page | Internet off, or named hosts only, enforced on the host |
| Price | $0.03 per session | Measured CPU plus memory while running |
Both run the code outside your servers. TCI is one API call away if your stack is Together-only and Python-only. Runtime gives you the machine: its size, image, network rules and how long it lives.
What sessions cost
A 1 vCPU, 2 GiB sandbox, as above, is billed at $0.025 per vCPU-hour of measured CPU, with a floor of a twentieth of a vCPU, and $0.0075 per GiB-hour of memory (pricing):
| Session | TCI | Runtime, 1 vCPU and 2 GiB |
|---|---|---|
| 5 minutes, 20 CPU-seconds of work | $0.03 | $0.00125 memory + $0.00014 CPU = $0.0014 |
| 1,000 such sessions | $30.00 | $1.39 |
| 60 minutes, mostly waiting (the CPU floor) | $0.03 | $0.015 + $0.00125 = $0.01625 |
| 60 minutes with the CPU busy throughout | $0.03 | $0.015 + $0.025 = $0.04 |
Tokens are billed by Together in both cases. A paused sandbox between turns stops compute billing and is charged as paused storage.
Keys
TOGETHER_API_KEY stays on your server: the loop calls Together, and the
sandbox receives only cells. If code inside the sandbox must call Together,
store the key as a Runtime secret bound to api.together.ai; the sandbox sees
a placeholder that only the host's proxy turns into the key
(secrets).
Start
New accounts get 50 free sandbox hours, no card. The first run prints a link to approve in your browser:
Terminalnpx withruntime sandbox run --trial -- python3 -c 'import pandas; print(pandas.__version__)'More: add a code interpreter to a chatbot, a data analysis agent, RL environments, what a code interpreter is.
Sources
Checked 25 September 2026.
- Together AI: function calling and agentic patterns
- Together AI: code interpreter
- Together AI: serverless models
- together-ai on npm (0.55.0) and together on PyPI (2.36.0)
Facts on this page were checked on 25 September 2026.