Docs/Skills and code/Top-level code
test-gemini.js
import { pathToFileURL } from "node:url";
import { parseJson } from "./src/llm.js";
import {
GEMMA_MODELS, LIMITS_URL, callGeminiWithRetry, explainGeminiError, listGemmaModels
} from "./src/gemini.js";
// ============================================================
// GOOGLE GEMINI API: GEMMA 4 TEST
//
// node test-gemini.js ask both Gemma 4 models a short question
// node test-gemini.js --list list the Gemma models your key can use
// node test-gemini.js --model=<id> test one model (gemma-4-31b-it or gemma-4-26b-a4b-it)
// node test-gemini.js --prompt="..." custom user prompt
// node test-gemini.js --no-stream wait for the full response instead of streaming
// node test-gemini.js --json ask for JSON and run it through parseJson()
// node test-gemini.js --thinking=minimal or =high; omitted means the model default
// node test-gemini.js --no-retry never wait and retry after a per-minute limit
//
// Needs GEMINI_API_KEY (or GOOGLE_API_KEY), from the environment or a .env file.
// Optional: GEMINI_MODEL, GEMINI_BASE_URL. The adapter itself lives in src/gemini.js.
// Limits are per project (requests/min, input tokens/min, requests/day); see LIMITS_URL below.
// ============================================================
const DEFAULT_PROMPT = "Reply with one short sentence confirming that you are working.";
const JSON_SYSTEM = "You are a classifier. Return only a JSON object, with no commentary.";
const JSON_PROMPT = [
'Classify the YouTube channel "Veritasium" against the ecosystem "science".',
'Return {"classification":"core|adjacent|unknown|irrelevant","confidence":0.0,"rationale":"one sentence"}.'
].join("\n");
const args = process.argv.slice(2);
const hasFlag = (name) => args.includes(`--${name}`);
const getOption = (name) => args.find((arg) => arg.startsWith(`--${name}=`))?.slice(name.length + 3);
async function main() {
const available = await listGemmaModels();
console.log(`Gemma models available to this key (${available.length}):`);
for (const m of available) {
console.log(` ${m.id} (input ${m.inputTokenLimit ?? "?"} tokens, output ${m.outputTokenLimit ?? "?"})`);
}
console.log(`Limits are per project: requests/min, input tokens/min, requests/day. Check yours at ${LIMITS_URL}`);
if (hasFlag("list")) {
return;
}
// GEMINI_MODEL, when set, narrows the run to that one model.
const explicit = getOption("model") || process.env.GEMINI_MODEL;
const candidates = explicit ? [explicit] : GEMMA_MODELS;
const availableIds = new Set(available.map((m) => m.id));
const useJson = hasFlag("json");
const stream = !hasFlag("no-stream");
const request = {
system: useJson ? JSON_SYSTEM : "",
user: getOption("prompt") || (useJson ? JSON_PROMPT : DEFAULT_PROMPT),
stream,
thinkingLevel: getOption("thinking") || ""
};
// A per-minute limit or a brief overload is worth one short wait; the daily limit never is.
const retry = {
maxRetries: hasFlag("no-retry") ? 0 : 1,
maxWaitSeconds: 65,
onWait: ({ error, waitSeconds }) =>
console.warn(` ${explainGeminiError(error)}\n Waiting ${waitSeconds}s, then retrying once ...`)
};
const summary = [];
for (const model of candidates) {
console.log(`\nCalling ${model} ...`);
if (!availableIds.has(model)) {
console.warn(` ${model} is not in the list above for this key; trying anyway.`);
}
try {
const result = await callGeminiWithRetry({ ...request, model }, retry);
if (!stream) {
console.log(`[LLM] ${result.content}`);
}
const u = result.usage ?? {};
console.log(
`OK: ${result.seconds.toFixed(1)}s, finish=${result.finishReason ?? "?"}, tokens in=${u.promptTokenCount ?? "?"} ` +
`out=${u.candidatesTokenCount ?? "?"} thinking=${u.thoughtsTokenCount ?? 0} total=${u.totalTokenCount ?? "?"}`
);
if (useJson) {
console.log("parseJson() result:");
console.log(parseJson(result.content));
}
summary.push({ model, status: "ok", seconds: Number(result.seconds.toFixed(1)), tries: result.attempts, tokens: u.totalTokenCount ?? null, note: "" });
} catch (error) {
const note = explainGeminiError(error);
console.error(`FAILED: ${note}`);
summary.push({ model, status: error.kind ?? "failed", seconds: null, tries: null, tokens: null, note: note.slice(0, 90) });
}
}
console.log("\nSummary:");
console.table(summary);
if (summary.some((row) => row.status !== "ok")) {
process.exitCode = 1;
}
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
main().catch((error) => {
console.error(explainGeminiError(error));
process.exitCode = 1;
});
}
Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.