SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
4.5 KB · 92 lines typescript
Raw Blame History
1import type { CatalogEntry } from "@/lib/ai/registry/catalog";2import type { ModelCapabilities, ModelPricing, ModelStatus } from "@/lib/ai/core/types";3import data from "./catalog-data.json";45/**6 * Gemini catalog — generated from docs/provider-research/gemini.models.json (official docs +7 * real probes, audited 2026-09-08) joined with the live `models.list` limits.8 *9 * Verified rules (Gemini Developer API):10 *  - temperature 0–2, topP, topK, seed, stopSequences accepted (sampling deprecated on 3.6+ but still accepted);11 *  - frequency/presence penalties → 400 "Penalty is not enabled for this model" on every model;12 *  - thinking is configured per family (`thinkingProfile`): `thinkingLevel` OR `thinkingBudget`, never both;13 *    3.8/3.7 Flash cannot disable thinking and reject MINIMAL; 3.5 Flash disables with budget 0;14 *    Flash-Lite models are off by default (omit the config for "none"); Gemma rejects any thinkingConfig;15 *  - thought signatures MUST be replayed on function-call parts for 3.x (400 otherwise);16 *  - responseJsonSchema for structured output; googleSearch grounding; 2.5-generation → 404 for new users.17 */18export interface ThinkingProfile {19  mode: "level" | "budget" | "none";20  levels?: string[];21  /** How to express "no reasoning": a thinkingLevel value, "budget0", "omit", or null when impossible. */22  none?: string | null;23}2425interface Row {26  id: string;27  displayName: string;28  family: string | null;29  contextTokens: number | null;30  maxOutputTokens: number | null;31  thinking: boolean;32  status: ModelStatus;33  capabilities: ModelCapabilities;34  pricing: { inputPerMillion: number | null; cachedInputPerMillion: number | null; outputPerMillion: number | null } | null;35  pricingNote: string | null;36  thinkingProfile: ThinkingProfile;37  chat: boolean;38  notes: string;39}4041function weight(id: string): number {42  if (id.startsWith("gemini-3.8-flash")) return 100;43  if (id.startsWith("gemini-3.1-pro")) return 96;44  if (id.startsWith("gemini-3.7-flash")) return 92;45  if (id.startsWith("gemini-3.6-flash")) return 90;46  if (id.startsWith("gemini-3.5-flash-lite")) return 84;47  if (id.startsWith("gemini-3.5-flash")) return 88;48  if (id.startsWith("gemini-3.1-flash-lite")) return 80;49  if (id.startsWith("gemini-3-flash")) return 76;50  if (id.endsWith("-latest")) return 60;51  if (id.startsWith("gemma")) return 40;52  return 10;53}5455export const GEMINI_CATALOG = new Map<string, CatalogEntry>(56  (data as Row[]).map((r) => {57    const tp = r.thinkingProfile;58    const reasoning = r.thinking && tp.mode !== "none";59    const effortLevels = tp.mode === "level" && tp.levels ? [...(tp.none ? ["none"] : []), ...tp.levels] : tp.mode === "budget" ? ["none", "low", "medium", "high"] : undefined;60    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;61    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 };62    const entry: CatalogEntry = {63      displayName: r.displayName,64      family: r.family ?? "Gemini",65      capabilities: { ...r.capabilities, reasoning },66      limits: { contextTokens: r.contextTokens ?? undefined, maxOutputTokens: r.maxOutputTokens ?? undefined },67      parameters: {68        temperature: true,69        topP: true,70        topK: true,71        seed: true,72        stop: true,73        maxTokens: true,74        frequencyPenalty: false,75        presencePenalty: false,76        reasoningEffort: Boolean(effortLevels?.length),77        reasoningEffortLevels: effortLevels,78        thinkingBudget: reasoning && tp.mode === "budget",79        thinkingBudgetRange: reasoning && tp.mode === "budget" ? { min: 128, max: 32_768 } : undefined,80        temperatureRange: { min: 0, max: 2 },81      },82      pricing,83      status: r.status,84      sortWeight: weight(r.id),85      metadata: { thinkingProfile: tp, pricingNote: r.pricingNote, codeExecution: !r.id.startsWith("gemma"), notes: r.notes, chat: r.chat },86    };87    return [r.id, entry];88  }),89);9091export const GEMINI_NON_CHAT = /tts|image|banana|lyria|veo|embedding|aqa|transcribe|robotics|computer-use|antigravity|deep-research|native-audio|live|imagen|omni/i;92