# Gemini — Function calling (`tools[].functionDeclarations`) **Status:** DOCUMENTED + LIVE_VERIFIED (2026-09-18 run — see "Live verification" section at the end) Sources: - https://ai.google.dev/gemini-api/docs/generate-content/function-calling (generateContent flavour) · https://ai.google.dev/gemini-api/docs/function-calling (Interactions flavour) - https://ai.google.dev/gemini-api/docs/generate-content/thought-signatures · https://ai.google.dev/gemini-api/docs/thinking#signatures - https://ai.google.dev/gemini-api/docs/tool-combination · https://ai.google.dev/gemini-api/docs/live-tools · https://ai.google.dev/api/live - https://ai.google.dev/api/generate-content (#Tool #FunctionDeclaration #FunctionCallingConfig #FunctionCall #FunctionResponse #FunctionResponsePart #Behavior #Scheduling) - SDK: https://github.com/googleapis/python-genai (README: AFC, MCP) · https://github.com/googleapis/js-genai Last verified: 2026-09-18 (docs only) ## 1. Flow 1. Declare functions in `tools[].functionDeclarations[]`. 2. Model answers with `functionCall` parts `{name, args, id}` — **Gemini 3 always returns an `id`**; the first `functionCall` of each step carries a `thoughtSignature`. 3. You execute the function(s) and append a content (role `user` — the reference also mentions role `function`) with `functionResponse` parts `{name, response, id}`. 4. Model produces text or further calls (compositional). Repeat. Check `candidates[].finishReason`: `STOP`, `MALFORMED_FUNCTION_CALL`, `UNEXPECTED_TOOL_CALL` (tool called but none enabled), `TOO_MANY_TOOL_CALLS`, `MISSING_THOUGHT_SIGNATURE`. ## 2. Declaration fields (`FunctionDeclaration`) | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | `[A-Za-z0-9_.:-]`, ≤128 chars; SDK: must start with letter/underscore; guide: no spaces/periods/dashes | | `description` | string | yes (REST ref) | Drives tool selection — be specific | | `parameters` | `Schema` | no | OpenAPI 3.03 **subset**: `type`, `properties`, `required`, `enum`, `description`, `items`, `format`, `nullable`, `anyOf`, `propertyOrdering`, `minimum/maximum`, `minItems/maxItems`, `minLength/maxLength`, `pattern`, `default`, `example`, `title`, `minProperties/maxProperties`. Type names may be upper-case (`OBJECT`, `STRING`). Param names ≤64 chars. Mutually exclusive with `parametersJsonSchema` | | `parametersJsonSchema` | JSON Schema | no | Full JSON Schema object (`additionalProperties`, etc.). Mutually exclusive with `parameters` | | `response` / `responseJsonSchema` | Schema / JSON Schema | no | Return-type description; mutually exclusive pair | | `behavior` | `UNSPECIFIED` \| `BLOCKING` \| `NON_BLOCKING` | no | **Live API only**; default BLOCKING | Max 512 declarations per request (SDK docstring). Best practice: 10–20 active tools; low temperature; strong typing/enums; validate high-impact calls with the user. ## 3. Modes (`toolConfig.functionCallingConfig`) | `mode` | Behaviour | `allowedFunctionNames` | |---|---|---| | `AUTO` (default when only function declarations) | call or natural-language reply | ignored | | `ANY` | always a function call, schema-constrained; large/deep schemas may be rejected | restricts choice | | `NONE` | never call (same as sending no declarations) | — | | `VALIDATED` | call or text, calls schema-validated by constrained decoding; **default and only allowed mode** when built-in tools or structured outputs are combined / `includeServerSideToolInvocations=true` | restricts choice | | `MODE_UNSPECIFIED` | do not use | | Interactions API twin: `generation_config.tool_choice: "auto"|"any"|"none"|"validated"` or `{"allowed_tools":{"mode":"any","tools":[...]}}`. ## 4. Parallel and compositional calls | | Parallel | Compositional (sequential) | |---|---|---| | Shape | several `functionCall` parts in one model content | one call per step, chained across turns | | Signatures (Gemini 3) | only the **first** `functionCall` part carries `thoughtSignature` | each step's first call carries one; all must be returned | | Reply | all `functionResponse` parts in one user content, **after** all calls; order may differ (matched by `id`/name); interleaving FC1,FR1,FC2,FR2 → 400 | one response per step | Supported per the guide table: Gemini 3.8/3.7/3.6/3.5 Flash, 3.5 Flash-Lite, 3.1 Pro Preview, 3.1 Flash-Lite, 2.5 Pro/Flash/Flash-Lite (function, parallel and compositional all ✔). ## 5. Thought signatures (mandatory on Gemini 3) | Rule | Detail | |---|---| | Where | `Part.thoughtSignature` (base64) on the first `functionCall` of each step; on the last part when no call (Gemini 3); Gemini 2.5: on the first part of any type, optional to return | | Must echo | Gemini 3: yes for the current turn (API walks back to the last user content with standard text; every step after it is validated) → HTTP 400 "Function call `X` in the `N.` content block is missing a `thought_signature`" | | Non-FC parts | echo recommended, not validated | | Don'ts | don't merge a signed part with an unsigned one; don't combine two signed parts; don't interleave parallel FC/FR | | Injected history | dummy signatures `"skip_thought_signature_validator"` or `"context_engineering_is_the_way_to_go"` skip validation | | Streaming | with no FC, the signature may arrive in an empty-text part — read until `finishReason` | | SDKs | handled automatically when you append the full response content to history | | OpenAI compat | signatures travel in `extra_content` (see thought-signatures guide) | ## 6. Function responses | Field | Notes | |---|---| | `name` | required | | `response` | required JSON object (any keys: `output`, `result`, `error`…) | | `id` | echo `functionCall.id` (mandatory mapping on Gemini 3) | | `parts[]` | `FunctionResponsePart{inlineData{mimeType,data}}` — **multimodal responses (Gemini 3)**: `image/png`, `image/jpeg`, `image/webp`, `application/pdf`, `text/plain`; reference from `response` with `{"$ref": ""}` | | `scheduling` | Live NON_BLOCKING only: `SILENT` \| `WHEN_IDLE` (default) \| `INTERRUPT` | | `willContinue` | Live NON_BLOCKING only: generator-style multiple responses; `false` + empty response ends the call (add `scheduling: SILENT` to avoid triggering generation) | ## 7. Live API (BidiGenerateContent) - Declare tools in `setup.tools`; server sends `toolCall {functionCalls[{id,name,args}]}` and may send `toolCallCancellation {ids[]}` on interruption; client answers `toolResponse {functionResponses[]}` (`session.send_tool_response`). No automatic tool handling. - `behavior: NON_BLOCKING` = asynchronous calling: default on `gemini-3.8-live` (BLOCKING still allowed), **only** mode on `gemini-3.8-live-extended-thinking` (BLOCKING = hard error, scheduling unsupported), **unsupported** on `gemini-3.1-flash-live-preview` (sync only), sync + async on `gemini-2.5-flash-native-audio-preview-12-2025`. - Only `googleSearch` can be combined with function declarations in Live. ## 8. Multi-tool (built-in + custom) — Gemini 3, Preview Set `toolConfig.includeServerSideToolInvocations: true`; response contains `toolCall`/`toolResponse` (server tools), `executableCode`/`codeExecutionResult` (code execution) and `functionCall` parts, each with `id` and `thoughtSignature`. Echo **every** part unchanged, then append your `functionResponse`. Mode is `VALIDATED`. See [index.md §4](index.md#4-multi-tool-combination-rules). ## 9. SDK conveniences | Feature | Python `google-genai` | JS `@google/genai` | |---|---|---| | Automatic function calling (AFC) | pass Python callables in `config.tools`; SDK declares (`FunctionDeclaration.from_callable`), executes, loops (`automatic_function_calling.maximum_remote_calls` default 10, `disable`, `ignore_call_history`; history in `response.automatic_function_calling_history`). Allowed param types: `int|float|bool|str|list|pydantic.BaseModel` (no dicts). Docstring becomes the whole description | AFC for `mcpToTool()` tools; `automaticFunctionCalling: {disable: true}` to opt out | | MCP | `tools=[ClientSession]` (experimental) | `tools: [mcpToTool(client)]` (experimental) — see [mcp.md](mcp.md) | | README warning | AFC will move from `Models.generate_content` to `Chats` in the next major version | same | ## 10. Minimal examples ```bash curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash-lite:generateContent" \ -H "x-goog-api-key: $GEMINI_API_KEY" -H "Content-Type: application/json" \ -d '{"contents":[{"role":"user","parts":[{"text":"Weather in Paris?"}]}], "tools":[{"functionDeclarations":[{"name":"get_weather","description":"Weather for a city.", "parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}]}], "toolConfig":{"functionCallingConfig":{"mode":"ANY","allowedFunctionNames":["get_weather"]}}}' ``` ```python from google import genai from google.genai import types client = genai.Client() # GEMINI_API_KEY decl = types.FunctionDeclaration(name="get_weather", description="Weather for a city.", parameters_json_schema={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}) r = client.models.generate_content(model="gemini-3.5-flash-lite", contents="Weather in Paris?", config=types.GenerateContentConfig(tools=[types.Tool(function_declarations=[decl])])) fc = r.candidates[0].content.parts[0] # echo the model part (with thought_signature) then reply: follow = client.models.generate_content(model="gemini-3.5-flash-lite", contents=[ types.Content(role="user", parts=[types.Part(text="Weather in Paris?")]), r.candidates[0].content, types.Content(role="user", parts=[types.Part(function_response=types.FunctionResponse( name=fc.function_call.name, id=fc.function_call.id, response={"temp_c": 18}))])], config=types.GenerateContentConfig(tools=[types.Tool(function_declarations=[decl])])) ``` ```ts import { GoogleGenAI } from '@google/genai'; const ai = new GoogleGenAI({}); const r = await ai.models.generateContent({ model: 'gemini-3.5-flash-lite', contents: 'Weather in Paris?', config: { tools: [{ functionDeclarations: [{ name: 'get_weather', description: 'Weather for a city.', parametersJsonSchema: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] } }] }] } }); console.log(r.functionCalls); ``` ## Live verification (2026-09-18) `gemini-3.5-flash-lite`, raws `tmp-live/gemini-tools/a*.json` (all HTTP 200 unless stated): - **Forced call** (`toolConfig.functionCallingConfig.mode: ANY` + `allowedFunctionNames`) → one part `{functionCall:{name:"get_weather", args:{city:"Paris"}, id:"call_356059"}, thoughtSignature:"…"}`, `finishReason: STOP`. - **Round trip**: appending the model turn verbatim + `{functionResponse:{name, response:{…}}}` → text answer. Stripping `thoughtSignature` → **400** `Function call is missing a thought_signature in functionCall parts. This is required for tools to work correctly… function call default_api:get_weather, position 2`. - **Parallel**: two declarations, mode ANY → parts `[functionCall+thoughtSignature, functionCall]` (`call_243490`, `call_243491`) — only the first carries a signature, exactly as the docs describe. - **`parametersJsonSchema`** (plain JSON Schema with an enum) → `args {city:"Rome", unit:"C"}`. - **`mode: NONE`** → text only; **`mode: VALIDATED`** → accepted (functionCall returned). - **`behavior: NON_BLOCKING`** in generateContent → **400** `FunctionDeclaration.behavior is only supported by the BidiGenerateContent method`. In the Live API the same declaration produced `toolCall{functionCalls:[{name,args,id:"function-call-11967657623789764093"}]}` and the model kept talking (audio) — non-blocking behaviour confirmed; `setup.toolConfig` is **rejected** (close 1007), so the mode cannot be forced in Live. - Interactions API twin: `tools:[{type:"function",…}]` + `generation_config.tool_choice{allowed_tools{mode:"any", tools:[…]}}` → `status:"requires_action"`, step `{id:"call_297992", type:"function_call", name, arguments}`; top-level `tool_choice` → 400 `Unknown parameter 'tool_choice'`; reply with `input:[{type:"function_result", name, call_id, result:[{type:"text", text}]}]` + `previous_interaction_id`. Examples: `examples/gemini/tools/function-calling/{function_calling.py,forced_call.sh,function_calling.ts}`, `examples/shared/tool-loop/gemini_tool_loop.{py,ts}` (all executed). Tests: `tests/gemini/test_tools.py`.