// Generic Gemini generateContent tool loop (TypeScript, @google/genai). // STATUS: LIVE_VERIFIED 2026-09-18 — `node --env-file=.env --experimental-strip-types examples/shared/tool-loop/gemini_tool_loop.ts` // (gemini-3.5-flash-lite, one get_weather + get_time parallel round trip). See docs/gemini/tool-loop.md for the patterns. import { GoogleGenAI, Type } from "@google/genai"; import { logRequest } from "./log.ts"; const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY }); const MODEL = "gemini-3.5-flash-lite"; type Handler = (args: Record) => unknown; export async function runToolLoop(prompt: string, declarations: any[], handlers: Record, maxTurns = 6): Promise { const contents: any[] = [{ role: "user", parts: [{ text: prompt }] }]; for (let turn = 0; turn < maxTurns; turn++) { const r = await ai.models.generateContent({ model: MODEL, contents, config: { tools: [{ functionDeclarations: declarations }], maxOutputTokens: 256 } }); logRequest("gemini", "POST", "/v1beta/models/{model}:generateContent", 200, 0.0001, `ts gemini tool loop turn ${turn}`); const modelTurn = r.candidates![0].content!; // append verbatim: functionCall parts carry thoughtSignature (required on echo) const calls = (modelTurn.parts ?? []).filter((p: any) => p.functionCall).map((p: any) => p.functionCall); if (calls.length === 0) return r.text ?? ""; const responses = calls.map((c: any) => { let out: unknown; try { out = handlers[c.name](c.args ?? {}); } catch (e) { out = { error: String(e) }; } console.log(` turn ${turn}: ${c.name}(${JSON.stringify(c.args)}) -> ${JSON.stringify(out).slice(0, 80)}`); return { functionResponse: { name: c.name, id: c.id, response: typeof out === "object" && out !== null ? out : { result: out } } }; }); contents.push(modelTurn, { role: "user", parts: responses }); } return "(max turns reached)"; } if (import.meta.url === `file://${process.argv[1]}`) { const decls = [ { name: "get_weather", description: "Current weather for a city.", parameters: { type: Type.OBJECT, properties: { city: { type: Type.STRING } }, required: ["city"] } }, { name: "get_time", description: "Current local time in a city.", parameters: { type: Type.OBJECT, properties: { city: { type: Type.STRING } }, required: ["city"] } }, ]; console.log(await runToolLoop("Weather in Paris and time in Tokyo? Use the tools, then answer in one sentence.", decls, { get_weather: (a) => ({ city: a.city, temperature_c: 18, conditions: "light rain" }), get_time: (a) => ({ city: a.city, time: "14:05" }) })); }