From e6897df911ef2a1adaabfce1cdd3e15521368395 Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Mon, 3 Aug 2026 22:21:20 -0700 Subject: [PATCH] Fix: match only gemini-1. in isGemini1Model (adk-python parity) isGemini1Model used a bare `startsWith('gemini-1')` prefix test with no digit boundary, so it accepted any id whose version field merely begins with the character `1`. A future `gemini-10.0-pro` would be routed down the legacy Gemini 1.x paths in GoogleSearchTool, VertexAiSearchTool, applyGoogleMapsGrounding and applyEnterpriseWebSearch, and would also be classified as both Gemini 1.x and Gemini 2.0+ at the same time. Replace the prefix test with the `^gemini-1\.\d+` regex adk-python's is_gemini_1_model already uses, hoisted to a module-level non-global constant so `.test()` stays stateless. This makes the existing doc comment ("using regex patterns") honest and restores mutual exclusivity with isGemini2OrAbove. Behaviour is unchanged for every real Gemini 1.x id; only gemini-10.0-pro, gemini-1, gemini-1-pro, gemini-1. and gemini-1x-foo flip to false. --- core/src/utils/model_name.ts | 4 +- core/test/utils/model_name_test.ts | 73 ++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/core/src/utils/model_name.ts b/core/src/utils/model_name.ts index 6851eaba4..ff1c29a56 100644 --- a/core/src/utils/model_name.ts +++ b/core/src/utils/model_name.ts @@ -9,6 +9,8 @@ import {getBooleanEnvVar} from './env_aware_utils.js'; const MODEL_NAME_PATTERN = '^projects/[^/]+/locations/[^/]+/publishers/[^/]+/models/(.+)$'; +const GEMINI_1_MODEL_PATTERN = /^gemini-1\.\d+/; + /** * Extract the actual model name from either simple or path-based format. * @@ -68,7 +70,7 @@ function parseVersion(versionString: string): ParsedVersion { export function isGemini1Model(modelString: string): boolean { const modelName = extractModelName(modelString); - return modelName.startsWith('gemini-1'); + return GEMINI_1_MODEL_PATTERN.test(modelName); } /** diff --git a/core/test/utils/model_name_test.ts b/core/test/utils/model_name_test.ts index caabe4022..792041b09 100644 --- a/core/test/utils/model_name_test.ts +++ b/core/test/utils/model_name_test.ts @@ -6,6 +6,79 @@ import {isGemini2OrAbove, isGemini3xFlashLive} from '@google/adk'; import {describe, expect, it} from 'vitest'; +import {isGemini1Model, isGeminiModel} from '../../src/utils/model_name.js'; + +describe('isGemini1Model', () => { + const gemini1Models = [ + 'gemini-1.5-flash', + 'gemini-1.0-pro', + 'gemini-1.5-pro-preview', + 'gemini-1.9-experimental', + 'projects/265104255505/locations/us-central1/publishers/google/models/gemini-1.5-flash', + 'projects/12345/locations/us-east1/publishers/google/models/gemini-1.0-pro-preview', + ]; + + describe('valid models', () => { + for (const model of gemini1Models) { + it(`should return true for model: ${model}`, () => { + expect(isGemini1Model(model)).toBe(true); + }); + } + }); + + describe('invalid models', () => { + const invalidModels = [ + // A double-digit major must not be read as Gemini 1.x. + 'gemini-10.0-pro', + // The dotted minor version is mandatory. + 'gemini-1', + 'gemini-1-pro', + 'gemini-1.', + 'gemini-1x-foo', + 'gemini-2.5-flash', + 'claude-3-sonnet', + // Present but not anchored at the start of the model name. + 'my-gemini-1.5-model', + '', + 'projects/265104255505/locations/us-central1/publishers/google/models/gemini-2.5-flash', + ]; + + for (const model of invalidModels) { + it(`should return false for model: ${model || ''}`, () => { + expect(isGemini1Model(model)).toBe(false); + }); + } + }); + + describe('classification invariants', () => { + const allModels = [ + ...gemini1Models, + 'gemini-10.0-pro', + 'gemini-1', + 'gemini-1-pro', + 'gemini-2.5-flash', + 'gemini-3-pro-preview', + 'claude-3-sonnet', + '', + ]; + + it('should never classify a model as both Gemini 1.x and Gemini 2.0+', () => { + expect( + allModels.filter( + (model) => isGemini1Model(model) && isGemini2OrAbove(model), + ), + ).toEqual([]); + }); + + it('should classify every Gemini 1.x model as a Gemini model', () => { + expect( + allModels.filter( + (model) => isGemini1Model(model) && !isGeminiModel(model), + ), + ).toEqual([]); + }); + }); +}); describe('isGemini2OrAbove', () => { describe('valid models', () => {