# DeepSeek tool calls: how to run DeepSeek's code safely Point the OpenAI SDK at `api.deepseek.com`, give the model a `run_python` tool, and run each call in a microVM that installs from PyPI only. **DeepSeek's API runs no code for you, so where its code runs is your decision, and a Runtime sandbox makes it a throwaway microVM that costs $0.03125 an hour while it waits.** DeepSeek's tool-calls guide says so plainly: "The model itself does not execute specific functions." Every Runtime sandbox is a Firecracker microVM with its own Linux kernel, and its network rules are enforced on the host, where root inside cannot change them ([security](/docs/security)). The examples use `deepseek-flash`, which DeepSeek's pricing page mapped to DeepSeek-V4.1-Flash on 25 September 2026. ## Let the model ask for packages Model-written Python often starts with an import the default image lacks. This tool takes a list of packages as well as the code. The handler installs them while the sandbox can reach PyPI and nothing else, then switches the internet off before the model's script runs. ```ts check import OpenAI from "openai"; import { Sandbox } from "withruntime"; const deepseek = new OpenAI({ baseURL: "https://api.deepseek.com", apiKey: process.env.DEEPSEEK_API_KEY, }); const PACKAGE = /^[A-Za-z0-9][A-Za-z0-9._-]*(==[A-Za-z0-9.]+)?$/; async function runPython(code: string, packages: string[]): Promise { const bad = packages.filter((name) => !PACKAGE.test(name)); if (bad.length) return JSON.stringify({ error: `not a package name: ${bad.join(", ")}` }); await using sbx = await Sandbox.create({ network: { internet: true, allow: ["pypi.org", "*.pythonhosted.org"] }, timeoutSeconds: 600, onLeaseEnd: "stop", }); if (packages.length) { const install = await sbx.exec(["pip", "install", "--quiet", ...packages], { timeoutMs: 180_000, }); if (install.exitCode !== 0) return JSON.stringify({ stage: "install", stderr: install.stderr }); } await sbx.network.set({ internet: false }); // nothing leaves while the script runs await sbx.files.write("/workspace/main.py", code); const run = await sbx.exec(["python3", "main.py"], { timeoutMs: 60_000 }); return JSON.stringify({ exit_code: run.exitCode, timed_out: run.timedOut, stdout: run.stdout, stderr: run.stderr, }); } const tools: OpenAI.Chat.ChatCompletionTool[] = [ { type: "function", function: { name: "run_python", description: "Run a Python 3.12 script in a disposable sandbox. List any PyPI packages it needs " + "in `packages`; they are installed first, then the script runs with no internet.", parameters: { type: "object", properties: { code: { type: "string" }, packages: { type: "array", items: { type: "string" } }, }, required: ["code", "packages"], }, }, }, ]; const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: "user", content: "Use sympy to factor x**6 - 1 and show the result." }, ]; for (let turn = 0; turn < 8; turn++) { const completion = await deepseek.chat.completions.create({ model: "deepseek-flash", messages, tools, }); const message = completion.choices[0]!.message; messages.push(message); // keeps reasoning_content, which DeepSeek requires back if (!message.tool_calls?.length) { console.log(message.content); break; } for (const call of message.tool_calls) { if (call.type !== "function") continue; const args = JSON.parse(call.function.arguments) as { code: string; packages?: string[] }; messages.push({ role: "tool", tool_call_id: call.id, content: await runPython(args.code, args.packages ?? []), }); } } ``` ```python check import json import os import re from openai import OpenAI from withruntime import Sandbox client = OpenAI(api_key=os.environ["DEEPSEEK_API_KEY"], base_url="https://api.deepseek.com") PACKAGE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*(==[A-Za-z0-9.]+)?$") def run_python(code: str, packages: list[str]) -> str: bad = [name for name in packages if not PACKAGE.match(name)] if bad: return json.dumps({"error": f"not a package name: {', '.join(bad)}"}) with Sandbox.create( network={"internet": True, "allow": ["pypi.org", "*.pythonhosted.org"]}, timeout_seconds=600, on_lease_end="stop", ) as sbx: if packages: install = sbx.exec(["pip", "install", "--quiet", *packages], timeout_ms=180_000) if install.exit_code != 0: return json.dumps({"stage": "install", "stderr": install.stderr}) sbx.network.set(internet=False) # nothing leaves while the script runs sbx.files.write("/workspace/main.py", code) run = sbx.exec(["python3", "main.py"], timeout_ms=60_000) return json.dumps({"exit_code": run.exit_code, "timed_out": run.timed_out, "stdout": run.stdout, "stderr": run.stderr}) tools = [ { "type": "function", "function": { "name": "run_python", "description": "Run a Python 3.12 script in a disposable sandbox. List any PyPI packages it needs " "in `packages`; they are installed first, then the script runs with no internet.", "parameters": { "type": "object", "properties": {"code": {"type": "string"}, "packages": {"type": "array", "items": {"type": "string"}}}, "required": ["code", "packages"], }, }, } ] messages = [{"role": "user", "content": "Use sympy to factor x**6 - 1 and show the result."}] for _ in range(8): message = client.chat.completions.create(model="deepseek-flash", messages=messages, tools=tools).choices[0].message messages.append(message) # keeps reasoning_content, which DeepSeek requires back if not message.tool_calls: print(message.content) break for call in message.tool_calls: args = json.loads(call.function.arguments) messages.append({"role": "tool", "tool_call_id": call.id, "content": run_python(args["code"], args.get("packages", []))}) ``` ## Why the handler is written this way | Line in the handler | What it stops | | ------------------------------------------- | --------------------------------------------------------------------- | | The package-name pattern | A "package" such as `--index-url=https://evil.example` changing pip | | `allow: ["pypi.org", "*.pythonhosted.org"]` | The install fetching from anywhere but PyPI | | `network.set({ internet: false })` | The script sending data out; the rule applies to open connections too | | `exec([...])` with an array | Any part of the model's text being read by a shell | | `timeoutMs` | An endless loop; the result comes back with `timedOut: true` | | `await using` / `with` | A sandbox left running after the answer | A package that runs code while it installs runs it inside the same throwaway machine, behind the same PyPI-only rule, so it can reach nothing else either ([egress control](/glossary/egress-control)). ## DeepSeek details that break loops - **Pass reasoning back.** DeepSeek's thinking mode is on by default, and its guide warns that for requests with `tools`, "the reasoning_content must be fully passed back to the API in all subsequent requests", or the API returns a 400 error. Appending the whole assistant `message`, as above, does that. - **Strict schemas are beta.** Setting `strict: true` on a function needs the base URL `https://api.deepseek.com/beta`. - **Prices change with the clock.** DeepSeek charges a peak rate from 01:00 to 04:00 and 06:00 to 10:00 UTC on weekdays, excluding Chinese public holidays. | Model (25 September 2026) | Input, cache miss, per 1M tokens | Output per 1M tokens | | ---------------------------------- | -------------------------------- | -------------------- | | `deepseek-flash`, off-peak / peak | $0.15 / $0.30 | $0.60 / $1.20 | | `deepseek-v4-pro`, off-peak / peak | $0.66 / $1.32 | $1.98 / $3.96 | ## Where the DeepSeek key goes The loop runs on your server, so `DEEPSEEK_API_KEY` stays in its environment and never enters the sandbox. Store it as a Runtime secret, bound to `api.deepseek.com`, only if something inside the sandbox must call DeepSeek itself, such as an agent you run there; the sandbox then sees a placeholder that works on that host alone ([secrets](/docs/security#secrets-sandboxes-never-see)). ## What the sandbox costs A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour waiting and $0.08 with both CPUs busy, billed on the CPU the code uses ([pricing](/docs/pricing)). The install is part of each run; to skip it for packages the model asks for often, bake them into a [custom image](/docs/images) and create from that. New accounts get 50 free sandbox hours, no card: ```bash no-run npx withruntime sandbox run --trial -- python3 -c 'print(6 * 7)' ``` More: [run untrusted LLM code](/use-cases/run-untrusted-llm-code), [turn off sandbox internet](/how-to/turn-off-sandbox-internet), [agent evals and SWE-bench](/use-cases/agent-evals-and-swe-bench). ## Sources Checked 25 September 2026. - [DeepSeek: tool calls](https://api-docs.deepseek.com/guides/tool_calls) - [DeepSeek: thinking mode](https://api-docs.deepseek.com/guides/thinking_mode), section on tool calls - [DeepSeek: models and pricing](https://api-docs.deepseek.com/quick_start/pricing) - [openai on npm](https://www.npmjs.com/package/openai) (7.23.0) and [on PyPI](https://pypi.org/project/openai/) (3.19.2) Facts on this page were checked on 25 September 2026.