Google Gemini’s function-calling API (FunctionDeclaration + FunctionCall, exposed via @google/genai in TS and google-genai in Python) is structurally similar to OpenAI’s tool-use and Anthropic’s tool_use: you declare schemas, the model emits a call, you execute it, you return the result, the model continues.
Gemini does ship a built-in code_execution tool. It’s fine for demos. It’s not fine for production: you don’t control the environment, data passes through Google’s infra, and there’s no persistence across calls. For real work you wire in your own hardware-isolated sandbox. Here’s how.
Install
# Python pip install google-genai podflare # or TypeScript npm install @google/genai podflare
Python: wire a Podflare sandbox into Gemini
import os
from google import genai
from google.genai import types
from podflare import Sandbox
from podflare.gemini import podflare_function_declaration, run_function_call
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
sandbox = Sandbox()
# podflare_function_declaration() returns the FunctionDeclaration
# Gemini expects, with the right schema for the code argument and
# a description that tells the model state persists across calls.
tools = types.Tool(function_declarations=[podflare_function_declaration()])
config = types.GenerateContentConfig(tools=[tools])
contents = [types.Content(role="user", parts=[types.Part(text=(
"Fetch the current weather in Nairobi, Lagos, and Accra via "
"wttr.in, and tell me which city is warmest right now."
))])]
while True:
resp = client.models.generate_content(
model="gemini-2.5-pro",
contents=contents,
config=config,
)
# Record the model's turn in history.
contents.append(resp.candidates[0].content)
fn_calls = [
p.function_call for p in resp.candidates[0].content.parts
if p.function_call
]
if not fn_calls:
print(resp.text) # final answer
break
# Execute every function call via Podflare, return tool results.
tool_parts = []
for call in fn_calls:
result = run_function_call(call, sandbox)
tool_parts.append(types.Part(
function_response=types.FunctionResponse(
name=call.name,
response={"result": result},
),
))
contents.append(types.Content(role="user", parts=tool_parts))
sandbox.close()podflare_function_declaration() is pre-built so you don’t have to write the FunctionDeclaration boilerplate. If you want to customize the description or add auth scoping, the adapter is ~40 lines — fork it.
TypeScript
import { GoogleGenAI } from "@google/genai";
import { Sandbox } from "podflare";
import { podflareFunctionDeclaration, runFunctionCall } from "podflare/gemini";
const genai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
const sandbox = new Sandbox();
await sandbox.open();
const tools = [{
functionDeclarations: [podflareFunctionDeclaration()],
}];
let contents = [{
role: "user",
parts: [{
text: "Compute 100 days of SPY daily returns. Report stdev."
}],
}];
while (true) {
const resp = await genai.models.generateContent({
model: "gemini-2.5-pro",
contents,
config: { tools },
});
contents.push(resp.candidates[0].content);
const fnCalls = (resp.candidates[0].content.parts ?? [])
.map((p) => p.functionCall)
.filter(Boolean);
if (fnCalls.length === 0) {
console.log(resp.text);
break;
}
const toolParts = await Promise.all(
fnCalls.map(async (call) => ({
functionResponse: {
name: call.name,
response: { result: await runFunctionCall(call, sandbox) },
},
})),
);
contents.push({ role: "user", parts: toolParts });
}
await sandbox.close();Why you want this over Gemini’s built-in code_execution
- Real environment control.
pip install matplotlib plotly scikit-learn polars— whatever. Gemini’s built-in has a fixed library set you can’t extend. - Persistent state across function calls. The sandbox’s Python REPL keeps
globals()alive between everyfunction_call. Gemini’s built-in drops state between turns. - Data stays in your perimeter. Gemini runs its built-in inside Google. Podflare runs in a region you chose.
- Fork for parallel hypotheses.
sandbox.fork(n=5)in ~80 ms server-side. Not available in the built-in. - Full-Linux access. Run shell commands, compile C, call
ffmpeg, whatever. The built-in is sandbox-restricted.
Multimodal bonus
Gemini is particularly strong at multimodal reasoning (images, audio, video). Combining that with a Python sandbox is powerful: the model can describe what it sees in an image, then run code to process the numbers it extracted. Upload an image to the sandbox first via sandbox.upload(bytes, "/tmp/chart.png"), then let the model generate Python to parse + analyze it.
Streaming
If you want the model’s reasoning + tool calls to stream to your UI as they emerge, swap to generateContentStream. The loop structure is the same — you just collect parts as they arrive.
Performance
From Vertex’s us-central1 or any Gemini region that’s near a Podflare region (us-central Dallas, us-east Ashburn), expect ~40 ms p50 round-trip from Gemini’s edge to a Podflare sandbox. The Gemini inference + Podflare exec latency are additive, but both are fast enough that interactive agent UX stays snappy.
Ship it
Free Podflare account. Full Gemini + Podflare example: Python | TypeScript.