feat: Stop using the obsolete Gemini 1.x / Gemini 2+ model-id check in ADK

Gemini 1.x is fully deprecated, so sorting Gemini model ids into "1.x"
and "or 2.0+" buckets no longer buys anything. Non-Gemini ids are unaffected: they still raise error.

PiperOrigin-RevId: 960655458
This commit is contained in:
Google Team Member
2026-08-06 20:15:22 -07:00
committed by Copybara-Service
parent 5835f5a4e5
commit 745de0ac13
17 changed files with 50 additions and 275 deletions
@@ -19,7 +19,7 @@ from typing_extensions import override
from ..agents.invocation_context import InvocationContext
from ..models.llm_request import LlmRequest
from ..utils.model_name_utils import is_gemini_eap_or_2_or_above
from ..utils.model_name_utils import is_gemini_model
from ..utils.model_name_utils import is_gemini_model_id_check_disabled
from .base_code_executor import BaseCodeExecutor
from .code_execution_utils import CodeExecutionInput
@@ -29,7 +29,7 @@ from .code_execution_utils import CodeExecutionResult
class BuiltInCodeExecutor(BaseCodeExecutor):
"""A code executor that uses the Model's built-in code executor.
Currently only supports Gemini 2.0+ models, but will be expanded to
Currently only supports Gemini models, but will be expanded to
other models.
"""
@@ -44,9 +44,9 @@ class BuiltInCodeExecutor(BaseCodeExecutor):
pass
def process_llm_request(self, llm_request: LlmRequest) -> None:
"""Pre-process the LLM request for Gemini 2.0+ models to use the code execution tool."""
"""Pre-process the LLM request for Gemini models to use the code execution tool."""
model_check_disabled = is_gemini_model_id_check_disabled()
if is_gemini_eap_or_2_or_above(llm_request.model) or model_check_disabled:
if is_gemini_model(llm_request.model) or model_check_disabled:
llm_request.config = llm_request.config or types.GenerateContentConfig()
llm_request.config.tools = llm_request.config.tools or []
llm_request.config.tools.append(
+2 -2
View File
@@ -19,7 +19,7 @@ from __future__ import annotations
from pydantic import BaseModel
from pydantic import ConfigDict
from ..utils.model_name_utils import is_gemini_eap_or_2_or_above
from ..utils.model_name_utils import is_gemini_model
from ..utils.variant_utils import get_google_llm_variant
from ..utils.variant_utils import GoogleLLMVariant
@@ -44,7 +44,7 @@ def gemini_output_schema_and_tools(model_name: str) -> bool:
"""
return (
get_google_llm_variant() == GoogleLLMVariant.VERTEX_AI
and is_gemini_eap_or_2_or_above(model_name)
and is_gemini_model(model_name)
)
@@ -19,7 +19,6 @@ from typing import TYPE_CHECKING
from google.genai import types
from typing_extensions import override
from ..utils.model_name_utils import is_gemini_1_model
from ..utils.model_name_utils import is_gemini_model
from ..utils.model_name_utils import is_gemini_model_id_check_disabled
from .base_tool import BaseTool
@@ -30,15 +29,13 @@ if TYPE_CHECKING:
class EnterpriseWebSearchTool(BaseTool):
"""A Gemini 2+ built-in tool using web grounding for Enterprise compliance.
"""A Gemini built-in tool using web grounding for Enterprise compliance.
NOTE: This tool is not the same as Vertex AI Search, which is used to be
called "Enterprise Search".
See the documentation for more details:
https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/web-grounding-enterprise.
"""
def __init__(self) -> None:
@@ -60,11 +57,6 @@ class EnterpriseWebSearchTool(BaseTool):
llm_request.config.tools = llm_request.config.tools or []
if is_gemini_model(llm_request.model) or model_check_disabled:
if is_gemini_1_model(llm_request.model) and llm_request.config.tools:
raise ValueError(
'Enterprise Web Search tool cannot be used with other tools in'
' Gemini 1.x.'
)
llm_request.config.tools.append(
types.Tool(enterprise_web_search=types.EnterpriseWebSearch())
)
@@ -19,7 +19,6 @@ from typing import TYPE_CHECKING
from google.genai import types
from typing_extensions import override
from ..utils.model_name_utils import is_gemini_1_model
from ..utils.model_name_utils import is_gemini_model
from ..utils.model_name_utils import is_gemini_model_id_check_disabled
from .base_tool import BaseTool
@@ -30,7 +29,7 @@ if TYPE_CHECKING:
class GoogleMapsGroundingTool(BaseTool):
"""A built-in tool that is automatically invoked by Gemini 2 models to ground query results with Google Maps.
"""A built-in tool that is automatically invoked by Gemini models to ground query results with Google Maps.
This tool operates internally within the model and does not require or perform
local code execution.
@@ -53,11 +52,7 @@ class GoogleMapsGroundingTool(BaseTool):
model_check_disabled = is_gemini_model_id_check_disabled()
llm_request.config = llm_request.config or types.GenerateContentConfig()
llm_request.config.tools = llm_request.config.tools or []
if is_gemini_1_model(llm_request.model):
raise ValueError(
'Google Maps grounding tool cannot be used with Gemini 1.x models.'
)
elif is_gemini_model(llm_request.model) or model_check_disabled:
if is_gemini_model(llm_request.model) or model_check_disabled:
llm_request.config.tools.append(
types.Tool(google_maps=types.GoogleMaps())
)
+1 -10
View File
@@ -20,7 +20,6 @@ from google.genai import types
from typing_extensions import override
from ..utils.model_name_utils import _is_managed_agent
from ..utils.model_name_utils import is_gemini_1_model
from ..utils.model_name_utils import is_gemini_model
from ..utils.model_name_utils import is_gemini_model_id_check_disabled
from .base_tool import BaseTool
@@ -72,15 +71,7 @@ class GoogleSearchTool(BaseTool):
model_check_disabled = is_gemini_model_id_check_disabled()
llm_request.config = llm_request.config or types.GenerateContentConfig()
llm_request.config.tools = llm_request.config.tools or []
if is_gemini_1_model(llm_request.model):
if llm_request.config.tools:
raise ValueError(
'Google search tool cannot be used with other tools in Gemini 1.x.'
)
llm_request.config.tools.append(
types.Tool(google_search_retrieval=types.GoogleSearchRetrieval())
)
elif (
if (
is_gemini_model(llm_request.model)
or model_check_disabled
or _is_managed_agent(llm_request)
@@ -24,7 +24,7 @@ from typing import TYPE_CHECKING
from google.genai import types
from typing_extensions import override
from ...utils.model_name_utils import is_gemini_eap_or_2_or_above
from ...utils.model_name_utils import is_gemini_model
from ...utils.model_name_utils import is_gemini_model_id_check_disabled
from ..tool_context import ToolContext
from .base_retrieval_tool import BaseRetrievalTool
@@ -64,9 +64,9 @@ class VertexAiRagRetrieval(BaseRetrievalTool):
tool_context: ToolContext,
llm_request: LlmRequest,
) -> None:
# Use Gemini built-in Vertex AI RAG tool for Gemini 2 models.
# Use Gemini built-in Vertex AI RAG tool for Gemini models.
model_check_disabled = is_gemini_model_id_check_disabled()
if is_gemini_eap_or_2_or_above(llm_request.model) or model_check_disabled:
if is_gemini_model(llm_request.model) or model_check_disabled:
llm_request.config = (
types.GenerateContentConfig()
if not llm_request.config
@@ -22,7 +22,6 @@ from google.genai import types
from typing_extensions import override
from ..agents.readonly_context import ReadonlyContext
from ..utils.model_name_utils import is_gemini_1_model
from ..utils.model_name_utils import is_gemini_model
from ..utils.model_name_utils import is_gemini_model_id_check_disabled
from .base_tool import BaseTool
@@ -147,12 +146,6 @@ class VertexAiSearchTool(BaseTool):
llm_request.config.tools = llm_request.config.tools or []
if is_gemini_model(llm_request.model) or model_check_disabled:
if is_gemini_1_model(llm_request.model) and llm_request.config.tools:
raise ValueError(
'Vertex AI search tool cannot be used with other tools in Gemini'
' 1.x.'
)
# Build the search config (can be overridden by subclasses)
vertex_ai_search_config = self._build_vertex_ai_search_config(
tool_context
+14 -2
View File
@@ -22,6 +22,7 @@ from typing import TYPE_CHECKING
from packaging.version import InvalidVersion
from packaging.version import Version
from typing_extensions import deprecated
from .env_utils import is_env_enabled
@@ -106,6 +107,10 @@ def is_gemini_model(model_string: Optional[str]) -> bool:
return re.match(r'^gemini-', model_name) is not None
@deprecated(
'ADK no longer distinguishes Gemini versions internally, because Gemini'
' 1.x is fully deprecated. Use is_gemini_model instead.'
)
def is_gemini_1_model(model_string: Optional[str]) -> bool:
"""Check if the model is a Gemini 1.x model using regex patterns.
@@ -122,6 +127,10 @@ def is_gemini_1_model(model_string: Optional[str]) -> bool:
return re.match(r'^gemini-1\.\d+', model_name) is not None
@deprecated(
'ADK no longer distinguishes Gemini versions internally, because Gemini'
' 1.x is fully deprecated. Use is_gemini_model instead.'
)
def is_gemini_eap_or_2_or_above(model_string: Optional[str]) -> bool:
"""Check if the model is a Gemini EAP or a Gemini 2.0+ model.
@@ -166,7 +175,8 @@ def _is_gemini_eap_model(model_string: Optional[str]) -> bool:
followed by a numeric suffix, e.g. ``gemini-flash-early-exp`` or
``gemini-flash-early-exp3``. ``<variant>`` is one or more
alphanumeric/underscore segments separated by ``-`` (e.g. ``flash``,
``pro``, ``flash-lite``).
``pro``, ``flash-lite``), and is optional: variant-less EAP ids such as
``gemini-early-exp`` are also matched.
Args:
model_string: Either a simple model name or path-based model name.
@@ -179,7 +189,9 @@ def _is_gemini_eap_model(model_string: Optional[str]) -> bool:
model_name = extract_model_name(model_string)
return (
re.match(r'^gemini-[a-z0-9_]+(?:-[a-z0-9_]+)*-early-exp\d*$', model_name)
re.match(
r'^gemini-(?:[a-z0-9_]+(?:-[a-z0-9_]+)*-)?early-exp\d*$', model_name
)
is not None
)
@@ -84,15 +84,15 @@ def test_process_llm_request_gemini_2_model_with_existing_tools(
)
def test_process_llm_request_non_gemini_2_model(
def test_process_llm_request_non_gemini_model(
built_in_executor: BuiltInCodeExecutor,
):
"""Tests that a ValueError is raised for non-Gemini 2 models."""
llm_request = LlmRequest(model="gemini-1.5-flash")
"""Tests that a ValueError is raised for non-Gemini models."""
llm_request = LlmRequest(model="claude-3-sonnet")
with pytest.raises(ValueError) as excinfo:
built_in_executor.process_llm_request(llm_request)
assert (
"Gemini code execution tool is not supported for model gemini-1.5-flash"
"Gemini code execution tool is not supported for model claude-3-sonnet"
in str(excinfo.value)
)
+3 -4
View File
@@ -126,7 +126,6 @@ def test_fallback_grants_a_gemini_named_model_and_warns(
('bare-model', '1'), # Not a Gemini id at all.
('gemini-2.5-pro', '0'), # Not on Vertex AI.
('gemini-2.5-pro', None), # Not on Vertex AI.
('gemini-1.5-pro', '1'), # Predates Gemini 2.
],
)
def test_fallback_stays_quiet_when_it_denies(
@@ -186,7 +185,7 @@ def test_subclass_can_override_a_capability():
('gemini-2.5-flash', '1', True),
('gemini-2.5-pro', '0', False),
('gemini-2.5-pro', None, False),
('gemini-1.5-pro', '1', False),
('gemini-early-exp', '1', True),
],
)
def test_gemini_output_schema_and_tools(
@@ -195,7 +194,7 @@ def test_gemini_output_schema_and_tools(
enterprise_mode: str | None,
expected: bool,
) -> None:
"""Gemini pairs schema with tools only on Vertex AI for Gemini 2+.
"""Gemini pairs schema with tools only on Vertex AI.
Declaring the capability itself, it never reaches the fallback on ``BaseLlm``
and so is never nagged to migrate.
@@ -227,7 +226,7 @@ def test_gemini_capabilities_follow_model_reassignment(
) -> None:
"""BaseLlm is mutable, so a reassigned model must be re-resolved."""
monkeypatch.setenv('GOOGLE_GENAI_USE_ENTERPRISE', '1')
gemini = Gemini(model='gemini-1.5-pro')
gemini = Gemini(model='not-a-gemini-model')
assert not gemini.capabilities.output_schema_and_tools
gemini.model = 'gemini-2.5-pro'
@@ -24,12 +24,12 @@ def noop_tool(x: str) -> str:
return x
def test_vertex_rag_retrieval_for_gemini_1_x():
def test_vertex_rag_retrieval_for_non_gemini():
responses = [
'response1',
]
mockModel = testing_utils.MockModel.create(responses=responses)
mockModel.model = 'gemini-1.5-pro'
mockModel.model = 'claude-3-sonnet'
# Calls the first time.
agent = Agent(
@@ -61,12 +61,12 @@ def test_vertex_rag_retrieval_for_gemini_1_x():
assert mockModel.requests[0].tools_dict['rag_retrieval'] is not None
def test_vertex_rag_retrieval_for_gemini_1_x_with_another_function_tool():
def test_vertex_rag_retrieval_for_non_gemini_with_another_function_tool():
responses = [
'response1',
]
mockModel = testing_utils.MockModel.create(responses=responses)
mockModel.model = 'gemini-1.5-pro'
mockModel.model = 'claude-3-sonnet'
# Calls the first time.
agent = Agent(
@@ -94,22 +94,3 @@ async def test_process_llm_request_non_gemini_with_disabled_check(monkeypatch):
== types.EnterpriseWebSearch()
)
@pytest.mark.asyncio
async def test_process_llm_request_failure_with_multiple_tools_gemini_1_models():
tool = EnterpriseWebSearchTool()
llm_request = LlmRequest(
model='gemini-1.5-flash',
config=types.GenerateContentConfig(
tools=[
types.Tool(google_search=types.GoogleSearch()),
]
),
)
tool_context = await _create_tool_context()
with pytest.raises(ValueError) as exc_info:
await tool.process_llm_request(
tool_context=tool_context, llm_request=llm_request
)
assert 'cannot be used with other tools in Gemini 1.x.' in str(exc_info.value)
@@ -54,61 +54,6 @@ class TestGoogleSearchTool:
assert isinstance(google_search, GoogleSearchTool)
assert google_search.name == 'google_search'
@pytest.mark.asyncio
async def test_process_llm_request_with_gemini_1_model(self):
"""Test processing LLM request with Gemini 1.x model."""
tool = GoogleSearchTool()
tool_context = await _create_tool_context()
llm_request = LlmRequest(
model='gemini-1.5-flash', config=types.GenerateContentConfig()
)
await tool.process_llm_request(
tool_context=tool_context, llm_request=llm_request
)
assert llm_request.config.tools is not None
assert len(llm_request.config.tools) == 1
assert llm_request.config.tools[0].google_search_retrieval is not None
@pytest.mark.asyncio
async def test_process_llm_request_with_path_based_gemini_1_model(self):
"""Test processing LLM request with path-based Gemini 1.x model."""
tool = GoogleSearchTool()
tool_context = await _create_tool_context()
llm_request = LlmRequest(
model='projects/265104255505/locations/us-central1/publishers/google/models/gemini-1.5-flash',
config=types.GenerateContentConfig(),
)
await tool.process_llm_request(
tool_context=tool_context, llm_request=llm_request
)
assert llm_request.config.tools is not None
assert len(llm_request.config.tools) == 1
assert llm_request.config.tools[0].google_search_retrieval is not None
@pytest.mark.asyncio
async def test_process_llm_request_with_gemini_1_0_model(self):
"""Test processing LLM request with Gemini 1.0 model."""
tool = GoogleSearchTool()
tool_context = await _create_tool_context()
llm_request = LlmRequest(
model='gemini-1.0-pro', config=types.GenerateContentConfig()
)
await tool.process_llm_request(
tool_context=tool_context, llm_request=llm_request
)
assert llm_request.config.tools is not None
assert len(llm_request.config.tools) == 1
assert llm_request.config.tools[0].google_search_retrieval is not None
@pytest.mark.asyncio
async def test_process_llm_request_with_gemini_2_model(self):
"""Test processing LLM request with Gemini 2.x model."""
@@ -164,64 +109,6 @@ class TestGoogleSearchTool:
assert len(llm_request.config.tools) == 1
assert llm_request.config.tools[0].google_search is not None
@pytest.mark.asyncio
async def test_process_llm_request_with_gemini_1_model_and_existing_tools_raises_error(
self,
):
"""Test that Gemini 1.x model with existing tools raises ValueError."""
tool = GoogleSearchTool()
tool_context = await _create_tool_context()
existing_tool = types.Tool(
function_declarations=[
types.FunctionDeclaration(name='test_function', description='test')
]
)
llm_request = LlmRequest(
model='gemini-1.5-flash',
config=types.GenerateContentConfig(tools=[existing_tool]),
)
with pytest.raises(
ValueError,
match=(
'Google search tool cannot be used with other tools in Gemini 1.x'
),
):
await tool.process_llm_request(
tool_context=tool_context, llm_request=llm_request
)
@pytest.mark.asyncio
async def test_process_llm_request_with_path_based_gemini_1_model_and_existing_tools_raises_error(
self,
):
"""Test that path-based Gemini 1.x model with existing tools raises ValueError."""
tool = GoogleSearchTool()
tool_context = await _create_tool_context()
existing_tool = types.Tool(
function_declarations=[
types.FunctionDeclaration(name='test_function', description='test')
]
)
llm_request = LlmRequest(
model='projects/265104255505/locations/us-central1/publishers/google/models/gemini-1.5-pro-preview',
config=types.GenerateContentConfig(tools=[existing_tool]),
)
with pytest.raises(
ValueError,
match=(
'Google search tool cannot be used with other tools in Gemini 1.x'
),
):
await tool.process_llm_request(
tool_context=tool_context, llm_request=llm_request
)
@pytest.mark.asyncio
async def test_process_llm_request_with_gemini_2_model_and_existing_tools_succeeds(
self,
@@ -430,36 +317,12 @@ class TestGoogleSearchTool:
tool = GoogleSearchTool()
tool_context = await _create_tool_context()
# Test various Gemini versions
gemini_1_models = [
'gemini-1.0-pro',
'gemini-1.5-flash',
'gemini-1.5-pro',
'gemini-1.9-experimental',
]
gemini_2_models = [
'gemini-2.0-pro',
'gemini-2.5-flash',
'gemini-2.5-pro',
]
# Test Gemini 1.x models use google_search_retrieval
for model in gemini_1_models:
llm_request = LlmRequest(
model=model, config=types.GenerateContentConfig()
)
await tool.process_llm_request(
tool_context=tool_context, llm_request=llm_request
)
assert llm_request.config.tools is not None
assert len(llm_request.config.tools) == 1
assert llm_request.config.tools[0].google_search_retrieval is not None
assert llm_request.config.tools[0].google_search is None
# Test Gemini 2.x models use google_search
for model in gemini_2_models:
llm_request = LlmRequest(
model=model, config=types.GenerateContentConfig()
@@ -154,13 +154,13 @@ class TestUrlContextTool:
assert llm_request.config.tools[0].url_context is not None
@pytest.mark.asyncio
async def test_process_llm_request_with_path_based_gemini_model(self):
async def test_process_llm_request_with_path_based_gemini_eap_model(self):
"""Test that a path-based Gemini model id is accepted."""
tool = UrlContextTool()
tool_context = await _create_tool_context()
llm_request = LlmRequest(
model='projects/265104255505/locations/us-central1/publishers/google/models/gemini-2.5-flash',
model='projects/265104255505/locations/global/publishers/google/models/gemini-early-exp',
config=types.GenerateContentConfig(),
)
@@ -297,66 +297,6 @@ class TestVertexAiSearchTool:
assert 'max_results=10' in log_message
assert 'data_store_specs=1 spec(s): [spec_store]' in log_message
@pytest.mark.asyncio
async def test_process_llm_request_with_gemini_1_and_other_tools_raises_error(
self,
):
"""Test that Gemini 1.x with other tools raises ValueError."""
tool = VertexAiSearchTool(data_store_id='test_data_store')
tool_context = await _create_tool_context()
existing_tool = types.Tool(
function_declarations=[
types.FunctionDeclaration(name='test_function', description='test')
]
)
llm_request = LlmRequest(
model='gemini-1.5-flash',
config=types.GenerateContentConfig(tools=[existing_tool]),
)
with pytest.raises(
ValueError,
match=(
'Vertex AI search tool cannot be used with other tools in'
' Gemini 1.x'
),
):
await tool.process_llm_request(
tool_context=tool_context, llm_request=llm_request
)
@pytest.mark.asyncio
async def test_process_llm_request_with_path_based_gemini_1_and_other_tools_raises_error(
self,
):
"""Test that path-based Gemini 1.x with other tools raises ValueError."""
tool = VertexAiSearchTool(data_store_id='test_data_store')
tool_context = await _create_tool_context()
existing_tool = types.Tool(
function_declarations=[
types.FunctionDeclaration(name='test_function', description='test')
]
)
llm_request = LlmRequest(
model='projects/265104255505/locations/us-central1/publishers/google/models/gemini-1.5-pro-preview',
config=types.GenerateContentConfig(tools=[existing_tool]),
)
with pytest.raises(
ValueError,
match=(
'Vertex AI search tool cannot be used with other tools in'
' Gemini 1.x'
),
):
await tool.process_llm_request(
tool_context=tool_context, llm_request=llm_request
)
@pytest.mark.asyncio
async def test_process_llm_request_with_non_gemini_model_raises_error(self):
"""Test that non-Gemini model raises ValueError."""
@@ -121,6 +121,8 @@ class TestIsGeminiModel:
assert is_gemini_model('gemini-1.5-flash') is True
assert is_gemini_model('gemini-1.0-pro') is True
assert is_gemini_model('gemini-2.5-flash') is True
assert is_gemini_model('gemini-early-exp') is True
assert is_gemini_model('gemini-flash-early-exp') is True
assert is_gemini_model('claude-3-sonnet') is False
assert is_gemini_model('gpt-4') is False
assert is_gemini_model('llama-2') is False
@@ -231,6 +233,8 @@ class TestIsGemini2Model:
assert is_gemini_eap_or_2_or_above('gemini-2-pro') is True
assert is_gemini_eap_or_2_or_above('gemini-2') is True
assert is_gemini_eap_or_2_or_above('gemini-3.0-pro') is True
assert is_gemini_eap_or_2_or_above('gemini-early-exp') is True
assert is_gemini_eap_or_2_or_above('gemini-early-exp2') is True
assert is_gemini_eap_or_2_or_above('gemini-flash-early-exp') is True
assert is_gemini_eap_or_2_or_above('gemini-flash-early-exp3') is True
assert is_gemini_eap_or_2_or_above('gemini-flash-lite-early-exp') is True
@@ -285,6 +289,11 @@ class TestIsGemini2Model:
assert is_gemini_eap_or_2_or_above('gemini-0.9-test') is False
assert is_gemini_eap_or_2_or_above('gemini-one') is False
# The EAP variant is optional, but the 'early-exp' marker is not.
assert is_gemini_eap_or_2_or_above('gemini-early') is False
assert is_gemini_eap_or_2_or_above('gemini-early-exp-flash') is False
assert is_gemini_eap_or_2_or_above('my-gemini-early-exp') is False
class TestModelNameUtilsIntegration:
"""Integration tests for model name utilities."""
@@ -56,9 +56,9 @@ def _make_litellm(model: str):
("gemini-2.5-flash", "1", True),
("gemini-2.5-flash", "0", False),
("gemini-2.5-flash", None, False),
("gemini-1.5-pro", "1", False),
("gemini-1.5-pro", "0", False),
("gemini-1.5-pro", None, False),
("gemini-early-exp", "1", True),
],
)
def test_can_use_output_schema_with_tools(