# Gemini function calling: run Gemini's code in a sandbox Declare a function tool, run each `function_call` step's code in an isolated microVM, and return a `function_result` with the output. **A Runtime sandbox lifts the two limits of Gemini's built-in code execution: the 30-second cap and the fixed library set.** A command runs for up to 24 hours, and a custom image carries any package you install into it. Each call still gets a fresh Firecracker microVM with the internet off, and a new sandbox ran its first Python command in 351 ms at the median on 24 September 2026 ([speed](/docs/speed)). The code uses `gemini-3.8-flash`, which Google's model list showed as its latest stable flagship on 25 September 2026. ## Build the image once Gemini's code execution page says "You can't install your own libraries." With your own tool you choose them. Build an image with the packages Gemini's code should find, and every sandbox starts with them ready: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.images.build({ name: "gemini-analysis", recipe: { pip: ["polars", "duckdb", "scipy"] }, }); ``` Building an image is free; a stored image is charged on its size ([custom images](/docs/images)). ## The tool loop Google's Interactions API returns `steps`. A step of type `function_call` carries an `id`, a `name` and `arguments` already parsed into an object. Your reply is a `function_result` step with the same `call_id`, sent with `previous_interaction_id` so Gemini keeps the context. ```ts check import { GoogleGenAI } from "@google/genai"; import { Sandbox } from "withruntime"; const ai = new GoogleGenAI({}); // GEMINI_API_KEY, read on your server async function runPython(code: string) { await using sbx = await Sandbox.create({ image: "gemini-analysis", network: { internet: false }, timeoutSeconds: 900, onLeaseEnd: "stop", }); await sbx.files.write("/workspace/main.py", code); const run = await sbx.exec(["python3", "main.py"], { timeoutMs: 600_000 }); return { run, text: JSON.stringify({ exit_code: run.exitCode, stdout: run.stdout, stderr: run.stderr }), }; } const tools = [ { type: "function" as const, name: "run_python", description: "Run a Python 3.12 script in a sandbox with polars, duckdb, scipy, pandas and numpy. " + "No internet. Up to 10 minutes. Returns exit_code, stdout and stderr.", parameters: { type: "object", properties: { code: { type: "string", description: "The complete script" } }, required: ["code"], }, }, ]; let interaction = await ai.interactions.create({ model: "gemini-3.8-flash", input: "Simulate 10 million dice rolls with numpy and report the mean and variance.", tools, }); for (let turn = 0; turn < 8; turn++) { const calls = (interaction.steps ?? []).filter((step) => step.type === "function_call"); if (calls.length === 0) break; const results = []; for (const call of calls) { const { run, text } = await runPython(String(call.arguments.code)); results.push({ type: "function_result" as const, name: call.name, call_id: call.id, is_error: run.exitCode !== 0, result: [{ type: "text" as const, text }], }); } interaction = await ai.interactions.create({ model: "gemini-3.8-flash", input: results, tools, previous_interaction_id: interaction.id, }); } console.log(interaction.output_text); ``` ```python check import json from google import genai from withruntime import Sandbox client = genai.Client() # GEMINI_API_KEY, read on your server def run_python(code: str) -> tuple[bool, str]: with Sandbox.create( image="gemini-analysis", network={"internet": False}, timeout_seconds=900, on_lease_end="stop", ) as sbx: sbx.files.write("/workspace/main.py", code) run = sbx.exec(["python3", "main.py"], timeout_ms=600_000) text = json.dumps({"exit_code": run.exit_code, "stdout": run.stdout, "stderr": run.stderr}) return run.exit_code != 0, text tools = [ { "type": "function", "name": "run_python", "description": "Run a Python 3.12 script in a sandbox with polars, duckdb, scipy, pandas and numpy. " "No internet. Up to 10 minutes. Returns exit_code, stdout and stderr.", "parameters": { "type": "object", "properties": {"code": {"type": "string", "description": "The complete script"}}, "required": ["code"], }, } ] interaction = client.interactions.create( model="gemini-3.8-flash", input="Simulate 10 million dice rolls with numpy and report the mean and variance.", tools=tools, ) for _ in range(8): calls = [step for step in (interaction.steps or []) if step.type == "function_call"] if not calls: break results = [] for call in calls: failed, text = run_python(call.arguments["code"]) results.append( { "type": "function_result", "name": call.name, "call_id": call.id, "is_error": failed, "result": [{"type": "text", "text": text}], } ) interaction = client.interactions.create( model="gemini-3.8-flash", input=results, tools=tools, previous_interaction_id=interaction.id ) print(interaction.output_text) ``` Each call gets its own sandbox, which stops when the handler returns. A recipe builds on Runtime's default image, so NumPy, pandas and matplotlib are there too ([custom images](/docs/images#build-from-an-image-or-a-recipe)). A script that loops forever ends at `timeoutMs` with `timedOut` set, rather than holding your server. ## Built-in code execution or your own tool? Gemini's `{"type": "code_execution"}` tool runs Python on Google's side. Its documentation, checked 25 September 2026, says "there's no additional charge for enabling code execution" beyond tokens, and sets these limits: | Question | Gemini code execution | `run_python` on Runtime | | ------------------ | ----------------------------------------------- | ----------------------------------------------------------------- | | Languages that run | "Gemini is only able to execute code in Python" | Python, Node.js, Bun, bash, gcc; the interpreter adds R, Java, Go | | Longest run | "The maximum runtime ... is 30 seconds" | `timeoutMs` up to 24 hours per command | | Libraries | 40+ pre-installed; "You can't install your own" | Anything in your image, or `pip install` behind an allow list | | Files | CSV and text in; output files come back inline | Any file, any size the disk holds, read back with `files.read` | | Retries on error | Up to 5 attempts, inside one request | As many turns as your loop allows | | Price | Tokens only | Measured CPU and memory while the sandbox runs | For quick arithmetic and small pandas jobs, the built-in tool is the shortest path. For a job that needs a package, more than 30 seconds, or a language besides Python, give Gemini your own tool. ## Keys stay where they are The Gemini key never enters the sandbox: your server calls Google, and the sandbox receives only the script. If code inside the sandbox must call Gemini itself, store the key as a Runtime secret bound to `generativelanguage.googleapis.com`; the sandbox then holds a placeholder that only the host's proxy can turn into the key, on that host alone ([secrets](/docs/security#secrets-sandboxes-never-see)). ## Google ADK An agent built with Google's Agent Development Kit takes Runtime's command and file tools as they are: `sandbox_tools(sbx)` in Python ([Google ADK](/docs/frameworks#google-adk)). ## What it costs Runtime bills the CPU a script uses at $0.025 per vCPU-hour, with a floor of a twentieth of a vCPU, and memory at $0.0075 per GiB-hour. A 2 vCPU, 4 GiB sandbox busy on both CPUs costs $0.08 an hour, so a 30-second run costs well under a tenth of a cent ([pricing](/docs/pricing)). New accounts get 50 free sandbox hours, no card: ```bash no-run npx withruntime sandbox run --trial -- python3 -c 'import numpy; print(numpy.__version__)' ``` More: [a data analysis agent](/use-cases/data-analysis-agent), [run untrusted LLM code](/use-cases/run-untrusted-llm-code), [what a code interpreter is](/glossary/code-interpreter), [Python in a sandbox](/languages/python). ## Sources Checked 25 September 2026. - [Google: Gemini function calling](https://ai.google.dev/gemini-api/docs/function-calling) - [Google: Gemini code execution](https://ai.google.dev/gemini-api/docs/code-execution) - [Google: Gemini models](https://ai.google.dev/gemini-api/docs/models) - [@google/genai on npm](https://www.npmjs.com/package/@google/genai) (2.24.0) and [google-genai on PyPI](https://pypi.org/project/google-genai/) (2.25.0) Facts on this page were checked on 25 September 2026.