From f6499d118251286d77e773eaac1a358c90c276dd Mon Sep 17 00:00:00 2001 From: Mahdi-Hasan <43575088+Mahdi-Hasan@users.noreply.github.com> Date: Mon, 2 Nov 2026 17:28:30 +0600 Subject: [PATCH 1/4] Python: Skip Gemini connection settings when a genai.Client is injected Gemini chat and embedding constructors parsed GOOGLE_API_KEY, the Enterprise/Vertex mode flags, project, and location before using an injected genai.Client. An invalid ambient value such as GOOGLE_GENAI_USE_ENTERPRISE=not-a-bool failed construction even though the caller's client needs none of those settings. With an injected client only the model settings are now loaded. Mode and service URL come from the SDK client. Owned-client validation is unchanged. Fixes #9959 Co-Authored-By: Claude Opus 5.5 --- .../agent_framework_gemini/_chat_client.py | 14 +++++---- .../_embedding_client.py | 11 ++++--- .../agent_framework_gemini/_sdk_client.py | 15 +++++++++- .../gemini/test_gemini_embedding_client.py | 19 ++++++++++++ .../gemini/tests/test_gemini_client.py | 30 +++++++++++++++++++ 5 files changed, 76 insertions(+), 13 deletions(-) diff --git a/python/packages/gemini/agent_framework_gemini/_chat_client.py b/python/packages/gemini/agent_framework_gemini/_chat_client.py index 1c41055639a..b801e67a25a 100644 --- a/python/packages/gemini/agent_framework_gemini/_chat_client.py +++ b/python/packages/gemini/agent_framework_gemini/_chat_client.py @@ -30,7 +30,7 @@ detect_media_type_from_base64, validate_tool_mode, ) -from agent_framework._settings import SecretString, load_settings +from agent_framework._settings import SecretString from agent_framework._telemetry import mark_feature_used from agent_framework._types import _get_data_bytes # type: ignore[reportPrivateUsage] from agent_framework.exceptions import ( @@ -51,6 +51,7 @@ from ._sdk_client import ( GoogleGeminiSettings, create_genai_client, + load_gemini_settings, ) if sys.version_info >= (3, 13): @@ -307,7 +308,8 @@ def __init__( Application Default Credentials. env_file_path: Path to a ``.env`` file for credential loading. env_file_encoding: Encoding for the ``.env`` file. - client: Pre-built ``genai.Client`` instance. When provided, connector auth settings are not required. + client: Pre-built ``genai.Client`` instance. When provided, it is used as-is and the API key, + mode, project, and location arguments and environment settings are not read. additional_properties: Extra properties stored on the client instance. max_tracked_thought_signatures: Maximum number of Gemini 3 thought signatures retained for replay, keyed by call ID. Least-recently-used entries are evicted beyond this. @@ -317,9 +319,8 @@ def __init__( """ if max_tracked_thought_signatures < 1: raise ValueError("max_tracked_thought_signatures must be greater than 0.") - google_settings = load_settings( - GoogleGeminiSettings, - env_prefix="GOOGLE_", + google_settings = load_gemini_settings( + client=client, api_key=api_key, model=model, genai_use_enterprise=enterprise, @@ -1397,7 +1398,8 @@ def __init__( Application Default Credentials. env_file_path: Path to a ``.env`` file for credential loading. env_file_encoding: Encoding for the ``.env`` file. - client: Pre-built ``genai.Client`` instance. When provided, connector auth settings are not required. + client: Pre-built ``genai.Client`` instance. When provided, it is used as-is and the API key, + mode, project, and location arguments and environment settings are not read. additional_properties: Extra properties stored on the client instance. middleware: Optional middleware chain applied to every call. function_invocation_configuration: Optional configuration for the function invocation loop. diff --git a/python/packages/gemini/agent_framework_gemini/_embedding_client.py b/python/packages/gemini/agent_framework_gemini/_embedding_client.py index fcbce481d87..fe517bb4eae 100644 --- a/python/packages/gemini/agent_framework_gemini/_embedding_client.py +++ b/python/packages/gemini/agent_framework_gemini/_embedding_client.py @@ -13,7 +13,6 @@ EmbeddingGenerationOptions, GeneratedEmbeddings, UsageDetails, - load_settings, ) from agent_framework._settings import SecretString from agent_framework._telemetry import mark_feature_used @@ -32,8 +31,8 @@ from ._feature_usage import FeatureIndex from ._sdk_client import ( - GoogleGeminiSettings, create_genai_client, + load_gemini_settings, ) if sys.version_info >= (3, 13): @@ -137,7 +136,8 @@ class RawGeminiEmbeddingClient( project: Enterprise (Vertex AI) project, or ``GOOGLE_CLOUD_PROJECT``. location: Enterprise (Vertex AI) region, or ``GOOGLE_CLOUD_LOCATION``. credentials: Google Cloud credentials for Enterprise; the SDK can also use ADC. - client: Preconfigured ``genai.Client``; the caller retains ownership of it. + client: Preconfigured ``genai.Client``; the caller retains ownership of it. When provided, the + API key, mode, project, and location arguments and environment settings are not read. additional_properties: Extra properties stored on the client instance. env_file_path: Optional ``.env`` file for settings. env_file_encoding: Encoding for the ``.env`` file. @@ -161,9 +161,8 @@ def __init__( env_file_encoding: str | None = None, ) -> None: """Initialize a raw Gemini embedding client.""" - google_settings = load_settings( - GoogleGeminiSettings, - env_prefix="GOOGLE_", + google_settings = load_gemini_settings( + client=client, api_key=api_key, embedding_model=model, genai_use_enterprise=enterprise, diff --git a/python/packages/gemini/agent_framework_gemini/_sdk_client.py b/python/packages/gemini/agent_framework_gemini/_sdk_client.py index f4978a15463..5c9bae4ad49 100644 --- a/python/packages/gemini/agent_framework_gemini/_sdk_client.py +++ b/python/packages/gemini/agent_framework_gemini/_sdk_client.py @@ -4,7 +4,7 @@ from typing import Any -from agent_framework._settings import SecretString +from agent_framework._settings import SecretString, load_settings from agent_framework._telemetry import get_user_agent from google import genai from google.auth.credentials import Credentials @@ -23,6 +23,19 @@ class GoogleGeminiSettings(TypedDict, total=False): cloud_location: str | None +class _GoogleGeminiModelSettings(TypedDict, total=False): + """The only connector settings an injected ``genai.Client`` still needs.""" + + model: str | None + embedding_model: str | None + + +def load_gemini_settings(*, client: genai.Client | None, **kwargs: Any) -> GoogleGeminiSettings: + """Load ``GOOGLE_*`` settings, skipping auth, mode, project, and location for an injected client.""" + settings_type: type[Any] = GoogleGeminiSettings if client is None else _GoogleGeminiModelSettings + return load_settings(settings_type, env_prefix="GOOGLE_", **kwargs) + + _GEMINI_API_BASE_URL = "https://generativelanguage.googleapis.com" _VERTEX_AI_BASE_URL = "https://aiplatform.googleapis.com" diff --git a/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py b/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py index 8ef332cfb31..814f4fdf3f8 100644 --- a/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py +++ b/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py @@ -219,6 +219,25 @@ def test_injected_vertex_client_controls_service_url() -> None: assert client.service_url() == "https://aiplatform.googleapis.com" +@pytest.mark.parametrize("env_var", ["GOOGLE_GENAI_USE_ENTERPRISE", "GOOGLE_GENAI_USE_VERTEXAI"]) +def test_injected_sdk_client_ignores_invalid_connection_settings( + clear_google_env: None, + env_var: str, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """An injected SDK client is used as-is, so connection settings it does not need are never parsed.""" + monkeypatch.setenv(env_var, "not-a-bool") + monkeypatch.setenv("GOOGLE_EMBEDDING_MODEL", "gemini-embedding-2-preview") + sdk = _make_sdk_client() + + client = RawGeminiEmbeddingClient(client=sdk) + + assert client._genai_client is sdk + assert client._vertexai is False + assert client.service_url() == "https://generativelanguage.googleapis.com" + assert client.model == "gemini-embedding-2-preview" + + async def test_close_only_closes_owned_sdk_client(clear_google_env: None) -> None: owned_sdk = _make_sdk_client() with patch("agent_framework_gemini._sdk_client.genai.Client", return_value=owned_sdk): diff --git a/python/packages/gemini/tests/test_gemini_client.py b/python/packages/gemini/tests/test_gemini_client.py index 484fdd144a5..f4064a77922 100644 --- a/python/packages/gemini/tests/test_gemini_client.py +++ b/python/packages/gemini/tests/test_gemini_client.py @@ -2999,6 +2999,36 @@ def test_injected_sdk_routing_is_shared_with_embeddings() -> None: assert chat.service_url() == embeddings.service_url() == "https://custom.example.test" +@pytest.mark.parametrize("client_type", [RawGeminiChatClient, GeminiChatClient]) +@pytest.mark.parametrize("env_var", ["GOOGLE_GENAI_USE_ENTERPRISE", "GOOGLE_GENAI_USE_VERTEXAI"]) +def test_injected_sdk_client_ignores_invalid_connection_settings( + client_type: type[RawGeminiChatClient], + env_var: str, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """An injected SDK client is used as-is, so connection settings it does not need are never parsed.""" + monkeypatch.setenv(env_var, "not-a-bool") + monkeypatch.setenv("GOOGLE_MODEL", "env-model") + sdk = MagicMock() + sdk._api_client.vertexai = False + sdk._api_client._http_options.base_url = "https://generativelanguage.googleapis.com/" + + client = client_type(client=sdk) + + assert client._genai_client is sdk + assert client._vertexai is False + assert client.service_url() == "https://generativelanguage.googleapis.com" + assert client.model == "env-model" + + +def test_owned_client_still_validates_connection_settings(monkeypatch: pytest.MonkeyPatch) -> None: + """Without an injected SDK client, invalid connection settings still fail construction.""" + monkeypatch.setenv("GOOGLE_GENAI_USE_ENTERPRISE", "not-a-bool") + + with pytest.raises(ValueError, match="GOOGLE_GENAI_USE_ENTERPRISE"): + GeminiChatClient(api_key="test-key", model="gemini-2.5-flash") + + # integration tests From d37fe55336c50349213c270187cd925ccaa0ac07 Mon Sep 17 00:00:00 2001 From: Mahdi-Hasan <43575088+Mahdi-Hasan@users.noreply.github.com> Date: Tue, 3 Nov 2026 14:58:09 +0600 Subject: [PATCH 2/4] Python: Keep explicit Gemini mode as fallback for injected clients An injected genai.Client still skips GOOGLE_* connection settings, but explicit enterprise/vertexai arguments are now kept. resolve_vertexai_mode() uses them when the SDK client does not expose a boolean _api_client.vertexai, so a Vertex client is not treated as the Developer API. Co-Authored-By: Claude Opus 5.5 --- .../agent_framework_gemini/_chat_client.py | 6 ++++-- .../_embedding_client.py | 3 ++- .../agent_framework_gemini/_sdk_client.py | 15 +++++++++++--- .../gemini/test_gemini_embedding_client.py | 17 ++++++++++++++++ .../gemini/tests/test_gemini_client.py | 20 +++++++++++++++++++ 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/python/packages/gemini/agent_framework_gemini/_chat_client.py b/python/packages/gemini/agent_framework_gemini/_chat_client.py index b801e67a25a..42230b6efa8 100644 --- a/python/packages/gemini/agent_framework_gemini/_chat_client.py +++ b/python/packages/gemini/agent_framework_gemini/_chat_client.py @@ -309,7 +309,8 @@ def __init__( env_file_path: Path to a ``.env`` file for credential loading. env_file_encoding: Encoding for the ``.env`` file. client: Pre-built ``genai.Client`` instance. When provided, it is used as-is and the API key, - mode, project, and location arguments and environment settings are not read. + project, and location arguments and environment settings are not read. Explicit mode + arguments only apply when the client does not expose its own mode. additional_properties: Extra properties stored on the client instance. max_tracked_thought_signatures: Maximum number of Gemini 3 thought signatures retained for replay, keyed by call ID. Least-recently-used entries are evicted beyond this. @@ -1399,7 +1400,8 @@ def __init__( env_file_path: Path to a ``.env`` file for credential loading. env_file_encoding: Encoding for the ``.env`` file. client: Pre-built ``genai.Client`` instance. When provided, it is used as-is and the API key, - mode, project, and location arguments and environment settings are not read. + project, and location arguments and environment settings are not read. Explicit mode + arguments only apply when the client does not expose its own mode. additional_properties: Extra properties stored on the client instance. middleware: Optional middleware chain applied to every call. function_invocation_configuration: Optional configuration for the function invocation loop. diff --git a/python/packages/gemini/agent_framework_gemini/_embedding_client.py b/python/packages/gemini/agent_framework_gemini/_embedding_client.py index fe517bb4eae..bdc008fd8e4 100644 --- a/python/packages/gemini/agent_framework_gemini/_embedding_client.py +++ b/python/packages/gemini/agent_framework_gemini/_embedding_client.py @@ -137,7 +137,8 @@ class RawGeminiEmbeddingClient( location: Enterprise (Vertex AI) region, or ``GOOGLE_CLOUD_LOCATION``. credentials: Google Cloud credentials for Enterprise; the SDK can also use ADC. client: Preconfigured ``genai.Client``; the caller retains ownership of it. When provided, the - API key, mode, project, and location arguments and environment settings are not read. + API key, project, and location arguments and environment settings are not read. Explicit mode + arguments only apply when the client does not expose its own mode. additional_properties: Extra properties stored on the client instance. env_file_path: Optional ``.env`` file for settings. env_file_encoding: Encoding for the ``.env`` file. diff --git a/python/packages/gemini/agent_framework_gemini/_sdk_client.py b/python/packages/gemini/agent_framework_gemini/_sdk_client.py index 5c9bae4ad49..05ab6429966 100644 --- a/python/packages/gemini/agent_framework_gemini/_sdk_client.py +++ b/python/packages/gemini/agent_framework_gemini/_sdk_client.py @@ -31,9 +31,18 @@ class _GoogleGeminiModelSettings(TypedDict, total=False): def load_gemini_settings(*, client: genai.Client | None, **kwargs: Any) -> GoogleGeminiSettings: - """Load ``GOOGLE_*`` settings, skipping auth, mode, project, and location for an injected client.""" - settings_type: type[Any] = GoogleGeminiSettings if client is None else _GoogleGeminiModelSettings - return load_settings(settings_type, env_prefix="GOOGLE_", **kwargs) + """Load ``GOOGLE_*`` settings, skipping auth, mode, project, and location for an injected client. + + For an injected client, explicit mode arguments are kept (environment mode is not read) so they + remain the fallback when the SDK client does not expose its mode. + """ + if client is None: + return load_settings(GoogleGeminiSettings, env_prefix="GOOGLE_", **kwargs) + settings: GoogleGeminiSettings = load_settings(_GoogleGeminiModelSettings, env_prefix="GOOGLE_", **kwargs) # type: ignore[assignment] + for key in ("genai_use_enterprise", "genai_use_vertexai"): + if kwargs.get(key) is not None: + settings[key] = kwargs[key] + return settings _GEMINI_API_BASE_URL = "https://generativelanguage.googleapis.com" diff --git a/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py b/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py index 814f4fdf3f8..2c1d78201b8 100644 --- a/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py +++ b/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py @@ -238,6 +238,23 @@ def test_injected_sdk_client_ignores_invalid_connection_settings( assert client.model == "gemini-embedding-2-preview" +@pytest.mark.parametrize("mode_arg", ["enterprise", "vertexai"]) +def test_injected_sdk_client_without_mode_uses_explicit_mode( + clear_google_env: None, + mode_arg: str, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """An explicit mode stays the fallback when the injected SDK client does not expose its mode.""" + monkeypatch.setenv("GOOGLE_GENAI_USE_VERTEXAI", "not-a-bool") + sdk = _make_sdk_client() + sdk._api_client.vertexai = None + + client = RawGeminiEmbeddingClient(client=sdk, model="gemini-embedding-2-preview", **{mode_arg: True}) + + assert client._genai_client is sdk + assert client._vertexai is True + + async def test_close_only_closes_owned_sdk_client(clear_google_env: None) -> None: owned_sdk = _make_sdk_client() with patch("agent_framework_gemini._sdk_client.genai.Client", return_value=owned_sdk): diff --git a/python/packages/gemini/tests/test_gemini_client.py b/python/packages/gemini/tests/test_gemini_client.py index f4064a77922..6b36c131cf5 100644 --- a/python/packages/gemini/tests/test_gemini_client.py +++ b/python/packages/gemini/tests/test_gemini_client.py @@ -3021,6 +3021,26 @@ def test_injected_sdk_client_ignores_invalid_connection_settings( assert client.model == "env-model" +@pytest.mark.parametrize("client_type", [RawGeminiChatClient, GeminiChatClient]) +@pytest.mark.parametrize("mode_arg", ["enterprise", "vertexai"]) +def test_injected_sdk_client_without_mode_uses_explicit_mode( + client_type: type[RawGeminiChatClient], + mode_arg: str, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """An explicit mode stays the fallback when the injected SDK client does not expose its mode.""" + monkeypatch.setenv("GOOGLE_GENAI_USE_VERTEXAI", "not-a-bool") + sdk = MagicMock() + sdk._api_client.vertexai = None + sdk._api_client._http_options.base_url = None + + client = client_type(client=sdk, model="gemini-2.5-flash", **{mode_arg: True}) + + assert client._genai_client is sdk + assert client._vertexai is True + assert client.service_url() == "https://aiplatform.googleapis.com" + + def test_owned_client_still_validates_connection_settings(monkeypatch: pytest.MonkeyPatch) -> None: """Without an injected SDK client, invalid connection settings still fail construction.""" monkeypatch.setenv("GOOGLE_GENAI_USE_ENTERPRISE", "not-a-bool") From fd0c7a3a9c5e0200741d7cc9a00a2430570d57d7 Mon Sep 17 00:00:00 2001 From: Mahdi-Hasan <43575088+Mahdi-Hasan@users.noreply.github.com> Date: Tue, 3 Nov 2026 15:08:38 +0600 Subject: [PATCH 3/4] Python: Type-check explicit Gemini mode for injected clients Explicit enterprise/vertexai arguments kept for an injected client now get the same bool check an owned client gets from load_settings, so a string like "false" is rejected instead of resolving to Vertex AI. Docstrings now say GOOGLE_MODEL / GOOGLE_EMBEDDING_MODEL still apply to injected clients. Co-Authored-By: Claude Opus 5.5 --- .../gemini/agent_framework_gemini/_chat_client.py | 14 ++++++++------ .../agent_framework_gemini/_embedding_client.py | 5 +++-- .../gemini/agent_framework_gemini/_sdk_client.py | 10 ++++++++-- .../tests/gemini/test_gemini_embedding_client.py | 10 ++++++++++ python/packages/gemini/tests/test_gemini_client.py | 10 ++++++++++ 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/python/packages/gemini/agent_framework_gemini/_chat_client.py b/python/packages/gemini/agent_framework_gemini/_chat_client.py index 7f6d58c8481..0750e741f11 100644 --- a/python/packages/gemini/agent_framework_gemini/_chat_client.py +++ b/python/packages/gemini/agent_framework_gemini/_chat_client.py @@ -304,9 +304,10 @@ def __init__( Application Default Credentials. env_file_path: Path to a ``.env`` file for credential loading. env_file_encoding: Encoding for the ``.env`` file. - client: Pre-built ``genai.Client`` instance. When provided, it is used as-is and the API key, - project, and location arguments and environment settings are not read. Explicit mode - arguments only apply when the client does not expose its own mode. + client: Pre-built ``genai.Client`` instance. When provided, it is used as-is: the API key, + project, and location arguments and connection environment settings are not read. + ``GOOGLE_MODEL`` still applies. Explicit mode arguments only apply when the client does not + expose its own mode. additional_properties: Extra properties stored on the client instance. max_tracked_thought_signatures: Maximum number of Gemini 3 thought signatures retained for replay, keyed by call ID. Least-recently-used entries are evicted beyond this. @@ -1395,9 +1396,10 @@ def __init__( Application Default Credentials. env_file_path: Path to a ``.env`` file for credential loading. env_file_encoding: Encoding for the ``.env`` file. - client: Pre-built ``genai.Client`` instance. When provided, it is used as-is and the API key, - project, and location arguments and environment settings are not read. Explicit mode - arguments only apply when the client does not expose its own mode. + client: Pre-built ``genai.Client`` instance. When provided, it is used as-is: the API key, + project, and location arguments and connection environment settings are not read. + ``GOOGLE_MODEL`` still applies. Explicit mode arguments only apply when the client does not + expose its own mode. additional_properties: Extra properties stored on the client instance. middleware: Optional middleware chain applied to every call. function_invocation_configuration: Optional configuration for the function invocation loop. diff --git a/python/packages/gemini/agent_framework_gemini/_embedding_client.py b/python/packages/gemini/agent_framework_gemini/_embedding_client.py index bdc008fd8e4..8ffbce8849d 100644 --- a/python/packages/gemini/agent_framework_gemini/_embedding_client.py +++ b/python/packages/gemini/agent_framework_gemini/_embedding_client.py @@ -137,8 +137,9 @@ class RawGeminiEmbeddingClient( location: Enterprise (Vertex AI) region, or ``GOOGLE_CLOUD_LOCATION``. credentials: Google Cloud credentials for Enterprise; the SDK can also use ADC. client: Preconfigured ``genai.Client``; the caller retains ownership of it. When provided, the - API key, project, and location arguments and environment settings are not read. Explicit mode - arguments only apply when the client does not expose its own mode. + API key, project, and location arguments and connection environment settings are not read. + ``GOOGLE_EMBEDDING_MODEL`` still applies. Explicit mode arguments only apply when the client + does not expose its own mode. additional_properties: Extra properties stored on the client instance. env_file_path: Optional ``.env`` file for settings. env_file_encoding: Encoding for the ``.env`` file. diff --git a/python/packages/gemini/agent_framework_gemini/_sdk_client.py b/python/packages/gemini/agent_framework_gemini/_sdk_client.py index 05ab6429966..481df79158f 100644 --- a/python/packages/gemini/agent_framework_gemini/_sdk_client.py +++ b/python/packages/gemini/agent_framework_gemini/_sdk_client.py @@ -40,8 +40,14 @@ def load_gemini_settings(*, client: genai.Client | None, **kwargs: Any) -> Googl return load_settings(GoogleGeminiSettings, env_prefix="GOOGLE_", **kwargs) settings: GoogleGeminiSettings = load_settings(_GoogleGeminiModelSettings, env_prefix="GOOGLE_", **kwargs) # type: ignore[assignment] for key in ("genai_use_enterprise", "genai_use_vertexai"): - if kwargs.get(key) is not None: - settings[key] = kwargs[key] + if (value := kwargs.get(key)) is not None: + # Match the override type check an owned client gets from load_settings. + if not isinstance(value, bool): + raise ValueError( + f"Invalid type for setting '{key}' from explicit override: " + f"expected bool, got {type(value).__name__}." + ) + settings[key] = value return settings diff --git a/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py b/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py index 2c1d78201b8..e9b539deb26 100644 --- a/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py +++ b/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py @@ -255,6 +255,16 @@ def test_injected_sdk_client_without_mode_uses_explicit_mode( assert client._vertexai is True +@pytest.mark.parametrize("mode_arg", ["enterprise", "vertexai"]) +def test_injected_sdk_client_rejects_non_bool_explicit_mode(mode_arg: str) -> None: + """Explicit mode arguments are type-checked for an injected client, like for an owned one.""" + sdk = MagicMock() + sdk._api_client.vertexai = None + + with pytest.raises(ValueError, match=f"genai_use_{mode_arg}"): + RawGeminiEmbeddingClient(client=sdk, model="gemini-embedding-2-preview", **{mode_arg: "false"}) + + async def test_close_only_closes_owned_sdk_client(clear_google_env: None) -> None: owned_sdk = _make_sdk_client() with patch("agent_framework_gemini._sdk_client.genai.Client", return_value=owned_sdk): diff --git a/python/packages/gemini/tests/test_gemini_client.py b/python/packages/gemini/tests/test_gemini_client.py index 4a1b57447af..d0f5e9c50a1 100644 --- a/python/packages/gemini/tests/test_gemini_client.py +++ b/python/packages/gemini/tests/test_gemini_client.py @@ -3041,6 +3041,16 @@ def test_injected_sdk_client_without_mode_uses_explicit_mode( assert client.service_url() == "https://aiplatform.googleapis.com" +@pytest.mark.parametrize("mode_arg", ["enterprise", "vertexai"]) +def test_injected_sdk_client_rejects_non_bool_explicit_mode(mode_arg: str) -> None: + """Explicit mode arguments are type-checked for an injected client, like for an owned one.""" + sdk = MagicMock() + sdk._api_client.vertexai = None + + with pytest.raises(ValueError, match=f"genai_use_{mode_arg}"): + RawGeminiChatClient(client=sdk, model="gemini-2.5-flash", **{mode_arg: "false"}) + + def test_owned_client_still_validates_connection_settings(monkeypatch: pytest.MonkeyPatch) -> None: """Without an injected SDK client, invalid connection settings still fail construction.""" monkeypatch.setenv("GOOGLE_GENAI_USE_ENTERPRISE", "not-a-bool") From 395fdc056702f26d578c6821662b5855d4079793 Mon Sep 17 00:00:00 2001 From: Mahdi-Hasan <43575088+Mahdi-Hasan@users.noreply.github.com> Date: Wed, 4 Nov 2026 12:43:20 +0600 Subject: [PATCH 4/4] Python: Cast unpacked mode kwargs in Gemini tests for pyrefly --- .../gemini/tests/gemini/test_gemini_embedding_client.py | 8 ++++++-- python/packages/gemini/tests/test_gemini_client.py | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py b/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py index e9b539deb26..182352c790a 100644 --- a/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py +++ b/python/packages/gemini/tests/gemini/test_gemini_embedding_client.py @@ -249,7 +249,9 @@ def test_injected_sdk_client_without_mode_uses_explicit_mode( sdk = _make_sdk_client() sdk._api_client.vertexai = None - client = RawGeminiEmbeddingClient(client=sdk, model="gemini-embedding-2-preview", **{mode_arg: True}) + client = RawGeminiEmbeddingClient( + client=sdk, model="gemini-embedding-2-preview", **cast(dict[str, Any], {mode_arg: True}) + ) assert client._genai_client is sdk assert client._vertexai is True @@ -262,7 +264,9 @@ def test_injected_sdk_client_rejects_non_bool_explicit_mode(mode_arg: str) -> No sdk._api_client.vertexai = None with pytest.raises(ValueError, match=f"genai_use_{mode_arg}"): - RawGeminiEmbeddingClient(client=sdk, model="gemini-embedding-2-preview", **{mode_arg: "false"}) + RawGeminiEmbeddingClient( + client=sdk, model="gemini-embedding-2-preview", **cast(dict[str, Any], {mode_arg: "false"}) + ) async def test_close_only_closes_owned_sdk_client(clear_google_env: None) -> None: diff --git a/python/packages/gemini/tests/test_gemini_client.py b/python/packages/gemini/tests/test_gemini_client.py index d0f5e9c50a1..5d442bdf67e 100644 --- a/python/packages/gemini/tests/test_gemini_client.py +++ b/python/packages/gemini/tests/test_gemini_client.py @@ -3034,7 +3034,7 @@ def test_injected_sdk_client_without_mode_uses_explicit_mode( sdk._api_client.vertexai = None sdk._api_client._http_options.base_url = None - client = client_type(client=sdk, model="gemini-2.5-flash", **{mode_arg: True}) + client = client_type(client=sdk, model="gemini-2.5-flash", **cast(dict[str, Any], {mode_arg: True})) assert client._genai_client is sdk assert client._vertexai is True @@ -3048,7 +3048,7 @@ def test_injected_sdk_client_rejects_non_bool_explicit_mode(mode_arg: str) -> No sdk._api_client.vertexai = None with pytest.raises(ValueError, match=f"genai_use_{mode_arg}"): - RawGeminiChatClient(client=sdk, model="gemini-2.5-flash", **{mode_arg: "false"}) + RawGeminiChatClient(client=sdk, model="gemini-2.5-flash", **cast(dict[str, Any], {mode_arg: "false"})) def test_owned_client_still_validates_connection_settings(monkeypatch: pytest.MonkeyPatch) -> None: