SPB Git forge

spb/doc-api

Public
2commits 1branches 0releases
15.7 MBsize
maindefault branch
13 days agolast push
Python 88.3% TypeScript 7.6% Shell 4.1%
2.6 KB · 38 lines typescript
Raw Blame History
1// Generic Gemini generateContent tool loop (TypeScript, @google/genai).2// STATUS: LIVE_VERIFIED 2026-09-18 — `node --env-file=.env --experimental-strip-types examples/shared/tool-loop/gemini_tool_loop.ts`3// (gemini-3.5-flash-lite, one get_weather + get_time parallel round trip). See docs/gemini/tool-loop.md for the patterns.4import { GoogleGenAI, Type } from "@google/genai";5import { logRequest } from "./log.ts";67const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });8const MODEL = "gemini-3.5-flash-lite";9type Handler = (args: Record<string, any>) => unknown;1011export async function runToolLoop(prompt: string, declarations: any[], handlers: Record<string, Handler>, maxTurns = 6): Promise<string> {12  const contents: any[] = [{ role: "user", parts: [{ text: prompt }] }];13  for (let turn = 0; turn < maxTurns; turn++) {14    const r = await ai.models.generateContent({ model: MODEL, contents, config: { tools: [{ functionDeclarations: declarations }], maxOutputTokens: 256 } });15    logRequest("gemini", "POST", "/v1beta/models/{model}:generateContent", 200, 0.0001, `ts gemini tool loop turn ${turn}`);16    const modelTurn = r.candidates![0].content!;                 // append verbatim: functionCall parts carry thoughtSignature (required on echo)17    const calls = (modelTurn.parts ?? []).filter((p: any) => p.functionCall).map((p: any) => p.functionCall);18    if (calls.length === 0) return r.text ?? "";19    const responses = calls.map((c: any) => {20      let out: unknown;21      try { out = handlers[c.name](c.args ?? {}); } catch (e) { out = { error: String(e) }; }22      console.log(`  turn ${turn}: ${c.name}(${JSON.stringify(c.args)}) -> ${JSON.stringify(out).slice(0, 80)}`);23      return { functionResponse: { name: c.name, id: c.id, response: typeof out === "object" && out !== null ? out : { result: out } } };24    });25    contents.push(modelTurn, { role: "user", parts: responses });26  }27  return "(max turns reached)";28}2930if (import.meta.url === `file://${process.argv[1]}`) {31  const decls = [32    { name: "get_weather", description: "Current weather for a city.", parameters: { type: Type.OBJECT, properties: { city: { type: Type.STRING } }, required: ["city"] } },33    { name: "get_time", description: "Current local time in a city.", parameters: { type: Type.OBJECT, properties: { city: { type: Type.STRING } }, required: ["city"] } },34  ];35  console.log(await runToolLoop("Weather in Paris and time in Tokyo? Use the tools, then answer in one sentence.", decls,36    { get_weather: (a) => ({ city: a.city, temperature_c: 18, conditions: "light rain" }), get_time: (a) => ({ city: a.city, time: "14:05" }) }));37}38