Python 88.3%
TypeScript 7.6%
Shell 4.1%
1#!/usr/bin/env python32"""Gemini function calling (REST): forced call (mode ANY), functionResponse round trip, parallel calls, parametersJsonSchema.3STATUS: LIVE_VERIFIED 2026-09-18 (gemini-3.5-flash-lite; identical requests tmp-live/gemini-tools/a1..a4*.json; < $0.001).4Key facts observed: functionCall parts carry `id` (call_…) and a `thoughtSignature`; omitting the signature when echoing the5model turn back → HTTP 400 "Function call is missing a thought_signature"; in a parallel turn only the FIRST call has one.6Run: .venv/bin/python examples/gemini/tools/function-calling/function_calling.py7"""8import json, sys9from pathlib import Path10sys.path.insert(0, str(Path(__file__).resolve().parents[4]))11from scripts.live import gemini_request12MODEL = "gemini-3.5-flash-lite"13PATH = f"/v1beta/models/{MODEL}:generateContent"14WEATHER = {"name": "get_weather", "description": "Get the current weather for a city.",15 "parameters": {"type": "OBJECT", "properties": {"city": {"type": "STRING", "description": "City name"}}, "required": ["city"]}}16TIME = {"name": "get_time", "description": "Get the current local time in a city.",17 "parameters": {"type": "OBJECT", "properties": {"city": {"type": "STRING"}}, "required": ["city"]}}1819# 1. forced call: toolConfig.functionCallingConfig.mode ANY (+ allowedFunctionNames)20user = {"role": "user", "parts": [{"text": "Weather in Paris?"}]}21st, r, _ = gemini_request("POST", PATH, {"contents": [user], "tools": [{"functionDeclarations": [WEATHER]}],22 "toolConfig": {"functionCallingConfig": {"mode": "ANY", "allowedFunctionNames": ["get_weather"]}},23 "generationConfig": {"maxOutputTokens": 64}}, note="function_calling.py forced")24model_turn = r["candidates"][0]["content"] # echo it back VERBATIM (keeps thoughtSignature)25fc = next(p for p in model_turn["parts"] if "functionCall" in p)26print("HTTP", st, "functionCall:", json.dumps(fc["functionCall"]), "thoughtSignature:", "yes" if fc.get("thoughtSignature") else "no")2728# 2. functionResponse round trip → final text29st, r, _ = gemini_request("POST", PATH, {"contents": [user, model_turn,30 {"role": "user", "parts": [{"functionResponse": {"name": "get_weather", "response": {"temperature_c": 18, "conditions": "light rain"}}}]}],31 "tools": [{"functionDeclarations": [WEATHER]}], "generationConfig": {"maxOutputTokens": 32}}, note="function_calling.py functionResponse")32print("HTTP", st, "final:", "".join(p.get("text", "") for p in r["candidates"][0]["content"]["parts"]))3334# 3. parallel calls (two declarations, mode ANY)35st, r, _ = gemini_request("POST", PATH, {"contents": [{"role": "user", "parts": [{"text": "What is the weather in Paris and the time in Tokyo? Use both tools."}]}],36 "tools": [{"functionDeclarations": [WEATHER, TIME]}], "toolConfig": {"functionCallingConfig": {"mode": "ANY"}},37 "generationConfig": {"maxOutputTokens": 128}}, note="function_calling.py parallel")38print("HTTP", st, "parallel:", [p["functionCall"]["name"] for p in r["candidates"][0]["content"]["parts"] if "functionCall" in p])3940# 4. parametersJsonSchema (plain JSON Schema instead of the OpenAPI subset)41decl = {"name": "get_weather", "description": "Get the current weather for a city.",42 "parametersJsonSchema": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["C", "F"]}}, "required": ["city"]}}43st, r, _ = gemini_request("POST", PATH, {"contents": [{"role": "user", "parts": [{"text": "Weather in Rome in Celsius?"}]}], "tools": [{"functionDeclarations": [decl]}],44 "toolConfig": {"functionCallingConfig": {"mode": "ANY"}}, "generationConfig": {"maxOutputTokens": 64}}, note="function_calling.py parametersJsonSchema")45print("HTTP", st, "jsonschema call:", json.dumps(next(p["functionCall"] for p in r["candidates"][0]["content"]["parts"] if "functionCall" in p)), "usage:", r.get("usageMetadata", {}).get("totalTokenCount"))46