From 0d6a519422a148c8bd7b850648d8c8d0c531bdca Mon Sep 17 00:00:00 2001 From: Corie Watson Date: Thu, 13 Aug 2026 17:35:23 +0100 Subject: [PATCH 1/3] fix(kits): add Gemini 3.x before 2.5 retirement Chatbot, translate, resize filter, and vector embeddings still pin retiring or retired Gemini model ids. --- kits/firestore-genai-chatbot/README.md | 2 +- kits/firestore-genai-chatbot/src/config.ts | 2 +- .../src/export-config.ts | 2 +- .../src/generative-client/genkit.ts | 58 ++++++++----------- .../tests/generative-client.test.ts | 15 +++++ kits/firestore-translate-text/README.md | 2 +- kits/firestore-translate-text/src/config.ts | 5 +- .../src/export-config.ts | 2 +- .../src/embeddings/client/genkit.ts | 6 +- .../src/content-filter.ts | 5 +- 10 files changed, 56 insertions(+), 43 deletions(-) diff --git a/kits/firestore-genai-chatbot/README.md b/kits/firestore-genai-chatbot/README.md index fd9922188..9f31fd48b 100644 --- a/kits/firestore-genai-chatbot/README.md +++ b/kits/firestore-genai-chatbot/README.md @@ -90,7 +90,7 @@ the CLI connects them to the function at deploy time. |---|---|---|---|---| | `provider` | `GENERATIVE_AI_PROVIDER` | no | `google-ai` | `google-ai` or `vertex-ai` | | `apiKey` | `API_KEY` | secret | — | Google AI API key | -| `model` | `MODEL` | no | `gemini-2.5-flash` | Model id | +| `model` | `MODEL` | no | `gemini-3.6-flash` | Model id | | `vertexModelLocation` | `VERTEX_AI_MODEL_LOCATION` | no | `null` | Vertex model region | | `collectionName` | `COLLECTION_NAME` | no | `generate` | Discussion collection | | `promptField` | `PROMPT_FIELD` | no | `prompt` | Prompt field name | diff --git a/kits/firestore-genai-chatbot/src/config.ts b/kits/firestore-genai-chatbot/src/config.ts index 52757a8c7..713fff00a 100644 --- a/kits/firestore-genai-chatbot/src/config.ts +++ b/kits/firestore-genai-chatbot/src/config.ts @@ -84,7 +84,7 @@ const params = { input: select([...GENERATIVE_AI_PROVIDER_OPTIONS]), }), apiKey: defineSecret("API_KEY"), - model: defineString("MODEL", { default: "gemini-2.5-flash" }), + model: defineString("MODEL", { default: "gemini-3.6-flash" }), vertexModelLocation: defineString("VERTEX_AI_MODEL_LOCATION", { default: "null", input: select([...VERTEX_MODEL_LOCATION_OPTIONS]), diff --git a/kits/firestore-genai-chatbot/src/export-config.ts b/kits/firestore-genai-chatbot/src/export-config.ts index 6c71012e0..5e85ed4e1 100644 --- a/kits/firestore-genai-chatbot/src/export-config.ts +++ b/kits/firestore-genai-chatbot/src/export-config.ts @@ -45,7 +45,7 @@ export interface GenaiChatbotConfig { provider?: GenerativeAIProvider | "google-ai" | "vertex-ai"; /** API key for the `google-ai` provider. */ apiKey?: string; - /** Model id, e.g. `gemini-2.5-flash`. */ + /** Model id, e.g. `gemini-3.6-flash`. */ model: string; /** Vertex AI model location. */ vertexModelLocation?: string; diff --git a/kits/firestore-genai-chatbot/src/generative-client/genkit.ts b/kits/firestore-genai-chatbot/src/generative-client/genkit.ts index cb68e1cfd..ecef43df5 100644 --- a/kits/firestore-genai-chatbot/src/generative-client/genkit.ts +++ b/kits/firestore-genai-chatbot/src/generative-client/genkit.ts @@ -103,41 +103,33 @@ export class GenkitDiscussionClient extends DiscussionClient< return genkit(genkitConfig); } - // TODO(migration): inherited verbatim from the legacy extension — this - // hardcoded model allowlist means new/custom/fine-tuned models need a package - // update. `googleAI.model()` / `vertexAI.model()` resolve any id dynamically; - // consider simplifying to that. Improvement, not a bug. Deferred from PR #431 review. + /** + * Resolves a Genkit model reference for the configured provider. + * + * Known ids are registered first so version aliases still match. Unknown + * ids fall through to `googleAI.model()` / `vertexAI.model()` so current + * Gemini releases work without a package update. + */ static createModelReference( model: string, provider: string ): ModelReference { - const modelReferences = - provider === "google-ai" - ? [ - googleAI.model("gemini-1.5-flash"), - googleAI.model("gemini-1.5-pro"), - googleAI.model("gemini-2.0-flash"), - googleAI.model("gemini-2.0-flash-lite"), - googleAI.model("gemini-2.5-flash-lite"), - googleAI.model("gemini-2.5-flash"), - googleAI.model("gemini-2.5-pro"), - googleAI.model("gemini-3-pro-preview"), - googleAI.model("gemini-3-pro-image-preview"), - ] - : [ - vertexAI.model("gemini-1.5-flash"), - vertexAI.model("gemini-1.5-pro"), - vertexAI.model("gemini-2.0-flash"), - vertexAI.model("gemini-2.0-flash-lite"), - vertexAI.model("gemini-2.0-flash-001"), - vertexAI.model("gemini-2.5-flash-lite"), - vertexAI.model("gemini-2.5-flash"), - vertexAI.model("gemini-2.5-pro"), - vertexAI.model("gemini-3-pro-preview"), - vertexAI.model("gemini-3-pro-image-preview"), - ]; - - const pluginName = provider === "google-ai" ? "googleai" : "vertexai"; + const isGoogleAi = provider === "google-ai"; + const pluginName = isGoogleAi ? "googleai" : "vertexai"; + const knownIds = [ + "gemini-3.6-flash", + "gemini-3.5-flash", + "gemini-3.5-flash-lite", + "gemini-3.1-flash-lite", + "gemini-3.1-pro-preview", + "gemini-2.5-flash-lite", + "gemini-2.5-flash", + "gemini-2.5-pro", + ] as const; + + const modelReferences = knownIds.map((id) => + isGoogleAi ? googleAI.model(id) : vertexAI.model(id) + ); for (const modelReference of modelReferences) { if (modelReference.name === `${pluginName}/${model}`) { @@ -147,7 +139,7 @@ export class GenkitDiscussionClient extends DiscussionClient< return modelReference.withVersion(model); } } - throw new Error("Model not found."); + return isGoogleAi ? googleAI.model(model) : vertexAI.model(model); } private createGenerateOptions( @@ -172,7 +164,7 @@ export class GenkitDiscussionClient extends DiscussionClient< }; } - /** Whether the Genkit client can serve this config (single candidate + known model). */ + /** Whether the Genkit client can serve this config (single candidate). */ static shouldUseGenkitClient(config: ResolvedGenaiChatbotConfig): boolean { const shouldReturnMultipleCandidates = config.candidateCount && config.candidateCount > 1; diff --git a/kits/firestore-genai-chatbot/tests/generative-client.test.ts b/kits/firestore-genai-chatbot/tests/generative-client.test.ts index 100a8977e..34c3eb5e5 100644 --- a/kits/firestore-genai-chatbot/tests/generative-client.test.ts +++ b/kits/firestore-genai-chatbot/tests/generative-client.test.ts @@ -64,6 +64,21 @@ describe("GenkitDiscussionClient.shouldUseGenkitClient", () => { const config = resolveConfig({ ...baseInput, candidateCount: 2 }); expect(GenkitDiscussionClient.shouldUseGenkitClient(config)).toBe(false); }); + + test("true for a current model id that is not in the legacy allowlist", () => { + const config = resolveConfig({ + ...baseInput, + model: "gemini-3.6-flash", + candidateCount: 1, + }); + expect(GenkitDiscussionClient.shouldUseGenkitClient(config)).toBe(true); + expect(() => + GenkitDiscussionClient.createModelReference( + "gemini-3.6-flash", + "google-ai" + ) + ).not.toThrow(); + }); }); describe("VertexDiscussionClient", () => { diff --git a/kits/firestore-translate-text/README.md b/kits/firestore-translate-text/README.md index 2d2281ca2..3aee2e8d9 100644 --- a/kits/firestore-translate-text/README.md +++ b/kits/firestore-translate-text/README.md @@ -90,7 +90,7 @@ the CLI connects them to the function at deploy time. | `languages` | `LANGUAGES` | no | `en,es,de,fr` | Target language codes | | `languagesFieldName` | `LANGUAGES_FIELD_NAME` | no | `languages` | Per-doc languages field | | `provider` | `TRANSLATION_PROVIDER` | yes | — | Translation provider | -| `geminiModel` | `GEMINI_MODEL` | no | `gemini-2.5-flash` | Gemini model when used | +| `geminiModel` | `GEMINI_MODEL` | no | `gemini-3.6-flash` | Gemini model when used | | `googleAiApiKey` | `GOOGLE_AI_API_KEY` | secret | — | Google AI API key (Gemini) | ## Multiple instances diff --git a/kits/firestore-translate-text/src/config.ts b/kits/firestore-translate-text/src/config.ts index e3d92d676..7cd54880c 100644 --- a/kits/firestore-translate-text/src/config.ts +++ b/kits/firestore-translate-text/src/config.ts @@ -37,6 +37,9 @@ const TRANSLATION_PROVIDER_OPTIONS = [ "gemini-vertexai", ] as const; const GEMINI_MODEL_OPTIONS = [ + "gemini-3.1-pro-preview", + "gemini-3.6-flash", + "gemini-3.1-flash-lite", "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", @@ -54,7 +57,7 @@ const params = { input: select([...TRANSLATION_PROVIDER_OPTIONS]), }), geminiModel: defineString("GEMINI_MODEL", { - default: "gemini-2.5-flash", + default: "gemini-3.6-flash", input: select([...GEMINI_MODEL_OPTIONS]), }), }; diff --git a/kits/firestore-translate-text/src/export-config.ts b/kits/firestore-translate-text/src/export-config.ts index 802037df3..5fc920204 100644 --- a/kits/firestore-translate-text/src/export-config.ts +++ b/kits/firestore-translate-text/src/export-config.ts @@ -49,7 +49,7 @@ export interface ResolvedTranslateConfig { } const DEFAULT_PROVIDER: TranslationProvider = "translate"; -const DEFAULT_GEMINI_MODEL = "gemini-2.5-flash"; +const DEFAULT_GEMINI_MODEL = "gemini-3.6-flash"; function toUniqueArray( languages: ReadonlyArray | string diff --git a/kits/firestore-vector-search/src/embeddings/client/genkit.ts b/kits/firestore-vector-search/src/embeddings/client/genkit.ts index 399f208df..3850fc152 100644 --- a/kits/firestore-vector-search/src/embeddings/client/genkit.ts +++ b/kits/firestore-vector-search/src/embeddings/client/genkit.ts @@ -19,6 +19,8 @@ import { type EmbedderReference, type Genkit, genkit } from "genkit"; import type { ResolvedVectorSearchConfig } from "../../export-config"; import { BaseEmbedClient } from "./base_class"; +const GEMINI_EMBEDDER_ID = "gemini-embedding-2"; + export class GenkitEmbedClient extends BaseEmbedClient { private readonly client: Genkit; private readonly embedder: EmbedderReference; @@ -29,10 +31,10 @@ export class GenkitEmbedClient extends BaseEmbedClient { this.dimension = config.dimension; const isVertex = config.embeddingProvider === "vertex"; this.embedder = isVertex - ? vertexAI.embedder("gemini-embedding-001", { + ? vertexAI.embedder(GEMINI_EMBEDDER_ID, { outputDimensionality: config.dimension, }) - : googleAI.embedder("gemini-embedding-001", { + : googleAI.embedder(GEMINI_EMBEDDER_ID, { outputDimensionality: config.dimension, }); this.client = genkit({ diff --git a/kits/storage-resize-images/src/content-filter.ts b/kits/storage-resize-images/src/content-filter.ts index a2c46b32b..add0b44da 100644 --- a/kits/storage-resize-images/src/content-filter.ts +++ b/kits/storage-resize-images/src/content-filter.ts @@ -27,6 +27,7 @@ const HARM_CATEGORIES = [ "HARM_CATEGORY_SEXUALLY_EXPLICIT", "HARM_CATEGORY_HARASSMENT", ] as const; +const CONTENT_FILTER_MODEL = "gemini-3.6-flash"; const RETRY_BASE_MS = 500; const RETRY_JITTER_MS = 200; const RETRY_MAX_MS = 5000; @@ -88,7 +89,7 @@ export async function checkImageContent( plugins: [ vertexAI({ location, - models: ["gemini-2.5-flash"], + models: [CONTENT_FILTER_MODEL], }), ], }); @@ -112,7 +113,7 @@ export async function checkImageContent( try { const result = await ai.generate({ - model: gemini("gemini-2.5-flash"), + model: gemini(CONTENT_FILTER_MODEL), messages: [ { role: "user", From 1e38335e1b29f0825014845de66c5422ded47905 Mon Sep 17 00:00:00 2001 From: Corie Watson Date: Thu, 13 Aug 2026 19:55:34 +0100 Subject: [PATCH 2/3] fix(translate): add Gemini 3.5 Flash models Picker listed every 2.5 model; add 3.5 Flash and Flash Lite to match the current Gemini set. --- kits/firestore-translate-text/src/config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kits/firestore-translate-text/src/config.ts b/kits/firestore-translate-text/src/config.ts index 7cd54880c..211f017b3 100644 --- a/kits/firestore-translate-text/src/config.ts +++ b/kits/firestore-translate-text/src/config.ts @@ -39,6 +39,8 @@ const TRANSLATION_PROVIDER_OPTIONS = [ const GEMINI_MODEL_OPTIONS = [ "gemini-3.1-pro-preview", "gemini-3.6-flash", + "gemini-3.5-flash", + "gemini-3.5-flash-lite", "gemini-3.1-flash-lite", "gemini-2.5-pro", "gemini-2.5-flash", From 619748452c9b5ef8dee39c5b0cdb34e39d2badfc Mon Sep 17 00:00:00 2001 From: Corie Watson Date: Thu, 13 Aug 2026 20:21:55 +0100 Subject: [PATCH 3/3] fix(resize): use gemini-3.1-flash-lite Similar price to previous gemini-2.5-flash and better quality. Avoid 3.5-flash-lite and higher cost. --- kits/storage-resize-images/src/content-filter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kits/storage-resize-images/src/content-filter.ts b/kits/storage-resize-images/src/content-filter.ts index add0b44da..ff7a957e6 100644 --- a/kits/storage-resize-images/src/content-filter.ts +++ b/kits/storage-resize-images/src/content-filter.ts @@ -27,7 +27,8 @@ const HARM_CATEGORIES = [ "HARM_CATEGORY_SEXUALLY_EXPLICIT", "HARM_CATEGORY_HARASSMENT", ] as const; -const CONTENT_FILTER_MODEL = "gemini-3.6-flash"; +/** Similar price to previous `gemini-2.5-flash`, better quality than 2.5 Flash. Higher Flash/Lite tiers cost more. */ +const CONTENT_FILTER_MODEL = "gemini-3.1-flash-lite"; const RETRY_BASE_MS = 500; const RETRY_JITTER_MS = 200; const RETRY_MAX_MS = 5000;