TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
1// Probe 07: thought-signature strictness on function calling (Gemini 3.x).2// 1) get a functionCall (records whether thoughtSignature is on the functionCall part)3// 2) send functionResponse WITHOUT echoing the signature → record exact error (if any)4// 3) send with the real signature → OK? 4) send with dummy "skip_thought_signature_validator" → OK?5// 5) multi-turn text-only without echoing signature → OK? (signatures on text parts are optional per docs)6import { ai, MODELS, SUFFIX, save, errInfo, shortText, withRetry, sleep, PACE_MS } from "./lib.js";78const model = MODELS[0];9const tool = { functionDeclarations: [{ name: "get_weather", description: "Get weather for a city.", parametersJsonSchema: { type: "object", properties: { city: { type: "string" } }, required: ["city"] } }] };10const results: any = { model };1112const first = await withRetry(() => ai.models.generateContent({ model, contents: [{ role: "user", parts: [{ text: "Weather in Montreal? Use the tool." }] }], config: { tools: [tool], maxOutputTokens: 2000 } }));13const parts: any[] = first.candidates?.[0]?.content?.parts ?? [];14results.firstTurnParts = parts.map((p) => ({ keys: Object.keys(p), fc: p.functionCall, sigLen: p.thoughtSignature?.length }));15console.log("first turn parts", JSON.stringify(results.firstTurnParts));16const fc = parts.find((p) => p.functionCall);17if (!fc) { console.log("no function call; abort"); save(`07-signature${SUFFIX}.json`, results); process.exit(0); }1819async function roundTrip(label: string, modelParts: any[]) {20 await sleep(PACE_MS);21 const contents = [22 { role: "user", parts: [{ text: "Weather in Montreal? Use the tool." }] },23 { role: "model", parts: modelParts },24 { role: "user", parts: [{ functionResponse: { name: fc.functionCall.name, id: fc.functionCall.id, response: { temperatureC: 21, condition: "sunny" } } }] },25 ];26 try {27 const r = await withRetry(() => ai.models.generateContent({ model, contents, config: { tools: [tool], maxOutputTokens: 2000 } }));28 results[label] = { ok: true, text: shortText(r), finishReason: r.candidates?.[0]?.finishReason };29 } catch (e) { results[label] = { ok: false, error: errInfo(e) }; }30 console.log(label, JSON.stringify(results[label]).slice(0, 400));31}32const stripSig = (p: any) => { const { thoughtSignature, ...rest } = p; return rest; };33await roundTrip("withSignature", parts);34await roundTrip("withoutSignature", parts.map(stripSig));35await roundTrip("dummySignature", parts.map((p) => (p.functionCall ? { ...stripSig(p), thoughtSignature: "skip_thought_signature_validator" } : stripSig(p))));3637// 5) text-only multi-turn without signatures38await sleep(PACE_MS);39try {40 const t1 = await withRetry(() => ai.models.generateContent({ model, contents: "Pick a random fruit and tell me only its name.", config: { maxOutputTokens: 1500 } }));41 const p1: any[] = (t1.candidates?.[0]?.content?.parts ?? []).map(stripSig);42 await sleep(PACE_MS);43 const t2 = await withRetry(() => ai.models.generateContent({ model, contents: [{ role: "user", parts: [{ text: "Pick a random fruit and tell me only its name." }] }, { role: "model", parts: p1 }, { role: "user", parts: [{ text: "What colour is it? One word." }] }], config: { maxOutputTokens: 1500 } }));44 results.textMultiTurnNoSig = { ok: true, fruit: shortText(t1), colour: shortText(t2), firstTurnHadSig: (t1.candidates?.[0]?.content?.parts ?? []).some((p: any) => p.thoughtSignature) };45} catch (e) { results.textMultiTurnNoSig = { ok: false, error: errInfo(e) }; }46console.log("textMultiTurnNoSig", JSON.stringify(results.textMultiTurnNoSig));47save(`07-signature${SUFFIX}.json`, results);48