From 6108d9bf4111c485794d84070e7f8483e1652e4c Mon Sep 17 00:00:00 2001 From: Google Team Member Date: Mon, 29 Jun 2026 05:51:15 -0700 Subject: [PATCH] fix(interactions): accept both dict and list[dict] for transform The backend API accepts both a single dict and a list of dicts for the transform field in allowlist entries. However, the Speakeasy generated Pydantic models strictly required List[Dict], causing validation failures for users passing a single dict. This CL updates the OpenAPI overlay to use oneOf for the transform field, allowing both Dict and List structures. The SDKs (Python and TypeScript) have been regenerated using Speakeasy to reflect this type change. PiperOrigin-RevId: 939784235 --- .../interactions/environment/__init__.py | 3 +- .../environment/allowlist/__init__.py | 21 -------------- .../_gaos/types/interactions/__init__.py | 11 +++++++- .../types/interactions/allowlistentry.py | 22 +++++++++++---- .../tests/interactions/test_integration.py | 28 ++++++++++++++++++- 5 files changed, 54 insertions(+), 31 deletions(-) delete mode 100644 google/genai/_gaos/resources/interactions/environment/allowlist/__init__.py diff --git a/google/genai/_gaos/resources/interactions/environment/__init__.py b/google/genai/_gaos/resources/interactions/environment/__init__.py index 06a67cbf3..56de8d46e 100644 --- a/google/genai/_gaos/resources/interactions/environment/__init__.py +++ b/google/genai/_gaos/resources/interactions/environment/__init__.py @@ -18,6 +18,5 @@ from ....types.interactions.environmentnetworkegressallowlist import Allowlist from ....types.interactions.source import Source -from . import allowlist -__all__ = ["Allowlist", "Source", "allowlist"] +__all__ = ["Allowlist", "Source"] diff --git a/google/genai/_gaos/resources/interactions/environment/allowlist/__init__.py b/google/genai/_gaos/resources/interactions/environment/allowlist/__init__.py deleted file mode 100644 index 63b9597f9..000000000 --- a/google/genai/_gaos/resources/interactions/environment/allowlist/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2026 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# pyformat: disable - -"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" - -from .....types.interactions.allowlistentry import AllowlistEntry as Allowlist - -__all__ = ["Allowlist"] diff --git a/google/genai/_gaos/types/interactions/__init__.py b/google/genai/_gaos/types/interactions/__init__.py index e66a6e8bd..9a8e1b433 100644 --- a/google/genai/_gaos/types/interactions/__init__.py +++ b/google/genai/_gaos/types/interactions/__init__.py @@ -23,7 +23,12 @@ if TYPE_CHECKING: from .agentoption import AgentOption from .allowedtools import AllowedTools, AllowedToolsParam - from .allowlistentry import AllowlistEntry, AllowlistEntryParam + from .allowlistentry import ( + AllowlistEntry, + AllowlistEntryParam, + Transform, + TransformParam, + ) from .annotation import Annotation, AnnotationParam, UnknownAnnotation from .argumentsdelta import ArgumentsDelta, ArgumentsDeltaTypedDict from .audiocontent import AudioContent, AudioContentMimeType, AudioContentParam @@ -660,6 +665,8 @@ "ToolChoiceParam", "ToolChoiceType", "ToolParam", + "Transform", + "TransformParam", "Turn", "TurnContent", "TurnContentParam", @@ -719,6 +726,8 @@ "AllowedToolsParam": ".allowedtools", "AllowlistEntry": ".allowlistentry", "AllowlistEntryParam": ".allowlistentry", + "Transform": ".allowlistentry", + "TransformParam": ".allowlistentry", "Annotation": ".annotation", "AnnotationParam": ".annotation", "UnknownAnnotation": ".annotation", diff --git a/google/genai/_gaos/types/interactions/allowlistentry.py b/google/genai/_gaos/types/interactions/allowlistentry.py index 2a4631635..3968fa31c 100644 --- a/google/genai/_gaos/types/interactions/allowlistentry.py +++ b/google/genai/_gaos/types/interactions/allowlistentry.py @@ -19,8 +19,18 @@ from __future__ import annotations from .. import BaseModel, UNSET_SENTINEL from pydantic import model_serializer -from typing import Dict, List, Optional -from typing_extensions import NotRequired, TypedDict +from typing import Dict, List, Optional, Union +from typing_extensions import NotRequired, TypeAliasType, TypedDict + + +TransformParam = TypeAliasType( + "TransformParam", Union[Dict[str, str], List[Dict[str, str]]] +) +r"""Headers to inject on all outbound requests matching this domain. Accepts a single dict or a list of dicts. The egress proxy injects these automatically.""" + + +Transform = TypeAliasType("Transform", Union[Dict[str, str], List[Dict[str, str]]]) +r"""Headers to inject on all outbound requests matching this domain. Accepts a single dict or a list of dicts. The egress proxy injects these automatically.""" class AllowlistEntryParam(TypedDict): @@ -28,8 +38,8 @@ class AllowlistEntryParam(TypedDict): domain: str r"""Domain to allow outbound requests to. Supports wildcards (e.g. '*.googleapis.com'). Use '*' to allow all domains.""" - transform: NotRequired[List[Dict[str, str]]] - r"""Headers to inject on all outbound requests matching this domain. Each entry is a flat {header_name: header_value} object. The egress proxy injects these automatically.""" + transform: NotRequired[TransformParam] + r"""Headers to inject on all outbound requests matching this domain. Accepts a single dict or a list of dicts. The egress proxy injects these automatically.""" class AllowlistEntry(BaseModel): @@ -38,8 +48,8 @@ class AllowlistEntry(BaseModel): domain: str r"""Domain to allow outbound requests to. Supports wildcards (e.g. '*.googleapis.com'). Use '*' to allow all domains.""" - transform: Optional[List[Dict[str, str]]] = None - r"""Headers to inject on all outbound requests matching this domain. Each entry is a flat {header_name: header_value} object. The egress proxy injects these automatically.""" + transform: Optional[Transform] = None + r"""Headers to inject on all outbound requests matching this domain. Accepts a single dict or a list of dicts. The egress proxy injects these automatically.""" @model_serializer(mode="wrap") def serialize_model(self, handler): diff --git a/google/genai/tests/interactions/test_integration.py b/google/genai/tests/interactions/test_integration.py index ae193fd72..d4bf53a41 100644 --- a/google/genai/tests/interactions/test_integration.py +++ b/google/genai/tests/interactions/test_integration.py @@ -23,7 +23,6 @@ pytest_plugins = ("pytest_asyncio",) - def test_client_timeout(): with mock.patch.object( gaos_google_genai, "GenAI", spec_set=True @@ -96,4 +95,31 @@ def test_unrecognized_model_request_serialization(): assert dumped["body"]["model"] == "gemini-3.5-flash" +def test_allowlist_entry_with_dict_transform(): + from ..._gaos.types.interactions.allowlistentry import AllowlistEntry + + # Should construct successfully with a single dict + entry = AllowlistEntry( + domain="github.com", transform={"Authorization": "Bearer TOKEN"} + ) + assert entry.domain == "github.com" + assert entry.transform == {"Authorization": "Bearer TOKEN"} + + # Serialization should preserve it as a dict + dumped = entry.model_dump() + assert dumped["transform"] == {"Authorization": "Bearer TOKEN"} + + +def test_allowlist_entry_with_list_transform(): + from ..._gaos.types.interactions.allowlistentry import AllowlistEntry + + # Should construct successfully with a list of dicts + entry = AllowlistEntry( + domain="github.com", transform=[{"Authorization": "Bearer TOKEN"}] + ) + assert entry.domain == "github.com" + assert entry.transform == [{"Authorization": "Bearer TOKEN"}] + # Serialization should preserve it as a list + dumped = entry.model_dump() + assert dumped["transform"] == [{"Authorization": "Bearer TOKEN"}]