import type { CatalogEntry } from "@/lib/ai/registry/catalog"; import type { ModelCapabilities, ModelPricing, ModelStatus } from "@/lib/ai/core/types"; import data from "./catalog-data.json"; /** * Gemini catalog — generated from docs/provider-research/gemini.models.json (official docs + * real probes, audited 2026-09-08) joined with the live `models.list` limits. * * Verified rules (Gemini Developer API): * - temperature 0–2, topP, topK, seed, stopSequences accepted (sampling deprecated on 3.6+ but still accepted); * - frequency/presence penalties → 400 "Penalty is not enabled for this model" on every model; * - thinking is configured per family (`thinkingProfile`): `thinkingLevel` OR `thinkingBudget`, never both; * 3.8/3.7 Flash cannot disable thinking and reject MINIMAL; 3.5 Flash disables with budget 0; * Flash-Lite models are off by default (omit the config for "none"); Gemma rejects any thinkingConfig; * - thought signatures MUST be replayed on function-call parts for 3.x (400 otherwise); * - responseJsonSchema for structured output; googleSearch grounding; 2.5-generation → 404 for new users. */ export interface ThinkingProfile { mode: "level" | "budget" | "none"; levels?: string[]; /** How to express "no reasoning": a thinkingLevel value, "budget0", "omit", or null when impossible. */ none?: string | null; } interface Row { id: string; displayName: string; family: string | null; contextTokens: number | null; maxOutputTokens: number | null; thinking: boolean; status: ModelStatus; capabilities: ModelCapabilities; pricing: { inputPerMillion: number | null; cachedInputPerMillion: number | null; outputPerMillion: number | null } | null; pricingNote: string | null; thinkingProfile: ThinkingProfile; chat: boolean; notes: string; } function weight(id: string): number { if (id.startsWith("gemini-3.8-flash")) return 100; if (id.startsWith("gemini-3.1-pro")) return 96; if (id.startsWith("gemini-3.7-flash")) return 92; if (id.startsWith("gemini-3.6-flash")) return 90; if (id.startsWith("gemini-3.5-flash-lite")) return 84; if (id.startsWith("gemini-3.5-flash")) return 88; if (id.startsWith("gemini-3.1-flash-lite")) return 80; if (id.startsWith("gemini-3-flash")) return 76; if (id.endsWith("-latest")) return 60; if (id.startsWith("gemma")) return 40; return 10; } export const GEMINI_CATALOG = new Map( (data as Row[]).map((r) => { const tp = r.thinkingProfile; const reasoning = r.thinking && tp.mode !== "none"; const effortLevels = tp.mode === "level" && tp.levels ? [...(tp.none ? ["none"] : []), ...tp.levels] : tp.mode === "budget" ? ["none", "low", "medium", "high"] : undefined; const pricing: ModelPricing | null = r.pricing && r.pricing.inputPerMillion !== null ? { inputPerMillion: r.pricing.inputPerMillion, cachedInputPerMillion: r.pricing.cachedInputPerMillion ?? undefined, outputPerMillion: r.pricing.outputPerMillion ?? undefined, source: "gemini-pricing-page", asOf: "2026-09-08" } : null; if (pricing && /^gemini-3\.1-pro|^gemini-pro-latest|^gemini-2\.5-pro/.test(r.id)) pricing.longContext = { thresholdTokens: 200_000, inputPerMillion: (pricing.inputPerMillion ?? 0) * 2, outputPerMillion: (pricing.outputPerMillion ?? 0) * 1.5, cachedInputPerMillion: pricing.cachedInputPerMillion ? pricing.cachedInputPerMillion * 2 : undefined }; const entry: CatalogEntry = { displayName: r.displayName, family: r.family ?? "Gemini", capabilities: { ...r.capabilities, reasoning }, limits: { contextTokens: r.contextTokens ?? undefined, maxOutputTokens: r.maxOutputTokens ?? undefined }, parameters: { temperature: true, topP: true, topK: true, seed: true, stop: true, maxTokens: true, frequencyPenalty: false, presencePenalty: false, reasoningEffort: Boolean(effortLevels?.length), reasoningEffortLevels: effortLevels, thinkingBudget: reasoning && tp.mode === "budget", thinkingBudgetRange: reasoning && tp.mode === "budget" ? { min: 128, max: 32_768 } : undefined, temperatureRange: { min: 0, max: 2 }, }, pricing, status: r.status, sortWeight: weight(r.id), metadata: { thinkingProfile: tp, pricingNote: r.pricingNote, codeExecution: !r.id.startsWith("gemma"), notes: r.notes, chat: r.chat }, }; return [r.id, entry]; }), ); export const GEMINI_NON_CHAT = /tts|image|banana|lyria|veo|embedding|aqa|transcribe|robotics|computer-use|antigravity|deep-research|native-audio|live|imagen|omni/i;