// Probe 05: (g) invalid key (SDK + REST), unknown model 404, bad param 400 raw body, countTokens, chats helper, // Authorization: Bearer test, v1 vs v1beta, models.get, explicit cache creation minimum error, and the Interactions API surface. import { GoogleGenAI } from "@google/genai"; import { ai, KEY, save, errInfo } from "./lib.js"; const results: any = {}; const BASE = "https://generativelanguage.googleapis.com"; // (g) invalid key — REST { const r = await fetch(`${BASE}/v1beta/models/gemini-2.5-flash:generateContent`, { method: "POST", headers: { "x-goog-api-key": "AQ.invalid-key-for-probe", "content-type": "application/json" }, body: JSON.stringify({ contents: [{ parts: [{ text: "hi" }] }] }) }); results.invalidKeyRest = { status: r.status, body: await r.json(), wwwAuthenticate: r.headers.get("www-authenticate") }; console.log("invalid key REST", r.status, JSON.stringify(results.invalidKeyRest.body).slice(0, 400)); } // (g) invalid key — SDK try { const bad = new GoogleGenAI({ apiKey: "AQ.invalid-key-for-probe" }); await bad.models.generateContent({ model: "gemini-2.5-flash", contents: "hi", config: { maxOutputTokens: 5 } }); } catch (e: any) { results.invalidKeySdk = { ...errInfo(e), rawMessage: String(e?.message).slice(0, 500), ctor: e?.constructor?.name }; console.log("invalid key SDK", JSON.stringify(results.invalidKeySdk).slice(0, 400)); } // Bearer auth (does Authorization: Bearer work?) { const r = await fetch(`${BASE}/v1beta/models?pageSize=1`, { headers: { authorization: `Bearer ${KEY}` } }); results.bearerAuth = { status: r.status, body: r.status !== 200 ? await r.json() : "(ok)" }; console.log("bearer", r.status, JSON.stringify(results.bearerAuth.body).slice(0, 300)); } // ?key= query param { const r = await fetch(`${BASE}/v1beta/models?pageSize=1&key=${encodeURIComponent(KEY)}`); results.keyQueryParam = { status: r.status }; console.log("?key=", r.status); } // Unknown model try { await ai.models.generateContent({ model: "gemini-9-ultra", contents: "hi" }); } catch (e) { results.unknownModel = errInfo(e); console.log("unknown model", JSON.stringify(results.unknownModel).slice(0, 400)); } // Raw 400 error body { const r = await fetch(`${BASE}/v1beta/models/gemini-2.5-flash:generateContent`, { method: "POST", headers: { "x-goog-api-key": KEY, "content-type": "application/json" }, body: JSON.stringify({ contents: [{ parts: [{ text: "hi" }] }], generationConfig: { temperature: 5 } }) }); results.raw400 = { status: r.status, body: await r.json() }; console.log("raw 400", JSON.stringify(results.raw400).slice(0, 500)); } // Streaming error (bad param) via SSE: does the error come as HTTP status or as an SSE event? { const r = await fetch(`${BASE}/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse`, { method: "POST", headers: { "x-goog-api-key": KEY, "content-type": "application/json" }, body: JSON.stringify({ contents: [{ parts: [{ text: "hi" }] }], generationConfig: { temperature: 5 } }) }); results.streamError = { status: r.status, contentType: r.headers.get("content-type"), body: (await r.text()).slice(0, 500) }; console.log("stream error", results.streamError.status, results.streamError.contentType); } // countTokens try { const c = await ai.models.countTokens({ model: "gemini-3.8-flash", contents: "The quick brown fox jumps over the lazy dog." }); results.countTokens = c; console.log("countTokens", JSON.stringify(c)); } catch (e) { results.countTokens = errInfo(e); console.log("countTokens ERR", JSON.stringify(results.countTokens)); } // countTokens with systemInstruction + tools via REST generateContentRequest { const r = await fetch(`${BASE}/v1beta/models/gemini-3.8-flash:countTokens`, { method: "POST", headers: { "x-goog-api-key": KEY, "content-type": "application/json" }, body: JSON.stringify({ generateContentRequest: { model: "models/gemini-3.8-flash", contents: [{ role: "user", parts: [{ text: "The quick brown fox jumps over the lazy dog." }] }], systemInstruction: { parts: [{ text: "You are a helpful assistant that answers briefly." }] }, tools: [{ functionDeclarations: [{ name: "f", description: "A function", parameters: { type: "OBJECT", properties: { a: { type: "STRING" } } } }] }] } }) }); results.countTokensFull = { status: r.status, body: await r.json() }; console.log("countTokens full", JSON.stringify(results.countTokensFull)); } // models.get try { const m = await ai.models.get({ model: "gemini-3.8-flash" }); results.modelsGet = m; console.log("models.get", JSON.stringify(m).slice(0, 400)); } catch (e) { results.modelsGet = errInfo(e); } // v1 API version: does gemini-3.8-flash exist on v1? does thinkingConfig work there? try { const v1 = new GoogleGenAI({ apiKey: KEY, httpOptions: { apiVersion: "v1" } }); const res = await v1.models.generateContent({ model: "gemini-3.8-flash", contents: "Say hi", config: { maxOutputTokens: 50, thinkingConfig: { thinkingLevel: "LOW" as any } } }); results.v1 = { ok: true, text: res.text, modelVersion: res.modelVersion }; } catch (e) { results.v1 = { ok: false, error: errInfo(e) }; } console.log("v1", JSON.stringify(results.v1).slice(0, 300)); // chats helper (client-side history) try { const chat = ai.chats.create({ model: "gemini-2.5-flash-lite", config: { maxOutputTokens: 60, thinkingConfig: { thinkingBudget: 0 } } }); await chat.sendMessage({ message: "My name is Simon. Reply OK." }); const r2 = await chat.sendMessage({ message: "What is my name? One word." }); results.chats = { ok: true, text: r2.text, historyLen: chat.getHistory().length, historyRoles: chat.getHistory().map((c) => c.role) }; } catch (e) { results.chats = { ok: false, error: errInfo(e) }; } console.log("chats", JSON.stringify(results.chats)); // explicit cache: small content should fail with minimum-token error → record exact message try { const c = await ai.caches.create({ model: "gemini-2.5-flash", config: { contents: [{ role: "user", parts: [{ text: "short" }] }], ttl: "60s" } }); results.cacheCreate = { ok: true, name: c.name }; await ai.caches.delete({ name: c.name! }); } catch (e) { results.cacheCreate = { ok: false, error: errInfo(e) }; } console.log("cache", JSON.stringify(results.cacheCreate).slice(0, 400)); // Interactions API (new recommended surface) — does the SDK expose it, and does it work with this key? try { const anyAi: any = ai; results.interactionsExposed = typeof anyAi.interactions?.create === "function"; if (results.interactionsExposed) { const it = await anyAi.interactions.create({ model: "gemini-3.8-flash", input: "Say hi in two words.", generation_config: { thinking_level: "low", max_output_tokens: 50 }, store: false }); results.interactions = { ok: true, keys: Object.keys(it), output_text: it.output_text, usage: it.usage, stepsTypes: (it.steps ?? it.outputs ?? []).map((s: any) => s.type) }; } } catch (e) { results.interactions = { ok: false, error: errInfo(e), raw: String((e as any)?.message).slice(0, 300) }; } console.log("interactions", JSON.stringify(results.interactions).slice(0, 500)); // Safety settings accepted? try { const res = await ai.models.generateContent({ model: "gemini-2.5-flash-lite", contents: "Say hi", config: { maxOutputTokens: 20, thinkingConfig: { thinkingBudget: 0 }, safetySettings: [{ category: "HARM_CATEGORY_HARASSMENT" as any, threshold: "BLOCK_NONE" as any }, { category: "HARM_CATEGORY_HATE_SPEECH" as any, threshold: "OFF" as any }] } }); results.safety = { ok: true, text: res.text, safetyRatings: res.candidates?.[0]?.safetyRatings ?? null, promptFeedback: res.promptFeedback ?? null }; } catch (e) { results.safety = { ok: false, error: errInfo(e) }; } console.log("safety", JSON.stringify(results.safety).slice(0, 300)); // Response headers on a normal request (rate-limit headers?) { const r = await fetch(`${BASE}/v1beta/models/gemini-2.5-flash-lite:generateContent`, { method: "POST", headers: { "x-goog-api-key": KEY, "content-type": "application/json" }, body: JSON.stringify({ contents: [{ parts: [{ text: "hi" }] }], generationConfig: { maxOutputTokens: 5, thinkingConfig: { thinkingBudget: 0 } } }) }); results.responseHeaders = Object.fromEntries([...r.headers.entries()]); await r.text(); console.log("headers", JSON.stringify(results.responseHeaders)); } save("05-errors-misc.json", results);