diff --git a/google/genai/_gaos/__init__.py b/google/genai/_gaos/__init__.py new file mode 100644 index 000000000..e32b0d9cd --- /dev/null +++ b/google/genai/_gaos/__init__.py @@ -0,0 +1,34 @@ +# 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 ._version import ( + __title__, + __version__, + __openapi_doc_version__, + __gen_version__, + __user_agent__, +) +from .sdk import * +from .sdkconfiguration import * +from . import resources + + +VERSION: str = __version__ +OPENAPI_DOC_VERSION = __openapi_doc_version__ +SPEAKEASY_GENERATOR_VERSION = __gen_version__ +USER_AGENT = __user_agent__ diff --git a/google/genai/_gaos/_hooks/__init__.py b/google/genai/_gaos/_hooks/__init__.py new file mode 100644 index 000000000..1dde3f4ff --- /dev/null +++ b/google/genai/_gaos/_hooks/__init__.py @@ -0,0 +1,21 @@ +# 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 .sdkhooks import * +from .types import * +from .registration import * diff --git a/google/genai/_gaos/_hooks/google_genai_auth.py b/google/genai/_gaos/_hooks/google_genai_auth.py new file mode 100644 index 000000000..eaa51a84c --- /dev/null +++ b/google/genai/_gaos/_hooks/google_genai_auth.py @@ -0,0 +1,157 @@ +# 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 + +"""Google GenAI authentication hooks for the embedded Speakeasy SDK.""" + +from __future__ import annotations + +from typing import Any, Callable, Mapping, Optional, Union, cast + +import httpx + +from .. import types, utils +from .types import BeforeRequestContext, BeforeRequestHook + + +GOOGLE_GENAI_API_REVISION = "2026-05-20" +_MANAGED_BEARER_AUTH = "google_genai_managed_bearer_auth" + + +class GoogleGenAISecurityProvider: + """Callable security source that keeps generated auth refreshes retry-safe.""" + + def __init__( + self, + *, + access_token: Callable[[], str], + default_headers: Optional[Mapping[str, str]] = None, + ) -> None: + self._access_token = access_token + self._default_headers = dict(default_headers or {}) + self._pending_access_token: Optional[str] = None + + def __call__(self) -> types.Security: + if self._pending_access_token is None: + self._pending_access_token = self._access_token() + + return types.Security( + access_token=self._pending_access_token, + default_headers=self._default_headers or None, + ) + + def consume(self) -> None: + self._pending_access_token = None + + +class GoogleGenAIAuthHook(BeforeRequestHook): + """Applies Google GenAI headers and custom auth before sending requests.""" + + def before_request( + self, + hook_ctx: BeforeRequestContext, + request: httpx.Request, + ) -> Union[httpx.Request, Exception]: + security = _resolve_security(hook_ctx.security_source) + + _apply_default_headers(security, request) + _apply_api_revision(hook_ctx, request) + _apply_user_project(hook_ctx, request) + _apply_auth(security, request) + + return request + + +def _resolve_security(security_source: Any) -> Optional[types.Security]: + security = security_source + + if callable(security_source): + security = security_source() + consume = getattr(security_source, "consume", None) + if callable(consume): + consume() + + security = utils.get_security_from_env(security, types.Security) + if security is None: + return None + + if isinstance(security, types.Security): + return security + + if isinstance(security, Mapping): + return types.Security(**security) + + return cast(types.Security, security) + + +def _apply_default_headers( + security: Optional[types.Security], request: httpx.Request +) -> None: + headers = security.default_headers if security else None + for key, value in (headers or {}).items(): + if request.headers.get(key) is None: + request.headers[key] = value + + +def _apply_api_revision( + hook_ctx: BeforeRequestContext, request: httpx.Request +) -> None: + if request.headers.get("Api-Revision") is not None: + return + + api_revision = hook_ctx.config.globals.api_revision + request.headers["Api-Revision"] = api_revision or GOOGLE_GENAI_API_REVISION + + +def _apply_user_project( + hook_ctx: BeforeRequestContext, request: httpx.Request +) -> None: + user_project = hook_ctx.config.globals.user_project + if user_project and request.headers.get("x-goog-user-project") is None: + request.headers["x-goog-user-project"] = user_project + + +def _has_auth_headers(request: httpx.Request) -> bool: + return ( + request.headers.get("authorization") is not None + or request.headers.get("x-goog-api-key") is not None + ) + + +def _has_user_auth_headers(request: httpx.Request) -> bool: + return ( + not request.extensions.get(_MANAGED_BEARER_AUTH) + and _has_auth_headers(request) + ) + + +def _apply_auth( + security: Optional[types.Security], request: httpx.Request +) -> None: + if security is None or _has_user_auth_headers(request): + return + + if security.api_key: + request.extensions.pop(_MANAGED_BEARER_AUTH, None) + request.headers["x-goog-api-key"] = security.api_key + return + + if security.access_token: + request.extensions[_MANAGED_BEARER_AUTH] = True + request.headers["Authorization"] = _bearer(security.access_token) + + +def _bearer(token: str) -> str: + return token if token.lower().startswith("bearer ") else f"Bearer {token}" diff --git a/google/genai/_gaos/_hooks/registration.py b/google/genai/_gaos/_hooks/registration.py new file mode 100644 index 000000000..b946c0d9d --- /dev/null +++ b/google/genai/_gaos/_hooks/registration.py @@ -0,0 +1,29 @@ +# 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 + +from .types import Hooks + + +# This file is only ever generated once on the first generation and then is free to be modified. +# Any hooks you wish to add should be registered in the init_hooks function. Feel free to define them +# in this file or in separate files in the hooks folder. + + +def init_hooks(hooks: Hooks): + # pylint: disable=unused-argument + """Add hooks by calling hooks.register{sdk_init/before_request/after_success/after_error}Hook + with an instance of a hook that implements that specific Hook interface + Hooks are registered per SDK instance, and are valid for the lifetime of the SDK instance""" diff --git a/google/genai/_gaos/_hooks/sdkhooks.py b/google/genai/_gaos/_hooks/sdkhooks.py new file mode 100644 index 000000000..c851f8a9d --- /dev/null +++ b/google/genai/_gaos/_hooks/sdkhooks.py @@ -0,0 +1,94 @@ +# 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.""" + +import httpx +from .types import ( + SDKInitHook, + BeforeRequestContext, + BeforeRequestHook, + AfterSuccessContext, + AfterSuccessHook, + AfterErrorContext, + AfterErrorHook, + Hooks, +) +from .registration import init_hooks +from .google_genai_auth import GoogleGenAIAuthHook +from typing import List, Optional, Tuple +from ..sdkconfiguration import SDKConfiguration + + +class SDKHooks(Hooks): + def __init__(self) -> None: + self.sdk_init_hooks: List[SDKInitHook] = [] + self.before_request_hooks: List[BeforeRequestHook] = [] + self.after_success_hooks: List[AfterSuccessHook] = [] + self.after_error_hooks: List[AfterErrorHook] = [] + init_hooks(self) + self.register_before_request_hook(GoogleGenAIAuthHook()) + + def register_sdk_init_hook(self, hook: SDKInitHook) -> None: + self.sdk_init_hooks.append(hook) + + def register_before_request_hook(self, hook: BeforeRequestHook) -> None: + self.before_request_hooks.append(hook) + + def register_after_success_hook(self, hook: AfterSuccessHook) -> None: + self.after_success_hooks.append(hook) + + def register_after_error_hook(self, hook: AfterErrorHook) -> None: + self.after_error_hooks.append(hook) + + def sdk_init(self, config: SDKConfiguration) -> SDKConfiguration: + for hook in self.sdk_init_hooks: + config = hook.sdk_init(config) + return config + + def before_request( + self, hook_ctx: BeforeRequestContext, request: httpx.Request + ) -> httpx.Request: + for hook in self.before_request_hooks: + out = hook.before_request(hook_ctx, request) + if isinstance(out, Exception): + raise out + request = out + + return request + + def after_success( + self, hook_ctx: AfterSuccessContext, response: httpx.Response + ) -> httpx.Response: + for hook in self.after_success_hooks: + out = hook.after_success(hook_ctx, response) + if isinstance(out, Exception): + raise out + response = out + return response + + def after_error( + self, + hook_ctx: AfterErrorContext, + response: Optional[httpx.Response], + error: Optional[Exception], + ) -> Tuple[Optional[httpx.Response], Optional[Exception]]: + for hook in self.after_error_hooks: + result = hook.after_error(hook_ctx, response, error) + if isinstance(result, Exception): + raise result + response, error = result + return response, error diff --git a/google/genai/_gaos/_hooks/types.py b/google/genai/_gaos/_hooks/types.py new file mode 100644 index 000000000..bc32dc082 --- /dev/null +++ b/google/genai/_gaos/_hooks/types.py @@ -0,0 +1,128 @@ +# 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 ..sdkconfiguration import SDKConfiguration +from abc import ABC, abstractmethod +import httpx +from typing import Any, Callable, List, Optional, Tuple, Union + + +class HookContext: + config: SDKConfiguration + base_url: str + operation_id: str + oauth2_scopes: Optional[List[str]] = None + security_source: Optional[Union[Any, Callable[[], Any]]] = None + + def __init__( + self, + config: SDKConfiguration, + base_url: str, + operation_id: str, + oauth2_scopes: Optional[List[str]], + security_source: Optional[Union[Any, Callable[[], Any]]], + ): + self.config = config + self.base_url = base_url + self.operation_id = operation_id + self.oauth2_scopes = oauth2_scopes + self.security_source = security_source + + +class BeforeRequestContext(HookContext): + def __init__(self, hook_ctx: HookContext): + super().__init__( + hook_ctx.config, + hook_ctx.base_url, + hook_ctx.operation_id, + hook_ctx.oauth2_scopes, + hook_ctx.security_source, + ) + + +class AfterSuccessContext(HookContext): + def __init__(self, hook_ctx: HookContext): + super().__init__( + hook_ctx.config, + hook_ctx.base_url, + hook_ctx.operation_id, + hook_ctx.oauth2_scopes, + hook_ctx.security_source, + ) + + +class AfterErrorContext(HookContext): + def __init__(self, hook_ctx: HookContext): + super().__init__( + hook_ctx.config, + hook_ctx.base_url, + hook_ctx.operation_id, + hook_ctx.oauth2_scopes, + hook_ctx.security_source, + ) + + +class SDKInitHook(ABC): + @abstractmethod + def sdk_init(self, config: SDKConfiguration) -> SDKConfiguration: + pass + + +class BeforeRequestHook(ABC): + @abstractmethod + def before_request( + self, hook_ctx: BeforeRequestContext, request: httpx.Request + ) -> Union[httpx.Request, Exception]: + pass + + +class AfterSuccessHook(ABC): + @abstractmethod + def after_success( + self, hook_ctx: AfterSuccessContext, response: httpx.Response + ) -> Union[httpx.Response, Exception]: + pass + + +class AfterErrorHook(ABC): + @abstractmethod + def after_error( + self, + hook_ctx: AfterErrorContext, + response: Optional[httpx.Response], + error: Optional[Exception], + ) -> Union[Tuple[Optional[httpx.Response], Optional[Exception]], Exception]: + pass + + +class Hooks(ABC): + @abstractmethod + def register_sdk_init_hook(self, hook: SDKInitHook): + pass + + @abstractmethod + def register_before_request_hook(self, hook: BeforeRequestHook): + pass + + @abstractmethod + def register_after_success_hook(self, hook: AfterSuccessHook): + pass + + @abstractmethod + def register_after_error_hook(self, hook: AfterErrorHook): + pass diff --git a/google/genai/_gaos/_version.py b/google/genai/_gaos/_version.py new file mode 100644 index 000000000..0fb080ebc --- /dev/null +++ b/google/genai/_gaos/_version.py @@ -0,0 +1,32 @@ +# 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.""" + +import importlib.metadata + +__title__: str = "google-genai" +__version__: str = "2.4.1-preview.5" +__response_mode_header__: str = "x-google-genai-response-mode" +__openapi_doc_version__: str = "v1beta" +__gen_version__: str = "2.903.2" +__user_agent__: str = "speakeasy-sdk/python 2.4.1-preview.5 2.903.2 v1beta google-genai" + +try: + if __package__ is not None: + __version__ = importlib.metadata.version(__package__) +except importlib.metadata.PackageNotFoundError: + pass diff --git a/google/genai/_gaos/agents.py b/google/genai/_gaos/agents.py new file mode 100644 index 000000000..ee753d5be --- /dev/null +++ b/google/genai/_gaos/agents.py @@ -0,0 +1,1145 @@ +# 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 . import errors, models, types, utils +from ._hooks import HookContext +from .basesdk import AsyncBaseSDK, BaseSDK +from .types import OptionalNullable, UNSET, agents, interactions +from .types.agents import agent as agents_agent, agenttool as agents_agenttool +from .utils import get_security_from_env, response_helpers +from .utils.unmarshal_json_response import unmarshal_json_response +import httpx +from typing import Any, Iterable, List, Mapping, Optional, Union, cast + + +class Agents(BaseSDK): + @property + def with_raw_response(self): + return AgentsWithRawResponse(self) + + @property + def with_streaming_response(self): + return AgentsWithStreamingResponse(self) + + def create( + self, + *, + api_version: Optional[str] = None, + id: Optional[str] = None, + base_agent: Optional[str] = None, + system_instruction: Optional[str] = None, + description: Optional[str] = None, + tools: Optional[ + Union[ + Iterable[agents_agenttool.AgentTool], + Iterable[agents_agenttool.AgentToolParam], + ] + ] = None, + base_environment: Optional[ + Union[agents_agent.BaseEnvironment, agents_agent.BaseEnvironmentParam] + ] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> agents.Agent: + r"""Creates a new Agent (Typed version for SDK). + + :param api_version: Which version of the API to use. + :param id: The unique identifier for the agent. + :param base_agent: The base agent to extend. + :param system_instruction: System instruction for the agent. + :param description: Agent description for developers to quickly read and understand. + :param tools: The tools available to the agent. + :param base_environment: The environment configuration for the agent. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.CreateAgentRequest( + api_version=api_version, + body=agents.Agent( + id=id, + base_agent=base_agent, + system_instruction=system_instruction, + description=description, + tools=utils.get_pydantic_model(tools, Optional[List[agents.AgentTool]]), + base_environment=utils.get_pydantic_model( + base_environment, Optional[agents.BaseEnvironment] + ), + ), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="POST", + path="/{api_version}/agents", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=True, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.CreateAgentGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body, False, False, "json", agents.Agent, extra_body=extra_body + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(agents.Agent, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="CreateAgent", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def list( + self, + *, + api_version: Optional[str] = None, + parent: Optional[str] = None, + page_size: Optional[int] = None, + page_token: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> agents.AgentListResponse: + r"""Lists all Agents. + + :param api_version: Which version of the API to use. + :param parent: + :param page_size: + :param page_token: + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.ListAgentsRequest( + api_version=api_version, + parent=parent, + page_size=page_size, + page_token=page_token, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="GET", + path="/{api_version}/agents", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.ListAgentsGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(agents.AgentListResponse, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="ListAgents", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def get( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> agents.Agent: + r"""Gets a specific Agent. + + :param id: + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.GetAgentRequest( + api_version=api_version, + id=id, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="GET", + path="/{api_version}/agents/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.GetAgentGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(agents.Agent, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="GetAgent", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def delete( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Empty: + r"""Deletes an Agent. + + :param id: + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.DeleteAgentRequest( + api_version=api_version, + id=id, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="DELETE", + path="/{api_version}/agents/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.DeleteAgentGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(interactions.Empty, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="DeleteAgent", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + +class AgentsWithRawResponse: + def __init__(self, sdk: Agents) -> None: + self._sdk = sdk + self.create = response_helpers.to_raw_response_wrapper( + sdk.create, "extra_headers" + ) + self.list = response_helpers.to_raw_response_wrapper(sdk.list, "extra_headers") + self.get = response_helpers.to_raw_response_wrapper(sdk.get, "extra_headers") + self.delete = response_helpers.to_raw_response_wrapper( + sdk.delete, "extra_headers" + ) + + +class AgentsWithStreamingResponse: + def __init__(self, sdk: Agents) -> None: + self._sdk = sdk + self.create = response_helpers.to_streamed_response_wrapper( + sdk.create, "extra_headers" + ) + self.list = response_helpers.to_streamed_response_wrapper( + sdk.list, "extra_headers" + ) + self.get = response_helpers.to_streamed_response_wrapper( + sdk.get, "extra_headers" + ) + self.delete = response_helpers.to_streamed_response_wrapper( + sdk.delete, "extra_headers" + ) + + +class AsyncAgents(AsyncBaseSDK): + @property + def with_raw_response(self): + return AsyncAgentsWithRawResponse(self) + + @property + def with_streaming_response(self): + return AsyncAgentsWithStreamingResponse(self) + + async def create( + self, + *, + api_version: Optional[str] = None, + id: Optional[str] = None, + base_agent: Optional[str] = None, + system_instruction: Optional[str] = None, + description: Optional[str] = None, + tools: Optional[ + Union[ + Iterable[agents_agenttool.AgentTool], + Iterable[agents_agenttool.AgentToolParam], + ] + ] = None, + base_environment: Optional[ + Union[agents_agent.BaseEnvironment, agents_agent.BaseEnvironmentParam] + ] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> agents.Agent: + r"""Creates a new Agent (Typed version for SDK). + + :param api_version: Which version of the API to use. + :param id: The unique identifier for the agent. + :param base_agent: The base agent to extend. + :param system_instruction: System instruction for the agent. + :param description: Agent description for developers to quickly read and understand. + :param tools: The tools available to the agent. + :param base_environment: The environment configuration for the agent. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.CreateAgentRequest( + api_version=api_version, + body=agents.Agent( + id=id, + base_agent=base_agent, + system_instruction=system_instruction, + description=description, + tools=utils.get_pydantic_model(tools, Optional[List[agents.AgentTool]]), + base_environment=utils.get_pydantic_model( + base_environment, Optional[agents.BaseEnvironment] + ), + ), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="POST", + path="/{api_version}/agents", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=True, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.CreateAgentGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body, False, False, "json", agents.Agent, extra_body=extra_body + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(agents.Agent, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="CreateAgent", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def list( + self, + *, + api_version: Optional[str] = None, + parent: Optional[str] = None, + page_size: Optional[int] = None, + page_token: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> agents.AgentListResponse: + r"""Lists all Agents. + + :param api_version: Which version of the API to use. + :param parent: + :param page_size: + :param page_token: + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.ListAgentsRequest( + api_version=api_version, + parent=parent, + page_size=page_size, + page_token=page_token, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="GET", + path="/{api_version}/agents", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.ListAgentsGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(agents.AgentListResponse, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="ListAgents", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def get( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> agents.Agent: + r"""Gets a specific Agent. + + :param id: + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.GetAgentRequest( + api_version=api_version, + id=id, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="GET", + path="/{api_version}/agents/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.GetAgentGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(agents.Agent, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="GetAgent", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def delete( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Empty: + r"""Deletes an Agent. + + :param id: + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.DeleteAgentRequest( + api_version=api_version, + id=id, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="DELETE", + path="/{api_version}/agents/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.DeleteAgentGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(interactions.Empty, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="DeleteAgent", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + +class AsyncAgentsWithRawResponse: + def __init__(self, sdk: AsyncAgents) -> None: + self._sdk = sdk + self.create = response_helpers.async_to_raw_response_wrapper( + sdk.create, "extra_headers" + ) + self.list = response_helpers.async_to_raw_response_wrapper( + sdk.list, "extra_headers" + ) + self.get = response_helpers.async_to_raw_response_wrapper( + sdk.get, "extra_headers" + ) + self.delete = response_helpers.async_to_raw_response_wrapper( + sdk.delete, "extra_headers" + ) + + +class AsyncAgentsWithStreamingResponse: + def __init__(self, sdk: AsyncAgents) -> None: + self._sdk = sdk + self.create = response_helpers.async_to_streamed_response_wrapper( + sdk.create, "extra_headers" + ) + self.list = response_helpers.async_to_streamed_response_wrapper( + sdk.list, "extra_headers" + ) + self.get = response_helpers.async_to_streamed_response_wrapper( + sdk.get, "extra_headers" + ) + self.delete = response_helpers.async_to_streamed_response_wrapper( + sdk.delete, "extra_headers" + ) diff --git a/google/genai/_gaos/basesdk.py b/google/genai/_gaos/basesdk.py new file mode 100644 index 000000000..3c958fc36 --- /dev/null +++ b/google/genai/_gaos/basesdk.py @@ -0,0 +1,582 @@ +# 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 . import errors, types, utils +from ._hooks import ( + AfterErrorContext, + AfterSuccessContext, + BeforeRequestContext, + HookContext, +) +from .sdkconfiguration import SDKConfiguration +from .utils import ( + RetryConfig, + SerializedRequestBody, + get_body_content, + run_sync_in_thread, +) +import httpx +from typing import Any, Callable, List, Mapping, Optional, Tuple +from urllib.parse import parse_qs, urlparse + + +class BaseSDK: + sdk_configuration: SDKConfiguration + parent_ref: Optional[object] = None + """ + Reference to the root SDK instance, if any. This will prevent it from + being garbage collected while there are active streams. + """ + + def __init__( + self, + sdk_config: SDKConfiguration, + parent_ref: Optional[object] = None, + ) -> None: + self.sdk_configuration = sdk_config + self.parent_ref = parent_ref + + def _get_url(self, base_url, url_variables): + sdk_url, sdk_variables = self.sdk_configuration.get_server_details() + + if base_url is None: + base_url = sdk_url + + if url_variables is None: + url_variables = sdk_variables + + return utils.template_url(base_url, url_variables) + + def _coerce_timeout_ms(self, timeout: Optional[Any]) -> Optional[int]: + if timeout is None: + return None + if isinstance(timeout, (int, float)): + return int(timeout * 1000) + if isinstance(timeout, httpx.Timeout): + values = [timeout.connect, timeout.read, timeout.write, timeout.pool] + finite_values = [value for value in values if value is not None] + if not finite_values: + return None + return int(max(finite_values) * 1000) + raise TypeError("timeout must be a float, int, httpx.Timeout, or None") + + def _build_request( + self, + method, + path, + base_url, + url_variables, + request, + request_body_required, + request_has_path_params, + request_has_query_params, + user_agent_header, + accept_header_value, + _globals=None, + security=None, + timeout_ms: Optional[int] = None, + get_serialized_body: Optional[ + Callable[[], Optional[SerializedRequestBody]] + ] = None, + url_override: Optional[str] = None, + http_headers: Optional[Mapping[str, str]] = None, + extra_query_params: Optional[Mapping[str, Any]] = None, + allow_empty_value: Optional[List[str]] = None, + allowed_fields: Optional[List[str]] = None, + ) -> httpx.Request: + client = self.sdk_configuration.client + return self._build_request_with_client( + client, + method, + path, + base_url, + url_variables, + request, + request_body_required, + request_has_path_params, + request_has_query_params, + user_agent_header, + accept_header_value, + _globals, + security, + timeout_ms, + get_serialized_body, + url_override, + http_headers, + extra_query_params, + allow_empty_value, + allowed_fields, + ) + + def _build_request_with_client( + self, + client, + method, + path, + base_url, + url_variables, + request, + request_body_required, + request_has_path_params, + request_has_query_params, + user_agent_header, + accept_header_value, + _globals=None, + security=None, + timeout_ms: Optional[int] = None, + get_serialized_body: Optional[ + Callable[[], Optional[SerializedRequestBody]] + ] = None, + url_override: Optional[str] = None, + http_headers: Optional[Mapping[str, str]] = None, + extra_query_params: Optional[Mapping[str, Any]] = None, + allow_empty_value: Optional[List[str]] = None, + allowed_fields: Optional[List[str]] = None, + ) -> httpx.Request: + query_params = {} + + url = url_override + if url is None: + url = utils.generate_url( + self._get_url(base_url, url_variables), + path, + request if request_has_path_params else None, + _globals if request_has_path_params else None, + ) + + query_params = utils.get_query_params( + request if request_has_query_params else None, + _globals if request_has_query_params else None, + allow_empty_value, + ) + else: + # Pick up the query parameter from the override so they can be + # preserved when building the request later on (necessary as of + # httpx 0.28). + parsed_override = urlparse(str(url_override)) + query_params = parse_qs(parsed_override.query, keep_blank_values=True) + + headers = utils.get_headers(request, _globals) + headers["Accept"] = accept_header_value + headers[user_agent_header] = self.sdk_configuration.user_agent + + if security is not None: + if callable(security): + security = security() + security = utils.get_security_from_env(security, types.Security) + if security is not None: + security_headers, security_query_params = utils.get_security( + security, allowed_fields + ) + headers = {**headers, **security_headers} + query_params = {**query_params, **security_query_params} + if extra_query_params is not None: + for name, value in extra_query_params.items(): + if value is None: + continue + if isinstance(value, (list, tuple)): + query_params[name] = [str(v) for v in value] + else: + query_params[name] = [str(value)] + + serialized_request_body = SerializedRequestBody() + if get_serialized_body is not None: + rb = get_serialized_body() + if request_body_required and rb is None: + raise ValueError("request body is required") + + if rb is not None: + serialized_request_body = rb + + if ( + serialized_request_body.media_type is not None + and serialized_request_body.media_type + not in ( + "multipart/form-data", + "multipart/mixed", + ) + ): + headers["content-type"] = serialized_request_body.media_type + + if http_headers is not None: + for header, value in http_headers.items(): + headers[header] = value + + timeout = timeout_ms / 1000 if timeout_ms is not None else None + + return client.build_request( + method, + url, + params=query_params, + content=serialized_request_body.content, + data=serialized_request_body.data, + files=serialized_request_body.files, + headers=headers, + timeout=timeout if timeout is not None else httpx.USE_CLIENT_DEFAULT, + ) + + def do_request( + self, + hook_ctx: HookContext, + request: httpx.Request, + is_error_status_code: Callable[[int], bool], + stream: bool = False, + retry_config: Optional[Tuple[RetryConfig, List[str]]] = None, + ) -> httpx.Response: + client = self.sdk_configuration.client + logger = self.sdk_configuration.debug_logger + + hooks = self.sdk_configuration.__dict__["_hooks"] + + def do(): + http_res = None + try: + req = hooks.before_request(BeforeRequestContext(hook_ctx), request) + if "timeout" in request.extensions and "timeout" not in req.extensions: + req.extensions["timeout"] = request.extensions["timeout"] + logger.debug( + "Request:\nMethod: %s\nURL: %s\nHeaders: %s\nBody: %s", + req.method, + req.url, + req.headers, + get_body_content(req), + ) + + if client is None: + raise ValueError("client is required") + + http_res = client.send(req, stream=stream) + except Exception as e: + _, e = hooks.after_error(AfterErrorContext(hook_ctx), None, e) + if e is not None: + logger.debug("Request Exception", exc_info=True) + raise e + + if http_res is None: + logger.debug("Raising no response SDK error") + raise errors.NoResponseError("No response received") + + logger.debug( + "Response:\nStatus Code: %s\nURL: %s\nHeaders: %s\nBody: %s", + http_res.status_code, + http_res.url, + http_res.headers, + "" if stream else http_res.text, + ) + + return http_res + + if retry_config is not None: + http_res = utils.retry(do, utils.Retries(retry_config[0], retry_config[1])) + else: + http_res = do() + + if is_error_status_code(http_res.status_code): + result, err = hooks.after_error(AfterErrorContext(hook_ctx), http_res, None) + if err is not None: + logger.debug("Request Exception", exc_info=True) + raise err + if result is not None: + http_res = result + else: + logger.debug("Raising unexpected SDK error") + raise errors.GenAiDefaultError("Unexpected error occurred", http_res) + else: + http_res = hooks.after_success(AfterSuccessContext(hook_ctx), http_res) + + return http_res + + +class AsyncBaseSDK: + sdk_configuration: SDKConfiguration + parent_ref: Optional[object] = None + """ + Reference to the root SDK instance, if any. This will prevent it from + being garbage collected while there are active streams. + """ + + def __init__( + self, + sdk_config: SDKConfiguration, + parent_ref: Optional[object] = None, + ) -> None: + self.sdk_configuration = sdk_config + self.parent_ref = parent_ref + + def _get_url(self, base_url, url_variables): + sdk_url, sdk_variables = self.sdk_configuration.get_server_details() + + if base_url is None: + base_url = sdk_url + + if url_variables is None: + url_variables = sdk_variables + + return utils.template_url(base_url, url_variables) + + def _coerce_timeout_ms(self, timeout: Optional[Any]) -> Optional[int]: + if timeout is None: + return None + if isinstance(timeout, (int, float)): + return int(timeout * 1000) + if isinstance(timeout, httpx.Timeout): + values = [timeout.connect, timeout.read, timeout.write, timeout.pool] + finite_values = [value for value in values if value is not None] + if not finite_values: + return None + return int(max(finite_values) * 1000) + raise TypeError("timeout must be a float, int, httpx.Timeout, or None") + + def _build_request_async( + self, + method, + path, + base_url, + url_variables, + request, + request_body_required, + request_has_path_params, + request_has_query_params, + user_agent_header, + accept_header_value, + _globals=None, + security=None, + timeout_ms: Optional[int] = None, + get_serialized_body: Optional[ + Callable[[], Optional[SerializedRequestBody]] + ] = None, + url_override: Optional[str] = None, + http_headers: Optional[Mapping[str, str]] = None, + extra_query_params: Optional[Mapping[str, Any]] = None, + allow_empty_value: Optional[List[str]] = None, + allowed_fields: Optional[List[str]] = None, + ) -> httpx.Request: + client = self.sdk_configuration.async_client + return self._build_request_with_client( + client, + method, + path, + base_url, + url_variables, + request, + request_body_required, + request_has_path_params, + request_has_query_params, + user_agent_header, + accept_header_value, + _globals, + security, + timeout_ms, + get_serialized_body, + url_override, + http_headers, + extra_query_params, + allow_empty_value, + allowed_fields, + ) + + def _build_request_with_client( + self, + client, + method, + path, + base_url, + url_variables, + request, + request_body_required, + request_has_path_params, + request_has_query_params, + user_agent_header, + accept_header_value, + _globals=None, + security=None, + timeout_ms: Optional[int] = None, + get_serialized_body: Optional[ + Callable[[], Optional[SerializedRequestBody]] + ] = None, + url_override: Optional[str] = None, + http_headers: Optional[Mapping[str, str]] = None, + extra_query_params: Optional[Mapping[str, Any]] = None, + allow_empty_value: Optional[List[str]] = None, + allowed_fields: Optional[List[str]] = None, + ) -> httpx.Request: + query_params = {} + + url = url_override + if url is None: + url = utils.generate_url( + self._get_url(base_url, url_variables), + path, + request if request_has_path_params else None, + _globals if request_has_path_params else None, + ) + + query_params = utils.get_query_params( + request if request_has_query_params else None, + _globals if request_has_query_params else None, + allow_empty_value, + ) + else: + # Pick up the query parameter from the override so they can be + # preserved when building the request later on (necessary as of + # httpx 0.28). + parsed_override = urlparse(str(url_override)) + query_params = parse_qs(parsed_override.query, keep_blank_values=True) + + headers = utils.get_headers(request, _globals) + headers["Accept"] = accept_header_value + headers[user_agent_header] = self.sdk_configuration.user_agent + + if security is not None: + if callable(security): + security = security() + security = utils.get_security_from_env(security, types.Security) + if security is not None: + security_headers, security_query_params = utils.get_security( + security, allowed_fields + ) + headers = {**headers, **security_headers} + query_params = {**query_params, **security_query_params} + if extra_query_params is not None: + for name, value in extra_query_params.items(): + if value is None: + continue + if isinstance(value, (list, tuple)): + query_params[name] = [str(v) for v in value] + else: + query_params[name] = [str(value)] + + serialized_request_body = SerializedRequestBody() + if get_serialized_body is not None: + rb = get_serialized_body() + if request_body_required and rb is None: + raise ValueError("request body is required") + + if rb is not None: + serialized_request_body = rb + + if ( + serialized_request_body.media_type is not None + and serialized_request_body.media_type + not in ( + "multipart/form-data", + "multipart/mixed", + ) + ): + headers["content-type"] = serialized_request_body.media_type + + if http_headers is not None: + for header, value in http_headers.items(): + headers[header] = value + + timeout = timeout_ms / 1000 if timeout_ms is not None else None + + return client.build_request( + method, + url, + params=query_params, + content=serialized_request_body.content, + data=serialized_request_body.data, + files=serialized_request_body.files, + headers=headers, + timeout=timeout if timeout is not None else httpx.USE_CLIENT_DEFAULT, + ) + + async def do_request_async( + self, + hook_ctx: HookContext, + request: httpx.Request, + is_error_status_code: Callable[[int], bool], + stream: bool = False, + retry_config: Optional[Tuple[RetryConfig, List[str]]] = None, + ) -> httpx.Response: + client = self.sdk_configuration.async_client + logger = self.sdk_configuration.debug_logger + + hooks = self.sdk_configuration.__dict__["_hooks"] + + async def do(): + http_res = None + try: + req = await run_sync_in_thread( + hooks.before_request, BeforeRequestContext(hook_ctx), request + ) + + if "timeout" in request.extensions and "timeout" not in req.extensions: + req.extensions["timeout"] = request.extensions["timeout"] + logger.debug( + "Request:\nMethod: %s\nURL: %s\nHeaders: %s\nBody: %s", + req.method, + req.url, + req.headers, + get_body_content(req), + ) + + if client is None: + raise ValueError("client is required") + + http_res = await client.send(req, stream=stream) + except Exception as e: + _, e = await run_sync_in_thread( + hooks.after_error, AfterErrorContext(hook_ctx), None, e + ) + + if e is not None: + logger.debug("Request Exception", exc_info=True) + raise e + + if http_res is None: + logger.debug("Raising no response SDK error") + raise errors.NoResponseError("No response received") + + logger.debug( + "Response:\nStatus Code: %s\nURL: %s\nHeaders: %s\nBody: %s", + http_res.status_code, + http_res.url, + http_res.headers, + "" if stream else http_res.text, + ) + + return http_res + + if retry_config is not None: + http_res = await utils.retry_async( + do, utils.Retries(retry_config[0], retry_config[1]) + ) + else: + http_res = await do() + + if is_error_status_code(http_res.status_code): + result, err = await run_sync_in_thread( + hooks.after_error, AfterErrorContext(hook_ctx), http_res, None + ) + + if err is not None: + logger.debug("Request Exception", exc_info=True) + raise err + if result is not None: + http_res = result + else: + logger.debug("Raising unexpected SDK error") + raise errors.GenAiDefaultError("Unexpected error occurred", http_res) + else: + http_res = await run_sync_in_thread( + hooks.after_success, AfterSuccessContext(hook_ctx), http_res + ) + + return http_res diff --git a/google/genai/_gaos/errors/__init__.py b/google/genai/_gaos/errors/__init__.py new file mode 100644 index 000000000..071907851 --- /dev/null +++ b/google/genai/_gaos/errors/__init__.py @@ -0,0 +1,106 @@ +# 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 .genaierror import GenAiError +from typing import Any, TYPE_CHECKING + +from ..utils.dynamic_imports import lazy_getattr, lazy_dir + +if TYPE_CHECKING: + from .cancelinteractionbyid import ( + CancelInteractionByIDClientError, + CancelInteractionByIDClientErrorData, + CancelInteractionByIDServerError, + CancelInteractionByIDServerErrorData, + ) + from .createinteraction import ( + CreateInteractionClientError, + CreateInteractionClientErrorData, + CreateInteractionServerError, + CreateInteractionServerErrorData, + ) + from .deleteinteraction import ( + DeleteInteractionClientError, + DeleteInteractionClientErrorData, + DeleteInteractionServerError, + DeleteInteractionServerErrorData, + ) + from .genaidefaulterror import GenAiDefaultError + from .getinteractionbyid import ( + GetInteractionByIDClientError, + GetInteractionByIDClientErrorData, + GetInteractionByIDServerError, + GetInteractionByIDServerErrorData, + ) + from .no_response_error import NoResponseError + from .responsevalidationerror import ResponseValidationError + +__all__ = [ + "CancelInteractionByIDClientError", + "CancelInteractionByIDClientErrorData", + "CancelInteractionByIDServerError", + "CancelInteractionByIDServerErrorData", + "CreateInteractionClientError", + "CreateInteractionClientErrorData", + "CreateInteractionServerError", + "CreateInteractionServerErrorData", + "DeleteInteractionClientError", + "DeleteInteractionClientErrorData", + "DeleteInteractionServerError", + "DeleteInteractionServerErrorData", + "GenAiDefaultError", + "GenAiError", + "GetInteractionByIDClientError", + "GetInteractionByIDClientErrorData", + "GetInteractionByIDServerError", + "GetInteractionByIDServerErrorData", + "NoResponseError", + "ResponseValidationError", +] + +_dynamic_imports: dict[str, str] = { + "CancelInteractionByIDClientError": ".cancelinteractionbyid", + "CancelInteractionByIDClientErrorData": ".cancelinteractionbyid", + "CancelInteractionByIDServerError": ".cancelinteractionbyid", + "CancelInteractionByIDServerErrorData": ".cancelinteractionbyid", + "CreateInteractionClientError": ".createinteraction", + "CreateInteractionClientErrorData": ".createinteraction", + "CreateInteractionServerError": ".createinteraction", + "CreateInteractionServerErrorData": ".createinteraction", + "DeleteInteractionClientError": ".deleteinteraction", + "DeleteInteractionClientErrorData": ".deleteinteraction", + "DeleteInteractionServerError": ".deleteinteraction", + "DeleteInteractionServerErrorData": ".deleteinteraction", + "GenAiDefaultError": ".genaidefaulterror", + "GetInteractionByIDClientError": ".getinteractionbyid", + "GetInteractionByIDClientErrorData": ".getinteractionbyid", + "GetInteractionByIDServerError": ".getinteractionbyid", + "GetInteractionByIDServerErrorData": ".getinteractionbyid", + "NoResponseError": ".no_response_error", + "ResponseValidationError": ".responsevalidationerror", +} + + +def __getattr__(attr_name: str) -> Any: + return lazy_getattr( + attr_name, package=__package__, dynamic_imports=_dynamic_imports + ) + + +def __dir__(): + return lazy_dir(dynamic_imports=_dynamic_imports) diff --git a/google/genai/_gaos/errors/cancelinteractionbyid.py b/google/genai/_gaos/errors/cancelinteractionbyid.py new file mode 100644 index 000000000..3ebea6d31 --- /dev/null +++ b/google/genai/_gaos/errors/cancelinteractionbyid.py @@ -0,0 +1,71 @@ +# 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 __future__ import annotations +from . import GenAiError +from ..types import BaseModel +from ..types.interactions import error as interactions_error +from dataclasses import dataclass, field +import httpx +from typing import Optional + + +class CancelInteractionByIDServerErrorData(BaseModel): + error: interactions_error.Error + r"""Error message from an interaction.""" + + +@dataclass(unsafe_hash=True) +class CancelInteractionByIDServerError(GenAiError): + r"""Error cancelling interaction""" + + data: CancelInteractionByIDServerErrorData = field(hash=False) + + def __init__( + self, + data: CancelInteractionByIDServerErrorData, + raw_response: httpx.Response, + body: Optional[str] = None, + ): + fallback = body or raw_response.text + message = str(data.error.message) or fallback + super().__init__(message, raw_response, body) + object.__setattr__(self, "data", data) + + +class CancelInteractionByIDClientErrorData(BaseModel): + error: interactions_error.Error + r"""Error message from an interaction.""" + + +@dataclass(unsafe_hash=True) +class CancelInteractionByIDClientError(GenAiError): + r"""Error cancelling interaction""" + + data: CancelInteractionByIDClientErrorData = field(hash=False) + + def __init__( + self, + data: CancelInteractionByIDClientErrorData, + raw_response: httpx.Response, + body: Optional[str] = None, + ): + fallback = body or raw_response.text + message = str(data.error.message) or fallback + super().__init__(message, raw_response, body) + object.__setattr__(self, "data", data) diff --git a/google/genai/_gaos/errors/createinteraction.py b/google/genai/_gaos/errors/createinteraction.py new file mode 100644 index 000000000..8e8332393 --- /dev/null +++ b/google/genai/_gaos/errors/createinteraction.py @@ -0,0 +1,71 @@ +# 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 __future__ import annotations +from . import GenAiError +from ..types import BaseModel +from ..types.interactions import error as interactions_error +from dataclasses import dataclass, field +import httpx +from typing import Optional + + +class CreateInteractionServerErrorData(BaseModel): + error: interactions_error.Error + r"""Error message from an interaction.""" + + +@dataclass(unsafe_hash=True) +class CreateInteractionServerError(GenAiError): + r"""Error creating interaction""" + + data: CreateInteractionServerErrorData = field(hash=False) + + def __init__( + self, + data: CreateInteractionServerErrorData, + raw_response: httpx.Response, + body: Optional[str] = None, + ): + fallback = body or raw_response.text + message = str(data.error.message) or fallback + super().__init__(message, raw_response, body) + object.__setattr__(self, "data", data) + + +class CreateInteractionClientErrorData(BaseModel): + error: interactions_error.Error + r"""Error message from an interaction.""" + + +@dataclass(unsafe_hash=True) +class CreateInteractionClientError(GenAiError): + r"""Error creating interaction""" + + data: CreateInteractionClientErrorData = field(hash=False) + + def __init__( + self, + data: CreateInteractionClientErrorData, + raw_response: httpx.Response, + body: Optional[str] = None, + ): + fallback = body or raw_response.text + message = str(data.error.message) or fallback + super().__init__(message, raw_response, body) + object.__setattr__(self, "data", data) diff --git a/google/genai/_gaos/errors/deleteinteraction.py b/google/genai/_gaos/errors/deleteinteraction.py new file mode 100644 index 000000000..4ae8ff328 --- /dev/null +++ b/google/genai/_gaos/errors/deleteinteraction.py @@ -0,0 +1,71 @@ +# 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 __future__ import annotations +from . import GenAiError +from ..types import BaseModel +from ..types.interactions import error as interactions_error +from dataclasses import dataclass, field +import httpx +from typing import Optional + + +class DeleteInteractionServerErrorData(BaseModel): + error: interactions_error.Error + r"""Error message from an interaction.""" + + +@dataclass(unsafe_hash=True) +class DeleteInteractionServerError(GenAiError): + r"""Error deleting interaction""" + + data: DeleteInteractionServerErrorData = field(hash=False) + + def __init__( + self, + data: DeleteInteractionServerErrorData, + raw_response: httpx.Response, + body: Optional[str] = None, + ): + fallback = body or raw_response.text + message = str(data.error.message) or fallback + super().__init__(message, raw_response, body) + object.__setattr__(self, "data", data) + + +class DeleteInteractionClientErrorData(BaseModel): + error: interactions_error.Error + r"""Error message from an interaction.""" + + +@dataclass(unsafe_hash=True) +class DeleteInteractionClientError(GenAiError): + r"""Error deleting interaction""" + + data: DeleteInteractionClientErrorData = field(hash=False) + + def __init__( + self, + data: DeleteInteractionClientErrorData, + raw_response: httpx.Response, + body: Optional[str] = None, + ): + fallback = body or raw_response.text + message = str(data.error.message) or fallback + super().__init__(message, raw_response, body) + object.__setattr__(self, "data", data) diff --git a/google/genai/_gaos/errors/genaidefaulterror.py b/google/genai/_gaos/errors/genaidefaulterror.py new file mode 100644 index 000000000..1dbbc6adc --- /dev/null +++ b/google/genai/_gaos/errors/genaidefaulterror.py @@ -0,0 +1,56 @@ +# 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.""" + +import httpx +from typing import Optional +from dataclasses import dataclass + +from . import GenAiError + +MAX_MESSAGE_LEN = 10_000 + + +@dataclass(unsafe_hash=True) +class GenAiDefaultError(GenAiError): + """The fallback error class if no more specific error class is matched.""" + + def __init__( + self, message: str, raw_response: httpx.Response, body: Optional[str] = None + ): + body_display = body or raw_response.text or '""' + + if message: + message += ": " + message += f"Status {raw_response.status_code}" + + headers = raw_response.headers + content_type = headers.get("content-type", '""') + if content_type != "application/json": + if " " in content_type: + content_type = f'"{content_type}"' + message += f" Content-Type {content_type}" + + if len(body_display) > MAX_MESSAGE_LEN: + truncated = body_display[:MAX_MESSAGE_LEN] + remaining = len(body_display) - MAX_MESSAGE_LEN + body_display = f"{truncated}...and {remaining} more chars" + + message += f". Body: {body_display}" + message = message.strip() + + super().__init__(message, raw_response, body) diff --git a/google/genai/_gaos/errors/genaierror.py b/google/genai/_gaos/errors/genaierror.py new file mode 100644 index 000000000..e3f6ef7e7 --- /dev/null +++ b/google/genai/_gaos/errors/genaierror.py @@ -0,0 +1,46 @@ +# 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.""" + +import httpx +from typing import Optional +from dataclasses import dataclass, field + + +@dataclass(unsafe_hash=True) +class GenAiError(Exception): + """The base class for all HTTP error responses.""" + + message: str + status_code: int + body: str + headers: httpx.Headers = field(hash=False) + raw_response: httpx.Response = field(hash=False) + + def __init__( + self, message: str, raw_response: httpx.Response, body: Optional[str] = None + ): + object.__setattr__(self, "message", message) + object.__setattr__(self, "status_code", raw_response.status_code) + object.__setattr__( + self, "body", body if body is not None else raw_response.text + ) + object.__setattr__(self, "headers", raw_response.headers) + object.__setattr__(self, "raw_response", raw_response) + + def __str__(self): + return self.message diff --git a/google/genai/_gaos/errors/getinteractionbyid.py b/google/genai/_gaos/errors/getinteractionbyid.py new file mode 100644 index 000000000..72b48a921 --- /dev/null +++ b/google/genai/_gaos/errors/getinteractionbyid.py @@ -0,0 +1,71 @@ +# 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 __future__ import annotations +from . import GenAiError +from ..types import BaseModel +from ..types.interactions import error as interactions_error +from dataclasses import dataclass, field +import httpx +from typing import Optional + + +class GetInteractionByIDServerErrorData(BaseModel): + error: interactions_error.Error + r"""Error message from an interaction.""" + + +@dataclass(unsafe_hash=True) +class GetInteractionByIDServerError(GenAiError): + r"""Error getting interaction""" + + data: GetInteractionByIDServerErrorData = field(hash=False) + + def __init__( + self, + data: GetInteractionByIDServerErrorData, + raw_response: httpx.Response, + body: Optional[str] = None, + ): + fallback = body or raw_response.text + message = str(data.error.message) or fallback + super().__init__(message, raw_response, body) + object.__setattr__(self, "data", data) + + +class GetInteractionByIDClientErrorData(BaseModel): + error: interactions_error.Error + r"""Error message from an interaction.""" + + +@dataclass(unsafe_hash=True) +class GetInteractionByIDClientError(GenAiError): + r"""Error getting interaction""" + + data: GetInteractionByIDClientErrorData = field(hash=False) + + def __init__( + self, + data: GetInteractionByIDClientErrorData, + raw_response: httpx.Response, + body: Optional[str] = None, + ): + fallback = body or raw_response.text + message = str(data.error.message) or fallback + super().__init__(message, raw_response, body) + object.__setattr__(self, "data", data) diff --git a/google/genai/_gaos/errors/no_response_error.py b/google/genai/_gaos/errors/no_response_error.py new file mode 100644 index 000000000..baecb569b --- /dev/null +++ b/google/genai/_gaos/errors/no_response_error.py @@ -0,0 +1,33 @@ +# 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 dataclasses import dataclass + + +@dataclass(unsafe_hash=True) +class NoResponseError(Exception): + """Error raised when no HTTP response is received from the server.""" + + message: str + + def __init__(self, message: str = "No response received"): + object.__setattr__(self, "message", message) + super().__init__(message) + + def __str__(self): + return self.message diff --git a/google/genai/_gaos/errors/responsevalidationerror.py b/google/genai/_gaos/errors/responsevalidationerror.py new file mode 100644 index 000000000..62fa3320e --- /dev/null +++ b/google/genai/_gaos/errors/responsevalidationerror.py @@ -0,0 +1,43 @@ +# 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.""" + +import httpx +from typing import Optional +from dataclasses import dataclass + +from . import GenAiError + + +@dataclass(unsafe_hash=True) +class ResponseValidationError(GenAiError): + """Error raised when there is a type mismatch between the response data and the expected Pydantic model.""" + + def __init__( + self, + message: str, + raw_response: httpx.Response, + cause: Exception, + body: Optional[str] = None, + ): + message = f"{message}: {cause}" + super().__init__(message, raw_response, body) + + @property + def cause(self): + """Normally the Pydantic ValidationError""" + return self.__cause__ diff --git a/google/genai/_gaos/google_genai.py b/google/genai/_gaos/google_genai.py new file mode 100644 index 000000000..c0fc51276 --- /dev/null +++ b/google/genai/_gaos/google_genai.py @@ -0,0 +1,520 @@ +# 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. +# + +"""Helpers for embedding the generated NextGen SDK in the Google GenAI client.""" + +# This bridge intentionally reuses the parent GenAI client's protected transport, +# auth, and http option internals so the NextGen resource client behaves like +# the public google-genai resource it replaces. +# pylint: disable=protected-access,too-many-arguments + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Mapping, Optional, TypeVar, Union, cast + +import httpx + +from ._hooks.google_genai_auth import ( + GOOGLE_GENAI_API_REVISION as _GOOGLE_GENAI_API_REVISION, + GoogleGenAISecurityProvider, +) +from .interactions import AsyncInteractions as GeneratedAsyncInteractions +from .interactions import Interactions as GeneratedInteractions +from .sdk import AsyncGenAI, GenAI +from .types import interactions +from .types.security import Security +from .utils import eventstreaming + + +GOOGLE_GENAI_API_REVISION = _GOOGLE_GENAI_API_REVISION +_LEGACY_LYRIA_MODELS = frozenset({'lyria-3-pro-preview', 'lyria-3-clip-preview'}) +ModelT = TypeVar('ModelT') + + +def get_google_genai_server_url(api_client: Any) -> str: + """Returns the server URL from the parent Google GenAI API client.""" + server_url = api_client._http_options.base_url + if not server_url: + raise ValueError('Base URL must be set.') + return str(server_url).rstrip('/') + + +def get_google_genai_api_version(api_client: Any) -> str: + """Returns the generated path parameter for the parent client mode.""" + api_version = api_client._http_options.api_version or '' + if api_client.vertexai and api_client.project and api_client.location: + return ( + f'{api_version}/projects/{api_client.project}' + f'/locations/{api_client.location}' + ) + return api_version + + +def _get_google_genai_default_headers( + api_client: Any, +) -> Optional[dict[str, str]]: + headers = api_client._http_options.headers + return dict(headers) if headers else None + + +def _get_google_genai_user_project(api_client: Any) -> Optional[str]: + credentials = getattr(api_client, '_credentials', None) + return getattr(credentials, 'quota_project_id', None) + + +class _GoogleGenAIAccessTokenProvider: + def __init__(self, api_client: Any) -> None: + self._api_client = api_client + + def __call__(self) -> str: + return self._api_client._access_token() + + +def _get_google_genai_security(api_client: Any) -> Optional[Any]: + default_headers = _get_google_genai_default_headers(api_client) + + if api_client.api_key: + return Security( + api_key=api_client.api_key, + default_headers=default_headers, + ) + + if api_client.vertexai and (api_client.project or api_client.location): + return GoogleGenAISecurityProvider( + access_token=_GoogleGenAIAccessTokenProvider(api_client), + default_headers=default_headers, + ) + + if default_headers: + return Security(default_headers=default_headers) + + return None + + +def build_google_genai_client( + api_client: Any, api_version: Optional[str] = None +) -> GenAI: + """Builds a generated NextGen client from the parent GenAI client.""" + http_options = api_client._http_options + sdk = GenAI( + security=_get_google_genai_security(api_client), + api_version=api_version or get_google_genai_api_version(api_client), + user_project=_get_google_genai_user_project(api_client), + server_url=get_google_genai_server_url(api_client), + client=getattr(api_client, '_httpx_client', None), + timeout_ms=http_options.timeout, + ) + return sdk + + +def build_google_genai_async_client( + api_client: Any, api_version: Optional[str] = None +) -> AsyncGenAI: + """Builds an async generated NextGen client from the parent GenAI client.""" + http_options = api_client._http_options + sdk = AsyncGenAI( + security=_get_google_genai_security(api_client), + api_version=api_version or get_google_genai_api_version(api_client), + user_project=_get_google_genai_user_project(api_client), + server_url=get_google_genai_server_url(api_client), + async_client=getattr(api_client, '_async_httpx_client', None), + timeout_ms=http_options.timeout, + ) + return sdk + + +class GeminiNextGenInteractions(GeneratedInteractions): + """Public interactions resource backed by the NextGen client. + + Subclasses the generated resource so newly generated methods (and the + raw/streaming response wrappers) are exposed automatically. Only the + methods that need legacy input/output normalization are overridden. + """ + + def __init__(self, api_client: Any): + sdk = build_google_genai_client(api_client) + super().__init__(sdk.sdk_configuration, parent_ref=sdk) + + + # Runtime-only overrides: type checkers see the inherited generated + # signatures (full overload sets) instead of hand-maintained stubs. + if not TYPE_CHECKING: + def create( + self, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + **body: Any, + ) -> Union[ + interactions.Interaction, + eventstreaming.Stream[interactions.InteractionSSEEvent], + ]: + stream = _optional_bool(body.get('stream'), default=False) + body = _normalize_create_body(body) + response = super().create( + api_version=api_version, + **cast(Any, body), + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + if stream: + return response + return _add_output_properties_if_interaction(response) + + + # Runtime-only overrides: type checkers see the inherited generated + # signatures (full overload sets) instead of hand-maintained stubs. + if not TYPE_CHECKING: + def get( + self, + id: str, + *, + api_version: Optional[str] = None, + include_input: Any = None, + last_event_id: Any = None, + stream: Any = False, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Union[ + interactions.Interaction, + eventstreaming.Stream[interactions.InteractionSSEEvent], + ]: + stream_bool = bool(_optional_bool(stream, default=False)) + response = super().get( + id=id, + api_version=api_version, + include_input=_optional_bool(include_input), + last_event_id=_optional_str(last_event_id), + stream=stream_bool, + extra_headers=extra_headers, + extra_query=extra_query, + timeout=timeout, + ) + if stream_bool: + return cast(eventstreaming.Stream[interactions.InteractionSSEEvent], response) + return cast( + interactions.Interaction, _add_output_properties_if_interaction(response) + ) + + # Runtime-only overrides: type checkers see the inherited generated + # signatures (full overload sets) instead of hand-maintained stubs. + if not TYPE_CHECKING: + def cancel( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Interaction: + return cast( + interactions.Interaction, + _add_output_properties_if_interaction( + super().cancel( + id=id, + api_version=api_version, + extra_headers=extra_headers, + extra_query=extra_query, + timeout=timeout, + ) + ), + ) + + +class AsyncGeminiNextGenInteractions(GeneratedAsyncInteractions): + """Async public interactions resource backed by the NextGen client. + + Subclasses the generated resource so newly generated methods (and the + raw/streaming response wrappers) are exposed automatically. Only the + methods that need legacy input/output normalization are overridden. + """ + + def __init__(self, api_client: Any): + sdk = build_google_genai_async_client(api_client) + super().__init__(sdk.sdk_configuration, parent_ref=sdk) + + + # Runtime-only overrides: type checkers see the inherited generated + # signatures (full overload sets) instead of hand-maintained stubs. + if not TYPE_CHECKING: + async def create( + self, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + **body: Any, + ) -> Union[ + interactions.Interaction, + eventstreaming.AsyncStream[interactions.InteractionSSEEvent], + ]: + stream = _optional_bool(body.get('stream'), default=False) + body = _normalize_create_body(body) + response = await super().create( + api_version=api_version, + **cast(Any, body), + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + if stream: + return response + return _add_output_properties_if_interaction(response) + + + # Runtime-only overrides: type checkers see the inherited generated + # signatures (full overload sets) instead of hand-maintained stubs. + if not TYPE_CHECKING: + async def get( + self, + id: str, + *, + api_version: Optional[str] = None, + include_input: Any = None, + last_event_id: Any = None, + stream: Any = False, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Union[ + interactions.Interaction, + eventstreaming.AsyncStream[interactions.InteractionSSEEvent], + ]: + stream_bool = bool(_optional_bool(stream, default=False)) + response = await super().get( + id=id, + api_version=api_version, + include_input=_optional_bool(include_input), + last_event_id=_optional_str(last_event_id), + stream=stream_bool, + extra_headers=extra_headers, + extra_query=extra_query, + timeout=timeout, + ) + if stream_bool: + return cast(eventstreaming.AsyncStream[interactions.InteractionSSEEvent], response) + return cast( + interactions.Interaction, _add_output_properties_if_interaction(response) + ) + + # Runtime-only overrides: type checkers see the inherited generated + # signatures (full overload sets) instead of hand-maintained stubs. + if not TYPE_CHECKING: + async def cancel( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Interaction: + return cast( + interactions.Interaction, + _add_output_properties_if_interaction( + await super().cancel( + id=id, + api_version=api_version, + extra_headers=extra_headers, + extra_query=extra_query, + timeout=timeout, + ) + ), + ) + + +def _add_output_properties_if_interaction(value: Any) -> Any: + normalized = _normalize_interaction_shape(value) + if normalized is None: + return value + + steps = _get_value(normalized, 'steps') + updates = _output_properties_from_steps(steps) + if not updates: + return value + + if hasattr(normalized, 'model_copy'): + return normalized.model_copy(update=updates) + if isinstance(normalized, dict): + return {**normalized, **updates} + + for name, output_value in updates.items(): + setattr(normalized, name, output_value) + return normalized + + +def _normalize_interaction_shape(value: Any) -> Optional[Any]: + steps = _get_value(value, 'steps') + if isinstance(steps, list): + return value + + model = _get_value(value, 'model') + if not isinstance(model, str) or model not in _LEGACY_LYRIA_MODELS: + return None + + outputs = _get_value(value, 'outputs') + if not isinstance(outputs, list): + outputs = [] + + steps = [{'type': 'model_output', 'content': outputs}] if outputs else [] + if hasattr(value, 'model_copy'): + return value.model_copy(update={'steps': steps, 'outputs': None}) + if isinstance(value, dict): + normalized = {**value, 'steps': steps} + normalized.pop('outputs', None) + return normalized + + setattr(value, 'steps', steps) + if hasattr(value, 'outputs'): + setattr(value, 'outputs', None) + return value + + +def _output_properties_from_steps(steps: list[Any]) -> dict[str, Any]: + text_parts: list[str] = [] + collecting = False + + for step in reversed(steps): + if _get_value(step, 'type') == 'user_input': + break + if _get_value(step, 'type') != 'model_output': + if collecting: + break + continue + + content = _get_value(step, 'content') + if not isinstance(content, list): + if collecting: + break + continue + + should_stop = False + for item in reversed(content): + if _get_value(item, 'type') == 'text': + collecting = True + text = _get_value(item, 'text') + text_parts.append(text if isinstance(text, str) else '') + elif collecting: + should_stop = True + break + if should_stop: + break + + updates: dict[str, Any] = {} + output_text = ''.join(reversed(text_parts)) + if output_text: + updates['output_text'] = output_text + + output_image = None + output_audio = None + output_video = None + + for step in reversed(steps): + if _get_value(step, 'type') == 'user_input': + break + if _get_value(step, 'type') != 'model_output': + continue + + content = _get_value(step, 'content') + if not isinstance(content, list): + continue + + for item in reversed(content): + content_type = _get_value(item, 'type') + if content_type == 'image' and output_image is None: + output_image = item + if content_type == 'audio' and output_audio is None: + output_audio = item + if content_type == 'video' and output_video is None: + output_video = item + + if output_image is not None: + updates['output_image'] = output_image + if output_audio is not None: + updates['output_audio'] = output_audio + if output_video is not None: + updates['output_video'] = output_video + + return updates + + +def _get_value(value: Any, name: str) -> Any: + if isinstance(value, dict): + return value.get(name) + return getattr(value, name, None) + + +def _normalize_create_body(body: dict[str, Any]) -> dict[str, Any]: + input_value = body.get('input') + if not _is_content_list(input_value): + return body + + return {**body, 'input': [{'type': 'user_input', 'content': input_value}]} + + +def _is_content_list(value: Any) -> bool: + return ( + isinstance(value, list) + and bool(value) + and all(_is_content_block(item) for item in value) + ) + + +def _is_content_block(value: Any) -> bool: + return isinstance(value, dict) and not _is_step_block(value) + + +def _is_step_block(value: dict[str, Any]) -> bool: + return value.get('type') in { + 'user_input', + 'model_output', + 'thought', + 'function_call', + 'code_execution_call', + 'url_context_call', + 'mcp_server_tool_call', + 'google_search_call', + 'file_search_call', + 'google_maps_call', + 'function_result', + 'code_execution_result', + 'url_context_result', + 'google_search_result', + 'mcp_server_tool_result', + 'file_search_result', + 'google_maps_result', + } + + +def _optional_bool(value: Any, default: Optional[bool] = None) -> Optional[bool]: + if isinstance(value, bool): + return value + return default + + +def _optional_str(value: Any) -> Optional[str]: + if isinstance(value, str): + return value + return None diff --git a/google/genai/_gaos/httpclient.py b/google/genai/_gaos/httpclient.py new file mode 100644 index 000000000..5a04259e1 --- /dev/null +++ b/google/genai/_gaos/httpclient.py @@ -0,0 +1,151 @@ +# 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.""" + +# pyright: reportReturnType = false +import asyncio +from typing_extensions import Protocol, runtime_checkable +import httpx +from typing import Any, Optional, Union + + +@runtime_checkable +class HttpClient(Protocol): + def send( + self, + request: httpx.Request, + *, + stream: bool = False, + auth: Union[ + httpx._types.AuthTypes, httpx._client.UseClientDefault, None + ] = httpx.USE_CLIENT_DEFAULT, + follow_redirects: Union[ + bool, httpx._client.UseClientDefault + ] = httpx.USE_CLIENT_DEFAULT, + ) -> httpx.Response: + pass + + def build_request( + self, + method: str, + url: httpx._types.URLTypes, + *, + content: Optional[httpx._types.RequestContent] = None, + data: Optional[httpx._types.RequestData] = None, + files: Optional[httpx._types.RequestFiles] = None, + json: Optional[Any] = None, + params: Optional[httpx._types.QueryParamTypes] = None, + headers: Optional[httpx._types.HeaderTypes] = None, + cookies: Optional[httpx._types.CookieTypes] = None, + timeout: Union[ + httpx._types.TimeoutTypes, httpx._client.UseClientDefault + ] = httpx.USE_CLIENT_DEFAULT, + extensions: Optional[httpx._types.RequestExtensions] = None, + ) -> httpx.Request: + pass + + def close(self) -> None: + pass + + +@runtime_checkable +class AsyncHttpClient(Protocol): + async def send( + self, + request: httpx.Request, + *, + stream: bool = False, + auth: Union[ + httpx._types.AuthTypes, httpx._client.UseClientDefault, None + ] = httpx.USE_CLIENT_DEFAULT, + follow_redirects: Union[ + bool, httpx._client.UseClientDefault + ] = httpx.USE_CLIENT_DEFAULT, + ) -> httpx.Response: + pass + + def build_request( + self, + method: str, + url: httpx._types.URLTypes, + *, + content: Optional[httpx._types.RequestContent] = None, + data: Optional[httpx._types.RequestData] = None, + files: Optional[httpx._types.RequestFiles] = None, + json: Optional[Any] = None, + params: Optional[httpx._types.QueryParamTypes] = None, + headers: Optional[httpx._types.HeaderTypes] = None, + cookies: Optional[httpx._types.CookieTypes] = None, + timeout: Union[ + httpx._types.TimeoutTypes, httpx._client.UseClientDefault + ] = httpx.USE_CLIENT_DEFAULT, + extensions: Optional[httpx._types.RequestExtensions] = None, + ) -> httpx.Request: + pass + + async def aclose(self) -> None: + pass + + +class ClientOwner(Protocol): + client: Union[HttpClient, None] + async_client: Union[AsyncHttpClient, None] + + +def close_clients( + owner: ClientOwner, sync_client: Union[HttpClient, None], sync_client_supplied: bool +) -> None: + """ + A finalizer function that is meant to be used with weakref.finalize to close + httpx clients used by an SDK so that underlying resources can be garbage + collected. + """ + + # Unset the client/async_client properties so there are no more references + # to them from the owning SDK instance and they can be reaped. + owner.client = None + if sync_client is not None and not sync_client_supplied: + try: + sync_client.close() + except Exception: + pass + + +def close_clients_async( + owner: ClientOwner, + async_client: Union[AsyncHttpClient, None], + async_client_supplied: bool, +) -> None: + """ + A finalizer function that is meant to be used with weakref.finalize to close + httpx clients used by an SDK so that underlying resources can be garbage + collected. + """ + + # Unset the client/async_client properties so there are no more references + # to them from the owning SDK instance and they can be reaped. + owner.async_client = None + if async_client is not None and not async_client_supplied: + try: + loop = asyncio.get_running_loop() + asyncio.run_coroutine_threadsafe(async_client.aclose(), loop) + except RuntimeError: + try: + asyncio.run(async_client.aclose()) + except RuntimeError: + # best effort + pass diff --git a/google/genai/_gaos/interactions.py b/google/genai/_gaos/interactions.py new file mode 100644 index 000000000..f1d820dd3 --- /dev/null +++ b/google/genai/_gaos/interactions.py @@ -0,0 +1,2053 @@ +# 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 . import errors, models, types, utils +from ._hooks import HookContext +from .basesdk import AsyncBaseSDK, BaseSDK +from .types import BaseModel, OptionalNullable, UNSET, interactions +from .utils import get_security_from_env, response_helpers +from .utils.eventstreaming import AsyncStream, Stream +from .utils.unmarshal_json_response import unmarshal_json_response +import httpx +from typing import Any, List, Literal, Mapping, Optional, Union, cast, overload + + +class Interactions(BaseSDK): + @property + def with_raw_response(self): + return InteractionsWithRawResponse(self) + + @property + def with_streaming_response(self): + return InteractionsWithStreamingResponse(self) + + @overload + def create( + self, + *, + request: Union[ + models.CreateInteractionRequest, models.CreateInteractionRequestParam + ], + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Union[interactions.Interaction, Stream[interactions.InteractionSSEEvent]]: + r"""Creating an interaction + + Creates a new interaction. + + :param body: The request body. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + def create( + self, + *, + api_version: Optional[str] = None, + model: interactions.Model, + background: bool = ..., + store: bool = ..., + stream: Union[Literal[False], None] = None, + environment: interactions.CreateModelInteractionEnvironmentParam = ..., + previous_interaction_id: str = ..., + response_mime_type: str = ..., + response_modalities: List[interactions.ResponseModality] = ..., + service_tier: interactions.ServiceTier = ..., + system_instruction: str = ..., + tools: List[interactions.ToolParam] = ..., + usage: interactions.UsageParam = ..., + webhook_config: interactions.WebhookConfigParam = ..., + generation_config: interactions.GenerationConfigParam = ..., + response_format: interactions.CreateModelInteractionResponseFormatParam = ..., + input: interactions.InteractionsInputParam, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Interaction: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param model: The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param generation_config: Configuration parameters for model interactions. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + def create( + self, + *, + api_version: Optional[str] = None, + model: interactions.Model, + background: bool = ..., + store: bool = ..., + stream: Literal[True], + environment: interactions.CreateModelInteractionEnvironmentParam = ..., + previous_interaction_id: str = ..., + response_mime_type: str = ..., + response_modalities: List[interactions.ResponseModality] = ..., + service_tier: interactions.ServiceTier = ..., + system_instruction: str = ..., + tools: List[interactions.ToolParam] = ..., + usage: interactions.UsageParam = ..., + webhook_config: interactions.WebhookConfigParam = ..., + generation_config: interactions.GenerationConfigParam = ..., + response_format: interactions.CreateModelInteractionResponseFormatParam = ..., + input: interactions.InteractionsInputParam, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Stream[interactions.InteractionSSEEvent]: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param model: The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param generation_config: Configuration parameters for model interactions. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + def create( + self, + *, + api_version: Optional[str] = None, + agent: interactions.AgentOption, + background: bool = ..., + store: bool = ..., + stream: Union[Literal[False], None] = None, + environment: interactions.CreateAgentInteractionEnvironmentParam = ..., + previous_interaction_id: str = ..., + response_mime_type: str = ..., + response_modalities: List[interactions.ResponseModality] = ..., + service_tier: interactions.ServiceTier = ..., + system_instruction: str = ..., + tools: List[interactions.ToolParam] = ..., + usage: interactions.UsageParam = ..., + webhook_config: interactions.WebhookConfigParam = ..., + agent_config: interactions.CreateAgentInteractionAgentConfigParam = ..., + response_format: interactions.CreateAgentInteractionResponseFormatParam = ..., + input: interactions.InteractionsInputParam, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Interaction: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param agent: The agent to interact with. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param agent_config: Configuration parameters for the agent interaction. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + def create( + self, + *, + api_version: Optional[str] = None, + agent: interactions.AgentOption, + background: bool = ..., + store: bool = ..., + stream: Literal[True], + environment: interactions.CreateAgentInteractionEnvironmentParam = ..., + previous_interaction_id: str = ..., + response_mime_type: str = ..., + response_modalities: List[interactions.ResponseModality] = ..., + service_tier: interactions.ServiceTier = ..., + system_instruction: str = ..., + tools: List[interactions.ToolParam] = ..., + usage: interactions.UsageParam = ..., + webhook_config: interactions.WebhookConfigParam = ..., + agent_config: interactions.CreateAgentInteractionAgentConfigParam = ..., + response_format: interactions.CreateAgentInteractionResponseFormatParam = ..., + input: interactions.InteractionsInputParam, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Stream[interactions.InteractionSSEEvent]: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param agent: The agent to interact with. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param agent_config: Configuration parameters for the agent interaction. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + def create( + self, + *, + request: OptionalNullable[ + Union[models.CreateInteractionRequest, models.CreateInteractionRequestParam] + ] = UNSET, + api_version: OptionalNullable[str] = UNSET, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + **body_kwargs: Any, + ) -> Union[interactions.Interaction, Stream[interactions.InteractionSSEEvent]]: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param model: The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param generation_config: Configuration parameters for model interactions. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param agent: The agent to interact with. + :param agent_config: Configuration parameters for the agent interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + def create( + self, + *, + request: OptionalNullable[ + Union[models.CreateInteractionRequest, models.CreateInteractionRequestParam] + ] = UNSET, + api_version: OptionalNullable[str] = UNSET, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + **body_kwargs: Any, + ) -> Union[interactions.Interaction, Stream[interactions.InteractionSSEEvent]]: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param model: The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param generation_config: Configuration parameters for model interactions. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param agent: The agent to interact with. + :param agent_config: Configuration parameters for the agent interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + if "model" in body_kwargs and "agent" in body_kwargs: + raise ValueError("Cannot supply both 'model' and 'agent'.") + if "model" in body_kwargs and "agent_config" in body_kwargs: + raise ValueError("Cannot supply both 'model' and 'agent_config'.") + if "generation_config" in body_kwargs and "agent" in body_kwargs: + raise ValueError("Cannot supply both 'generation_config' and 'agent'.") + if "generation_config" in body_kwargs and "agent_config" in body_kwargs: + raise ValueError( + "Cannot supply both 'generation_config' and 'agent_config'." + ) + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + if request is not UNSET: + request = cast( + models.CreateInteractionRequest, + request + if isinstance(request, BaseModel) + else utils.unmarshal( + cast(Any, request), models.CreateInteractionRequest + ), + ) + else: + _body_kwargs = dict(body_kwargs) + _body_kwargs = {k: v for k, v in _body_kwargs.items() if v is not UNSET} + _request_kwargs: dict[str, Any] = {"api_version": api_version} + _request_kwargs = { + k: v for k, v in _request_kwargs.items() if v is not UNSET + } + _request_kwargs["body"] = _body_kwargs + request = cast( + models.CreateInteractionRequest, + utils.unmarshal(_request_kwargs, models.CreateInteractionRequest), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="POST", + path="/{api_version}/interactions", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=True, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="text/event-stream" + if getattr(getattr(request, "body", None), "stream", False) is True + else "application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.CreateInteractionGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body, + False, + False, + "json", + models.CreateInteractionRequestBody, + extra_body=extra_body, + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + response_data: Any = None + if utils.match_response(http_res, "200", "application/json"): + http_res_text = utils.stream_to_text(http_res) + return unmarshal_json_response( + interactions.Interaction, http_res, http_res_text + ) + if utils.match_response(http_res, "200", "text/event-stream"): + return Stream( + http_res, + lambda raw: utils.unmarshal_json( + raw, interactions.InteractionSSEStreamEvent + ).data, + sentinel="[DONE]", + client_ref=self, + ) + if utils.match_response(http_res, "4XX", "application/json"): + http_res_text = utils.stream_to_text(http_res) + response_data = unmarshal_json_response( + errors.CreateInteractionClientErrorData, http_res, http_res_text + ) + raise errors.CreateInteractionClientError( + response_data, http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "application/json"): + http_res_text = utils.stream_to_text(http_res) + response_data = unmarshal_json_response( + errors.CreateInteractionServerErrorData, http_res, http_res_text + ) + raise errors.CreateInteractionServerError( + response_data, http_res, http_res_text + ) + + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "Unexpected response received", http_res, http_res_text + ) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="CreateInteraction", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=getattr(getattr(request, "body", None), "stream", False) is True + or _speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode=( + "event_stream" + if getattr(getattr(request, "body", None), "stream", False) + is True + else "buffered" + ), + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + @overload + def get( + self, + id: str, + *, + stream: Union[Literal[False], None] = None, + last_event_id: Optional[str] = None, + include_input: Optional[bool] = False, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Interaction: + r"""Retrieving an interaction + + Retrieves the full details of a single interaction based on its `Interaction.id`. + + :param id: The unique identifier of the interaction to retrieve. + :param stream: If set to true, the generated content will be streamed incrementally. + :param last_event_id: Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + :param include_input: If set to true, includes the input in the response. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + def get( + self, + id: str, + *, + stream: Literal[True], + last_event_id: Optional[str] = None, + include_input: Optional[bool] = False, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Stream[interactions.InteractionSSEEvent]: + r"""Retrieving an interaction + + Retrieves the full details of a single interaction based on its `Interaction.id`. + + :param id: The unique identifier of the interaction to retrieve. + :param stream: If set to true, the generated content will be streamed incrementally. + :param last_event_id: Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + :param include_input: If set to true, includes the input in the response. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + def get( + self, + id: str, + *, + stream: bool, + last_event_id: Optional[str] = None, + include_input: Optional[bool] = False, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Union[interactions.Interaction, Stream[interactions.InteractionSSEEvent]]: + r"""Retrieving an interaction + + Retrieves the full details of a single interaction based on its `Interaction.id`. + + :param id: The unique identifier of the interaction to retrieve. + :param stream: If set to true, the generated content will be streamed incrementally. + :param last_event_id: Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + :param include_input: If set to true, includes the input in the response. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + def get( + self, + id: str, + *, + stream: Optional[bool] = False, + last_event_id: Optional[str] = None, + include_input: Optional[bool] = False, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Union[interactions.Interaction, Stream[interactions.InteractionSSEEvent]]: + r"""Retrieving an interaction + + Retrieves the full details of a single interaction based on its `Interaction.id`. + + :param id: The unique identifier of the interaction to retrieve. + :param stream: If set to true, the generated content will be streamed incrementally. + :param last_event_id: Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + :param include_input: If set to true, includes the input in the response. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.GetInteractionByIDRequest( + id=id, + stream=stream, + last_event_id=last_event_id, + include_input=include_input, + api_version=api_version, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="GET", + path="/{api_version}/interactions/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="text/event-stream" + if stream is True + else "application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.GetInteractionByIDGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + response_data: Any = None + if utils.match_response(http_res, "200", "application/json"): + http_res_text = utils.stream_to_text(http_res) + return unmarshal_json_response( + interactions.Interaction, http_res, http_res_text + ) + if utils.match_response(http_res, "200", "text/event-stream"): + return Stream( + http_res, + lambda raw: utils.unmarshal_json( + raw, interactions.InteractionSSEStreamEvent + ).data, + sentinel="[DONE]", + client_ref=self, + ) + if utils.match_response(http_res, "4XX", "application/json"): + http_res_text = utils.stream_to_text(http_res) + response_data = unmarshal_json_response( + errors.GetInteractionByIDClientErrorData, http_res, http_res_text + ) + raise errors.GetInteractionByIDClientError( + response_data, http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "application/json"): + http_res_text = utils.stream_to_text(http_res) + response_data = unmarshal_json_response( + errors.GetInteractionByIDServerErrorData, http_res, http_res_text + ) + raise errors.GetInteractionByIDServerError( + response_data, http_res, http_res_text + ) + + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "Unexpected response received", http_res, http_res_text + ) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="getInteractionById", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=stream is True or _speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode=("event_stream" if stream is True else "buffered"), + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def delete( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ): + r"""Deleting an interaction + + Deletes the interaction by id. + + :param id: The unique identifier of the interaction to delete. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.DeleteInteractionRequest( + id=id, + api_version=api_version, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="DELETE", + path="/{api_version}/interactions/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.DeleteInteractionGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + response_data: Any = None + if utils.match_response(http_res, "200", "*"): + return + if utils.match_response(http_res, "4XX", "application/json"): + response_data = unmarshal_json_response( + errors.DeleteInteractionClientErrorData, http_res + ) + raise errors.DeleteInteractionClientError(response_data, http_res) + if utils.match_response(http_res, "5XX", "application/json"): + response_data = unmarshal_json_response( + errors.DeleteInteractionServerErrorData, http_res + ) + raise errors.DeleteInteractionServerError(response_data, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="deleteInteraction", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def cancel( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Interaction: + r"""Canceling an interaction + + Cancels an interaction by id. This only applies to background interactions that are still running. + + :param id: The unique identifier of the interaction to cancel. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.CancelInteractionByIDRequest( + id=id, + api_version=api_version, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="POST", + path="/{api_version}/interactions/{id}/cancel", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.CancelInteractionByIDGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + response_data: Any = None + if utils.match_response(http_res, "200", "application/json"): + return unmarshal_json_response(interactions.Interaction, http_res) + if utils.match_response(http_res, "4XX", "application/json"): + response_data = unmarshal_json_response( + errors.CancelInteractionByIDClientErrorData, http_res + ) + raise errors.CancelInteractionByIDClientError(response_data, http_res) + if utils.match_response(http_res, "5XX", "application/json"): + response_data = unmarshal_json_response( + errors.CancelInteractionByIDServerErrorData, http_res + ) + raise errors.CancelInteractionByIDServerError(response_data, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="cancelInteractionById", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + +class InteractionsWithRawResponse: + def __init__(self, sdk: Interactions) -> None: + self._sdk = sdk + self.create = response_helpers.to_raw_response_wrapper( + sdk.create, "extra_headers" + ) + self.get = response_helpers.to_raw_response_wrapper(sdk.get, "extra_headers") + self.delete = response_helpers.to_raw_response_wrapper( + sdk.delete, "extra_headers" + ) + self.cancel = response_helpers.to_raw_response_wrapper( + sdk.cancel, "extra_headers" + ) + + +class InteractionsWithStreamingResponse: + def __init__(self, sdk: Interactions) -> None: + self._sdk = sdk + self.create = response_helpers.to_streamed_response_wrapper( + sdk.create, "extra_headers" + ) + self.get = response_helpers.to_streamed_response_wrapper( + sdk.get, "extra_headers" + ) + self.delete = response_helpers.to_streamed_response_wrapper( + sdk.delete, "extra_headers" + ) + self.cancel = response_helpers.to_streamed_response_wrapper( + sdk.cancel, "extra_headers" + ) + + +class AsyncInteractions(AsyncBaseSDK): + @property + def with_raw_response(self): + return AsyncInteractionsWithRawResponse(self) + + @property + def with_streaming_response(self): + return AsyncInteractionsWithStreamingResponse(self) + + @overload + async def create( + self, + *, + request: Union[ + models.CreateInteractionRequest, models.CreateInteractionRequestParam + ], + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Union[interactions.Interaction, AsyncStream[interactions.InteractionSSEEvent]]: + r"""Creating an interaction + + Creates a new interaction. + + :param body: The request body. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + async def create( + self, + *, + api_version: Optional[str] = None, + model: interactions.Model, + background: bool = ..., + store: bool = ..., + stream: Union[Literal[False], None] = None, + environment: interactions.CreateModelInteractionEnvironmentParam = ..., + previous_interaction_id: str = ..., + response_mime_type: str = ..., + response_modalities: List[interactions.ResponseModality] = ..., + service_tier: interactions.ServiceTier = ..., + system_instruction: str = ..., + tools: List[interactions.ToolParam] = ..., + usage: interactions.UsageParam = ..., + webhook_config: interactions.WebhookConfigParam = ..., + generation_config: interactions.GenerationConfigParam = ..., + response_format: interactions.CreateModelInteractionResponseFormatParam = ..., + input: interactions.InteractionsInputParam, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Interaction: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param model: The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param generation_config: Configuration parameters for model interactions. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + async def create( + self, + *, + api_version: Optional[str] = None, + model: interactions.Model, + background: bool = ..., + store: bool = ..., + stream: Literal[True], + environment: interactions.CreateModelInteractionEnvironmentParam = ..., + previous_interaction_id: str = ..., + response_mime_type: str = ..., + response_modalities: List[interactions.ResponseModality] = ..., + service_tier: interactions.ServiceTier = ..., + system_instruction: str = ..., + tools: List[interactions.ToolParam] = ..., + usage: interactions.UsageParam = ..., + webhook_config: interactions.WebhookConfigParam = ..., + generation_config: interactions.GenerationConfigParam = ..., + response_format: interactions.CreateModelInteractionResponseFormatParam = ..., + input: interactions.InteractionsInputParam, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> AsyncStream[interactions.InteractionSSEEvent]: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param model: The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param generation_config: Configuration parameters for model interactions. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + async def create( + self, + *, + api_version: Optional[str] = None, + agent: interactions.AgentOption, + background: bool = ..., + store: bool = ..., + stream: Union[Literal[False], None] = None, + environment: interactions.CreateAgentInteractionEnvironmentParam = ..., + previous_interaction_id: str = ..., + response_mime_type: str = ..., + response_modalities: List[interactions.ResponseModality] = ..., + service_tier: interactions.ServiceTier = ..., + system_instruction: str = ..., + tools: List[interactions.ToolParam] = ..., + usage: interactions.UsageParam = ..., + webhook_config: interactions.WebhookConfigParam = ..., + agent_config: interactions.CreateAgentInteractionAgentConfigParam = ..., + response_format: interactions.CreateAgentInteractionResponseFormatParam = ..., + input: interactions.InteractionsInputParam, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Interaction: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param agent: The agent to interact with. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param agent_config: Configuration parameters for the agent interaction. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + async def create( + self, + *, + api_version: Optional[str] = None, + agent: interactions.AgentOption, + background: bool = ..., + store: bool = ..., + stream: Literal[True], + environment: interactions.CreateAgentInteractionEnvironmentParam = ..., + previous_interaction_id: str = ..., + response_mime_type: str = ..., + response_modalities: List[interactions.ResponseModality] = ..., + service_tier: interactions.ServiceTier = ..., + system_instruction: str = ..., + tools: List[interactions.ToolParam] = ..., + usage: interactions.UsageParam = ..., + webhook_config: interactions.WebhookConfigParam = ..., + agent_config: interactions.CreateAgentInteractionAgentConfigParam = ..., + response_format: interactions.CreateAgentInteractionResponseFormatParam = ..., + input: interactions.InteractionsInputParam, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> AsyncStream[interactions.InteractionSSEEvent]: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param agent: The agent to interact with. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param agent_config: Configuration parameters for the agent interaction. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + async def create( + self, + *, + request: OptionalNullable[ + Union[models.CreateInteractionRequest, models.CreateInteractionRequestParam] + ] = UNSET, + api_version: OptionalNullable[str] = UNSET, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + **body_kwargs: Any, + ) -> Union[interactions.Interaction, AsyncStream[interactions.InteractionSSEEvent]]: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param model: The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param generation_config: Configuration parameters for model interactions. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param agent: The agent to interact with. + :param agent_config: Configuration parameters for the agent interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + async def create( + self, + *, + request: OptionalNullable[ + Union[models.CreateInteractionRequest, models.CreateInteractionRequestParam] + ] = UNSET, + api_version: OptionalNullable[str] = UNSET, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + **body_kwargs: Any, + ) -> Union[interactions.Interaction, AsyncStream[interactions.InteractionSSEEvent]]: + r"""Creating an interaction + + Creates a new interaction. + + :param api_version: Which version of the API to use. + :param model: The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details. + :param background: Input only. Whether to run the model interaction in the background. + :param store: Input only. Whether to store the response and request for later retrieval. + :param stream: Input only. Whether the interaction will be streamed. + :param environment: The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + :param previous_interaction_id: The ID of the previous interaction, if any. + :param response_mime_type: The mime type of the response. This is required if response_format is set. + :param response_modalities: The requested modalities of the response (TEXT, IMAGE, AUDIO). + :param service_tier: + :param system_instruction: System instruction for the interaction. + :param tools: A list of tool declarations the model may call during interaction. + :param usage: Statistics on the interaction request's token usage. + :param webhook_config: Message for configuring webhook events for a request. + :param generation_config: Configuration parameters for model interactions. + :param response_format: Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + :param input: The input for the interaction. + :param agent: The agent to interact with. + :param agent_config: Configuration parameters for the agent interaction. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + if "model" in body_kwargs and "agent" in body_kwargs: + raise ValueError("Cannot supply both 'model' and 'agent'.") + if "model" in body_kwargs and "agent_config" in body_kwargs: + raise ValueError("Cannot supply both 'model' and 'agent_config'.") + if "generation_config" in body_kwargs and "agent" in body_kwargs: + raise ValueError("Cannot supply both 'generation_config' and 'agent'.") + if "generation_config" in body_kwargs and "agent_config" in body_kwargs: + raise ValueError( + "Cannot supply both 'generation_config' and 'agent_config'." + ) + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + if request is not UNSET: + request = cast( + models.CreateInteractionRequest, + request + if isinstance(request, BaseModel) + else utils.unmarshal( + cast(Any, request), models.CreateInteractionRequest + ), + ) + else: + _body_kwargs = dict(body_kwargs) + _body_kwargs = {k: v for k, v in _body_kwargs.items() if v is not UNSET} + _request_kwargs: dict[str, Any] = {"api_version": api_version} + _request_kwargs = { + k: v for k, v in _request_kwargs.items() if v is not UNSET + } + _request_kwargs["body"] = _body_kwargs + request = cast( + models.CreateInteractionRequest, + utils.unmarshal(_request_kwargs, models.CreateInteractionRequest), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="POST", + path="/{api_version}/interactions", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=True, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="text/event-stream" + if getattr(getattr(request, "body", None), "stream", False) is True + else "application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.CreateInteractionGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body, + False, + False, + "json", + models.CreateInteractionRequestBody, + extra_body=extra_body, + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + response_data: Any = None + if utils.match_response(http_res, "200", "application/json"): + http_res_text = await utils.stream_to_text_async(http_res) + return unmarshal_json_response( + interactions.Interaction, http_res, http_res_text + ) + if utils.match_response(http_res, "200", "text/event-stream"): + return AsyncStream( + http_res, + lambda raw: utils.unmarshal_json( + raw, interactions.InteractionSSEStreamEvent + ).data, + sentinel="[DONE]", + client_ref=self, + ) + if utils.match_response(http_res, "4XX", "application/json"): + http_res_text = await utils.stream_to_text_async(http_res) + response_data = unmarshal_json_response( + errors.CreateInteractionClientErrorData, http_res, http_res_text + ) + raise errors.CreateInteractionClientError( + response_data, http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "application/json"): + http_res_text = await utils.stream_to_text_async(http_res) + response_data = unmarshal_json_response( + errors.CreateInteractionServerErrorData, http_res, http_res_text + ) + raise errors.CreateInteractionServerError( + response_data, http_res, http_res_text + ) + + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "Unexpected response received", http_res, http_res_text + ) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="CreateInteraction", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=getattr(getattr(request, "body", None), "stream", False) is True + or _speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode=( + "event_stream" + if getattr(getattr(request, "body", None), "stream", False) + is True + else "buffered" + ), + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + @overload + async def get( + self, + id: str, + *, + stream: Union[Literal[False], None] = None, + last_event_id: Optional[str] = None, + include_input: Optional[bool] = False, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Interaction: + r"""Retrieving an interaction + + Retrieves the full details of a single interaction based on its `Interaction.id`. + + :param id: The unique identifier of the interaction to retrieve. + :param stream: If set to true, the generated content will be streamed incrementally. + :param last_event_id: Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + :param include_input: If set to true, includes the input in the response. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + async def get( + self, + id: str, + *, + stream: Literal[True], + last_event_id: Optional[str] = None, + include_input: Optional[bool] = False, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> AsyncStream[interactions.InteractionSSEEvent]: + r"""Retrieving an interaction + + Retrieves the full details of a single interaction based on its `Interaction.id`. + + :param id: The unique identifier of the interaction to retrieve. + :param stream: If set to true, the generated content will be streamed incrementally. + :param last_event_id: Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + :param include_input: If set to true, includes the input in the response. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + @overload + async def get( + self, + id: str, + *, + stream: bool, + last_event_id: Optional[str] = None, + include_input: Optional[bool] = False, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Union[interactions.Interaction, AsyncStream[interactions.InteractionSSEEvent]]: + r"""Retrieving an interaction + + Retrieves the full details of a single interaction based on its `Interaction.id`. + + :param id: The unique identifier of the interaction to retrieve. + :param stream: If set to true, the generated content will be streamed incrementally. + :param last_event_id: Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + :param include_input: If set to true, includes the input in the response. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + + async def get( + self, + id: str, + *, + stream: Optional[bool] = False, + last_event_id: Optional[str] = None, + include_input: Optional[bool] = False, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> Union[interactions.Interaction, AsyncStream[interactions.InteractionSSEEvent]]: + r"""Retrieving an interaction + + Retrieves the full details of a single interaction based on its `Interaction.id`. + + :param id: The unique identifier of the interaction to retrieve. + :param stream: If set to true, the generated content will be streamed incrementally. + :param last_event_id: Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + :param include_input: If set to true, includes the input in the response. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.GetInteractionByIDRequest( + id=id, + stream=stream, + last_event_id=last_event_id, + include_input=include_input, + api_version=api_version, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="GET", + path="/{api_version}/interactions/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="text/event-stream" + if stream is True + else "application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.GetInteractionByIDGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + response_data: Any = None + if utils.match_response(http_res, "200", "application/json"): + http_res_text = await utils.stream_to_text_async(http_res) + return unmarshal_json_response( + interactions.Interaction, http_res, http_res_text + ) + if utils.match_response(http_res, "200", "text/event-stream"): + return AsyncStream( + http_res, + lambda raw: utils.unmarshal_json( + raw, interactions.InteractionSSEStreamEvent + ).data, + sentinel="[DONE]", + client_ref=self, + ) + if utils.match_response(http_res, "4XX", "application/json"): + http_res_text = await utils.stream_to_text_async(http_res) + response_data = unmarshal_json_response( + errors.GetInteractionByIDClientErrorData, http_res, http_res_text + ) + raise errors.GetInteractionByIDClientError( + response_data, http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "application/json"): + http_res_text = await utils.stream_to_text_async(http_res) + response_data = unmarshal_json_response( + errors.GetInteractionByIDServerErrorData, http_res, http_res_text + ) + raise errors.GetInteractionByIDServerError( + response_data, http_res, http_res_text + ) + + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "Unexpected response received", http_res, http_res_text + ) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="getInteractionById", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=stream is True or _speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode=("event_stream" if stream is True else "buffered"), + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def delete( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ): + r"""Deleting an interaction + + Deletes the interaction by id. + + :param id: The unique identifier of the interaction to delete. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.DeleteInteractionRequest( + id=id, + api_version=api_version, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="DELETE", + path="/{api_version}/interactions/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.DeleteInteractionGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + response_data: Any = None + if utils.match_response(http_res, "200", "*"): + return + if utils.match_response(http_res, "4XX", "application/json"): + response_data = unmarshal_json_response( + errors.DeleteInteractionClientErrorData, http_res + ) + raise errors.DeleteInteractionClientError(response_data, http_res) + if utils.match_response(http_res, "5XX", "application/json"): + response_data = unmarshal_json_response( + errors.DeleteInteractionServerErrorData, http_res + ) + raise errors.DeleteInteractionServerError(response_data, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="deleteInteraction", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def cancel( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Interaction: + r"""Canceling an interaction + + Cancels an interaction by id. This only applies to background interactions that are still running. + + :param id: The unique identifier of the interaction to cancel. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.CancelInteractionByIDRequest( + id=id, + api_version=api_version, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="POST", + path="/{api_version}/interactions/{id}/cancel", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.CancelInteractionByIDGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + response_data: Any = None + if utils.match_response(http_res, "200", "application/json"): + return unmarshal_json_response(interactions.Interaction, http_res) + if utils.match_response(http_res, "4XX", "application/json"): + response_data = unmarshal_json_response( + errors.CancelInteractionByIDClientErrorData, http_res + ) + raise errors.CancelInteractionByIDClientError(response_data, http_res) + if utils.match_response(http_res, "5XX", "application/json"): + response_data = unmarshal_json_response( + errors.CancelInteractionByIDServerErrorData, http_res + ) + raise errors.CancelInteractionByIDServerError(response_data, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="cancelInteractionById", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + +class AsyncInteractionsWithRawResponse: + def __init__(self, sdk: AsyncInteractions) -> None: + self._sdk = sdk + self.create = response_helpers.async_to_raw_response_wrapper( + sdk.create, "extra_headers" + ) + self.get = response_helpers.async_to_raw_response_wrapper( + sdk.get, "extra_headers" + ) + self.delete = response_helpers.async_to_raw_response_wrapper( + sdk.delete, "extra_headers" + ) + self.cancel = response_helpers.async_to_raw_response_wrapper( + sdk.cancel, "extra_headers" + ) + + +class AsyncInteractionsWithStreamingResponse: + def __init__(self, sdk: AsyncInteractions) -> None: + self._sdk = sdk + self.create = response_helpers.async_to_streamed_response_wrapper( + sdk.create, "extra_headers" + ) + self.get = response_helpers.async_to_streamed_response_wrapper( + sdk.get, "extra_headers" + ) + self.delete = response_helpers.async_to_streamed_response_wrapper( + sdk.delete, "extra_headers" + ) + self.cancel = response_helpers.async_to_streamed_response_wrapper( + sdk.cancel, "extra_headers" + ) diff --git a/google/genai/_gaos/models/__init__.py b/google/genai/_gaos/models/__init__.py new file mode 100644 index 000000000..29a9317ef --- /dev/null +++ b/google/genai/_gaos/models/__init__.py @@ -0,0 +1,273 @@ +# 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 typing import Any, TYPE_CHECKING + +from ..utils.dynamic_imports import lazy_getattr, lazy_dir + +if TYPE_CHECKING: + from .cancelinteractionbyid import ( + CancelInteractionByIDGlobals, + CancelInteractionByIDGlobalsTypedDict, + CancelInteractionByIDRequest, + CancelInteractionByIDRequestParam, + ) + from .createagent import ( + CreateAgentGlobals, + CreateAgentGlobalsTypedDict, + CreateAgentRequest, + CreateAgentRequestParam, + ) + from .createinteraction import ( + CreateInteractionGlobals, + CreateInteractionGlobalsTypedDict, + CreateInteractionRequest, + CreateInteractionRequestBody, + CreateInteractionRequestBodyParam, + CreateInteractionRequestParam, + CreateInteractionResponse, + CreateInteractionResponseTypedDict, + ) + from .createwebhook import ( + CreateWebhookGlobals, + CreateWebhookGlobalsTypedDict, + CreateWebhookRequest, + CreateWebhookRequestParam, + ) + from .deleteagent import ( + DeleteAgentGlobals, + DeleteAgentGlobalsTypedDict, + DeleteAgentRequest, + DeleteAgentRequestParam, + ) + from .deleteinteraction import ( + DeleteInteractionGlobals, + DeleteInteractionGlobalsTypedDict, + DeleteInteractionRequest, + DeleteInteractionRequestParam, + ) + from .deletewebhook import ( + DeleteWebhookGlobals, + DeleteWebhookGlobalsTypedDict, + DeleteWebhookRequest, + DeleteWebhookRequestParam, + ) + from .getagent import ( + GetAgentGlobals, + GetAgentGlobalsTypedDict, + GetAgentRequest, + GetAgentRequestParam, + ) + from .getinteractionbyid import ( + GetInteractionByIDGlobals, + GetInteractionByIDGlobalsTypedDict, + GetInteractionByIDRequest, + GetInteractionByIDRequestParam, + GetInteractionByIDResponse, + GetInteractionByIDResponseTypedDict, + ) + from .getwebhook import ( + GetWebhookGlobals, + GetWebhookGlobalsTypedDict, + GetWebhookRequest, + GetWebhookRequestParam, + ) + from .listagents import ( + ListAgentsGlobals, + ListAgentsGlobalsTypedDict, + ListAgentsRequest, + ListAgentsRequestParam, + ) + from .listwebhooks import ( + ListWebhooksGlobals, + ListWebhooksGlobalsTypedDict, + ListWebhooksRequest, + ListWebhooksRequestParam, + ) + from .pingwebhook import ( + PingWebhookGlobals, + PingWebhookGlobalsTypedDict, + PingWebhookRequest, + PingWebhookRequestParam, + ) + from .rotatesigningsecret import ( + RotateSigningSecretGlobals, + RotateSigningSecretGlobalsTypedDict, + RotateSigningSecretRequest, + RotateSigningSecretRequestParam, + ) + from .updatewebhook import ( + UpdateWebhookGlobals, + UpdateWebhookGlobalsTypedDict, + UpdateWebhookRequest, + UpdateWebhookRequestParam, + ) + from . import internal + +__all__ = [ + "CancelInteractionByIDGlobals", + "CancelInteractionByIDGlobalsTypedDict", + "CancelInteractionByIDRequest", + "CancelInteractionByIDRequestParam", + "CreateAgentGlobals", + "CreateAgentGlobalsTypedDict", + "CreateAgentRequest", + "CreateAgentRequestParam", + "CreateInteractionGlobals", + "CreateInteractionGlobalsTypedDict", + "CreateInteractionRequest", + "CreateInteractionRequestBody", + "CreateInteractionRequestBodyParam", + "CreateInteractionRequestParam", + "CreateInteractionResponse", + "CreateInteractionResponseTypedDict", + "CreateWebhookGlobals", + "CreateWebhookGlobalsTypedDict", + "CreateWebhookRequest", + "CreateWebhookRequestParam", + "DeleteAgentGlobals", + "DeleteAgentGlobalsTypedDict", + "DeleteAgentRequest", + "DeleteAgentRequestParam", + "DeleteInteractionGlobals", + "DeleteInteractionGlobalsTypedDict", + "DeleteInteractionRequest", + "DeleteInteractionRequestParam", + "DeleteWebhookGlobals", + "DeleteWebhookGlobalsTypedDict", + "DeleteWebhookRequest", + "DeleteWebhookRequestParam", + "GetAgentGlobals", + "GetAgentGlobalsTypedDict", + "GetAgentRequest", + "GetAgentRequestParam", + "GetInteractionByIDGlobals", + "GetInteractionByIDGlobalsTypedDict", + "GetInteractionByIDRequest", + "GetInteractionByIDRequestParam", + "GetInteractionByIDResponse", + "GetInteractionByIDResponseTypedDict", + "GetWebhookGlobals", + "GetWebhookGlobalsTypedDict", + "GetWebhookRequest", + "GetWebhookRequestParam", + "ListAgentsGlobals", + "ListAgentsGlobalsTypedDict", + "ListAgentsRequest", + "ListAgentsRequestParam", + "ListWebhooksGlobals", + "ListWebhooksGlobalsTypedDict", + "ListWebhooksRequest", + "ListWebhooksRequestParam", + "PingWebhookGlobals", + "PingWebhookGlobalsTypedDict", + "PingWebhookRequest", + "PingWebhookRequestParam", + "RotateSigningSecretGlobals", + "RotateSigningSecretGlobalsTypedDict", + "RotateSigningSecretRequest", + "RotateSigningSecretRequestParam", + "UpdateWebhookGlobals", + "UpdateWebhookGlobalsTypedDict", + "UpdateWebhookRequest", + "UpdateWebhookRequestParam", +] + +_dynamic_imports: dict[str, str] = { + "CancelInteractionByIDGlobals": ".cancelinteractionbyid", + "CancelInteractionByIDGlobalsTypedDict": ".cancelinteractionbyid", + "CancelInteractionByIDRequest": ".cancelinteractionbyid", + "CancelInteractionByIDRequestParam": ".cancelinteractionbyid", + "CreateAgentGlobals": ".createagent", + "CreateAgentGlobalsTypedDict": ".createagent", + "CreateAgentRequest": ".createagent", + "CreateAgentRequestParam": ".createagent", + "CreateInteractionGlobals": ".createinteraction", + "CreateInteractionGlobalsTypedDict": ".createinteraction", + "CreateInteractionRequest": ".createinteraction", + "CreateInteractionRequestBody": ".createinteraction", + "CreateInteractionRequestBodyParam": ".createinteraction", + "CreateInteractionRequestParam": ".createinteraction", + "CreateInteractionResponse": ".createinteraction", + "CreateInteractionResponseTypedDict": ".createinteraction", + "CreateWebhookGlobals": ".createwebhook", + "CreateWebhookGlobalsTypedDict": ".createwebhook", + "CreateWebhookRequest": ".createwebhook", + "CreateWebhookRequestParam": ".createwebhook", + "DeleteAgentGlobals": ".deleteagent", + "DeleteAgentGlobalsTypedDict": ".deleteagent", + "DeleteAgentRequest": ".deleteagent", + "DeleteAgentRequestParam": ".deleteagent", + "DeleteInteractionGlobals": ".deleteinteraction", + "DeleteInteractionGlobalsTypedDict": ".deleteinteraction", + "DeleteInteractionRequest": ".deleteinteraction", + "DeleteInteractionRequestParam": ".deleteinteraction", + "DeleteWebhookGlobals": ".deletewebhook", + "DeleteWebhookGlobalsTypedDict": ".deletewebhook", + "DeleteWebhookRequest": ".deletewebhook", + "DeleteWebhookRequestParam": ".deletewebhook", + "GetAgentGlobals": ".getagent", + "GetAgentGlobalsTypedDict": ".getagent", + "GetAgentRequest": ".getagent", + "GetAgentRequestParam": ".getagent", + "GetInteractionByIDGlobals": ".getinteractionbyid", + "GetInteractionByIDGlobalsTypedDict": ".getinteractionbyid", + "GetInteractionByIDRequest": ".getinteractionbyid", + "GetInteractionByIDRequestParam": ".getinteractionbyid", + "GetInteractionByIDResponse": ".getinteractionbyid", + "GetInteractionByIDResponseTypedDict": ".getinteractionbyid", + "GetWebhookGlobals": ".getwebhook", + "GetWebhookGlobalsTypedDict": ".getwebhook", + "GetWebhookRequest": ".getwebhook", + "GetWebhookRequestParam": ".getwebhook", + "ListAgentsGlobals": ".listagents", + "ListAgentsGlobalsTypedDict": ".listagents", + "ListAgentsRequest": ".listagents", + "ListAgentsRequestParam": ".listagents", + "ListWebhooksGlobals": ".listwebhooks", + "ListWebhooksGlobalsTypedDict": ".listwebhooks", + "ListWebhooksRequest": ".listwebhooks", + "ListWebhooksRequestParam": ".listwebhooks", + "PingWebhookGlobals": ".pingwebhook", + "PingWebhookGlobalsTypedDict": ".pingwebhook", + "PingWebhookRequest": ".pingwebhook", + "PingWebhookRequestParam": ".pingwebhook", + "RotateSigningSecretGlobals": ".rotatesigningsecret", + "RotateSigningSecretGlobalsTypedDict": ".rotatesigningsecret", + "RotateSigningSecretRequest": ".rotatesigningsecret", + "RotateSigningSecretRequestParam": ".rotatesigningsecret", + "UpdateWebhookGlobals": ".updatewebhook", + "UpdateWebhookGlobalsTypedDict": ".updatewebhook", + "UpdateWebhookRequest": ".updatewebhook", + "UpdateWebhookRequestParam": ".updatewebhook", +} + +_sub_packages = ["internal"] + + +def __getattr__(attr_name: str) -> Any: + return lazy_getattr( + attr_name, + package=__package__, + dynamic_imports=_dynamic_imports, + sub_packages=_sub_packages, + ) + + +def __dir__(): + return lazy_dir(dynamic_imports=_dynamic_imports, sub_packages=_sub_packages) diff --git a/google/genai/_gaos/models/cancelinteractionbyid.py b/google/genai/_gaos/models/cancelinteractionbyid.py new file mode 100644 index 000000000..4db947eb1 --- /dev/null +++ b/google/genai/_gaos/models/cancelinteractionbyid.py @@ -0,0 +1,89 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..utils import FieldMetadata, PathParamMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class CancelInteractionByIDGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class CancelInteractionByIDGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class CancelInteractionByIDRequestParam(TypedDict): + id: str + r"""The unique identifier of the interaction to cancel.""" + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class CancelInteractionByIDRequest(BaseModel): + id: Annotated[ + str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) + ] + r"""The unique identifier of the interaction to cancel.""" + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/createagent.py b/google/genai/_gaos/models/createagent.py new file mode 100644 index 000000000..ff72ddf45 --- /dev/null +++ b/google/genai/_gaos/models/createagent.py @@ -0,0 +1,91 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..types.agents import agent as agents_agent +from ..utils import FieldMetadata, PathParamMetadata, RequestMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class CreateAgentGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class CreateAgentGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class CreateAgentRequestParam(TypedDict): + body: agents_agent.AgentParam + r"""The request body.""" + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class CreateAgentRequest(BaseModel): + body: Annotated[ + agents_agent.Agent, + FieldMetadata(request=RequestMetadata(media_type="application/json")), + ] + r"""The request body.""" + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/createinteraction.py b/google/genai/_gaos/models/createinteraction.py new file mode 100644 index 000000000..6e9a57206 --- /dev/null +++ b/google/genai/_gaos/models/createinteraction.py @@ -0,0 +1,141 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..types.interactions import ( + createagentinteraction as interactions_createagentinteraction, + createmodelinteraction as interactions_createmodelinteraction, + interaction as interactions_interaction, + interactionsseevent as interactions_interactionsseevent, +) +from ..utils import FieldMetadata, PathParamMetadata, RequestMetadata +from ..utils.eventstreaming import AsyncStream, Stream +from pydantic import model_serializer +from typing import Optional, Union +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict + + +class CreateInteractionGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class CreateInteractionGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +CreateInteractionRequestBodyParam = TypeAliasType( + "CreateInteractionRequestBodyParam", + Union[ + interactions_createmodelinteraction.CreateModelInteractionParam, + interactions_createagentinteraction.CreateAgentInteractionParam, + ], +) +r"""The request body.""" + + +CreateInteractionRequestBody = TypeAliasType( + "CreateInteractionRequestBody", + Union[ + interactions_createmodelinteraction.CreateModelInteraction, + interactions_createagentinteraction.CreateAgentInteraction, + ], +) +r"""The request body.""" + + +class CreateInteractionRequestParam(TypedDict): + body: CreateInteractionRequestBodyParam + r"""The request body.""" + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class CreateInteractionRequest(BaseModel): + body: Annotated[ + CreateInteractionRequestBody, + FieldMetadata(request=RequestMetadata(media_type="application/json")), + ] + r"""The request body.""" + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +CreateInteractionResponseTypedDict = TypeAliasType( + "CreateInteractionResponseTypedDict", + Union[ + interactions_interaction.InteractionTypedDict, + Union[ + Stream[interactions_interactionsseevent.InteractionSSEEventTypedDict], + AsyncStream[interactions_interactionsseevent.InteractionSSEEventTypedDict], + ], + ], +) + + +CreateInteractionResponse = TypeAliasType( + "CreateInteractionResponse", + Union[ + interactions_interaction.Interaction, + Union[ + Stream[interactions_interactionsseevent.InteractionSSEEvent], + AsyncStream[interactions_interactionsseevent.InteractionSSEEvent], + ], + ], +) diff --git a/google/genai/_gaos/models/createwebhook.py b/google/genai/_gaos/models/createwebhook.py new file mode 100644 index 000000000..8999f2f82 --- /dev/null +++ b/google/genai/_gaos/models/createwebhook.py @@ -0,0 +1,91 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..types.webhooks import webhook as webhooks_webhook +from ..utils import FieldMetadata, PathParamMetadata, RequestMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class CreateWebhookGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class CreateWebhookGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class CreateWebhookRequestParam(TypedDict): + body: webhooks_webhook.WebhookInputParam + r"""Required. The webhook to create.""" + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class CreateWebhookRequest(BaseModel): + body: Annotated[ + webhooks_webhook.WebhookInput, + FieldMetadata(request=RequestMetadata(media_type="application/json")), + ] + r"""Required. The webhook to create.""" + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/deleteagent.py b/google/genai/_gaos/models/deleteagent.py new file mode 100644 index 000000000..2c14d54c3 --- /dev/null +++ b/google/genai/_gaos/models/deleteagent.py @@ -0,0 +1,87 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..utils import FieldMetadata, PathParamMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class DeleteAgentGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class DeleteAgentGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class DeleteAgentRequestParam(TypedDict): + id: str + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class DeleteAgentRequest(BaseModel): + id: Annotated[ + str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) + ] + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/deleteinteraction.py b/google/genai/_gaos/models/deleteinteraction.py new file mode 100644 index 000000000..34cfdd4d7 --- /dev/null +++ b/google/genai/_gaos/models/deleteinteraction.py @@ -0,0 +1,89 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..utils import FieldMetadata, PathParamMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class DeleteInteractionGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class DeleteInteractionGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class DeleteInteractionRequestParam(TypedDict): + id: str + r"""The unique identifier of the interaction to delete.""" + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class DeleteInteractionRequest(BaseModel): + id: Annotated[ + str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) + ] + r"""The unique identifier of the interaction to delete.""" + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/deletewebhook.py b/google/genai/_gaos/models/deletewebhook.py new file mode 100644 index 000000000..378082d2a --- /dev/null +++ b/google/genai/_gaos/models/deletewebhook.py @@ -0,0 +1,93 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..utils import FieldMetadata, PathParamMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class DeleteWebhookGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class DeleteWebhookGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class DeleteWebhookRequestParam(TypedDict): + id: str + r"""Required. The ID of the webhook to delete. + Format: `{webhook_id}` + """ + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class DeleteWebhookRequest(BaseModel): + id: Annotated[ + str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) + ] + r"""Required. The ID of the webhook to delete. + Format: `{webhook_id}` + """ + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/getagent.py b/google/genai/_gaos/models/getagent.py new file mode 100644 index 000000000..0dbaf2ab7 --- /dev/null +++ b/google/genai/_gaos/models/getagent.py @@ -0,0 +1,87 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..utils import FieldMetadata, PathParamMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GetAgentGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class GetAgentGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class GetAgentRequestParam(TypedDict): + id: str + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class GetAgentRequest(BaseModel): + id: Annotated[ + str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) + ] + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/getinteractionbyid.py b/google/genai/_gaos/models/getinteractionbyid.py new file mode 100644 index 000000000..9360d64f6 --- /dev/null +++ b/google/genai/_gaos/models/getinteractionbyid.py @@ -0,0 +1,148 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..types.interactions import ( + interaction as interactions_interaction, + interactionsseevent as interactions_interactionsseevent, +) +from ..utils import FieldMetadata, PathParamMetadata, QueryParamMetadata +from ..utils.eventstreaming import AsyncStream, Stream +import pydantic +from pydantic import model_serializer +from typing import Optional, Union +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict + + +class GetInteractionByIDGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class GetInteractionByIDGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class GetInteractionByIDRequestParam(TypedDict): + id: str + r"""The unique identifier of the interaction to retrieve.""" + stream: NotRequired[bool] + r"""If set to true, the generated content will be streamed incrementally.""" + last_event_id: NotRequired[str] + r"""Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true.""" + include_input: NotRequired[bool] + r"""If set to true, includes the input in the response.""" + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class GetInteractionByIDRequest(BaseModel): + id: Annotated[ + str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) + ] + r"""The unique identifier of the interaction to retrieve.""" + + stream: Annotated[ + Optional[bool], + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = False + r"""If set to true, the generated content will be streamed incrementally.""" + + last_event_id: Annotated[ + Optional[str], + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = None + r"""Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true.""" + + include_input: Annotated[ + Optional[bool], + pydantic.Field( + deprecated="warning: ** DEPRECATED ** - This will be removed in a future release, please migrate away from it as soon as possible." + ), + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = False + r"""If set to true, includes the input in the response.""" + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + ["stream", "last_event_id", "include_input", "api_version"] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +GetInteractionByIDResponseTypedDict = TypeAliasType( + "GetInteractionByIDResponseTypedDict", + Union[ + interactions_interaction.InteractionTypedDict, + Union[ + Stream[interactions_interactionsseevent.InteractionSSEEventTypedDict], + AsyncStream[interactions_interactionsseevent.InteractionSSEEventTypedDict], + ], + ], +) + + +GetInteractionByIDResponse = TypeAliasType( + "GetInteractionByIDResponse", + Union[ + interactions_interaction.Interaction, + Union[ + Stream[interactions_interactionsseevent.InteractionSSEEvent], + AsyncStream[interactions_interactionsseevent.InteractionSSEEvent], + ], + ], +) diff --git a/google/genai/_gaos/models/getwebhook.py b/google/genai/_gaos/models/getwebhook.py new file mode 100644 index 000000000..bb0ac9a89 --- /dev/null +++ b/google/genai/_gaos/models/getwebhook.py @@ -0,0 +1,89 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..utils import FieldMetadata, PathParamMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GetWebhookGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class GetWebhookGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class GetWebhookRequestParam(TypedDict): + id: str + r"""Required. The ID of the webhook to retrieve.""" + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class GetWebhookRequest(BaseModel): + id: Annotated[ + str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) + ] + r"""Required. The ID of the webhook to retrieve.""" + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/internal/__init__.py b/google/genai/_gaos/models/internal/__init__.py new file mode 100644 index 000000000..1c75dd9a1 --- /dev/null +++ b/google/genai/_gaos/models/internal/__init__.py @@ -0,0 +1,41 @@ +# 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 typing import Any, TYPE_CHECKING + +from ...utils.dynamic_imports import lazy_getattr, lazy_dir + +if TYPE_CHECKING: + from .globals import Globals, GlobalsTypedDict + +__all__ = ["Globals", "GlobalsTypedDict"] + +_dynamic_imports: dict[str, str] = { + "Globals": ".globals", + "GlobalsTypedDict": ".globals", +} + + +def __getattr__(attr_name: str) -> Any: + return lazy_getattr( + attr_name, package=__package__, dynamic_imports=_dynamic_imports + ) + + +def __dir__(): + return lazy_dir(dynamic_imports=_dynamic_imports) diff --git a/google/genai/_gaos/models/internal/globals.py b/google/genai/_gaos/models/internal/globals.py new file mode 100644 index 000000000..3dca99074 --- /dev/null +++ b/google/genai/_gaos/models/internal/globals.py @@ -0,0 +1,72 @@ +# 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 __future__ import annotations +from ...types import BaseModel, UNSET_SENTINEL +from ...utils import FieldMetadata, HeaderMetadata, PathParamMetadata +import pydantic +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + api_revision: NotRequired[str] + r"""API revision header to send with Google GenAI API requests.""" + user_project: NotRequired[str] + r"""Quota project header to send with Google GenAI API requests.""" + + +class Globals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + api_revision: Annotated[ + Optional[str], + pydantic.Field(alias="Api-Revision"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""API revision header to send with Google GenAI API requests.""" + + user_project: Annotated[ + Optional[str], + pydantic.Field(alias="x-goog-user-project"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""Quota project header to send with Google GenAI API requests.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version", "api_revision", "user_project"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/listagents.py b/google/genai/_gaos/models/listagents.py new file mode 100644 index 000000000..431484162 --- /dev/null +++ b/google/genai/_gaos/models/listagents.py @@ -0,0 +1,103 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..utils import FieldMetadata, PathParamMetadata, QueryParamMetadata +import pydantic +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ListAgentsGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class ListAgentsGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class ListAgentsRequestParam(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + parent: NotRequired[str] + page_size: NotRequired[int] + page_token: NotRequired[str] + + +class ListAgentsRequest(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + parent: Annotated[ + Optional[str], + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = None + + page_size: Annotated[ + Optional[int], + pydantic.Field(alias="pageSize"), + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = None + + page_token: Annotated[ + Optional[str], + pydantic.Field(alias="pageToken"), + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version", "parent", "pageSize", "pageToken"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/listwebhooks.py b/google/genai/_gaos/models/listwebhooks.py new file mode 100644 index 000000000..352f56600 --- /dev/null +++ b/google/genai/_gaos/models/listwebhooks.py @@ -0,0 +1,108 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..utils import FieldMetadata, PathParamMetadata, QueryParamMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ListWebhooksGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class ListWebhooksGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class ListWebhooksRequestParam(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + page_size: NotRequired[int] + r"""Optional. The maximum number of webhooks to return. The service may return fewer than + this value. If unspecified, at most 50 webhooks will be returned. + The maximum value is 1000. + """ + page_token: NotRequired[str] + r"""Optional. A page token, received from a previous `ListWebhooks` call. + Provide this to retrieve the subsequent page. + """ + + +class ListWebhooksRequest(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + page_size: Annotated[ + Optional[int], + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = None + r"""Optional. The maximum number of webhooks to return. The service may return fewer than + this value. If unspecified, at most 50 webhooks will be returned. + The maximum value is 1000. + """ + + page_token: Annotated[ + Optional[str], + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = None + r"""Optional. A page token, received from a previous `ListWebhooks` call. + Provide this to retrieve the subsequent page. + """ + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version", "page_size", "page_token"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/pingwebhook.py b/google/genai/_gaos/models/pingwebhook.py new file mode 100644 index 000000000..f2644c154 --- /dev/null +++ b/google/genai/_gaos/models/pingwebhook.py @@ -0,0 +1,102 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..types.webhooks import pingwebhookrequest as webhooks_pingwebhookrequest +from ..utils import FieldMetadata, PathParamMetadata, RequestMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class PingWebhookGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class PingWebhookGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class PingWebhookRequestParam(TypedDict): + id: str + r"""Required. The ID of the webhook to ping. + Format: `{webhook_id}` + """ + api_version: NotRequired[str] + r"""Which version of the API to use.""" + body: NotRequired[webhooks_pingwebhookrequest.PingWebhookRequestParam] + r"""The request body.""" + + +class PingWebhookRequest(BaseModel): + id: Annotated[ + str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) + ] + r"""Required. The ID of the webhook to ping. + Format: `{webhook_id}` + """ + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + body: Annotated[ + Optional[webhooks_pingwebhookrequest.PingWebhookRequest], + FieldMetadata(request=RequestMetadata(media_type="application/json")), + ] = None + r"""The request body.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version", "body"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/rotatesigningsecret.py b/google/genai/_gaos/models/rotatesigningsecret.py new file mode 100644 index 000000000..8727ac5c3 --- /dev/null +++ b/google/genai/_gaos/models/rotatesigningsecret.py @@ -0,0 +1,106 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..types.webhooks import ( + rotatesigningsecretrequest as webhooks_rotatesigningsecretrequest, +) +from ..utils import FieldMetadata, PathParamMetadata, RequestMetadata +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class RotateSigningSecretGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class RotateSigningSecretGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class RotateSigningSecretRequestParam(TypedDict): + id: str + r"""Required. The ID of the webhook for which to generate a signing secret. + Format: `{webhook_id}` + """ + api_version: NotRequired[str] + r"""Which version of the API to use.""" + body: NotRequired[ + webhooks_rotatesigningsecretrequest.RotateSigningSecretRequestParam + ] + r"""The request body.""" + + +class RotateSigningSecretRequest(BaseModel): + id: Annotated[ + str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) + ] + r"""Required. The ID of the webhook for which to generate a signing secret. + Format: `{webhook_id}` + """ + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + body: Annotated[ + Optional[webhooks_rotatesigningsecretrequest.RotateSigningSecretRequest], + FieldMetadata(request=RequestMetadata(media_type="application/json")), + ] = None + r"""The request body.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version", "body"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/models/updatewebhook.py b/google/genai/_gaos/models/updatewebhook.py new file mode 100644 index 000000000..7dc6a0109 --- /dev/null +++ b/google/genai/_gaos/models/updatewebhook.py @@ -0,0 +1,111 @@ +# 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 __future__ import annotations +from ..types import BaseModel, UNSET_SENTINEL +from ..types.webhooks import webhookupdate as webhooks_webhookupdate +from ..utils import ( + FieldMetadata, + PathParamMetadata, + QueryParamMetadata, + RequestMetadata, +) +from pydantic import model_serializer +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class UpdateWebhookGlobalsTypedDict(TypedDict): + api_version: NotRequired[str] + r"""Which version of the API to use.""" + + +class UpdateWebhookGlobals(BaseModel): + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class UpdateWebhookRequestParam(TypedDict): + id: str + r"""Required. The ID of the webhook to update.""" + api_version: NotRequired[str] + r"""Which version of the API to use.""" + update_mask: NotRequired[str] + r"""Optional. The list of fields to update.""" + body: NotRequired[webhooks_webhookupdate.WebhookUpdateParam] + r"""Required. The webhook to update.""" + + +class UpdateWebhookRequest(BaseModel): + id: Annotated[ + str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) + ] + r"""Required. The ID of the webhook to update.""" + + api_version: Annotated[ + Optional[str], + FieldMetadata(path=PathParamMetadata(style="simple", explode=False)), + ] = None + r"""Which version of the API to use.""" + + update_mask: Annotated[ + Optional[str], + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = None + r"""Optional. The list of fields to update.""" + + body: Annotated[ + Optional[webhooks_webhookupdate.WebhookUpdate], + FieldMetadata(request=RequestMetadata(media_type="application/json")), + ] = None + r"""Required. The webhook to update.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["api_version", "update_mask", "body"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/resources/__init__.py b/google/genai/_gaos/resources/__init__.py new file mode 100644 index 000000000..e8225e9ae --- /dev/null +++ b/google/genai/_gaos/resources/__init__.py @@ -0,0 +1,23 @@ +# 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 . import agents +from . import interactions +from . import webhooks + +__all__ = ["agents", "interactions", "webhooks"] diff --git a/google/genai/_gaos/resources/agents/__init__.py b/google/genai/_gaos/resources/agents/__init__.py new file mode 100644 index 000000000..c8766a927 --- /dev/null +++ b/google/genai/_gaos/resources/agents/__init__.py @@ -0,0 +1,37 @@ +# 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 ...models.createagent import CreateAgentRequestParam as AgentCreateParams +from ...models.deleteagent import DeleteAgentRequestParam as AgentDeleteParams +from ...models.getagent import GetAgentRequestParam as AgentGetParams +from ...models.listagents import ListAgentsRequestParam as AgentListParams +from ...types.agents.agent import Agent +from ...types.agents.agentlistresponse import AgentListResponse +from ...types.agents.agenttool import AgentTool +from ...types.interactions.empty import Empty as AgentDeleteResponse + +__all__ = [ + "Agent", + "AgentCreateParams", + "AgentDeleteParams", + "AgentDeleteResponse", + "AgentGetParams", + "AgentListParams", + "AgentListResponse", + "AgentTool", +] diff --git a/google/genai/_gaos/resources/interactions/__init__.py b/google/genai/_gaos/resources/interactions/__init__.py new file mode 100644 index 000000000..9b1a2a911 --- /dev/null +++ b/google/genai/_gaos/resources/interactions/__init__.py @@ -0,0 +1,319 @@ +# 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 ...models.createinteraction import ( + CreateInteractionRequestParam as InteractionCreateParams, +) +from ...models.deleteinteraction import ( + DeleteInteractionRequestParam as InteractionDeleteParams, +) +from ...models.getinteractionbyid import ( + GetInteractionByIDRequestParam as InteractionGetParams, +) +from ...models.listagents import ListAgentsRequestParam as AgentListParams +from ...models.listwebhooks import ListWebhooksRequestParam as WebhookListParams +from ...types.agents.agent import Agent, AgentParam as AgentCreateParams +from ...types.agents.agentlistresponse import AgentListResponse +from ...types.interactions.agentoption import AgentOption +from ...types.interactions.allowedtools import AllowedTools +from ...types.interactions.allowlistentry import AllowlistEntry +from ...types.interactions.annotation import Annotation +from ...types.interactions.argumentsdelta import ArgumentsDelta +from ...types.interactions.audiocontent import AudioContent, AudioContentParam +from ...types.interactions.audiodelta import AudioDelta +from ...types.interactions.audioresponseformat import AudioResponseFormat +from ...types.interactions.codeexecution import CodeExecution +from ...types.interactions.codeexecutioncallarguments import CodeExecutionCallArguments +from ...types.interactions.codeexecutioncalldelta import CodeExecutionCallDelta +from ...types.interactions.codeexecutioncallstep import CodeExecutionCallStep +from ...types.interactions.codeexecutionresultdelta import CodeExecutionResultDelta +from ...types.interactions.codeexecutionresultstep import CodeExecutionResultStep +from ...types.interactions.computeruse import ComputerUse +from ...types.interactions.content import Content +from ...types.interactions.createagentinteraction import CreateAgentInteraction +from ...types.interactions.createmodelinteraction import CreateModelInteraction +from ...types.interactions.deepresearchagentconfig import DeepResearchAgentConfig +from ...types.interactions.documentcontent import DocumentContent +from ...types.interactions.documentdelta import DocumentDelta +from ...types.interactions.dynamicagentconfig import DynamicAgentConfig +from ...types.interactions.empty import Empty +from ...types.interactions.environment import Environment +from ...types.interactions.environmentnetworkegressallowlist_union import ( + EnvironmentNetworkEgressAllowlistUnion, +) +from ...types.interactions.error import Error +from ...types.interactions.errorevent import ErrorEvent, ErrorEventMetadata +from ...types.interactions.filecitation import FileCitation +from ...types.interactions.filesearch import FileSearch +from ...types.interactions.filesearchcalldelta import FileSearchCallDelta +from ...types.interactions.filesearchcallstep import FileSearchCallStep +from ...types.interactions.filesearchresult import FileSearchResult +from ...types.interactions.filesearchresultdelta import FileSearchResultDelta +from ...types.interactions.filesearchresultstep import FileSearchResultStep +from ...types.interactions.function import Function +from ...types.interactions.functioncallstep import FunctionCallStep +from ...types.interactions.functionresultdelta import FunctionResultDelta +from ...types.interactions.functionresultstep import FunctionResultStep +from ...types.interactions.generationconfig import GenerationConfig +from ...types.interactions.googlemaps import GoogleMaps +from ...types.interactions.googlemapscallarguments import GoogleMapsCallArguments +from ...types.interactions.googlemapscalldelta import GoogleMapsCallDelta +from ...types.interactions.googlemapscallstep import GoogleMapsCallStep +from ...types.interactions.googlemapsresult import GoogleMapsResult +from ...types.interactions.googlemapsresultdelta import GoogleMapsResultDelta +from ...types.interactions.googlemapsresultplaces import GoogleMapsResultPlaces +from ...types.interactions.googlemapsresultstep import GoogleMapsResultStep +from ...types.interactions.googlesearch import GoogleSearch +from ...types.interactions.googlesearchcallarguments import GoogleSearchCallArguments +from ...types.interactions.googlesearchcalldelta import GoogleSearchCallDelta +from ...types.interactions.googlesearchcallstep import GoogleSearchCallStep +from ...types.interactions.googlesearchresult import GoogleSearchResult +from ...types.interactions.googlesearchresultdelta import GoogleSearchResultDelta +from ...types.interactions.googlesearchresultstep import GoogleSearchResultStep +from ...types.interactions.groundingtoolcount import GroundingToolCount +from ...types.interactions.imageconfig import ImageConfig +from ...types.interactions.imagecontent import ImageContent +from ...types.interactions.imagedelta import ImageDelta +from ...types.interactions.imageresponseformat import ImageResponseFormat +from ...types.interactions.interaction import Interaction +from ...types.interactions.interactioncompletedevent import InteractionCompletedEvent +from ...types.interactions.interactioncreatedevent import InteractionCreatedEvent +from ...types.interactions.interactionsinput import InteractionsInput +from ...types.interactions.interactionsseevent import InteractionSSEEvent +from ...types.interactions.interactionsseeventinteraction import ( + InteractionSseEventInteraction, +) +from ...types.interactions.interactionssestreamevent import InteractionSSEStreamEvent +from ...types.interactions.interactionstatusupdate import ( + InteractionStatusUpdate, + InteractionStatusUpdateMetadata, +) +from ...types.interactions.mcpserver import MCPServer +from ...types.interactions.mcpservertoolcalldelta import MCPServerToolCallDelta +from ...types.interactions.mcpservertoolcallstep import MCPServerToolCallStep +from ...types.interactions.mcpservertoolresultdelta import MCPServerToolResultDelta +from ...types.interactions.mcpservertoolresultstep import MCPServerToolResultStep +from ...types.interactions.mediaresolution import MediaResolution +from ...types.interactions.modalitytokens import ModalityTokens +from ...types.interactions.model import Model +from ...types.interactions.modeloutputstep import ModelOutputStep +from ...types.interactions.placecitation import PlaceCitation +from ...types.interactions.responseformat import ResponseFormat +from ...types.interactions.responsemodality import ResponseModality +from ...types.interactions.retrieval import Retrieval +from ...types.interactions.reviewsnippet import ReviewSnippet +from ...types.interactions.servicetier import ServiceTier +from ...types.interactions.source import Source +from ...types.interactions.speechconfig import SpeechConfig +from ...types.interactions.step import Step +from ...types.interactions.stepdelta import StepDelta +from ...types.interactions.stepdeltadata import StepDeltaData +from ...types.interactions.stepdeltametadata import StepDeltaMetadata +from ...types.interactions.stepstart import StepStart, StepStartMetadata +from ...types.interactions.stepstop import StepStop, StepStopMetadata +from ...types.interactions.textannotationdelta import TextAnnotationDelta +from ...types.interactions.textcontent import TextContent +from ...types.interactions.textdelta import TextDelta +from ...types.interactions.textresponseformat import TextResponseFormat +from ...types.interactions.thinkinglevel import ThinkingLevel +from ...types.interactions.thinkingsummaries import ThinkingSummaries +from ...types.interactions.thoughtsignaturedelta import ThoughtSignatureDelta +from ...types.interactions.thoughtstep import ThoughtStep +from ...types.interactions.thoughtsummarycontent import ThoughtSummaryContent +from ...types.interactions.thoughtsummarydelta import ThoughtSummaryDelta +from ...types.interactions.tool import Tool +from ...types.interactions.toolchoiceconfig import ToolChoiceConfig +from ...types.interactions.toolchoicetype import ToolChoiceType +from ...types.interactions.urlcitation import URLCitation +from ...types.interactions.urlcontext import URLContext +from ...types.interactions.urlcontextcallarguments import URLContextCallArguments +from ...types.interactions.urlcontextcalldelta import URLContextCallDelta +from ...types.interactions.urlcontextcallstep import Arguments, URLContextCallStep +from ...types.interactions.urlcontextresult import URLContextResult +from ...types.interactions.urlcontextresultdelta import URLContextResultDelta +from ...types.interactions.urlcontextresultstep import URLContextResultStep +from ...types.interactions.usage import Usage +from ...types.interactions.userinputstep import UserInputStep +from ...types.interactions.vertexaisearchconfig import VertexAISearchConfig +from ...types.interactions.videocontent import VideoContent, VideoContentParam +from ...types.interactions.videodelta import VideoDelta +from ...types.interactions.webhookconfig import WebhookConfig +from ...types.webhooks.pingwebhookrequest import ( + PingWebhookRequestParam as WebhookPingParams, +) +from ...types.webhooks.rotatesigningsecretrequest import ( + RotateSigningSecretRequestParam as WebhookRotateSigningSecretParams, +) +from ...types.webhooks.signingsecret import SigningSecret +from ...types.webhooks.webhook import Webhook, WebhookInputParam as WebhookCreateParams +from ...types.webhooks.webhooklistresponse import WebhookListResponse +from ...types.webhooks.webhookpingresponse import WebhookPingResponse +from ...types.webhooks.webhookrotatesigningsecretresponse import ( + WebhookRotateSigningSecretResponse, +) +from ...types.webhooks.webhookupdate import WebhookUpdateParam as WebhookUpdateParams + +AgentDeleteResponse = Empty +ModelParam = Model +WebhookDeleteResponse = Empty +__all__ = [ + "Agent", + "AgentCreateParams", + "AgentDeleteResponse", + "AgentListParams", + "AgentListResponse", + "AgentOption", + "AllowedTools", + "AllowlistEntry", + "Annotation", + "Arguments", + "ArgumentsDelta", + "AudioContent", + "AudioContentParam", + "AudioDelta", + "AudioResponseFormat", + "CodeExecution", + "CodeExecutionCallArguments", + "CodeExecutionCallDelta", + "CodeExecutionCallStep", + "CodeExecutionResultDelta", + "CodeExecutionResultStep", + "ComputerUse", + "Content", + "CreateAgentInteraction", + "CreateModelInteraction", + "DeepResearchAgentConfig", + "DocumentContent", + "DocumentDelta", + "DynamicAgentConfig", + "Empty", + "Environment", + "EnvironmentNetworkEgressAllowlistUnion", + "Error", + "ErrorEvent", + "ErrorEventMetadata", + "FileCitation", + "FileSearch", + "FileSearchCallDelta", + "FileSearchCallStep", + "FileSearchResult", + "FileSearchResultDelta", + "FileSearchResultStep", + "Function", + "FunctionCallStep", + "FunctionResultDelta", + "FunctionResultStep", + "GenerationConfig", + "GoogleMaps", + "GoogleMapsCallArguments", + "GoogleMapsCallDelta", + "GoogleMapsCallStep", + "GoogleMapsResult", + "GoogleMapsResultDelta", + "GoogleMapsResultPlaces", + "GoogleMapsResultStep", + "GoogleSearch", + "GoogleSearchCallArguments", + "GoogleSearchCallDelta", + "GoogleSearchCallStep", + "GoogleSearchResult", + "GoogleSearchResultDelta", + "GoogleSearchResultStep", + "GroundingToolCount", + "ImageConfig", + "ImageContent", + "ImageDelta", + "ImageResponseFormat", + "Interaction", + "InteractionCompletedEvent", + "InteractionCreateParams", + "InteractionCreatedEvent", + "InteractionDeleteParams", + "InteractionGetParams", + "InteractionSSEEvent", + "InteractionSSEStreamEvent", + "InteractionSseEventInteraction", + "InteractionStatusUpdate", + "InteractionStatusUpdateMetadata", + "InteractionsInput", + "MCPServer", + "MCPServerToolCallDelta", + "MCPServerToolCallStep", + "MCPServerToolResultDelta", + "MCPServerToolResultStep", + "MediaResolution", + "ModalityTokens", + "Model", + "ModelOutputStep", + "ModelParam", + "PlaceCitation", + "ResponseFormat", + "ResponseModality", + "Retrieval", + "ReviewSnippet", + "ServiceTier", + "SigningSecret", + "Source", + "SpeechConfig", + "Step", + "StepDelta", + "StepDeltaData", + "StepDeltaMetadata", + "StepStart", + "StepStartMetadata", + "StepStop", + "StepStopMetadata", + "TextAnnotationDelta", + "TextContent", + "TextDelta", + "TextResponseFormat", + "ThinkingLevel", + "ThinkingSummaries", + "ThoughtSignatureDelta", + "ThoughtStep", + "ThoughtSummaryContent", + "ThoughtSummaryDelta", + "Tool", + "ToolChoiceConfig", + "ToolChoiceType", + "URLCitation", + "URLContext", + "URLContextCallArguments", + "URLContextCallDelta", + "URLContextCallStep", + "URLContextResult", + "URLContextResultDelta", + "URLContextResultStep", + "Usage", + "UserInputStep", + "VertexAISearchConfig", + "VideoContent", + "VideoContentParam", + "VideoDelta", + "Webhook", + "WebhookConfig", + "WebhookCreateParams", + "WebhookDeleteResponse", + "WebhookListParams", + "WebhookListResponse", + "WebhookPingParams", + "WebhookPingResponse", + "WebhookRotateSigningSecretParams", + "WebhookRotateSigningSecretResponse", + "WebhookUpdateParams", +] diff --git a/google/genai/_gaos/resources/webhooks/__init__.py b/google/genai/_gaos/resources/webhooks/__init__.py new file mode 100644 index 000000000..ae9b071ca --- /dev/null +++ b/google/genai/_gaos/resources/webhooks/__init__.py @@ -0,0 +1,58 @@ +# 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 ...models.createwebhook import CreateWebhookRequestParam as WebhookCreateParams +from ...models.deletewebhook import DeleteWebhookRequestParam as WebhookDeleteParams +from ...models.getwebhook import GetWebhookRequestParam as WebhookGetParams +from ...models.listwebhooks import ListWebhooksRequestParam as WebhookListParams +from ...models.pingwebhook import PingWebhookRequestParam as WebhookPingParams +from ...models.rotatesigningsecret import ( + RotateSigningSecretRequestParam as WebhookRotateSigningSecretParams, +) +from ...models.updatewebhook import UpdateWebhookRequestParam as WebhookUpdateParams +from ...types.interactions.empty import Empty as WebhookDeleteResponse +from ...types.webhooks.pingwebhookrequest import PingWebhookRequest +from ...types.webhooks.rotatesigningsecretrequest import RotateSigningSecretRequest +from ...types.webhooks.signingsecret import SigningSecret +from ...types.webhooks.webhook import Webhook, WebhookInput +from ...types.webhooks.webhooklistresponse import WebhookListResponse +from ...types.webhooks.webhookpingresponse import WebhookPingResponse +from ...types.webhooks.webhookrotatesigningsecretresponse import ( + WebhookRotateSigningSecretResponse, +) +from ...types.webhooks.webhookupdate import WebhookUpdate + +__all__ = [ + "PingWebhookRequest", + "RotateSigningSecretRequest", + "SigningSecret", + "Webhook", + "WebhookCreateParams", + "WebhookDeleteParams", + "WebhookDeleteResponse", + "WebhookGetParams", + "WebhookInput", + "WebhookListParams", + "WebhookListResponse", + "WebhookPingParams", + "WebhookPingResponse", + "WebhookRotateSigningSecretParams", + "WebhookRotateSigningSecretResponse", + "WebhookUpdate", + "WebhookUpdateParams", +] diff --git a/google/genai/_gaos/sdk.py b/google/genai/_gaos/sdk.py new file mode 100644 index 000000000..1bbe7706d --- /dev/null +++ b/google/genai/_gaos/sdk.py @@ -0,0 +1,427 @@ +# 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 . import models, types, utils +from ._hooks import SDKHooks +from .basesdk import AsyncBaseSDK, BaseSDK +from .httpclient import ( + AsyncHttpClient, + ClientOwner, + HttpClient, + close_clients, + close_clients_async, +) +from .sdkconfiguration import SDKConfiguration +from .types import OptionalNullable, UNSET +from .utils.logger import Logger, get_default_logger +from .utils.retries import RetryConfig +import httpx +import importlib +import importlib.util +import sys +from typing import Callable, Dict, Optional, TYPE_CHECKING, Union, cast +import weakref + +if TYPE_CHECKING: + from .agents import Agents, AsyncAgents + from .interactions import AsyncInteractions, Interactions + from .webhooks import AsyncWebhooks, Webhooks + + +class GenAI(BaseSDK): + r"""Gemini API: The Gemini Interactions API is an experimental API that allows developers to build generative AI applications using Gemini models. Gemini is our most capable model, built from the ground up to be multimodal. It can generalize and seamlessly understand, operate across, and combine different types of information including language, images, audio, video, and code. You can use the Gemini API for use cases like reasoning across text and images, content generation, dialogue agents, summarization and classification systems, and more.""" + + @property + def with_raw_response(self): + return GenAIWithRawResponse(self) + + @property + def with_streaming_response(self): + return GenAIWithStreamingResponse(self) + + interactions: "Interactions" + webhooks: "Webhooks" + agents: "Agents" + _sub_sdk_map = { + "interactions": (".interactions", "Interactions"), + "webhooks": (".webhooks", "Webhooks"), + "agents": (".agents", "Agents"), + } + + def __init__( + self, + security: Optional[Union[types.Security, Callable[[], types.Security]]] = None, + api_version: Optional[str] = None, + api_revision: Optional[str] = None, + user_project: Optional[str] = None, + server_idx: Optional[int] = None, + url_params: Optional[Dict[str, str]] = None, + server_url: Optional[str] = None, + client: Optional[HttpClient] = None, + retry_config: OptionalNullable[RetryConfig] = UNSET, + timeout_ms: Optional[int] = None, + debug_logger: Optional[Logger] = None, + ) -> None: + r"""Instantiates the SDK configuring it with the provided parameters. + + :param security: The security details required for authentication + :param api_version: Configures the api_version parameter for all supported operations + :param api_revision: Configures the api_revision parameter for all supported operations + :param user_project: Configures the user_project parameter for all supported operations + :param server_idx: The index of the server to use for all methods + :param server_url: The server URL to use for all methods + :param url_params: Parameters to optionally template the server URL with + :param client: The HTTP client to use for all synchronous methods + :param retry_config: The retry configuration to use for all supported methods + :param timeout_ms: Optional request timeout applied to each operation in milliseconds + """ + client_supplied = True + if client is None: + client = httpx.Client(follow_redirects=True) + client_supplied = False + + assert issubclass( + type(client), HttpClient + ), "The provided client must implement the HttpClient protocol." + + if debug_logger is None: + debug_logger = get_default_logger() + + if server_url is not None: + if url_params is not None: + server_url = utils.template_url(server_url, url_params) + + _globals = models.internal.Globals( + api_version=utils.get_global_from_env( + api_version, "GOOGLE_GENAI_API_VERSION", str + ), + api_revision=utils.get_global_from_env( + api_revision, "GOOGLE_GENAI_API_REVISION", str + ), + user_project=utils.get_global_from_env( + user_project, "GOOGLE_GENAI_USER_PROJECT", str + ), + ) + + BaseSDK.__init__( + self, + SDKConfiguration( + client=client, + client_supplied=client_supplied, + async_client=None, + async_client_supplied=False, + globals=_globals, + security=security, + server_url=server_url, + server_idx=server_idx, + retry_config=retry_config, + timeout_ms=timeout_ms, + debug_logger=debug_logger, + ), + parent_ref=self, + ) + + hooks = SDKHooks() + + # pylint: disable=protected-access + self.sdk_configuration.__dict__["_hooks"] = hooks + + self.sdk_configuration = hooks.sdk_init(self.sdk_configuration) + + weakref.finalize( + self, + close_clients, + cast(ClientOwner, self.sdk_configuration), + self.sdk_configuration.client, + self.sdk_configuration.client_supplied, + ) + + def dynamic_import(self, modname, retries=3): + for attempt in range(retries): + try: + return importlib.import_module(modname, package=__package__) + except KeyError: + # Clear any half-initialized module and retry + resolved = importlib.util.resolve_name(modname, __package__) + sys.modules.pop(resolved, None) + if attempt == retries - 1: + break + raise KeyError(f"Failed to import module '{modname}' after {retries} attempts") + + def __getattr__(self, name: str): + if name in self._sub_sdk_map: + module_path, class_name = self._sub_sdk_map[name] + try: + module = self.dynamic_import(module_path) + klass = getattr(module, class_name) + instance = klass(self.sdk_configuration, parent_ref=self) + setattr(self, name, instance) + return instance + except ImportError as e: + raise AttributeError( + f"Failed to import module {module_path} for attribute {name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to find class {class_name} in module {module_path} for attribute {name}: {e}" + ) from e + + raise AttributeError( + f"'{type(self).__name__}' object has no attribute '{name}'" + ) + + def __dir__(self): + default_attrs = list(super().__dir__()) + lazy_attrs = list(self._sub_sdk_map.keys()) + return sorted(list(set(default_attrs + lazy_attrs))) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if ( + self.sdk_configuration.client is not None + and not self.sdk_configuration.client_supplied + ): + self.sdk_configuration.client.close() + self.sdk_configuration.client = None + + +class GenAIWithRawResponse: + def __init__(self, sdk: GenAI) -> None: + self._sdk = sdk + + @property + def interactions(self): + return self._sdk.interactions.with_raw_response + + @property + def webhooks(self): + return self._sdk.webhooks.with_raw_response + + @property + def agents(self): + return self._sdk.agents.with_raw_response + + +class GenAIWithStreamingResponse: + def __init__(self, sdk: GenAI) -> None: + self._sdk = sdk + + @property + def interactions(self): + return self._sdk.interactions.with_streaming_response + + @property + def webhooks(self): + return self._sdk.webhooks.with_streaming_response + + @property + def agents(self): + return self._sdk.agents.with_streaming_response + + +class AsyncGenAI(AsyncBaseSDK): + r"""Gemini API: The Gemini Interactions API is an experimental API that allows developers to build generative AI applications using Gemini models. Gemini is our most capable model, built from the ground up to be multimodal. It can generalize and seamlessly understand, operate across, and combine different types of information including language, images, audio, video, and code. You can use the Gemini API for use cases like reasoning across text and images, content generation, dialogue agents, summarization and classification systems, and more.""" + + @property + def with_raw_response(self): + return AsyncGenAIWithRawResponse(self) + + @property + def with_streaming_response(self): + return AsyncGenAIWithStreamingResponse(self) + + interactions: "AsyncInteractions" + webhooks: "AsyncWebhooks" + agents: "AsyncAgents" + _sub_sdk_map = { + "interactions": (".interactions", "AsyncInteractions"), + "webhooks": (".webhooks", "AsyncWebhooks"), + "agents": (".agents", "AsyncAgents"), + } + + def __init__( + self, + security: Optional[Union[types.Security, Callable[[], types.Security]]] = None, + api_version: Optional[str] = None, + api_revision: Optional[str] = None, + user_project: Optional[str] = None, + server_idx: Optional[int] = None, + url_params: Optional[Dict[str, str]] = None, + server_url: Optional[str] = None, + async_client: Optional[AsyncHttpClient] = None, + retry_config: OptionalNullable[RetryConfig] = UNSET, + timeout_ms: Optional[int] = None, + debug_logger: Optional[Logger] = None, + ) -> None: + r"""Instantiates the SDK configuring it with the provided parameters. + + :param security: The security details required for authentication + :param api_version: Configures the api_version parameter for all supported operations + :param api_revision: Configures the api_revision parameter for all supported operations + :param user_project: Configures the user_project parameter for all supported operations + :param server_idx: The index of the server to use for all methods + :param server_url: The server URL to use for all methods + :param url_params: Parameters to optionally template the server URL with + :param async_client: The Async HTTP client to use for all asynchronous methods + :param retry_config: The retry configuration to use for all supported methods + :param timeout_ms: Optional request timeout applied to each operation in milliseconds + """ + async_client_supplied = True + if async_client is None: + async_client = httpx.AsyncClient(follow_redirects=True) + async_client_supplied = False + if debug_logger is None: + debug_logger = get_default_logger() + assert issubclass( + type(async_client), AsyncHttpClient + ), "The provided async_client must implement the AsyncHttpClient protocol." + + if server_url is not None: + if url_params is not None: + server_url = utils.template_url(server_url, url_params) + + _globals = models.internal.Globals( + api_version=utils.get_global_from_env( + api_version, "GOOGLE_GENAI_API_VERSION", str + ), + api_revision=utils.get_global_from_env( + api_revision, "GOOGLE_GENAI_API_REVISION", str + ), + user_project=utils.get_global_from_env( + user_project, "GOOGLE_GENAI_USER_PROJECT", str + ), + ) + + AsyncBaseSDK.__init__( + self, + SDKConfiguration( + client=None, + client_supplied=False, + async_client=async_client, + async_client_supplied=async_client_supplied, + globals=_globals, + security=security, + server_url=server_url, + server_idx=server_idx, + retry_config=retry_config, + timeout_ms=timeout_ms, + debug_logger=debug_logger, + ), + parent_ref=self, + ) + + hooks = SDKHooks() + + # pylint: disable=protected-access + self.sdk_configuration.__dict__["_hooks"] = hooks + + self.sdk_configuration = hooks.sdk_init(self.sdk_configuration) + + weakref.finalize( + self, + close_clients_async, + cast(ClientOwner, self.sdk_configuration), + self.sdk_configuration.async_client, + self.sdk_configuration.async_client_supplied, + ) + + def dynamic_import(self, modname, retries=3): + for attempt in range(retries): + try: + return importlib.import_module(modname, package=__package__) + except KeyError: + # Clear any half-initialized module and retry + resolved = importlib.util.resolve_name(modname, __package__) + sys.modules.pop(resolved, None) + if attempt == retries - 1: + break + raise KeyError(f"Failed to import module '{modname}' after {retries} attempts") + + def __getattr__(self, name: str): + if name in self._sub_sdk_map: + module_path, class_name = self._sub_sdk_map[name] + try: + module = self.dynamic_import(module_path) + klass = getattr(module, class_name) + instance = klass(self.sdk_configuration, parent_ref=self) + setattr(self, name, instance) + return instance + except ImportError as e: + raise AttributeError( + f"Failed to import module {module_path} for attribute {name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to find class {class_name} in module {module_path} for attribute {name}: {e}" + ) from e + + raise AttributeError( + f"'{type(self).__name__}' object has no attribute '{name}'" + ) + + def __dir__(self): + default_attrs = list(super().__dir__()) + lazy_attrs = list(self._sub_sdk_map.keys()) + return sorted(list(set(default_attrs + lazy_attrs))) + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if ( + self.sdk_configuration.async_client is not None + and not self.sdk_configuration.async_client_supplied + ): + await self.sdk_configuration.async_client.aclose() + self.sdk_configuration.async_client = None + + +class AsyncGenAIWithRawResponse: + def __init__(self, sdk: AsyncGenAI) -> None: + self._sdk = sdk + + @property + def interactions(self): + return self._sdk.interactions.with_raw_response + + @property + def webhooks(self): + return self._sdk.webhooks.with_raw_response + + @property + def agents(self): + return self._sdk.agents.with_raw_response + + +class AsyncGenAIWithStreamingResponse: + def __init__(self, sdk: AsyncGenAI) -> None: + self._sdk = sdk + + @property + def interactions(self): + return self._sdk.interactions.with_streaming_response + + @property + def webhooks(self): + return self._sdk.webhooks.with_streaming_response + + @property + def agents(self): + return self._sdk.agents.with_streaming_response diff --git a/google/genai/_gaos/sdkconfiguration.py b/google/genai/_gaos/sdkconfiguration.py new file mode 100644 index 000000000..fb3d0c91a --- /dev/null +++ b/google/genai/_gaos/sdkconfiguration.py @@ -0,0 +1,66 @@ +# 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 . import models, types +from ._version import ( + __gen_version__, + __openapi_doc_version__, + __user_agent__, + __version__, +) +from .httpclient import AsyncHttpClient, HttpClient +from .types import OptionalNullable, UNSET +from .utils import Logger, RetryConfig, remove_suffix +from dataclasses import dataclass +from pydantic import Field +from typing import Callable, Dict, Optional, Tuple, Union + + +SERVERS = [ + "https://generativelanguage.googleapis.com", + # Global Endpoint +] +"""Contains the list of servers available to the SDK""" + + +@dataclass +class SDKConfiguration: + client: Union[HttpClient, None] + client_supplied: bool + async_client: Union[AsyncHttpClient, None] + async_client_supplied: bool + debug_logger: Logger + globals: models.internal.Globals + security: Optional[Union[types.Security, Callable[[], types.Security]]] = None + server_url: Optional[str] = "" + server_idx: Optional[int] = 0 + language: str = "python" + openapi_doc_version: str = __openapi_doc_version__ + sdk_version: str = __version__ + gen_version: str = __gen_version__ + user_agent: str = __user_agent__ + retry_config: OptionalNullable[RetryConfig] = Field(default_factory=lambda: UNSET) + timeout_ms: Optional[int] = None + + def get_server_details(self) -> Tuple[str, Dict[str, str]]: + if self.server_url is not None and self.server_url: + return remove_suffix(self.server_url, "/"), {} + if self.server_idx is None: + self.server_idx = 0 + + return SERVERS[self.server_idx], {} diff --git a/google/genai/_gaos/types/__init__.py b/google/genai/_gaos/types/__init__.py new file mode 100644 index 000000000..995e476ee --- /dev/null +++ b/google/genai/_gaos/types/__init__.py @@ -0,0 +1,69 @@ +# 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 .basemodel import ( + BaseModel, + Nullable, + OptionalNullable, + UnrecognizedInt, + UnrecognizedStr, + UNSET, + UNSET_SENTINEL, +) +from .base64fileinput import Base64EncodedString, Base64FileInput +from typing import Any, TYPE_CHECKING + +from ..utils.dynamic_imports import lazy_getattr, lazy_dir + +if TYPE_CHECKING: + from .security import Security, SecurityTypedDict + from . import agents, interactions, webhooks + +__all__ = [ + "Base64EncodedString", + "Base64FileInput", + "BaseModel", + "Nullable", + "OptionalNullable", + "Security", + "SecurityTypedDict", + "UNSET", + "UNSET_SENTINEL", + "UnrecognizedInt", + "UnrecognizedStr", +] + +_dynamic_imports: dict[str, str] = { + "Security": ".security", + "SecurityTypedDict": ".security", +} + +_sub_packages = ["agents", "interactions", "webhooks"] + + +def __getattr__(attr_name: str) -> Any: + return lazy_getattr( + attr_name, + package=__package__, + dynamic_imports=_dynamic_imports, + sub_packages=_sub_packages, + ) + + +def __dir__(): + return lazy_dir(dynamic_imports=_dynamic_imports, sub_packages=_sub_packages) diff --git a/google/genai/_gaos/types/agents/__init__.py b/google/genai/_gaos/types/agents/__init__.py new file mode 100644 index 000000000..35a662b4b --- /dev/null +++ b/google/genai/_gaos/types/agents/__init__.py @@ -0,0 +1,60 @@ +# 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 typing import Any, TYPE_CHECKING + +from ...utils.dynamic_imports import lazy_getattr, lazy_dir + +if TYPE_CHECKING: + from .agent import Agent, AgentParam, BaseEnvironment, BaseEnvironmentParam + from .agentlistresponse import AgentListResponse, AgentListResponseTypedDict + from .agenttool import AgentTool, AgentToolParam, UnknownAgentTool + +__all__ = [ + "Agent", + "AgentListResponse", + "AgentListResponseTypedDict", + "AgentParam", + "AgentTool", + "AgentToolParam", + "BaseEnvironment", + "BaseEnvironmentParam", + "UnknownAgentTool", +] + +_dynamic_imports: dict[str, str] = { + "Agent": ".agent", + "AgentParam": ".agent", + "BaseEnvironment": ".agent", + "BaseEnvironmentParam": ".agent", + "AgentListResponse": ".agentlistresponse", + "AgentListResponseTypedDict": ".agentlistresponse", + "AgentTool": ".agenttool", + "AgentToolParam": ".agenttool", + "UnknownAgentTool": ".agenttool", +} + + +def __getattr__(attr_name: str) -> Any: + return lazy_getattr( + attr_name, package=__package__, dynamic_imports=_dynamic_imports + ) + + +def __dir__(): + return lazy_dir(dynamic_imports=_dynamic_imports) diff --git a/google/genai/_gaos/types/agents/agent.py b/google/genai/_gaos/types/agents/agent.py new file mode 100644 index 000000000..0cb763245 --- /dev/null +++ b/google/genai/_gaos/types/agents/agent.py @@ -0,0 +1,121 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ..interactions import environment as interactions_environment +from .agenttool import AgentTool, AgentToolParam +from pydantic import model_serializer +from typing import List, Optional, Union +from typing_extensions import NotRequired, TypeAliasType, TypedDict + + +BaseEnvironmentParam = TypeAliasType( + "BaseEnvironmentParam", Union[interactions_environment.EnvironmentParam, str] +) +r"""The environment configuration for the agent.""" + + +BaseEnvironment = TypeAliasType( + "BaseEnvironment", Union[interactions_environment.Environment, str] +) +r"""The environment configuration for the agent.""" + + +class AgentParam(TypedDict): + r"""An agent definition for the CreateAgent API. + This message is the target for annotation-parser-based JSON parsing. + New format: + { + \"id\": \"customer-sentinel\", + \"base_agent\": \"\", + \"system_instruction\": \"...\", + \"base_environment\": { \"type\": \"remote\", \"sources\": [...] }, + \"tools\": [ {\"type\": \"code_execution\"} ] + } + """ + + id: NotRequired[str] + r"""The unique identifier for the agent.""" + base_agent: NotRequired[str] + r"""The base agent to extend.""" + system_instruction: NotRequired[str] + r"""System instruction for the agent.""" + description: NotRequired[str] + r"""Agent description for developers to quickly read and understand.""" + tools: NotRequired[List[AgentToolParam]] + r"""The tools available to the agent.""" + base_environment: NotRequired[BaseEnvironmentParam] + r"""The environment configuration for the agent.""" + + +class Agent(BaseModel): + r"""An agent definition for the CreateAgent API. + This message is the target for annotation-parser-based JSON parsing. + New format: + { + \"id\": \"customer-sentinel\", + \"base_agent\": \"\", + \"system_instruction\": \"...\", + \"base_environment\": { \"type\": \"remote\", \"sources\": [...] }, + \"tools\": [ {\"type\": \"code_execution\"} ] + } + """ + + id: Optional[str] = None + r"""The unique identifier for the agent.""" + + base_agent: Optional[str] = None + r"""The base agent to extend.""" + + system_instruction: Optional[str] = None + r"""System instruction for the agent.""" + + description: Optional[str] = None + r"""Agent description for developers to quickly read and understand.""" + + tools: Optional[List[AgentTool]] = None + r"""The tools available to the agent.""" + + base_environment: Optional[BaseEnvironment] = None + r"""The environment configuration for the agent.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + [ + "id", + "base_agent", + "system_instruction", + "description", + "tools", + "base_environment", + ] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/agents/agentlistresponse.py b/google/genai/_gaos/types/agents/agentlistresponse.py new file mode 100644 index 000000000..6f83ea600 --- /dev/null +++ b/google/genai/_gaos/types/agents/agentlistresponse.py @@ -0,0 +1,51 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .agent import Agent, AgentParam +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class AgentListResponseTypedDict(TypedDict): + agents: NotRequired[List[AgentParam]] + next_page_token: NotRequired[str] + + +class AgentListResponse(BaseModel): + agents: Optional[List[Agent]] = None + + next_page_token: Optional[str] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["agents", "next_page_token"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/agents/agenttool.py b/google/genai/_gaos/types/agents/agenttool.py new file mode 100644 index 000000000..e25cdb23d --- /dev/null +++ b/google/genai/_gaos/types/agents/agenttool.py @@ -0,0 +1,83 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils.unions import parse_open_union +from ..interactions import ( + codeexecution as interactions_codeexecution, + googlesearch as interactions_googlesearch, + mcpserver as interactions_mcpserver, + urlcontext as interactions_urlcontext, +) +from functools import partial +from pydantic import ConfigDict +from pydantic.functional_validators import BeforeValidator +from typing import Any, Literal, Union +from typing_extensions import Annotated, TypeAliasType + + +AgentToolParam = TypeAliasType( + "AgentToolParam", + Union[ + interactions_codeexecution.CodeExecutionParam, + interactions_urlcontext.URLContextParam, + interactions_googlesearch.GoogleSearchParam, + interactions_mcpserver.MCPServerParam, + ], +) +r"""A tool that the agent can use.""" + + +class UnknownAgentTool(BaseModel): + r"""A AgentTool variant the SDK doesn't recognize. Preserves the raw payload.""" + + type: Literal["UNKNOWN"] = "UNKNOWN" + raw: Any + is_unknown: Literal[True] = True + + model_config = ConfigDict(frozen=True) + + +_AGENT_TOOL_VARIANTS: dict[str, Any] = { + "code_execution": interactions_codeexecution.CodeExecution, + "google_search": interactions_googlesearch.GoogleSearch, + "url_context": interactions_urlcontext.URLContext, + "mcp_server": interactions_mcpserver.MCPServer, +} + + +AgentTool = Annotated[ + Union[ + interactions_codeexecution.CodeExecution, + interactions_googlesearch.GoogleSearch, + interactions_urlcontext.URLContext, + interactions_mcpserver.MCPServer, + UnknownAgentTool, + ], + BeforeValidator( + partial( + parse_open_union, + disc_key="type", + variants=_AGENT_TOOL_VARIANTS, + unknown_cls=UnknownAgentTool, + union_name="AgentTool", + ) + ), +] +r"""A tool that the agent can use.""" diff --git a/google/genai/_gaos/types/base64fileinput.py b/google/genai/_gaos/types/base64fileinput.py new file mode 100644 index 000000000..9d12230c7 --- /dev/null +++ b/google/genai/_gaos/types/base64fileinput.py @@ -0,0 +1,55 @@ +# 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 __future__ import annotations + +import base64 +import io +from os import PathLike +from typing import IO, Any, Union + +from pydantic.functional_validators import BeforeValidator +from typing_extensions import Annotated + + +Base64FileInput = Union[IO[bytes], PathLike[str]] + + +def encode_base64_file_input(value: Any) -> Any: + """Convert PathLike or IO[bytes] inputs to a base64 string. All standard binary streams + that inherit from io.IOBase are handled. Other values pass through. + """ + if isinstance(value, (PathLike, io.IOBase)): + if isinstance(value, PathLike): + with open(value, "rb") as fh: + binary = fh.read() + else: + binary = value.read() + if isinstance(binary, str): + binary = binary.encode() + if not isinstance(binary, (bytes, bytearray)): + raise TypeError( + f"Base64FileInput expected binary IO returning bytes; got {type(binary).__name__}" + ) + return base64.b64encode(binary).decode("ascii") + return value + + +# Non-str inputs are converted to base64 by the BeforeValidator at construction time. +# Callers can also pass a pre-encoded base64 str. +Base64EncodedString = Annotated[str, BeforeValidator(encode_base64_file_input)] diff --git a/google/genai/_gaos/types/basemodel.py b/google/genai/_gaos/types/basemodel.py new file mode 100644 index 000000000..7fca2df60 --- /dev/null +++ b/google/genai/_gaos/types/basemodel.py @@ -0,0 +1,210 @@ +# 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.""" + +import json + +from pydantic import ConfigDict, model_serializer +from pydantic import BaseModel as PydanticBaseModel +from pydantic_core import core_schema, to_jsonable_python +from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, TypeVar, Union +from typing_extensions import TypeAliasType, TypeAlias + + +class BaseModel(PydanticBaseModel): + model_config = ConfigDict( + populate_by_name=True, + arbitrary_types_allowed=True, + protected_namespaces=(), + extra="allow", + ) + + def model_dump( + self, + *, + mode: Literal["json", "python"] | str = "python", + include: Any = None, + exclude: Any = None, + context: Any | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + ) -> dict[str, Any]: + dumped = super().model_dump( + mode=mode, + include=include, + exclude=exclude, + context=context, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + exclude_computed_fields=exclude_computed_fields, + round_trip=round_trip, + warnings=warnings, + fallback=fallback, + serialize_as_any=serialize_as_any, + ) + extra = self.__pydantic_extra__ or {} + for key, value in extra.items(): + if key in dumped or (exclude_none and value is None): + continue + dumped[key] = to_jsonable_python(value) if mode == "json" else value + return dumped + + def model_dump_json( + self, + *, + indent: int | None = None, + ensure_ascii: bool = False, + include: Any = None, + exclude: Any = None, + context: Any | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + ) -> str: + dumped = self.model_dump( + mode="json", + include=include, + exclude=exclude, + context=context, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + exclude_computed_fields=exclude_computed_fields, + round_trip=round_trip, + warnings=warnings, + fallback=fallback, + serialize_as_any=serialize_as_any, + ) + return json.dumps(dumped, indent=indent, ensure_ascii=ensure_ascii) + + def to_dict( + self, + *, + mode: Literal["json", "python"] = "python", + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> dict[str, Any]: + return self.model_dump( + mode=mode, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + def to_json( + self, + *, + indent: int | None = 2, + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> str: + return self.model_dump_json( + indent=indent, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + +class Unset(BaseModel): + @model_serializer(mode="plain") + def serialize_model(self): + return UNSET_SENTINEL + + def __bool__(self) -> Literal[False]: + return False + + +UNSET = Unset() +UNSET_SENTINEL = "~?~unset~?~sentinel~?~" + + +T = TypeVar("T") +if TYPE_CHECKING: + Nullable: TypeAlias = Union[T, None] + OptionalNullable: TypeAlias = Union[Optional[Nullable[T]], Unset] +else: + Nullable = TypeAliasType("Nullable", Union[T, None], type_params=(T,)) + OptionalNullable = TypeAliasType( + "OptionalNullable", Union[Optional[Nullable[T]], Unset], type_params=(T,) + ) + + +class UnrecognizedStr(str): + @classmethod + def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> core_schema.CoreSchema: + # Make UnrecognizedStr only work in lax mode, not strict mode + # This makes it a "fallback" option when more specific types (like Literals) don't match + def validate_lax(v: Any) -> 'UnrecognizedStr': + if isinstance(v, cls): + return v + return cls(str(v)) + + # Use lax_or_strict_schema where strict always fails + # This forces Pydantic to prefer other union members in strict mode + # and only fall back to UnrecognizedStr in lax mode + return core_schema.lax_or_strict_schema( + lax_schema=core_schema.chain_schema([ + core_schema.str_schema(), + core_schema.no_info_plain_validator_function(validate_lax) + ]), + strict_schema=core_schema.none_schema(), # Always fails in strict mode + ) + + +class UnrecognizedInt(int): + @classmethod + def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> core_schema.CoreSchema: + # Make UnrecognizedInt only work in lax mode, not strict mode + # This makes it a "fallback" option when more specific types (like Literals) don't match + def validate_lax(v: Any) -> 'UnrecognizedInt': + if isinstance(v, cls): + return v + return cls(int(v)) + return core_schema.lax_or_strict_schema( + lax_schema=core_schema.chain_schema([ + core_schema.int_schema(), + core_schema.no_info_plain_validator_function(validate_lax) + ]), + strict_schema=core_schema.none_schema(), # Always fails in strict mode + ) diff --git a/google/genai/_gaos/types/interactions/__init__.py b/google/genai/_gaos/types/interactions/__init__.py new file mode 100644 index 000000000..29c32068d --- /dev/null +++ b/google/genai/_gaos/types/interactions/__init__.py @@ -0,0 +1,999 @@ +# 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 typing import Any, TYPE_CHECKING + +from ...utils.dynamic_imports import lazy_getattr, lazy_dir + +if TYPE_CHECKING: + from .agentoption import AgentOption + from .allowedtools import AllowedTools, AllowedToolsParam + from .allowlistentry import AllowlistEntry, AllowlistEntryParam + from .annotation import Annotation, AnnotationParam, UnknownAnnotation + from .argumentsdelta import ArgumentsDelta, ArgumentsDeltaTypedDict + from .audiocontent import AudioContent, AudioContentMimeType, AudioContentParam + from .audiodelta import AudioDelta, AudioDeltaMimeType, AudioDeltaTypedDict + from .audioresponseformat import ( + AudioResponseFormat, + AudioResponseFormatDelivery, + AudioResponseFormatMimeType, + AudioResponseFormatParam, + ) + from .codeexecution import CodeExecution, CodeExecutionParam + from .codeexecutioncallarguments import ( + CodeExecutionCallArguments, + CodeExecutionCallArgumentsParam, + Language, + ) + from .codeexecutioncalldelta import ( + CodeExecutionCallDelta, + CodeExecutionCallDeltaTypedDict, + ) + from .codeexecutioncallstep import CodeExecutionCallStep, CodeExecutionCallStepParam + from .codeexecutionresultdelta import ( + CodeExecutionResultDelta, + CodeExecutionResultDeltaTypedDict, + ) + from .codeexecutionresultstep import ( + CodeExecutionResultStep, + CodeExecutionResultStepParam, + ) + from .computeruse import ComputerUse, ComputerUseParam, EnvironmentEnum + from .content import Content, ContentParam, UnknownContent + from .createagentinteraction import ( + CreateAgentInteraction, + CreateAgentInteractionAgentConfig, + CreateAgentInteractionAgentConfigParam, + CreateAgentInteractionEnvironment, + CreateAgentInteractionEnvironmentParam, + CreateAgentInteractionParam, + CreateAgentInteractionResponseFormat, + CreateAgentInteractionResponseFormatParam, + ) + from .createmodelinteraction import ( + CreateModelInteraction, + CreateModelInteractionEnvironment, + CreateModelInteractionEnvironmentParam, + CreateModelInteractionParam, + CreateModelInteractionResponseFormat, + CreateModelInteractionResponseFormatParam, + ) + from .deepresearchagentconfig import ( + DeepResearchAgentConfig, + DeepResearchAgentConfigParam, + Visualization, + ) + from .documentcontent import ( + DocumentContent, + DocumentContentMimeType, + DocumentContentParam, + ) + from .documentdelta import ( + DocumentDelta, + DocumentDeltaMimeType, + DocumentDeltaTypedDict, + ) + from .dynamicagentconfig import DynamicAgentConfig, DynamicAgentConfigParam + from .empty import Empty, EmptyTypedDict + from .environment import ( + Environment, + EnvironmentParam, + Network, + NetworkEnum, + NetworkParam, + ) + from .environmentnetworkegressallowlist_union import ( + EnvironmentNetworkEgressAllowlist, + EnvironmentNetworkEgressAllowlistEnum, + EnvironmentNetworkEgressAllowlistParam, + EnvironmentNetworkEgressAllowlistUnion, + EnvironmentNetworkEgressAllowlistUnionParam, + ) + from .error import Error, ErrorTypedDict + from .errorevent import ( + ErrorEvent, + ErrorEventMetadata, + ErrorEventMetadataTypedDict, + ErrorEventTypedDict, + ) + from .filecitation import FileCitation, FileCitationParam + from .filesearch import FileSearch, FileSearchParam + from .filesearchcalldelta import FileSearchCallDelta, FileSearchCallDeltaTypedDict + from .filesearchcallstep import FileSearchCallStep, FileSearchCallStepParam + from .filesearchresult import FileSearchResult, FileSearchResultTypedDict + from .filesearchresultdelta import ( + FileSearchResultDelta, + FileSearchResultDeltaTypedDict, + ) + from .filesearchresultstep import FileSearchResultStep, FileSearchResultStepParam + from .function import Function, FunctionParam + from .functioncallstep import FunctionCallStep, FunctionCallStepParam + from .functionresultdelta import ( + FunctionResultDelta, + FunctionResultDeltaResult, + FunctionResultDeltaResultTypedDict, + FunctionResultDeltaResultUnion, + FunctionResultDeltaResultUnionTypedDict, + FunctionResultDeltaTypedDict, + ) + from .functionresultstep import ( + FunctionResultStep, + FunctionResultStepParam, + FunctionResultStepResult, + FunctionResultStepResultParam, + FunctionResultStepResultUnion, + FunctionResultStepResultUnionParam, + ) + from .generationconfig import ( + GenerationConfig, + GenerationConfigParam, + ToolChoice, + ToolChoiceParam, + ) + from .googlemaps import GoogleMaps, GoogleMapsParam + from .googlemapscallarguments import ( + GoogleMapsCallArguments, + GoogleMapsCallArgumentsParam, + ) + from .googlemapscalldelta import GoogleMapsCallDelta, GoogleMapsCallDeltaTypedDict + from .googlemapscallstep import GoogleMapsCallStep, GoogleMapsCallStepParam + from .googlemapsresult import GoogleMapsResult, GoogleMapsResultParam + from .googlemapsresultdelta import ( + GoogleMapsResultDelta, + GoogleMapsResultDeltaTypedDict, + ) + from .googlemapsresultplaces import ( + GoogleMapsResultPlaces, + GoogleMapsResultPlacesParam, + ) + from .googlemapsresultstep import GoogleMapsResultStep, GoogleMapsResultStepParam + from .googlesearch import GoogleSearch, GoogleSearchParam, GoogleSearchSearchType + from .googlesearchcallarguments import ( + GoogleSearchCallArguments, + GoogleSearchCallArgumentsParam, + ) + from .googlesearchcalldelta import ( + GoogleSearchCallDelta, + GoogleSearchCallDeltaTypedDict, + ) + from .googlesearchcallstep import ( + GoogleSearchCallStep, + GoogleSearchCallStepParam, + GoogleSearchCallStepSearchType, + ) + from .googlesearchresult import GoogleSearchResult, GoogleSearchResultParam + from .googlesearchresultdelta import ( + GoogleSearchResultDelta, + GoogleSearchResultDeltaTypedDict, + ) + from .googlesearchresultstep import ( + GoogleSearchResultStep, + GoogleSearchResultStepParam, + ) + from .groundingtoolcount import ( + GroundingToolCount, + GroundingToolCountParam, + GroundingToolCountType, + ) + from .imageconfig import ( + ImageConfig, + ImageConfigAspectRatio, + ImageConfigImageSize, + ImageConfigParam, + ) + from .imagecontent import ImageContent, ImageContentMimeType, ImageContentParam + from .imagedelta import ImageDelta, ImageDeltaMimeType, ImageDeltaTypedDict + from .imageresponseformat import ( + ImageResponseFormat, + ImageResponseFormatAspectRatio, + ImageResponseFormatDelivery, + ImageResponseFormatImageSize, + ImageResponseFormatMimeType, + ImageResponseFormatParam, + ) + from .interaction import ( + Interaction, + InteractionAgentConfig, + InteractionAgentConfigTypedDict, + InteractionEnvironment, + InteractionEnvironmentTypedDict, + InteractionResponseFormat, + InteractionResponseFormatTypedDict, + InteractionStatus, + InteractionTypedDict, + UnknownInteractionAgentConfig, + ) + from .interactioncompletedevent import ( + InteractionCompletedEvent, + InteractionCompletedEventTypedDict, + ) + from .interactioncreatedevent import ( + InteractionCreatedEvent, + InteractionCreatedEventTypedDict, + ) + from .interactionsinput import InteractionsInput, InteractionsInputParam + from .interactionsseevent import ( + InteractionSSEEvent, + InteractionSSEEventTypedDict, + UnknownInteractionSSEEvent, + ) + from .interactionsseeventinteraction import ( + InteractionSseEventInteraction, + InteractionSseEventInteractionStatus, + InteractionSseEventInteractionTypedDict, + ) + from .interactionssestreamevent import ( + InteractionSSEStreamEvent, + InteractionSSEStreamEventTypedDict, + ) + from .interactionstatusupdate import ( + InteractionStatusUpdate, + InteractionStatusUpdateMetadata, + InteractionStatusUpdateMetadataTypedDict, + InteractionStatusUpdateStatus, + InteractionStatusUpdateTypedDict, + ) + from .mcpserver import MCPServer, MCPServerParam + from .mcpservertoolcalldelta import ( + MCPServerToolCallDelta, + MCPServerToolCallDeltaTypedDict, + ) + from .mcpservertoolcallstep import MCPServerToolCallStep, MCPServerToolCallStepParam + from .mcpservertoolresultdelta import ( + MCPServerToolResultDelta, + MCPServerToolResultDeltaResult, + MCPServerToolResultDeltaResultTypedDict, + MCPServerToolResultDeltaResultUnion, + MCPServerToolResultDeltaResultUnionTypedDict, + MCPServerToolResultDeltaTypedDict, + ) + from .mcpservertoolresultstep import ( + MCPServerToolResultStep, + MCPServerToolResultStepParam, + MCPServerToolResultStepResult, + MCPServerToolResultStepResultParam, + MCPServerToolResultStepResultUnion, + MCPServerToolResultStepResultUnionParam, + ) + from .mediaresolution import MediaResolution + from .modalitytokens import ModalityTokens, ModalityTokensParam + from .model import Model + from .modeloutputstep import ModelOutputStep, ModelOutputStepParam + from .placecitation import PlaceCitation, PlaceCitationParam + from .responseformat import ResponseFormat, ResponseFormatParam + from .responsemodality import ResponseModality + from .retrieval import Retrieval, RetrievalParam, RetrievalType + from .reviewsnippet import ReviewSnippet, ReviewSnippetParam + from .servicetier import ServiceTier + from .source import Source, SourceParam, SourceType + from .speechconfig import SpeechConfig, SpeechConfigParam + from .step import Step, StepParam, UnknownStep + from .stepdelta import StepDelta, StepDeltaTypedDict + from .stepdeltadata import ( + StepDeltaData, + StepDeltaDataTypedDict, + UnknownStepDeltaData, + ) + from .stepdeltametadata import StepDeltaMetadata, StepDeltaMetadataTypedDict + from .stepstart import ( + StepStart, + StepStartMetadata, + StepStartMetadataTypedDict, + StepStartTypedDict, + ) + from .stepstop import ( + StepStop, + StepStopMetadata, + StepStopMetadataTypedDict, + StepStopTypedDict, + ) + from .textannotationdelta import TextAnnotationDelta, TextAnnotationDeltaTypedDict + from .textcontent import TextContent, TextContentParam + from .textdelta import TextDelta, TextDeltaTypedDict + from .textresponseformat import ( + TextResponseFormat, + TextResponseFormatMimeType, + TextResponseFormatParam, + ) + from .thinkinglevel import ThinkingLevel + from .thinkingsummaries import ThinkingSummaries + from .thoughtsignaturedelta import ( + ThoughtSignatureDelta, + ThoughtSignatureDeltaTypedDict, + ) + from .thoughtstep import ThoughtStep, ThoughtStepParam + from .thoughtsummarycontent import ( + ThoughtSummaryContent, + ThoughtSummaryContentTypedDict, + UnknownThoughtSummaryContent, + ) + from .thoughtsummarydelta import ThoughtSummaryDelta, ThoughtSummaryDeltaTypedDict + from .tool import Tool, ToolParam, UnknownTool + from .toolchoiceconfig import ToolChoiceConfig, ToolChoiceConfigParam + from .toolchoicetype import ToolChoiceType + from .urlcitation import URLCitation, URLCitationParam + from .urlcontext import URLContext, URLContextParam + from .urlcontextcallarguments import ( + URLContextCallArguments, + URLContextCallArgumentsTypedDict, + ) + from .urlcontextcalldelta import URLContextCallDelta, URLContextCallDeltaTypedDict + from .urlcontextcallstep import ( + Arguments, + ArgumentsParam, + URLContextCallStep, + URLContextCallStepParam, + ) + from .urlcontextresult import ( + URLContextResult, + URLContextResultParam, + URLContextResultStatus, + ) + from .urlcontextresultdelta import ( + URLContextResultDelta, + URLContextResultDeltaTypedDict, + ) + from .urlcontextresultstep import URLContextResultStep, URLContextResultStepParam + from .usage import Usage, UsageParam + from .userinputstep import UserInputStep, UserInputStepParam + from .vertexaisearchconfig import VertexAISearchConfig, VertexAISearchConfigParam + from .videocontent import VideoContent, VideoContentMimeType, VideoContentParam + from .videodelta import VideoDelta, VideoDeltaMimeType, VideoDeltaTypedDict + from .webhookconfig import WebhookConfig, WebhookConfigParam + +__all__ = [ + "AgentOption", + "AllowedTools", + "AllowedToolsParam", + "AllowlistEntry", + "AllowlistEntryParam", + "Annotation", + "AnnotationParam", + "Arguments", + "ArgumentsDelta", + "ArgumentsDeltaTypedDict", + "ArgumentsParam", + "AudioContent", + "AudioContentMimeType", + "AudioContentParam", + "AudioDelta", + "AudioDeltaMimeType", + "AudioDeltaTypedDict", + "AudioResponseFormat", + "AudioResponseFormatDelivery", + "AudioResponseFormatMimeType", + "AudioResponseFormatParam", + "CodeExecution", + "CodeExecutionCallArguments", + "CodeExecutionCallArgumentsParam", + "CodeExecutionCallDelta", + "CodeExecutionCallDeltaTypedDict", + "CodeExecutionCallStep", + "CodeExecutionCallStepParam", + "CodeExecutionParam", + "CodeExecutionResultDelta", + "CodeExecutionResultDeltaTypedDict", + "CodeExecutionResultStep", + "CodeExecutionResultStepParam", + "ComputerUse", + "ComputerUseParam", + "Content", + "ContentParam", + "CreateAgentInteraction", + "CreateAgentInteractionAgentConfig", + "CreateAgentInteractionAgentConfigParam", + "CreateAgentInteractionEnvironment", + "CreateAgentInteractionEnvironmentParam", + "CreateAgentInteractionParam", + "CreateAgentInteractionResponseFormat", + "CreateAgentInteractionResponseFormatParam", + "CreateModelInteraction", + "CreateModelInteractionEnvironment", + "CreateModelInteractionEnvironmentParam", + "CreateModelInteractionParam", + "CreateModelInteractionResponseFormat", + "CreateModelInteractionResponseFormatParam", + "DeepResearchAgentConfig", + "DeepResearchAgentConfigParam", + "DocumentContent", + "DocumentContentMimeType", + "DocumentContentParam", + "DocumentDelta", + "DocumentDeltaMimeType", + "DocumentDeltaTypedDict", + "DynamicAgentConfig", + "DynamicAgentConfigParam", + "Empty", + "EmptyTypedDict", + "Environment", + "EnvironmentEnum", + "EnvironmentNetworkEgressAllowlist", + "EnvironmentNetworkEgressAllowlistEnum", + "EnvironmentNetworkEgressAllowlistParam", + "EnvironmentNetworkEgressAllowlistUnion", + "EnvironmentNetworkEgressAllowlistUnionParam", + "EnvironmentParam", + "Error", + "ErrorEvent", + "ErrorEventMetadata", + "ErrorEventMetadataTypedDict", + "ErrorEventTypedDict", + "ErrorTypedDict", + "FileCitation", + "FileCitationParam", + "FileSearch", + "FileSearchCallDelta", + "FileSearchCallDeltaTypedDict", + "FileSearchCallStep", + "FileSearchCallStepParam", + "FileSearchParam", + "FileSearchResult", + "FileSearchResultDelta", + "FileSearchResultDeltaTypedDict", + "FileSearchResultStep", + "FileSearchResultStepParam", + "FileSearchResultTypedDict", + "Function", + "FunctionCallStep", + "FunctionCallStepParam", + "FunctionParam", + "FunctionResultDelta", + "FunctionResultDeltaResult", + "FunctionResultDeltaResultTypedDict", + "FunctionResultDeltaResultUnion", + "FunctionResultDeltaResultUnionTypedDict", + "FunctionResultDeltaTypedDict", + "FunctionResultStep", + "FunctionResultStepParam", + "FunctionResultStepResult", + "FunctionResultStepResultParam", + "FunctionResultStepResultUnion", + "FunctionResultStepResultUnionParam", + "GenerationConfig", + "GenerationConfigParam", + "GoogleMaps", + "GoogleMapsCallArguments", + "GoogleMapsCallArgumentsParam", + "GoogleMapsCallDelta", + "GoogleMapsCallDeltaTypedDict", + "GoogleMapsCallStep", + "GoogleMapsCallStepParam", + "GoogleMapsParam", + "GoogleMapsResult", + "GoogleMapsResultDelta", + "GoogleMapsResultDeltaTypedDict", + "GoogleMapsResultParam", + "GoogleMapsResultPlaces", + "GoogleMapsResultPlacesParam", + "GoogleMapsResultStep", + "GoogleMapsResultStepParam", + "GoogleSearch", + "GoogleSearchCallArguments", + "GoogleSearchCallArgumentsParam", + "GoogleSearchCallDelta", + "GoogleSearchCallDeltaTypedDict", + "GoogleSearchCallStep", + "GoogleSearchCallStepParam", + "GoogleSearchCallStepSearchType", + "GoogleSearchParam", + "GoogleSearchResult", + "GoogleSearchResultDelta", + "GoogleSearchResultDeltaTypedDict", + "GoogleSearchResultParam", + "GoogleSearchResultStep", + "GoogleSearchResultStepParam", + "GoogleSearchSearchType", + "GroundingToolCount", + "GroundingToolCountParam", + "GroundingToolCountType", + "ImageConfig", + "ImageConfigAspectRatio", + "ImageConfigImageSize", + "ImageConfigParam", + "ImageContent", + "ImageContentMimeType", + "ImageContentParam", + "ImageDelta", + "ImageDeltaMimeType", + "ImageDeltaTypedDict", + "ImageResponseFormat", + "ImageResponseFormatAspectRatio", + "ImageResponseFormatDelivery", + "ImageResponseFormatImageSize", + "ImageResponseFormatMimeType", + "ImageResponseFormatParam", + "Interaction", + "InteractionAgentConfig", + "InteractionAgentConfigTypedDict", + "InteractionCompletedEvent", + "InteractionCompletedEventTypedDict", + "InteractionCreatedEvent", + "InteractionCreatedEventTypedDict", + "InteractionEnvironment", + "InteractionEnvironmentTypedDict", + "InteractionResponseFormat", + "InteractionResponseFormatTypedDict", + "InteractionSSEEvent", + "InteractionSSEEventTypedDict", + "InteractionSSEStreamEvent", + "InteractionSSEStreamEventTypedDict", + "InteractionSseEventInteraction", + "InteractionSseEventInteractionStatus", + "InteractionSseEventInteractionTypedDict", + "InteractionStatus", + "InteractionStatusUpdate", + "InteractionStatusUpdateMetadata", + "InteractionStatusUpdateMetadataTypedDict", + "InteractionStatusUpdateStatus", + "InteractionStatusUpdateTypedDict", + "InteractionTypedDict", + "InteractionsInput", + "InteractionsInputParam", + "Language", + "MCPServer", + "MCPServerParam", + "MCPServerToolCallDelta", + "MCPServerToolCallDeltaTypedDict", + "MCPServerToolCallStep", + "MCPServerToolCallStepParam", + "MCPServerToolResultDelta", + "MCPServerToolResultDeltaResult", + "MCPServerToolResultDeltaResultTypedDict", + "MCPServerToolResultDeltaResultUnion", + "MCPServerToolResultDeltaResultUnionTypedDict", + "MCPServerToolResultDeltaTypedDict", + "MCPServerToolResultStep", + "MCPServerToolResultStepParam", + "MCPServerToolResultStepResult", + "MCPServerToolResultStepResultParam", + "MCPServerToolResultStepResultUnion", + "MCPServerToolResultStepResultUnionParam", + "MediaResolution", + "ModalityTokens", + "ModalityTokensParam", + "Model", + "ModelOutputStep", + "ModelOutputStepParam", + "Network", + "NetworkEnum", + "NetworkParam", + "PlaceCitation", + "PlaceCitationParam", + "ResponseFormat", + "ResponseFormatParam", + "ResponseModality", + "Retrieval", + "RetrievalParam", + "RetrievalType", + "ReviewSnippet", + "ReviewSnippetParam", + "ServiceTier", + "Source", + "SourceParam", + "SourceType", + "SpeechConfig", + "SpeechConfigParam", + "Step", + "StepDelta", + "StepDeltaData", + "StepDeltaDataTypedDict", + "StepDeltaMetadata", + "StepDeltaMetadataTypedDict", + "StepDeltaTypedDict", + "StepParam", + "StepStart", + "StepStartMetadata", + "StepStartMetadataTypedDict", + "StepStartTypedDict", + "StepStop", + "StepStopMetadata", + "StepStopMetadataTypedDict", + "StepStopTypedDict", + "TextAnnotationDelta", + "TextAnnotationDeltaTypedDict", + "TextContent", + "TextContentParam", + "TextDelta", + "TextDeltaTypedDict", + "TextResponseFormat", + "TextResponseFormatMimeType", + "TextResponseFormatParam", + "ThinkingLevel", + "ThinkingSummaries", + "ThoughtSignatureDelta", + "ThoughtSignatureDeltaTypedDict", + "ThoughtStep", + "ThoughtStepParam", + "ThoughtSummaryContent", + "ThoughtSummaryContentTypedDict", + "ThoughtSummaryDelta", + "ThoughtSummaryDeltaTypedDict", + "Tool", + "ToolChoice", + "ToolChoiceConfig", + "ToolChoiceConfigParam", + "ToolChoiceParam", + "ToolChoiceType", + "ToolParam", + "URLCitation", + "URLCitationParam", + "URLContext", + "URLContextCallArguments", + "URLContextCallArgumentsTypedDict", + "URLContextCallDelta", + "URLContextCallDeltaTypedDict", + "URLContextCallStep", + "URLContextCallStepParam", + "URLContextParam", + "URLContextResult", + "URLContextResultDelta", + "URLContextResultDeltaTypedDict", + "URLContextResultParam", + "URLContextResultStatus", + "URLContextResultStep", + "URLContextResultStepParam", + "UnknownAnnotation", + "UnknownContent", + "UnknownInteractionAgentConfig", + "UnknownInteractionSSEEvent", + "UnknownStep", + "UnknownStepDeltaData", + "UnknownThoughtSummaryContent", + "UnknownTool", + "Usage", + "UsageParam", + "UserInputStep", + "UserInputStepParam", + "VertexAISearchConfig", + "VertexAISearchConfigParam", + "VideoContent", + "VideoContentMimeType", + "VideoContentParam", + "VideoDelta", + "VideoDeltaMimeType", + "VideoDeltaTypedDict", + "Visualization", + "WebhookConfig", + "WebhookConfigParam", +] + +_dynamic_imports: dict[str, str] = { + "AgentOption": ".agentoption", + "AllowedTools": ".allowedtools", + "AllowedToolsParam": ".allowedtools", + "AllowlistEntry": ".allowlistentry", + "AllowlistEntryParam": ".allowlistentry", + "Annotation": ".annotation", + "AnnotationParam": ".annotation", + "UnknownAnnotation": ".annotation", + "ArgumentsDelta": ".argumentsdelta", + "ArgumentsDeltaTypedDict": ".argumentsdelta", + "AudioContent": ".audiocontent", + "AudioContentMimeType": ".audiocontent", + "AudioContentParam": ".audiocontent", + "AudioDelta": ".audiodelta", + "AudioDeltaMimeType": ".audiodelta", + "AudioDeltaTypedDict": ".audiodelta", + "AudioResponseFormat": ".audioresponseformat", + "AudioResponseFormatDelivery": ".audioresponseformat", + "AudioResponseFormatMimeType": ".audioresponseformat", + "AudioResponseFormatParam": ".audioresponseformat", + "CodeExecution": ".codeexecution", + "CodeExecutionParam": ".codeexecution", + "CodeExecutionCallArguments": ".codeexecutioncallarguments", + "CodeExecutionCallArgumentsParam": ".codeexecutioncallarguments", + "Language": ".codeexecutioncallarguments", + "CodeExecutionCallDelta": ".codeexecutioncalldelta", + "CodeExecutionCallDeltaTypedDict": ".codeexecutioncalldelta", + "CodeExecutionCallStep": ".codeexecutioncallstep", + "CodeExecutionCallStepParam": ".codeexecutioncallstep", + "CodeExecutionResultDelta": ".codeexecutionresultdelta", + "CodeExecutionResultDeltaTypedDict": ".codeexecutionresultdelta", + "CodeExecutionResultStep": ".codeexecutionresultstep", + "CodeExecutionResultStepParam": ".codeexecutionresultstep", + "ComputerUse": ".computeruse", + "ComputerUseParam": ".computeruse", + "EnvironmentEnum": ".computeruse", + "Content": ".content", + "ContentParam": ".content", + "UnknownContent": ".content", + "CreateAgentInteraction": ".createagentinteraction", + "CreateAgentInteractionAgentConfig": ".createagentinteraction", + "CreateAgentInteractionAgentConfigParam": ".createagentinteraction", + "CreateAgentInteractionEnvironment": ".createagentinteraction", + "CreateAgentInteractionEnvironmentParam": ".createagentinteraction", + "CreateAgentInteractionParam": ".createagentinteraction", + "CreateAgentInteractionResponseFormat": ".createagentinteraction", + "CreateAgentInteractionResponseFormatParam": ".createagentinteraction", + "CreateModelInteraction": ".createmodelinteraction", + "CreateModelInteractionEnvironment": ".createmodelinteraction", + "CreateModelInteractionEnvironmentParam": ".createmodelinteraction", + "CreateModelInteractionParam": ".createmodelinteraction", + "CreateModelInteractionResponseFormat": ".createmodelinteraction", + "CreateModelInteractionResponseFormatParam": ".createmodelinteraction", + "DeepResearchAgentConfig": ".deepresearchagentconfig", + "DeepResearchAgentConfigParam": ".deepresearchagentconfig", + "Visualization": ".deepresearchagentconfig", + "DocumentContent": ".documentcontent", + "DocumentContentMimeType": ".documentcontent", + "DocumentContentParam": ".documentcontent", + "DocumentDelta": ".documentdelta", + "DocumentDeltaMimeType": ".documentdelta", + "DocumentDeltaTypedDict": ".documentdelta", + "DynamicAgentConfig": ".dynamicagentconfig", + "DynamicAgentConfigParam": ".dynamicagentconfig", + "Empty": ".empty", + "EmptyTypedDict": ".empty", + "Environment": ".environment", + "EnvironmentParam": ".environment", + "Network": ".environment", + "NetworkEnum": ".environment", + "NetworkParam": ".environment", + "EnvironmentNetworkEgressAllowlist": ".environmentnetworkegressallowlist_union", + "EnvironmentNetworkEgressAllowlistEnum": ".environmentnetworkegressallowlist_union", + "EnvironmentNetworkEgressAllowlistParam": ".environmentnetworkegressallowlist_union", + "EnvironmentNetworkEgressAllowlistUnion": ".environmentnetworkegressallowlist_union", + "EnvironmentNetworkEgressAllowlistUnionParam": ".environmentnetworkegressallowlist_union", + "Error": ".error", + "ErrorTypedDict": ".error", + "ErrorEvent": ".errorevent", + "ErrorEventMetadata": ".errorevent", + "ErrorEventMetadataTypedDict": ".errorevent", + "ErrorEventTypedDict": ".errorevent", + "FileCitation": ".filecitation", + "FileCitationParam": ".filecitation", + "FileSearch": ".filesearch", + "FileSearchParam": ".filesearch", + "FileSearchCallDelta": ".filesearchcalldelta", + "FileSearchCallDeltaTypedDict": ".filesearchcalldelta", + "FileSearchCallStep": ".filesearchcallstep", + "FileSearchCallStepParam": ".filesearchcallstep", + "FileSearchResult": ".filesearchresult", + "FileSearchResultTypedDict": ".filesearchresult", + "FileSearchResultDelta": ".filesearchresultdelta", + "FileSearchResultDeltaTypedDict": ".filesearchresultdelta", + "FileSearchResultStep": ".filesearchresultstep", + "FileSearchResultStepParam": ".filesearchresultstep", + "Function": ".function", + "FunctionParam": ".function", + "FunctionCallStep": ".functioncallstep", + "FunctionCallStepParam": ".functioncallstep", + "FunctionResultDelta": ".functionresultdelta", + "FunctionResultDeltaResult": ".functionresultdelta", + "FunctionResultDeltaResultTypedDict": ".functionresultdelta", + "FunctionResultDeltaResultUnion": ".functionresultdelta", + "FunctionResultDeltaResultUnionTypedDict": ".functionresultdelta", + "FunctionResultDeltaTypedDict": ".functionresultdelta", + "FunctionResultStep": ".functionresultstep", + "FunctionResultStepParam": ".functionresultstep", + "FunctionResultStepResult": ".functionresultstep", + "FunctionResultStepResultParam": ".functionresultstep", + "FunctionResultStepResultUnion": ".functionresultstep", + "FunctionResultStepResultUnionParam": ".functionresultstep", + "GenerationConfig": ".generationconfig", + "GenerationConfigParam": ".generationconfig", + "ToolChoice": ".generationconfig", + "ToolChoiceParam": ".generationconfig", + "GoogleMaps": ".googlemaps", + "GoogleMapsParam": ".googlemaps", + "GoogleMapsCallArguments": ".googlemapscallarguments", + "GoogleMapsCallArgumentsParam": ".googlemapscallarguments", + "GoogleMapsCallDelta": ".googlemapscalldelta", + "GoogleMapsCallDeltaTypedDict": ".googlemapscalldelta", + "GoogleMapsCallStep": ".googlemapscallstep", + "GoogleMapsCallStepParam": ".googlemapscallstep", + "GoogleMapsResult": ".googlemapsresult", + "GoogleMapsResultParam": ".googlemapsresult", + "GoogleMapsResultDelta": ".googlemapsresultdelta", + "GoogleMapsResultDeltaTypedDict": ".googlemapsresultdelta", + "GoogleMapsResultPlaces": ".googlemapsresultplaces", + "GoogleMapsResultPlacesParam": ".googlemapsresultplaces", + "GoogleMapsResultStep": ".googlemapsresultstep", + "GoogleMapsResultStepParam": ".googlemapsresultstep", + "GoogleSearch": ".googlesearch", + "GoogleSearchParam": ".googlesearch", + "GoogleSearchSearchType": ".googlesearch", + "GoogleSearchCallArguments": ".googlesearchcallarguments", + "GoogleSearchCallArgumentsParam": ".googlesearchcallarguments", + "GoogleSearchCallDelta": ".googlesearchcalldelta", + "GoogleSearchCallDeltaTypedDict": ".googlesearchcalldelta", + "GoogleSearchCallStep": ".googlesearchcallstep", + "GoogleSearchCallStepParam": ".googlesearchcallstep", + "GoogleSearchCallStepSearchType": ".googlesearchcallstep", + "GoogleSearchResult": ".googlesearchresult", + "GoogleSearchResultParam": ".googlesearchresult", + "GoogleSearchResultDelta": ".googlesearchresultdelta", + "GoogleSearchResultDeltaTypedDict": ".googlesearchresultdelta", + "GoogleSearchResultStep": ".googlesearchresultstep", + "GoogleSearchResultStepParam": ".googlesearchresultstep", + "GroundingToolCount": ".groundingtoolcount", + "GroundingToolCountParam": ".groundingtoolcount", + "GroundingToolCountType": ".groundingtoolcount", + "ImageConfig": ".imageconfig", + "ImageConfigAspectRatio": ".imageconfig", + "ImageConfigImageSize": ".imageconfig", + "ImageConfigParam": ".imageconfig", + "ImageContent": ".imagecontent", + "ImageContentMimeType": ".imagecontent", + "ImageContentParam": ".imagecontent", + "ImageDelta": ".imagedelta", + "ImageDeltaMimeType": ".imagedelta", + "ImageDeltaTypedDict": ".imagedelta", + "ImageResponseFormat": ".imageresponseformat", + "ImageResponseFormatAspectRatio": ".imageresponseformat", + "ImageResponseFormatDelivery": ".imageresponseformat", + "ImageResponseFormatImageSize": ".imageresponseformat", + "ImageResponseFormatMimeType": ".imageresponseformat", + "ImageResponseFormatParam": ".imageresponseformat", + "Interaction": ".interaction", + "InteractionAgentConfig": ".interaction", + "InteractionAgentConfigTypedDict": ".interaction", + "InteractionEnvironment": ".interaction", + "InteractionEnvironmentTypedDict": ".interaction", + "InteractionResponseFormat": ".interaction", + "InteractionResponseFormatTypedDict": ".interaction", + "InteractionStatus": ".interaction", + "InteractionTypedDict": ".interaction", + "UnknownInteractionAgentConfig": ".interaction", + "InteractionCompletedEvent": ".interactioncompletedevent", + "InteractionCompletedEventTypedDict": ".interactioncompletedevent", + "InteractionCreatedEvent": ".interactioncreatedevent", + "InteractionCreatedEventTypedDict": ".interactioncreatedevent", + "InteractionsInput": ".interactionsinput", + "InteractionsInputParam": ".interactionsinput", + "InteractionSSEEvent": ".interactionsseevent", + "InteractionSSEEventTypedDict": ".interactionsseevent", + "UnknownInteractionSSEEvent": ".interactionsseevent", + "InteractionSseEventInteraction": ".interactionsseeventinteraction", + "InteractionSseEventInteractionStatus": ".interactionsseeventinteraction", + "InteractionSseEventInteractionTypedDict": ".interactionsseeventinteraction", + "InteractionSSEStreamEvent": ".interactionssestreamevent", + "InteractionSSEStreamEventTypedDict": ".interactionssestreamevent", + "InteractionStatusUpdate": ".interactionstatusupdate", + "InteractionStatusUpdateMetadata": ".interactionstatusupdate", + "InteractionStatusUpdateMetadataTypedDict": ".interactionstatusupdate", + "InteractionStatusUpdateStatus": ".interactionstatusupdate", + "InteractionStatusUpdateTypedDict": ".interactionstatusupdate", + "MCPServer": ".mcpserver", + "MCPServerParam": ".mcpserver", + "MCPServerToolCallDelta": ".mcpservertoolcalldelta", + "MCPServerToolCallDeltaTypedDict": ".mcpservertoolcalldelta", + "MCPServerToolCallStep": ".mcpservertoolcallstep", + "MCPServerToolCallStepParam": ".mcpservertoolcallstep", + "MCPServerToolResultDelta": ".mcpservertoolresultdelta", + "MCPServerToolResultDeltaResult": ".mcpservertoolresultdelta", + "MCPServerToolResultDeltaResultTypedDict": ".mcpservertoolresultdelta", + "MCPServerToolResultDeltaResultUnion": ".mcpservertoolresultdelta", + "MCPServerToolResultDeltaResultUnionTypedDict": ".mcpservertoolresultdelta", + "MCPServerToolResultDeltaTypedDict": ".mcpservertoolresultdelta", + "MCPServerToolResultStep": ".mcpservertoolresultstep", + "MCPServerToolResultStepParam": ".mcpservertoolresultstep", + "MCPServerToolResultStepResult": ".mcpservertoolresultstep", + "MCPServerToolResultStepResultParam": ".mcpservertoolresultstep", + "MCPServerToolResultStepResultUnion": ".mcpservertoolresultstep", + "MCPServerToolResultStepResultUnionParam": ".mcpservertoolresultstep", + "MediaResolution": ".mediaresolution", + "ModalityTokens": ".modalitytokens", + "ModalityTokensParam": ".modalitytokens", + "Model": ".model", + "ModelOutputStep": ".modeloutputstep", + "ModelOutputStepParam": ".modeloutputstep", + "PlaceCitation": ".placecitation", + "PlaceCitationParam": ".placecitation", + "ResponseFormat": ".responseformat", + "ResponseFormatParam": ".responseformat", + "ResponseModality": ".responsemodality", + "Retrieval": ".retrieval", + "RetrievalParam": ".retrieval", + "RetrievalType": ".retrieval", + "ReviewSnippet": ".reviewsnippet", + "ReviewSnippetParam": ".reviewsnippet", + "ServiceTier": ".servicetier", + "Source": ".source", + "SourceParam": ".source", + "SourceType": ".source", + "SpeechConfig": ".speechconfig", + "SpeechConfigParam": ".speechconfig", + "Step": ".step", + "StepParam": ".step", + "UnknownStep": ".step", + "StepDelta": ".stepdelta", + "StepDeltaTypedDict": ".stepdelta", + "StepDeltaData": ".stepdeltadata", + "StepDeltaDataTypedDict": ".stepdeltadata", + "UnknownStepDeltaData": ".stepdeltadata", + "StepDeltaMetadata": ".stepdeltametadata", + "StepDeltaMetadataTypedDict": ".stepdeltametadata", + "StepStart": ".stepstart", + "StepStartMetadata": ".stepstart", + "StepStartMetadataTypedDict": ".stepstart", + "StepStartTypedDict": ".stepstart", + "StepStop": ".stepstop", + "StepStopMetadata": ".stepstop", + "StepStopMetadataTypedDict": ".stepstop", + "StepStopTypedDict": ".stepstop", + "TextAnnotationDelta": ".textannotationdelta", + "TextAnnotationDeltaTypedDict": ".textannotationdelta", + "TextContent": ".textcontent", + "TextContentParam": ".textcontent", + "TextDelta": ".textdelta", + "TextDeltaTypedDict": ".textdelta", + "TextResponseFormat": ".textresponseformat", + "TextResponseFormatMimeType": ".textresponseformat", + "TextResponseFormatParam": ".textresponseformat", + "ThinkingLevel": ".thinkinglevel", + "ThinkingSummaries": ".thinkingsummaries", + "ThoughtSignatureDelta": ".thoughtsignaturedelta", + "ThoughtSignatureDeltaTypedDict": ".thoughtsignaturedelta", + "ThoughtStep": ".thoughtstep", + "ThoughtStepParam": ".thoughtstep", + "ThoughtSummaryContent": ".thoughtsummarycontent", + "ThoughtSummaryContentTypedDict": ".thoughtsummarycontent", + "UnknownThoughtSummaryContent": ".thoughtsummarycontent", + "ThoughtSummaryDelta": ".thoughtsummarydelta", + "ThoughtSummaryDeltaTypedDict": ".thoughtsummarydelta", + "Tool": ".tool", + "ToolParam": ".tool", + "UnknownTool": ".tool", + "ToolChoiceConfig": ".toolchoiceconfig", + "ToolChoiceConfigParam": ".toolchoiceconfig", + "ToolChoiceType": ".toolchoicetype", + "URLCitation": ".urlcitation", + "URLCitationParam": ".urlcitation", + "URLContext": ".urlcontext", + "URLContextParam": ".urlcontext", + "URLContextCallArguments": ".urlcontextcallarguments", + "URLContextCallArgumentsTypedDict": ".urlcontextcallarguments", + "URLContextCallDelta": ".urlcontextcalldelta", + "URLContextCallDeltaTypedDict": ".urlcontextcalldelta", + "Arguments": ".urlcontextcallstep", + "ArgumentsParam": ".urlcontextcallstep", + "URLContextCallStep": ".urlcontextcallstep", + "URLContextCallStepParam": ".urlcontextcallstep", + "URLContextResult": ".urlcontextresult", + "URLContextResultParam": ".urlcontextresult", + "URLContextResultStatus": ".urlcontextresult", + "URLContextResultDelta": ".urlcontextresultdelta", + "URLContextResultDeltaTypedDict": ".urlcontextresultdelta", + "URLContextResultStep": ".urlcontextresultstep", + "URLContextResultStepParam": ".urlcontextresultstep", + "Usage": ".usage", + "UsageParam": ".usage", + "UserInputStep": ".userinputstep", + "UserInputStepParam": ".userinputstep", + "VertexAISearchConfig": ".vertexaisearchconfig", + "VertexAISearchConfigParam": ".vertexaisearchconfig", + "VideoContent": ".videocontent", + "VideoContentMimeType": ".videocontent", + "VideoContentParam": ".videocontent", + "VideoDelta": ".videodelta", + "VideoDeltaMimeType": ".videodelta", + "VideoDeltaTypedDict": ".videodelta", + "WebhookConfig": ".webhookconfig", + "WebhookConfigParam": ".webhookconfig", +} + + +def __getattr__(attr_name: str) -> Any: + return lazy_getattr( + attr_name, package=__package__, dynamic_imports=_dynamic_imports + ) + + +def __dir__(): + return lazy_dir(dynamic_imports=_dynamic_imports) diff --git a/google/genai/_gaos/types/interactions/agentoption.py b/google/genai/_gaos/types/interactions/agentoption.py new file mode 100644 index 000000000..270727eb3 --- /dev/null +++ b/google/genai/_gaos/types/interactions/agentoption.py @@ -0,0 +1,35 @@ +# 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 __future__ import annotations +from .. import UnrecognizedStr +from typing import Literal, Union + + +AgentOption = Union[ + Literal[ + # Gemini Deep Research Agent + "deep-research-pro-preview-12-2025", + # Gemini Deep Research Agent + "deep-research-preview-04-2026", + # Gemini Deep Research Max Agent + "deep-research-max-preview-04-2026", + ], + UnrecognizedStr, +] +r"""The agent to interact with.""" diff --git a/google/genai/_gaos/types/interactions/allowedtools.py b/google/genai/_gaos/types/interactions/allowedtools.py new file mode 100644 index 000000000..475367ca9 --- /dev/null +++ b/google/genai/_gaos/types/interactions/allowedtools.py @@ -0,0 +1,57 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .toolchoicetype import ToolChoiceType +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class AllowedToolsParam(TypedDict): + r"""The configuration for allowed tools.""" + + mode: NotRequired[ToolChoiceType] + tools: NotRequired[List[str]] + r"""The names of the allowed tools.""" + + +class AllowedTools(BaseModel): + r"""The configuration for allowed tools.""" + + mode: Optional[ToolChoiceType] = None + + tools: Optional[List[str]] = None + r"""The names of the allowed tools.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["mode", "tools"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/allowlistentry.py b/google/genai/_gaos/types/interactions/allowlistentry.py new file mode 100644 index 000000000..2a4631635 --- /dev/null +++ b/google/genai/_gaos/types/interactions/allowlistentry.py @@ -0,0 +1,58 @@ +# 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 __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 + + +class AllowlistEntryParam(TypedDict): + r"""A single domain allowlist rule with optional header injection.""" + + 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.""" + + +class AllowlistEntry(BaseModel): + r"""A single domain allowlist rule with optional header injection.""" + + 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.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["transform"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/annotation.py b/google/genai/_gaos/types/interactions/annotation.py new file mode 100644 index 000000000..78169a7f9 --- /dev/null +++ b/google/genai/_gaos/types/interactions/annotation.py @@ -0,0 +1,67 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils.unions import parse_open_union +from .filecitation import FileCitation, FileCitationParam +from .placecitation import PlaceCitation, PlaceCitationParam +from .urlcitation import URLCitation, URLCitationParam +from functools import partial +from pydantic import ConfigDict +from pydantic.functional_validators import BeforeValidator +from typing import Any, Literal, Union +from typing_extensions import Annotated, TypeAliasType + + +AnnotationParam = TypeAliasType( + "AnnotationParam", Union[URLCitationParam, PlaceCitationParam, FileCitationParam] +) +r"""Citation information for model-generated content.""" + + +class UnknownAnnotation(BaseModel): + r"""A Annotation variant the SDK doesn't recognize. Preserves the raw payload.""" + + type: Literal["UNKNOWN"] = "UNKNOWN" + raw: Any + is_unknown: Literal[True] = True + + model_config = ConfigDict(frozen=True) + + +_ANNOTATION_VARIANTS: dict[str, Any] = { + "file_citation": FileCitation, + "place_citation": PlaceCitation, + "url_citation": URLCitation, +} + + +Annotation = Annotated[ + Union[FileCitation, PlaceCitation, URLCitation, UnknownAnnotation], + BeforeValidator( + partial( + parse_open_union, + disc_key="type", + variants=_ANNOTATION_VARIANTS, + unknown_cls=UnknownAnnotation, + union_name="Annotation", + ) + ), +] +r"""Citation information for model-generated content.""" diff --git a/google/genai/_gaos/types/interactions/argumentsdelta.py b/google/genai/_gaos/types/interactions/argumentsdelta.py new file mode 100644 index 000000000..893d9cb39 --- /dev/null +++ b/google/genai/_gaos/types/interactions/argumentsdelta.py @@ -0,0 +1,65 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ArgumentsDeltaTypedDict(TypedDict): + type: Literal["arguments_delta"] + arguments: NotRequired[str] + + +class ArgumentsDelta(BaseModel): + type: Annotated[ + Annotated[ + Literal["arguments_delta"], + AfterValidator(validate_const("arguments_delta")), + ], + pydantic.Field(alias="type"), + ] = "arguments_delta" + + arguments: Optional[str] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["arguments"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + ArgumentsDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/audiocontent.py b/google/genai/_gaos/types/interactions/audiocontent.py new file mode 100644 index 000000000..8d2c30462 --- /dev/null +++ b/google/genai/_gaos/types/interactions/audiocontent.py @@ -0,0 +1,114 @@ +# 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 __future__ import annotations +from .. import ( + Base64EncodedString, + Base64FileInput, + BaseModel, + UNSET_SENTINEL, + UnrecognizedStr, +) +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +AudioContentMimeType = Union[ + Literal[ + "audio/wav", + "audio/mp3", + "audio/aiff", + "audio/aac", + "audio/ogg", + "audio/flac", + "audio/mpeg", + "audio/m4a", + "audio/l16", + "audio/opus", + "audio/alaw", + "audio/mulaw", + ], + UnrecognizedStr, +] +r"""The mime type of the audio.""" + + +class AudioContentParam(TypedDict): + r"""An audio content block.""" + + type: Literal["audio"] + data: NotRequired[Union[str, Base64FileInput]] + r"""The audio content.""" + uri: NotRequired[str] + r"""The URI of the audio.""" + mime_type: NotRequired[AudioContentMimeType] + r"""The mime type of the audio.""" + channels: NotRequired[int] + r"""The number of audio channels.""" + sample_rate: NotRequired[int] + r"""The sample rate of the audio.""" + + +class AudioContent(BaseModel): + r"""An audio content block.""" + + type: Annotated[ + Annotated[Literal["audio"], AfterValidator(validate_const("audio"))], + pydantic.Field(alias="type"), + ] = "audio" + + data: Optional[Base64EncodedString] = None + r"""The audio content.""" + + uri: Optional[str] = None + r"""The URI of the audio.""" + + mime_type: Optional[AudioContentMimeType] = None + r"""The mime type of the audio.""" + + channels: Optional[int] = None + r"""The number of audio channels.""" + + sample_rate: Optional[int] = None + r"""The sample rate of the audio.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["data", "uri", "mime_type", "channels", "sample_rate"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + AudioContent.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/audiodelta.py b/google/genai/_gaos/types/interactions/audiodelta.py new file mode 100644 index 000000000..4a990c623 --- /dev/null +++ b/google/genai/_gaos/types/interactions/audiodelta.py @@ -0,0 +1,109 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +AudioDeltaMimeType = Union[ + Literal[ + "audio/wav", + "audio/mp3", + "audio/aiff", + "audio/aac", + "audio/ogg", + "audio/flac", + "audio/mpeg", + "audio/m4a", + "audio/l16", + "audio/opus", + "audio/alaw", + "audio/mulaw", + ], + UnrecognizedStr, +] + + +class AudioDeltaTypedDict(TypedDict): + type: Literal["audio"] + data: NotRequired[str] + uri: NotRequired[str] + mime_type: NotRequired[AudioDeltaMimeType] + rate: NotRequired[int] + r"""Deprecated. Use sample_rate instead. The value is ignored.""" + sample_rate: NotRequired[int] + r"""The sample rate of the audio.""" + channels: NotRequired[int] + r"""The number of audio channels.""" + + +class AudioDelta(BaseModel): + type: Annotated[ + Annotated[Literal["audio"], AfterValidator(validate_const("audio"))], + pydantic.Field(alias="type"), + ] = "audio" + + data: Optional[str] = None + + uri: Optional[str] = None + + mime_type: Optional[AudioDeltaMimeType] = None + + rate: Annotated[ + Optional[int], + pydantic.Field( + deprecated="warning: ** DEPRECATED ** - This will be removed in a future release, please migrate away from it as soon as possible." + ), + ] = None + r"""Deprecated. Use sample_rate instead. The value is ignored.""" + + sample_rate: Optional[int] = None + r"""The sample rate of the audio.""" + + channels: Optional[int] = None + r"""The number of audio channels.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + ["data", "uri", "mime_type", "rate", "sample_rate", "channels"] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + AudioDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/audioresponseformat.py b/google/genai/_gaos/types/interactions/audioresponseformat.py new file mode 100644 index 000000000..09d159658 --- /dev/null +++ b/google/genai/_gaos/types/interactions/audioresponseformat.py @@ -0,0 +1,111 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +AudioResponseFormatMimeType = Union[ + Literal[ + "audio/mp3", + "audio/ogg_opus", + "audio/l16", + "audio/wav", + "audio/alaw", + "audio/mulaw", + ], + UnrecognizedStr, +] +r"""The MIME type of the audio output.""" + + +AudioResponseFormatDelivery = Union[ + Literal[ + "inline", + "uri", + ], + UnrecognizedStr, +] +r"""The delivery mode for the audio output.""" + + +class AudioResponseFormatParam(TypedDict): + r"""Configuration for audio output format.""" + + type: Literal["audio"] + mime_type: NotRequired[AudioResponseFormatMimeType] + r"""The MIME type of the audio output.""" + delivery: NotRequired[AudioResponseFormatDelivery] + r"""The delivery mode for the audio output.""" + sample_rate: NotRequired[int] + r"""Sample rate in Hz.""" + bit_rate: NotRequired[int] + r"""Bit rate in bits per second (bps). Only applicable for compressed formats + (MP3, Opus). + """ + + +class AudioResponseFormat(BaseModel): + r"""Configuration for audio output format.""" + + type: Annotated[ + Annotated[Literal["audio"], AfterValidator(validate_const("audio"))], + pydantic.Field(alias="type"), + ] = "audio" + + mime_type: Optional[AudioResponseFormatMimeType] = None + r"""The MIME type of the audio output.""" + + delivery: Optional[AudioResponseFormatDelivery] = None + r"""The delivery mode for the audio output.""" + + sample_rate: Optional[int] = None + r"""Sample rate in Hz.""" + + bit_rate: Optional[int] = None + r"""Bit rate in bits per second (bps). Only applicable for compressed formats + (MP3, Opus). + """ + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["mime_type", "delivery", "sample_rate", "bit_rate"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + AudioResponseFormat.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/codeexecution.py b/google/genai/_gaos/types/interactions/codeexecution.py new file mode 100644 index 000000000..e3f77e2af --- /dev/null +++ b/google/genai/_gaos/types/interactions/codeexecution.py @@ -0,0 +1,48 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils import validate_const +import pydantic +from pydantic.functional_validators import AfterValidator +from typing import Literal +from typing_extensions import Annotated, TypedDict + + +class CodeExecutionParam(TypedDict): + r"""A tool that can be used by the model to execute code.""" + + type: Literal["code_execution"] + + +class CodeExecution(BaseModel): + r"""A tool that can be used by the model to execute code.""" + + type: Annotated[ + Annotated[ + Literal["code_execution"], AfterValidator(validate_const("code_execution")) + ], + pydantic.Field(alias="type"), + ] = "code_execution" + + +try: + CodeExecution.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/codeexecutioncallarguments.py b/google/genai/_gaos/types/interactions/codeexecutioncallarguments.py new file mode 100644 index 000000000..8e0af7701 --- /dev/null +++ b/google/genai/_gaos/types/interactions/codeexecutioncallarguments.py @@ -0,0 +1,62 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import Literal, Optional +from typing_extensions import NotRequired, TypedDict + + +Language = Literal["python",] +r"""Programming language of the `code`.""" + + +class CodeExecutionCallArgumentsParam(TypedDict): + r"""The arguments to pass to the code execution.""" + + language: NotRequired[Language] + r"""Programming language of the `code`.""" + code: NotRequired[str] + r"""The code to be executed.""" + + +class CodeExecutionCallArguments(BaseModel): + r"""The arguments to pass to the code execution.""" + + language: Optional[Language] = None + r"""Programming language of the `code`.""" + + code: Optional[str] = None + r"""The code to be executed.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["language", "code"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/codeexecutioncalldelta.py b/google/genai/_gaos/types/interactions/codeexecutioncalldelta.py new file mode 100644 index 000000000..ed701bf2b --- /dev/null +++ b/google/genai/_gaos/types/interactions/codeexecutioncalldelta.py @@ -0,0 +1,76 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .codeexecutioncallarguments import ( + CodeExecutionCallArguments, + CodeExecutionCallArgumentsParam, +) +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class CodeExecutionCallDeltaTypedDict(TypedDict): + arguments: CodeExecutionCallArgumentsParam + r"""The arguments to pass to the code execution.""" + type: Literal["code_execution_call"] + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class CodeExecutionCallDelta(BaseModel): + arguments: CodeExecutionCallArguments + r"""The arguments to pass to the code execution.""" + + type: Annotated[ + Annotated[ + Literal["code_execution_call"], + AfterValidator(validate_const("code_execution_call")), + ], + pydantic.Field(alias="type"), + ] = "code_execution_call" + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + CodeExecutionCallDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/codeexecutioncallstep.py b/google/genai/_gaos/types/interactions/codeexecutioncallstep.py new file mode 100644 index 000000000..87a6bf647 --- /dev/null +++ b/google/genai/_gaos/types/interactions/codeexecutioncallstep.py @@ -0,0 +1,85 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .codeexecutioncallarguments import ( + CodeExecutionCallArguments, + CodeExecutionCallArgumentsParam, +) +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class CodeExecutionCallStepParam(TypedDict): + r"""Code execution call step.""" + + arguments: CodeExecutionCallArgumentsParam + r"""The arguments to pass to the code execution.""" + id: str + r"""Required. A unique ID for this specific tool call.""" + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + type: Literal["code_execution_call"] + + +class CodeExecutionCallStep(BaseModel): + r"""Code execution call step.""" + + arguments: CodeExecutionCallArguments + r"""The arguments to pass to the code execution.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + type: Annotated[ + Annotated[ + Literal["code_execution_call"], + AfterValidator(validate_const("code_execution_call")), + ], + pydantic.Field(alias="type"), + ] = "code_execution_call" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + CodeExecutionCallStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/codeexecutionresultdelta.py b/google/genai/_gaos/types/interactions/codeexecutionresultdelta.py new file mode 100644 index 000000000..7f1b24fb0 --- /dev/null +++ b/google/genai/_gaos/types/interactions/codeexecutionresultdelta.py @@ -0,0 +1,73 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class CodeExecutionResultDeltaTypedDict(TypedDict): + result: str + type: Literal["code_execution_result"] + is_error: NotRequired[bool] + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class CodeExecutionResultDelta(BaseModel): + result: str + + type: Annotated[ + Annotated[ + Literal["code_execution_result"], + AfterValidator(validate_const("code_execution_result")), + ], + pydantic.Field(alias="type"), + ] = "code_execution_result" + + is_error: Optional[bool] = None + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["is_error", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + CodeExecutionResultDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/codeexecutionresultstep.py b/google/genai/_gaos/types/interactions/codeexecutionresultstep.py new file mode 100644 index 000000000..d78bfbebc --- /dev/null +++ b/google/genai/_gaos/types/interactions/codeexecutionresultstep.py @@ -0,0 +1,86 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class CodeExecutionResultStepParam(TypedDict): + r"""Code execution result step.""" + + result: str + r"""Required. The output of the code execution.""" + call_id: str + r"""Required. ID to match the ID from the function call block.""" + type: Literal["code_execution_result"] + is_error: NotRequired[bool] + r"""Whether the code execution resulted in an error.""" + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + + +class CodeExecutionResultStep(BaseModel): + r"""Code execution result step.""" + + result: str + r"""Required. The output of the code execution.""" + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + + type: Annotated[ + Annotated[ + Literal["code_execution_result"], + AfterValidator(validate_const("code_execution_result")), + ], + pydantic.Field(alias="type"), + ] = "code_execution_result" + + is_error: Optional[bool] = None + r"""Whether the code execution resulted in an error.""" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["is_error", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + CodeExecutionResultStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/computeruse.py b/google/genai/_gaos/types/interactions/computeruse.py new file mode 100644 index 000000000..f60fd1f1e --- /dev/null +++ b/google/genai/_gaos/types/interactions/computeruse.py @@ -0,0 +1,79 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +EnvironmentEnum = Literal["browser",] +r"""The environment being operated.""" + + +class ComputerUseParam(TypedDict): + r"""A tool that can be used by the model to interact with the computer.""" + + type: Literal["computer_use"] + environment: NotRequired[EnvironmentEnum] + r"""The environment being operated.""" + excluded_predefined_functions: NotRequired[List[str]] + r"""The list of predefined functions that are excluded from the model call.""" + + +class ComputerUse(BaseModel): + r"""A tool that can be used by the model to interact with the computer.""" + + type: Annotated[ + Annotated[ + Literal["computer_use"], AfterValidator(validate_const("computer_use")) + ], + pydantic.Field(alias="type"), + ] = "computer_use" + + environment: Optional[EnvironmentEnum] = None + r"""The environment being operated.""" + + excluded_predefined_functions: Optional[List[str]] = None + r"""The list of predefined functions that are excluded from the model call.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["environment", "excluded_predefined_functions"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + ComputerUse.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/content.py b/google/genai/_gaos/types/interactions/content.py new file mode 100644 index 000000000..1d42ebf87 --- /dev/null +++ b/google/genai/_gaos/types/interactions/content.py @@ -0,0 +1,85 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils.unions import parse_open_union +from .audiocontent import AudioContent, AudioContentParam +from .documentcontent import DocumentContent, DocumentContentParam +from .imagecontent import ImageContent, ImageContentParam +from .textcontent import TextContent, TextContentParam +from .videocontent import VideoContent, VideoContentParam +from functools import partial +from pydantic import ConfigDict +from pydantic.functional_validators import BeforeValidator +from typing import Any, Literal, Union +from typing_extensions import Annotated, TypeAliasType + + +ContentParam = TypeAliasType( + "ContentParam", + Union[ + TextContentParam, + DocumentContentParam, + ImageContentParam, + VideoContentParam, + AudioContentParam, + ], +) +r"""The content of the response.""" + + +class UnknownContent(BaseModel): + r"""A Content variant the SDK doesn't recognize. Preserves the raw payload.""" + + type: Literal["UNKNOWN"] = "UNKNOWN" + raw: Any + is_unknown: Literal[True] = True + + model_config = ConfigDict(frozen=True) + + +_CONTENT_VARIANTS: dict[str, Any] = { + "text": TextContent, + "image": ImageContent, + "audio": AudioContent, + "document": DocumentContent, + "video": VideoContent, +} + + +Content = Annotated[ + Union[ + TextContent, + ImageContent, + AudioContent, + DocumentContent, + VideoContent, + UnknownContent, + ], + BeforeValidator( + partial( + parse_open_union, + disc_key="type", + variants=_CONTENT_VARIANTS, + unknown_cls=UnknownContent, + union_name="Content", + ) + ), +] +r"""The content of the response.""" diff --git a/google/genai/_gaos/types/interactions/createagentinteraction.py b/google/genai/_gaos/types/interactions/createagentinteraction.py new file mode 100644 index 000000000..bfabcbffd --- /dev/null +++ b/google/genai/_gaos/types/interactions/createagentinteraction.py @@ -0,0 +1,201 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .agentoption import AgentOption +from .deepresearchagentconfig import ( + DeepResearchAgentConfig, + DeepResearchAgentConfigParam, +) +from .dynamicagentconfig import DynamicAgentConfig, DynamicAgentConfigParam +from .environment import Environment, EnvironmentParam +from .interactionsinput import InteractionsInput, InteractionsInputParam +from .responseformat import ResponseFormat, ResponseFormatParam +from .responsemodality import ResponseModality +from .servicetier import ServiceTier +from .tool import Tool, ToolParam +from .usage import Usage, UsageParam +from .webhookconfig import WebhookConfig, WebhookConfigParam +import pydantic +from pydantic import Field, model_serializer +from typing import List, Optional, Union +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict + + +CreateAgentInteractionEnvironmentParam = TypeAliasType( + "CreateAgentInteractionEnvironmentParam", Union[EnvironmentParam, str] +) +r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + + +CreateAgentInteractionEnvironment = TypeAliasType( + "CreateAgentInteractionEnvironment", Union[Environment, str] +) +r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + + +CreateAgentInteractionAgentConfigParam = TypeAliasType( + "CreateAgentInteractionAgentConfigParam", + Union[DynamicAgentConfigParam, DeepResearchAgentConfigParam], +) +r"""Configuration parameters for the agent interaction.""" + + +CreateAgentInteractionAgentConfig = Annotated[ + Union[DynamicAgentConfig, DeepResearchAgentConfig], Field(discriminator="type") +] +r"""Configuration parameters for the agent interaction.""" + + +CreateAgentInteractionResponseFormatParam = TypeAliasType( + "CreateAgentInteractionResponseFormatParam", + Union[List[ResponseFormatParam], ResponseFormatParam], +) +r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + +CreateAgentInteractionResponseFormat = TypeAliasType( + "CreateAgentInteractionResponseFormat", Union[List[ResponseFormat], ResponseFormat] +) +r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + +class CreateAgentInteractionParam(TypedDict): + r"""Parameters for creating agent interactions""" + + agent: AgentOption + r"""The agent to interact with.""" + input: InteractionsInputParam + r"""The input for the interaction.""" + background: NotRequired[bool] + r"""Input only. Whether to run the model interaction in the background.""" + store: NotRequired[bool] + r"""Input only. Whether to store the response and request for later retrieval.""" + stream: NotRequired[bool] + r"""Input only. Whether the interaction will be streamed.""" + environment: NotRequired[CreateAgentInteractionEnvironmentParam] + r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + previous_interaction_id: NotRequired[str] + r"""The ID of the previous interaction, if any.""" + response_mime_type: NotRequired[str] + r"""The mime type of the response. This is required if response_format is set.""" + response_modalities: NotRequired[List[ResponseModality]] + r"""The requested modalities of the response (TEXT, IMAGE, AUDIO).""" + service_tier: NotRequired[ServiceTier] + system_instruction: NotRequired[str] + r"""System instruction for the interaction.""" + tools: NotRequired[List[ToolParam]] + r"""A list of tool declarations the model may call during interaction.""" + usage: NotRequired[UsageParam] + r"""Statistics on the interaction request's token usage.""" + webhook_config: NotRequired[WebhookConfigParam] + r"""Message for configuring webhook events for a request.""" + agent_config: NotRequired[CreateAgentInteractionAgentConfigParam] + r"""Configuration parameters for the agent interaction.""" + response_format: NotRequired[CreateAgentInteractionResponseFormatParam] + r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + +class CreateAgentInteraction(BaseModel): + r"""Parameters for creating agent interactions""" + + agent: AgentOption + r"""The agent to interact with.""" + + input: InteractionsInput + r"""The input for the interaction.""" + + background: Optional[bool] = None + r"""Input only. Whether to run the model interaction in the background.""" + + store: Optional[bool] = None + r"""Input only. Whether to store the response and request for later retrieval.""" + + stream: Optional[bool] = None + r"""Input only. Whether the interaction will be streamed.""" + + environment: Optional[CreateAgentInteractionEnvironment] = None + r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + + previous_interaction_id: Optional[str] = None + r"""The ID of the previous interaction, if any.""" + + response_mime_type: Annotated[ + Optional[str], + pydantic.Field( + deprecated="warning: ** DEPRECATED ** - This will be removed in a future release, please migrate away from it as soon as possible." + ), + ] = None + r"""The mime type of the response. This is required if response_format is set.""" + + response_modalities: Optional[List[ResponseModality]] = None + r"""The requested modalities of the response (TEXT, IMAGE, AUDIO).""" + + service_tier: Optional[ServiceTier] = None + + system_instruction: Optional[str] = None + r"""System instruction for the interaction.""" + + tools: Optional[List[Tool]] = None + r"""A list of tool declarations the model may call during interaction.""" + + usage: Optional[Usage] = None + r"""Statistics on the interaction request's token usage.""" + + webhook_config: Optional[WebhookConfig] = None + r"""Message for configuring webhook events for a request.""" + + agent_config: Optional[CreateAgentInteractionAgentConfig] = None + r"""Configuration parameters for the agent interaction.""" + + response_format: Optional[CreateAgentInteractionResponseFormat] = None + r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + [ + "background", + "store", + "stream", + "environment", + "previous_interaction_id", + "response_mime_type", + "response_modalities", + "service_tier", + "system_instruction", + "tools", + "usage", + "webhook_config", + "agent_config", + "response_format", + ] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/createmodelinteraction.py b/google/genai/_gaos/types/interactions/createmodelinteraction.py new file mode 100644 index 000000000..35acaca99 --- /dev/null +++ b/google/genai/_gaos/types/interactions/createmodelinteraction.py @@ -0,0 +1,184 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .environment import Environment, EnvironmentParam +from .generationconfig import GenerationConfig, GenerationConfigParam +from .interactionsinput import InteractionsInput, InteractionsInputParam +from .model import Model +from .responseformat import ResponseFormat, ResponseFormatParam +from .responsemodality import ResponseModality +from .servicetier import ServiceTier +from .tool import Tool, ToolParam +from .usage import Usage, UsageParam +from .webhookconfig import WebhookConfig, WebhookConfigParam +import pydantic +from pydantic import model_serializer +from typing import List, Optional, Union +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict + + +CreateModelInteractionEnvironmentParam = TypeAliasType( + "CreateModelInteractionEnvironmentParam", Union[EnvironmentParam, str] +) +r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + + +CreateModelInteractionEnvironment = TypeAliasType( + "CreateModelInteractionEnvironment", Union[Environment, str] +) +r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + + +CreateModelInteractionResponseFormatParam = TypeAliasType( + "CreateModelInteractionResponseFormatParam", + Union[List[ResponseFormatParam], ResponseFormatParam], +) +r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + +CreateModelInteractionResponseFormat = TypeAliasType( + "CreateModelInteractionResponseFormat", Union[List[ResponseFormat], ResponseFormat] +) +r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + +class CreateModelInteractionParam(TypedDict): + r"""Parameters for creating model interactions""" + + model: Model + r"""The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details.""" + input: InteractionsInputParam + r"""The input for the interaction.""" + background: NotRequired[bool] + r"""Input only. Whether to run the model interaction in the background.""" + store: NotRequired[bool] + r"""Input only. Whether to store the response and request for later retrieval.""" + stream: NotRequired[bool] + r"""Input only. Whether the interaction will be streamed.""" + environment: NotRequired[CreateModelInteractionEnvironmentParam] + r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + previous_interaction_id: NotRequired[str] + r"""The ID of the previous interaction, if any.""" + response_mime_type: NotRequired[str] + r"""The mime type of the response. This is required if response_format is set.""" + response_modalities: NotRequired[List[ResponseModality]] + r"""The requested modalities of the response (TEXT, IMAGE, AUDIO).""" + service_tier: NotRequired[ServiceTier] + system_instruction: NotRequired[str] + r"""System instruction for the interaction.""" + tools: NotRequired[List[ToolParam]] + r"""A list of tool declarations the model may call during interaction.""" + usage: NotRequired[UsageParam] + r"""Statistics on the interaction request's token usage.""" + webhook_config: NotRequired[WebhookConfigParam] + r"""Message for configuring webhook events for a request.""" + generation_config: NotRequired[GenerationConfigParam] + r"""Configuration parameters for model interactions.""" + response_format: NotRequired[CreateModelInteractionResponseFormatParam] + r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + +class CreateModelInteraction(BaseModel): + r"""Parameters for creating model interactions""" + + model: Model + r"""The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details.""" + + input: InteractionsInput + r"""The input for the interaction.""" + + background: Optional[bool] = None + r"""Input only. Whether to run the model interaction in the background.""" + + store: Optional[bool] = None + r"""Input only. Whether to store the response and request for later retrieval.""" + + stream: Optional[bool] = None + r"""Input only. Whether the interaction will be streamed.""" + + environment: Optional[CreateModelInteractionEnvironment] = None + r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + + previous_interaction_id: Optional[str] = None + r"""The ID of the previous interaction, if any.""" + + response_mime_type: Annotated[ + Optional[str], + pydantic.Field( + deprecated="warning: ** DEPRECATED ** - This will be removed in a future release, please migrate away from it as soon as possible." + ), + ] = None + r"""The mime type of the response. This is required if response_format is set.""" + + response_modalities: Optional[List[ResponseModality]] = None + r"""The requested modalities of the response (TEXT, IMAGE, AUDIO).""" + + service_tier: Optional[ServiceTier] = None + + system_instruction: Optional[str] = None + r"""System instruction for the interaction.""" + + tools: Optional[List[Tool]] = None + r"""A list of tool declarations the model may call during interaction.""" + + usage: Optional[Usage] = None + r"""Statistics on the interaction request's token usage.""" + + webhook_config: Optional[WebhookConfig] = None + r"""Message for configuring webhook events for a request.""" + + generation_config: Optional[GenerationConfig] = None + r"""Configuration parameters for model interactions.""" + + response_format: Optional[CreateModelInteractionResponseFormat] = None + r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + @model_serializer(mode="wrap") + def _serialize_model(self, handler): + optional_fields = set( + [ + "background", + "store", + "stream", + "environment", + "previous_interaction_id", + "response_mime_type", + "response_modalities", + "service_tier", + "system_instruction", + "tools", + "usage", + "webhook_config", + "generation_config", + "response_format", + ] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/deepresearchagentconfig.py b/google/genai/_gaos/types/interactions/deepresearchagentconfig.py new file mode 100644 index 000000000..fd1989c0c --- /dev/null +++ b/google/genai/_gaos/types/interactions/deepresearchagentconfig.py @@ -0,0 +1,99 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils import validate_const +from .thinkingsummaries import ThinkingSummaries +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +Visualization = Union[ + Literal[ + "off", + "auto", + ], + UnrecognizedStr, +] +r"""Whether to include visualizations in the response.""" + + +class DeepResearchAgentConfigParam(TypedDict): + r"""Configuration for the Deep Research agent.""" + + collaborative_planning: NotRequired[bool] + r"""Enables human-in-the-loop planning for the Deep Research agent. If set to + true, the Deep Research agent will provide a research plan in its response. + The agent will then proceed only if the user confirms the plan in the next + turn. + """ + thinking_summaries: NotRequired[ThinkingSummaries] + type: Literal["deep-research"] + visualization: NotRequired[Visualization] + r"""Whether to include visualizations in the response.""" + + +class DeepResearchAgentConfig(BaseModel): + r"""Configuration for the Deep Research agent.""" + + collaborative_planning: Optional[bool] = None + r"""Enables human-in-the-loop planning for the Deep Research agent. If set to + true, the Deep Research agent will provide a research plan in its response. + The agent will then proceed only if the user confirms the plan in the next + turn. + """ + + thinking_summaries: Optional[ThinkingSummaries] = None + + type: Annotated[ + Annotated[ + Literal["deep-research"], AfterValidator(validate_const("deep-research")) + ], + pydantic.Field(alias="type"), + ] = "deep-research" + + visualization: Optional[Visualization] = None + r"""Whether to include visualizations in the response.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + ["collaborative_planning", "thinking_summaries", "visualization"] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + DeepResearchAgentConfig.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/documentcontent.py b/google/genai/_gaos/types/interactions/documentcontent.py new file mode 100644 index 000000000..ad2b333bc --- /dev/null +++ b/google/genai/_gaos/types/interactions/documentcontent.py @@ -0,0 +1,94 @@ +# 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 __future__ import annotations +from .. import ( + Base64EncodedString, + Base64FileInput, + BaseModel, + UNSET_SENTINEL, + UnrecognizedStr, +) +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +DocumentContentMimeType = Union[ + Literal[ + "application/pdf", + "text/csv", + ], + UnrecognizedStr, +] +r"""The mime type of the document.""" + + +class DocumentContentParam(TypedDict): + r"""A document content block.""" + + type: Literal["document"] + data: NotRequired[Union[str, Base64FileInput]] + r"""The document content.""" + uri: NotRequired[str] + r"""The URI of the document.""" + mime_type: NotRequired[DocumentContentMimeType] + r"""The mime type of the document.""" + + +class DocumentContent(BaseModel): + r"""A document content block.""" + + type: Annotated[ + Annotated[Literal["document"], AfterValidator(validate_const("document"))], + pydantic.Field(alias="type"), + ] = "document" + + data: Optional[Base64EncodedString] = None + r"""The document content.""" + + uri: Optional[str] = None + r"""The URI of the document.""" + + mime_type: Optional[DocumentContentMimeType] = None + r"""The mime type of the document.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["data", "uri", "mime_type"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + DocumentContent.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/documentdelta.py b/google/genai/_gaos/types/interactions/documentdelta.py new file mode 100644 index 000000000..a99b408ae --- /dev/null +++ b/google/genai/_gaos/types/interactions/documentdelta.py @@ -0,0 +1,77 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +DocumentDeltaMimeType = Union[ + Literal[ + "application/pdf", + "text/csv", + ], + UnrecognizedStr, +] + + +class DocumentDeltaTypedDict(TypedDict): + type: Literal["document"] + data: NotRequired[str] + uri: NotRequired[str] + mime_type: NotRequired[DocumentDeltaMimeType] + + +class DocumentDelta(BaseModel): + type: Annotated[ + Annotated[Literal["document"], AfterValidator(validate_const("document"))], + pydantic.Field(alias="type"), + ] = "document" + + data: Optional[str] = None + + uri: Optional[str] = None + + mime_type: Optional[DocumentDeltaMimeType] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["data", "uri", "mime_type"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + DocumentDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/dynamicagentconfig.py b/google/genai/_gaos/types/interactions/dynamicagentconfig.py new file mode 100644 index 000000000..f712c0f63 --- /dev/null +++ b/google/genai/_gaos/types/interactions/dynamicagentconfig.py @@ -0,0 +1,60 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils import validate_const +import pydantic +from pydantic import ConfigDict +from pydantic.functional_validators import AfterValidator +from typing import Any, Dict, Literal +from typing_extensions import Annotated, TypedDict + + +class DynamicAgentConfigParam(TypedDict): + r"""Configuration for dynamic agents.""" + + type: Literal["dynamic"] + + +class DynamicAgentConfig(BaseModel): + r"""Configuration for dynamic agents.""" + + model_config = ConfigDict( + populate_by_name=True, arbitrary_types_allowed=True, extra="allow" + ) + __pydantic_extra__: Dict[str, Any] = pydantic.Field(init=False) + + type: Annotated[ + Annotated[Literal["dynamic"], AfterValidator(validate_const("dynamic"))], + pydantic.Field(alias="type"), + ] = "dynamic" + + @property + def additional_properties(self): + return self.__pydantic_extra__ + + @additional_properties.setter + def additional_properties(self, value): + self.__pydantic_extra__ = value # pyright: ignore[reportIncompatibleVariableOverride] + + +try: + DynamicAgentConfig.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/empty.py b/google/genai/_gaos/types/interactions/empty.py new file mode 100644 index 000000000..8165e89a4 --- /dev/null +++ b/google/genai/_gaos/types/interactions/empty.py @@ -0,0 +1,43 @@ +# 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 __future__ import annotations +from .. import BaseModel +from typing_extensions import TypedDict + + +class EmptyTypedDict(TypedDict): + r"""A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to use it as the request + or the response type of an API method. For instance: + + service Foo { + rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); + } + """ + + +class Empty(BaseModel): + r"""A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to use it as the request + or the response type of an API method. For instance: + + service Foo { + rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); + } + """ diff --git a/google/genai/_gaos/types/interactions/environment.py b/google/genai/_gaos/types/interactions/environment.py new file mode 100644 index 000000000..ab3bb77fc --- /dev/null +++ b/google/genai/_gaos/types/interactions/environment.py @@ -0,0 +1,91 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .environmentnetworkegressallowlist_union import ( + EnvironmentNetworkEgressAllowlistUnion, + EnvironmentNetworkEgressAllowlistUnionParam, +) +from .source import Source, SourceParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict + + +NetworkEnum = Literal["disabled",] + + +NetworkParam = TypeAliasType( + "NetworkParam", Union[EnvironmentNetworkEgressAllowlistUnionParam, NetworkEnum] +) +r"""Network configuration for the environment.""" + + +Network = TypeAliasType( + "Network", Union[EnvironmentNetworkEgressAllowlistUnion, NetworkEnum] +) +r"""Network configuration for the environment.""" + + +class EnvironmentParam(TypedDict): + r"""Configuration for a custom environment.""" + + type: Literal["remote"] + sources: NotRequired[List[SourceParam]] + network: NotRequired[NetworkParam] + r"""Network configuration for the environment.""" + + +class Environment(BaseModel): + r"""Configuration for a custom environment.""" + + type: Annotated[ + Annotated[Literal["remote"], AfterValidator(validate_const("remote"))], + pydantic.Field(alias="type"), + ] = "remote" + + sources: Optional[List[Source]] = None + + network: Optional[Network] = None + r"""Network configuration for the environment.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["sources", "network"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + Environment.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/environmentnetworkegressallowlist_union.py b/google/genai/_gaos/types/interactions/environmentnetworkegressallowlist_union.py new file mode 100644 index 000000000..94990ab5b --- /dev/null +++ b/google/genai/_gaos/types/interactions/environmentnetworkegressallowlist_union.py @@ -0,0 +1,74 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .allowlistentry import AllowlistEntry, AllowlistEntryParam +from pydantic import model_serializer +from typing import List, Literal, Optional, Union +from typing_extensions import NotRequired, TypeAliasType, TypedDict + + +EnvironmentNetworkEgressAllowlistEnum = Literal["disabled",] +r"""Turns all network off.""" + + +class EnvironmentNetworkEgressAllowlistParam(TypedDict): + r"""Outbound networking configuration for the sandbox. When specified, restricts which external domains the sandbox can reach. Omit entirely to allow all outbound traffic with no header injection.""" + + allowlist: NotRequired[List[AllowlistEntryParam]] + r"""List of allowed outbound domains. Only requests to listed domains are permitted. Use [{'domain': '*'}] to allow all domains while still injecting headers on specific ones.""" + + +class EnvironmentNetworkEgressAllowlist(BaseModel): + r"""Outbound networking configuration for the sandbox. When specified, restricts which external domains the sandbox can reach. Omit entirely to allow all outbound traffic with no header injection.""" + + allowlist: Optional[List[AllowlistEntry]] = None + r"""List of allowed outbound domains. Only requests to listed domains are permitted. Use [{'domain': '*'}] to allow all domains while still injecting headers on specific ones.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["allowlist"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +EnvironmentNetworkEgressAllowlistUnionParam = TypeAliasType( + "EnvironmentNetworkEgressAllowlistUnionParam", + Union[ + EnvironmentNetworkEgressAllowlistParam, EnvironmentNetworkEgressAllowlistEnum + ], +) +r"""Outbound networking configuration for the sandbox. Accepts an object with an 'allowlist' array to restrict traffic, or the string 'disabled' to turn off all network access. Omit entirely to allow all outbound traffic with no header injection.""" + + +EnvironmentNetworkEgressAllowlistUnion = TypeAliasType( + "EnvironmentNetworkEgressAllowlistUnion", + Union[EnvironmentNetworkEgressAllowlist, EnvironmentNetworkEgressAllowlistEnum], +) +r"""Outbound networking configuration for the sandbox. Accepts an object with an 'allowlist' array to restrict traffic, or the string 'disabled' to turn off all network access. Omit entirely to allow all outbound traffic with no header injection.""" diff --git a/google/genai/_gaos/types/interactions/error.py b/google/genai/_gaos/types/interactions/error.py new file mode 100644 index 000000000..83b78b497 --- /dev/null +++ b/google/genai/_gaos/types/interactions/error.py @@ -0,0 +1,58 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import Optional +from typing_extensions import NotRequired, TypedDict + + +class ErrorTypedDict(TypedDict): + r"""Error message from an interaction.""" + + code: NotRequired[str] + r"""A URI that identifies the error type.""" + message: NotRequired[str] + r"""A human-readable error message.""" + + +class Error(BaseModel): + r"""Error message from an interaction.""" + + code: Optional[str] = None + r"""A URI that identifies the error type.""" + + message: Optional[str] = None + r"""A human-readable error message.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["code", "message"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/errorevent.py b/google/genai/_gaos/types/interactions/errorevent.py new file mode 100644 index 000000000..6982b65a4 --- /dev/null +++ b/google/genai/_gaos/types/interactions/errorevent.py @@ -0,0 +1,110 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .error import Error, ErrorTypedDict +from .usage import Usage, UsageParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ErrorEventMetadataTypedDict(TypedDict): + r"""Optional metadata accompanying ANY streamed event.""" + + total_usage: NotRequired[UsageParam] + r"""Statistics on the interaction request's token usage.""" + + +class ErrorEventMetadata(BaseModel): + r"""Optional metadata accompanying ANY streamed event.""" + + total_usage: Optional[Usage] = None + r"""Statistics on the interaction request's token usage.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["total_usage"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class ErrorEventTypedDict(TypedDict): + event_type: Literal["error"] + error: NotRequired[ErrorTypedDict] + r"""Error message from an interaction.""" + event_id: NotRequired[str] + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + metadata: NotRequired[ErrorEventMetadataTypedDict] + r"""Optional metadata accompanying ANY streamed event.""" + + +class ErrorEvent(BaseModel): + event_type: Annotated[ + Annotated[Literal["error"], AfterValidator(validate_const("error"))], + pydantic.Field(alias="event_type"), + ] = "error" + + error: Optional[Error] = None + r"""Error message from an interaction.""" + + event_id: Optional[str] = None + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + + metadata: Optional[ErrorEventMetadata] = None + r"""Optional metadata accompanying ANY streamed event.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["error", "event_id", "metadata"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + ErrorEvent.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/filecitation.py b/google/genai/_gaos/types/interactions/filecitation.py new file mode 100644 index 000000000..4b3de1127 --- /dev/null +++ b/google/genai/_gaos/types/interactions/filecitation.py @@ -0,0 +1,122 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Any, Dict, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class FileCitationParam(TypedDict): + r"""A file citation annotation.""" + + type: Literal["file_citation"] + document_uri: NotRequired[str] + r"""The URI of the file.""" + file_name: NotRequired[str] + r"""The name of the file.""" + source: NotRequired[str] + r"""Source attributed for a portion of the text.""" + custom_metadata: NotRequired[Dict[str, Any]] + r"""User provided metadata about the retrieved context.""" + page_number: NotRequired[int] + r"""Page number of the cited document, if applicable.""" + media_id: NotRequired[str] + r"""Media ID in-case of image citations, if applicable.""" + start_index: NotRequired[int] + r"""Start of segment of the response that is attributed to this source. + + Index indicates the start of the segment, measured in bytes. + """ + end_index: NotRequired[int] + r"""End of the attributed segment, exclusive.""" + + +class FileCitation(BaseModel): + r"""A file citation annotation.""" + + type: Annotated[ + Annotated[ + Literal["file_citation"], AfterValidator(validate_const("file_citation")) + ], + pydantic.Field(alias="type"), + ] = "file_citation" + + document_uri: Optional[str] = None + r"""The URI of the file.""" + + file_name: Optional[str] = None + r"""The name of the file.""" + + source: Optional[str] = None + r"""Source attributed for a portion of the text.""" + + custom_metadata: Optional[Dict[str, Any]] = None + r"""User provided metadata about the retrieved context.""" + + page_number: Optional[int] = None + r"""Page number of the cited document, if applicable.""" + + media_id: Optional[str] = None + r"""Media ID in-case of image citations, if applicable.""" + + start_index: Optional[int] = None + r"""Start of segment of the response that is attributed to this source. + + Index indicates the start of the segment, measured in bytes. + """ + + end_index: Optional[int] = None + r"""End of the attributed segment, exclusive.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + [ + "document_uri", + "file_name", + "source", + "custom_metadata", + "page_number", + "media_id", + "start_index", + "end_index", + ] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + FileCitation.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/filesearch.py b/google/genai/_gaos/types/interactions/filesearch.py new file mode 100644 index 000000000..e01d50a9b --- /dev/null +++ b/google/genai/_gaos/types/interactions/filesearch.py @@ -0,0 +1,80 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class FileSearchParam(TypedDict): + r"""A tool that can be used by the model to search files.""" + + type: Literal["file_search"] + file_search_store_names: NotRequired[List[str]] + r"""The file search store names to search.""" + top_k: NotRequired[int] + r"""The number of semantic retrieval chunks to retrieve.""" + metadata_filter: NotRequired[str] + r"""Metadata filter to apply to the semantic retrieval documents and chunks.""" + + +class FileSearch(BaseModel): + r"""A tool that can be used by the model to search files.""" + + type: Annotated[ + Annotated[ + Literal["file_search"], AfterValidator(validate_const("file_search")) + ], + pydantic.Field(alias="type"), + ] = "file_search" + + file_search_store_names: Optional[List[str]] = None + r"""The file search store names to search.""" + + top_k: Optional[int] = None + r"""The number of semantic retrieval chunks to retrieve.""" + + metadata_filter: Optional[str] = None + r"""Metadata filter to apply to the semantic retrieval documents and chunks.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["file_search_store_names", "top_k", "metadata_filter"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + FileSearch.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/filesearchcalldelta.py b/google/genai/_gaos/types/interactions/filesearchcalldelta.py new file mode 100644 index 000000000..53f1c41c4 --- /dev/null +++ b/google/genai/_gaos/types/interactions/filesearchcalldelta.py @@ -0,0 +1,67 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class FileSearchCallDeltaTypedDict(TypedDict): + type: Literal["file_search_call"] + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class FileSearchCallDelta(BaseModel): + type: Annotated[ + Annotated[ + Literal["file_search_call"], + AfterValidator(validate_const("file_search_call")), + ], + pydantic.Field(alias="type"), + ] = "file_search_call" + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + FileSearchCallDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/filesearchcallstep.py b/google/genai/_gaos/types/interactions/filesearchcallstep.py new file mode 100644 index 000000000..aaf56b681 --- /dev/null +++ b/google/genai/_gaos/types/interactions/filesearchcallstep.py @@ -0,0 +1,76 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class FileSearchCallStepParam(TypedDict): + r"""File Search call step.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + type: Literal["file_search_call"] + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + + +class FileSearchCallStep(BaseModel): + r"""File Search call step.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + + type: Annotated[ + Annotated[ + Literal["file_search_call"], + AfterValidator(validate_const("file_search_call")), + ], + pydantic.Field(alias="type"), + ] = "file_search_call" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + FileSearchCallStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/filesearchresult.py b/google/genai/_gaos/types/interactions/filesearchresult.py new file mode 100644 index 000000000..55fb63bcd --- /dev/null +++ b/google/genai/_gaos/types/interactions/filesearchresult.py @@ -0,0 +1,29 @@ +# 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 __future__ import annotations +from .. import BaseModel +from typing_extensions import TypedDict + + +class FileSearchResultTypedDict(TypedDict): + r"""The result of the File Search.""" + + +class FileSearchResult(BaseModel): + r"""The result of the File Search.""" diff --git a/google/genai/_gaos/types/interactions/filesearchresultdelta.py b/google/genai/_gaos/types/interactions/filesearchresultdelta.py new file mode 100644 index 000000000..e1cd83a4d --- /dev/null +++ b/google/genai/_gaos/types/interactions/filesearchresultdelta.py @@ -0,0 +1,71 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .filesearchresult import FileSearchResult, FileSearchResultTypedDict +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class FileSearchResultDeltaTypedDict(TypedDict): + result: List[FileSearchResultTypedDict] + type: Literal["file_search_result"] + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class FileSearchResultDelta(BaseModel): + result: List[FileSearchResult] + + type: Annotated[ + Annotated[ + Literal["file_search_result"], + AfterValidator(validate_const("file_search_result")), + ], + pydantic.Field(alias="type"), + ] = "file_search_result" + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + FileSearchResultDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/filesearchresultstep.py b/google/genai/_gaos/types/interactions/filesearchresultstep.py new file mode 100644 index 000000000..a6a70ca4d --- /dev/null +++ b/google/genai/_gaos/types/interactions/filesearchresultstep.py @@ -0,0 +1,76 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class FileSearchResultStepParam(TypedDict): + r"""File Search result step.""" + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + type: Literal["file_search_result"] + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + + +class FileSearchResultStep(BaseModel): + r"""File Search result step.""" + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + + type: Annotated[ + Annotated[ + Literal["file_search_result"], + AfterValidator(validate_const("file_search_result")), + ], + pydantic.Field(alias="type"), + ] = "file_search_result" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + FileSearchResultStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/function.py b/google/genai/_gaos/types/interactions/function.py new file mode 100644 index 000000000..83822c568 --- /dev/null +++ b/google/genai/_gaos/types/interactions/function.py @@ -0,0 +1,78 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Any, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class FunctionParam(TypedDict): + r"""A tool that can be used by the model.""" + + description: NotRequired[str] + r"""A description of the function.""" + name: NotRequired[str] + r"""The name of the function.""" + parameters: NotRequired[Any] + r"""The JSON Schema for the function's parameters.""" + type: Literal["function"] + + +class Function(BaseModel): + r"""A tool that can be used by the model.""" + + description: Optional[str] = None + r"""A description of the function.""" + + name: Optional[str] = None + r"""The name of the function.""" + + parameters: Optional[Any] = None + r"""The JSON Schema for the function's parameters.""" + + type: Annotated[ + Annotated[Literal["function"], AfterValidator(validate_const("function"))], + pydantic.Field(alias="type"), + ] = "function" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["description", "name", "parameters"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + Function.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/functioncallstep.py b/google/genai/_gaos/types/interactions/functioncallstep.py new file mode 100644 index 000000000..fba815def --- /dev/null +++ b/google/genai/_gaos/types/interactions/functioncallstep.py @@ -0,0 +1,85 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Any, Dict, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class FunctionCallStepParam(TypedDict): + r"""A function tool call step.""" + + name: str + r"""Required. The name of the tool to call.""" + arguments: Dict[str, Any] + r"""Required. The arguments to pass to the function.""" + id: str + r"""Required. A unique ID for this specific tool call.""" + type: Literal["function_call"] + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + + +class FunctionCallStep(BaseModel): + r"""A function tool call step.""" + + name: str + r"""Required. The name of the tool to call.""" + + arguments: Dict[str, Any] + r"""Required. The arguments to pass to the function.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + + type: Annotated[ + Annotated[ + Literal["function_call"], AfterValidator(validate_const("function_call")) + ], + pydantic.Field(alias="type"), + ] = "function_call" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + FunctionCallStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/functionresultdelta.py b/google/genai/_gaos/types/interactions/functionresultdelta.py new file mode 100644 index 000000000..8c6e1b22f --- /dev/null +++ b/google/genai/_gaos/types/interactions/functionresultdelta.py @@ -0,0 +1,97 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .content import Content, ContentParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict + + +class FunctionResultDeltaResultTypedDict(TypedDict): + pass + + +class FunctionResultDeltaResult(BaseModel): + pass + + +FunctionResultDeltaResultUnionTypedDict = TypeAliasType( + "FunctionResultDeltaResultUnionTypedDict", + Union[FunctionResultDeltaResultTypedDict, List[ContentParam], str], +) + + +FunctionResultDeltaResultUnion = TypeAliasType( + "FunctionResultDeltaResultUnion", + Union[FunctionResultDeltaResult, List[Content], str], +) + + +class FunctionResultDeltaTypedDict(TypedDict): + call_id: str + r"""Required. ID to match the ID from the function call block.""" + result: FunctionResultDeltaResultUnionTypedDict + type: Literal["function_result"] + name: NotRequired[str] + is_error: NotRequired[bool] + + +class FunctionResultDelta(BaseModel): + call_id: str + r"""Required. ID to match the ID from the function call block.""" + + result: FunctionResultDeltaResultUnion + + type: Annotated[ + Annotated[ + Literal["function_result"], + AfterValidator(validate_const("function_result")), + ], + pydantic.Field(alias="type"), + ] = "function_result" + + name: Optional[str] = None + + is_error: Optional[bool] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["name", "is_error"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + FunctionResultDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/functionresultstep.py b/google/genai/_gaos/types/interactions/functionresultstep.py new file mode 100644 index 000000000..2aa13bea7 --- /dev/null +++ b/google/genai/_gaos/types/interactions/functionresultstep.py @@ -0,0 +1,113 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .content import Content, ContentParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict + + +class FunctionResultStepResultParam(TypedDict): + pass + + +class FunctionResultStepResult(BaseModel): + pass + + +FunctionResultStepResultUnionParam = TypeAliasType( + "FunctionResultStepResultUnionParam", + Union[FunctionResultStepResultParam, List[ContentParam], str], +) +r"""The result of the tool call.""" + + +FunctionResultStepResultUnion = TypeAliasType( + "FunctionResultStepResultUnion", Union[FunctionResultStepResult, List[Content], str] +) +r"""The result of the tool call.""" + + +class FunctionResultStepParam(TypedDict): + r"""Result of a function tool call.""" + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + result: FunctionResultStepResultUnionParam + r"""The result of the tool call.""" + type: Literal["function_result"] + name: NotRequired[str] + r"""The name of the tool that was called.""" + is_error: NotRequired[bool] + r"""Whether the tool call resulted in an error.""" + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + + +class FunctionResultStep(BaseModel): + r"""Result of a function tool call.""" + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + + result: FunctionResultStepResultUnion + r"""The result of the tool call.""" + + type: Annotated[ + Annotated[ + Literal["function_result"], + AfterValidator(validate_const("function_result")), + ], + pydantic.Field(alias="type"), + ] = "function_result" + + name: Optional[str] = None + r"""The name of the tool that was called.""" + + is_error: Optional[bool] = None + r"""Whether the tool call resulted in an error.""" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["name", "is_error", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + FunctionResultStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/generationconfig.py b/google/genai/_gaos/types/interactions/generationconfig.py new file mode 100644 index 000000000..d47600584 --- /dev/null +++ b/google/genai/_gaos/types/interactions/generationconfig.py @@ -0,0 +1,123 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .imageconfig import ImageConfig, ImageConfigParam +from .speechconfig import SpeechConfig, SpeechConfigParam +from .thinkinglevel import ThinkingLevel +from .thinkingsummaries import ThinkingSummaries +from .toolchoiceconfig import ToolChoiceConfig, ToolChoiceConfigParam +from .toolchoicetype import ToolChoiceType +from pydantic import model_serializer +from typing import List, Optional, Union +from typing_extensions import NotRequired, TypeAliasType, TypedDict + + +ToolChoiceParam = TypeAliasType( + "ToolChoiceParam", Union[ToolChoiceConfigParam, ToolChoiceType] +) +r"""The tool choice configuration.""" + + +ToolChoice = TypeAliasType("ToolChoice", Union[ToolChoiceConfig, ToolChoiceType]) +r"""The tool choice configuration.""" + + +class GenerationConfigParam(TypedDict): + r"""Configuration parameters for model interactions.""" + + image_config: NotRequired[ImageConfigParam] + r"""The configuration for image interaction.""" + max_output_tokens: NotRequired[int] + r"""The maximum number of tokens to include in the response.""" + seed: NotRequired[int] + r"""Seed used in decoding for reproducibility.""" + speech_config: NotRequired[List[SpeechConfigParam]] + r"""Configuration for speech interaction.""" + stop_sequences: NotRequired[List[str]] + r"""A list of character sequences that will stop output interaction.""" + temperature: NotRequired[float] + r"""Controls the randomness of the output.""" + thinking_level: NotRequired[ThinkingLevel] + thinking_summaries: NotRequired[ThinkingSummaries] + tool_choice: NotRequired[ToolChoiceParam] + r"""The tool choice configuration.""" + top_p: NotRequired[float] + r"""The maximum cumulative probability of tokens to consider when sampling.""" + + +class GenerationConfig(BaseModel): + r"""Configuration parameters for model interactions.""" + + image_config: Optional[ImageConfig] = None + r"""The configuration for image interaction.""" + + max_output_tokens: Optional[int] = None + r"""The maximum number of tokens to include in the response.""" + + seed: Optional[int] = None + r"""Seed used in decoding for reproducibility.""" + + speech_config: Optional[List[SpeechConfig]] = None + r"""Configuration for speech interaction.""" + + stop_sequences: Optional[List[str]] = None + r"""A list of character sequences that will stop output interaction.""" + + temperature: Optional[float] = None + r"""Controls the randomness of the output.""" + + thinking_level: Optional[ThinkingLevel] = None + + thinking_summaries: Optional[ThinkingSummaries] = None + + tool_choice: Optional[ToolChoice] = None + r"""The tool choice configuration.""" + + top_p: Optional[float] = None + r"""The maximum cumulative probability of tokens to consider when sampling.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + [ + "image_config", + "max_output_tokens", + "seed", + "speech_config", + "stop_sequences", + "temperature", + "thinking_level", + "thinking_summaries", + "tool_choice", + "top_p", + ] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/googlemaps.py b/google/genai/_gaos/types/interactions/googlemaps.py new file mode 100644 index 000000000..b4a70e844 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlemaps.py @@ -0,0 +1,84 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GoogleMapsParam(TypedDict): + r"""A tool that can be used by the model to call Google Maps.""" + + type: Literal["google_maps"] + enable_widget: NotRequired[bool] + r"""Whether to return a widget context token in the tool call result of the + response. + """ + latitude: NotRequired[float] + r"""The latitude of the user's location.""" + longitude: NotRequired[float] + r"""The longitude of the user's location.""" + + +class GoogleMaps(BaseModel): + r"""A tool that can be used by the model to call Google Maps.""" + + type: Annotated[ + Annotated[ + Literal["google_maps"], AfterValidator(validate_const("google_maps")) + ], + pydantic.Field(alias="type"), + ] = "google_maps" + + enable_widget: Optional[bool] = None + r"""Whether to return a widget context token in the tool call result of the + response. + """ + + latitude: Optional[float] = None + r"""The latitude of the user's location.""" + + longitude: Optional[float] = None + r"""The longitude of the user's location.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["enable_widget", "latitude", "longitude"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + GoogleMaps.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/googlemapscallarguments.py b/google/genai/_gaos/types/interactions/googlemapscallarguments.py new file mode 100644 index 000000000..691c3da8d --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlemapscallarguments.py @@ -0,0 +1,53 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class GoogleMapsCallArgumentsParam(TypedDict): + r"""The arguments to pass to the Google Maps tool.""" + + queries: NotRequired[List[str]] + r"""The queries to be executed.""" + + +class GoogleMapsCallArguments(BaseModel): + r"""The arguments to pass to the Google Maps tool.""" + + queries: Optional[List[str]] = None + r"""The queries to be executed.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["queries"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/googlemapscalldelta.py b/google/genai/_gaos/types/interactions/googlemapscalldelta.py new file mode 100644 index 000000000..56f5c3cf8 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlemapscalldelta.py @@ -0,0 +1,76 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .googlemapscallarguments import ( + GoogleMapsCallArguments, + GoogleMapsCallArgumentsParam, +) +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GoogleMapsCallDeltaTypedDict(TypedDict): + arguments: NotRequired[GoogleMapsCallArgumentsParam] + r"""The arguments to pass to the Google Maps tool.""" + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + type: Literal["google_maps_call"] + + +class GoogleMapsCallDelta(BaseModel): + arguments: Optional[GoogleMapsCallArguments] = None + r"""The arguments to pass to the Google Maps tool.""" + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + type: Annotated[ + Annotated[ + Literal["google_maps_call"], + AfterValidator(validate_const("google_maps_call")), + ], + pydantic.Field(alias="type"), + ] = "google_maps_call" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["arguments", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + GoogleMapsCallDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/googlemapscallstep.py b/google/genai/_gaos/types/interactions/googlemapscallstep.py new file mode 100644 index 000000000..b00c3fa8e --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlemapscallstep.py @@ -0,0 +1,85 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .googlemapscallarguments import ( + GoogleMapsCallArguments, + GoogleMapsCallArgumentsParam, +) +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GoogleMapsCallStepParam(TypedDict): + r"""Google Maps call step.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + arguments: NotRequired[GoogleMapsCallArgumentsParam] + r"""The arguments to pass to the Google Maps tool.""" + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + type: Literal["google_maps_call"] + + +class GoogleMapsCallStep(BaseModel): + r"""Google Maps call step.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + + arguments: Optional[GoogleMapsCallArguments] = None + r"""The arguments to pass to the Google Maps tool.""" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + type: Annotated[ + Annotated[ + Literal["google_maps_call"], + AfterValidator(validate_const("google_maps_call")), + ], + pydantic.Field(alias="type"), + ] = "google_maps_call" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["arguments", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + GoogleMapsCallStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/googlemapsresult.py b/google/genai/_gaos/types/interactions/googlemapsresult.py new file mode 100644 index 000000000..abd335336 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlemapsresult.py @@ -0,0 +1,55 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .googlemapsresultplaces import GoogleMapsResultPlaces, GoogleMapsResultPlacesParam +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class GoogleMapsResultParam(TypedDict): + r"""The result of the Google Maps.""" + + places: NotRequired[List[GoogleMapsResultPlacesParam]] + widget_context_token: NotRequired[str] + + +class GoogleMapsResult(BaseModel): + r"""The result of the Google Maps.""" + + places: Optional[List[GoogleMapsResultPlaces]] = None + + widget_context_token: Optional[str] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["places", "widget_context_token"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/googlemapsresultdelta.py b/google/genai/_gaos/types/interactions/googlemapsresultdelta.py new file mode 100644 index 000000000..edc7d7bf2 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlemapsresultdelta.py @@ -0,0 +1,73 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .googlemapsresult import GoogleMapsResult, GoogleMapsResultParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GoogleMapsResultDeltaTypedDict(TypedDict): + type: Literal["google_maps_result"] + result: NotRequired[List[GoogleMapsResultParam]] + r"""The results of the Google Maps.""" + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class GoogleMapsResultDelta(BaseModel): + type: Annotated[ + Annotated[ + Literal["google_maps_result"], + AfterValidator(validate_const("google_maps_result")), + ], + pydantic.Field(alias="type"), + ] = "google_maps_result" + + result: Optional[List[GoogleMapsResult]] = None + r"""The results of the Google Maps.""" + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["result", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + GoogleMapsResultDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/googlemapsresultplaces.py b/google/genai/_gaos/types/interactions/googlemapsresultplaces.py new file mode 100644 index 000000000..981e430b9 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlemapsresultplaces.py @@ -0,0 +1,57 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .reviewsnippet import ReviewSnippet, ReviewSnippetParam +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class GoogleMapsResultPlacesParam(TypedDict): + place_id: NotRequired[str] + name: NotRequired[str] + url: NotRequired[str] + review_snippets: NotRequired[List[ReviewSnippetParam]] + + +class GoogleMapsResultPlaces(BaseModel): + place_id: Optional[str] = None + + name: Optional[str] = None + + url: Optional[str] = None + + review_snippets: Optional[List[ReviewSnippet]] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["place_id", "name", "url", "review_snippets"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/googlemapsresultstep.py b/google/genai/_gaos/types/interactions/googlemapsresultstep.py new file mode 100644 index 000000000..1617a82c0 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlemapsresultstep.py @@ -0,0 +1,80 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .googlemapsresult import GoogleMapsResult, GoogleMapsResultParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GoogleMapsResultStepParam(TypedDict): + r"""Google Maps result step.""" + + result: List[GoogleMapsResultParam] + call_id: str + r"""Required. ID to match the ID from the function call block.""" + type: Literal["google_maps_result"] + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + + +class GoogleMapsResultStep(BaseModel): + r"""Google Maps result step.""" + + result: List[GoogleMapsResult] + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + + type: Annotated[ + Annotated[ + Literal["google_maps_result"], + AfterValidator(validate_const("google_maps_result")), + ], + pydantic.Field(alias="type"), + ] = "google_maps_result" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + GoogleMapsResultStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/googlesearch.py b/google/genai/_gaos/types/interactions/googlesearch.py new file mode 100644 index 000000000..31cdd8e23 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlesearch.py @@ -0,0 +1,80 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +GoogleSearchSearchType = Union[ + Literal[ + "web_search", + "image_search", + "enterprise_web_search", + ], + UnrecognizedStr, +] + + +class GoogleSearchParam(TypedDict): + r"""A tool that can be used by the model to search Google.""" + + type: Literal["google_search"] + search_types: NotRequired[List[GoogleSearchSearchType]] + r"""The types of search grounding to enable.""" + + +class GoogleSearch(BaseModel): + r"""A tool that can be used by the model to search Google.""" + + type: Annotated[ + Annotated[ + Literal["google_search"], AfterValidator(validate_const("google_search")) + ], + pydantic.Field(alias="type"), + ] = "google_search" + + search_types: Optional[List[GoogleSearchSearchType]] = None + r"""The types of search grounding to enable.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["search_types"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + GoogleSearch.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/googlesearchcallarguments.py b/google/genai/_gaos/types/interactions/googlesearchcallarguments.py new file mode 100644 index 000000000..9d18de7a6 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlesearchcallarguments.py @@ -0,0 +1,53 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class GoogleSearchCallArgumentsParam(TypedDict): + r"""The arguments to pass to Google Search.""" + + queries: NotRequired[List[str]] + r"""Web search queries for the following-up web search.""" + + +class GoogleSearchCallArguments(BaseModel): + r"""The arguments to pass to Google Search.""" + + queries: Optional[List[str]] = None + r"""Web search queries for the following-up web search.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["queries"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/googlesearchcalldelta.py b/google/genai/_gaos/types/interactions/googlesearchcalldelta.py new file mode 100644 index 000000000..44b54601c --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlesearchcalldelta.py @@ -0,0 +1,76 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .googlesearchcallarguments import ( + GoogleSearchCallArguments, + GoogleSearchCallArgumentsParam, +) +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GoogleSearchCallDeltaTypedDict(TypedDict): + arguments: GoogleSearchCallArgumentsParam + r"""The arguments to pass to Google Search.""" + type: Literal["google_search_call"] + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class GoogleSearchCallDelta(BaseModel): + arguments: GoogleSearchCallArguments + r"""The arguments to pass to Google Search.""" + + type: Annotated[ + Annotated[ + Literal["google_search_call"], + AfterValidator(validate_const("google_search_call")), + ], + pydantic.Field(alias="type"), + ] = "google_search_call" + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + GoogleSearchCallDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/googlesearchcallstep.py b/google/genai/_gaos/types/interactions/googlesearchcallstep.py new file mode 100644 index 000000000..74c67b303 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlesearchcallstep.py @@ -0,0 +1,107 @@ +# 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 __future__ import annotations +from .. import ( + Base64EncodedString, + Base64FileInput, + BaseModel, + UNSET_SENTINEL, + UnrecognizedStr, +) +from ...utils import validate_const +from .googlesearchcallarguments import ( + GoogleSearchCallArguments, + GoogleSearchCallArgumentsParam, +) +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +GoogleSearchCallStepSearchType = Union[ + Literal[ + "web_search", + "image_search", + "enterprise_web_search", + ], + UnrecognizedStr, +] +r"""The type of search grounding enabled.""" + + +class GoogleSearchCallStepParam(TypedDict): + r"""Google Search call step.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + arguments: NotRequired[GoogleSearchCallArgumentsParam] + r"""The arguments to pass to Google Search.""" + search_type: NotRequired[GoogleSearchCallStepSearchType] + r"""The type of search grounding enabled.""" + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + type: Literal["google_search_call"] + + +class GoogleSearchCallStep(BaseModel): + r"""Google Search call step.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + + arguments: Optional[GoogleSearchCallArguments] = None + r"""The arguments to pass to Google Search.""" + + search_type: Optional[GoogleSearchCallStepSearchType] = None + r"""The type of search grounding enabled.""" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + type: Annotated[ + Annotated[ + Literal["google_search_call"], + AfterValidator(validate_const("google_search_call")), + ], + pydantic.Field(alias="type"), + ] = "google_search_call" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["arguments", "search_type", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + GoogleSearchCallStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/googlesearchresult.py b/google/genai/_gaos/types/interactions/googlesearchresult.py new file mode 100644 index 000000000..a6533dd6c --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlesearchresult.py @@ -0,0 +1,53 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import Optional +from typing_extensions import NotRequired, TypedDict + + +class GoogleSearchResultParam(TypedDict): + r"""The result of the Google Search.""" + + search_suggestions: NotRequired[str] + r"""Web content snippet that can be embedded in a web page or an app webview.""" + + +class GoogleSearchResult(BaseModel): + r"""The result of the Google Search.""" + + search_suggestions: Optional[str] = None + r"""Web content snippet that can be embedded in a web page or an app webview.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["search_suggestions"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/googlesearchresultdelta.py b/google/genai/_gaos/types/interactions/googlesearchresultdelta.py new file mode 100644 index 000000000..515a19932 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlesearchresultdelta.py @@ -0,0 +1,74 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .googlesearchresult import GoogleSearchResult, GoogleSearchResultParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GoogleSearchResultDeltaTypedDict(TypedDict): + result: List[GoogleSearchResultParam] + type: Literal["google_search_result"] + is_error: NotRequired[bool] + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class GoogleSearchResultDelta(BaseModel): + result: List[GoogleSearchResult] + + type: Annotated[ + Annotated[ + Literal["google_search_result"], + AfterValidator(validate_const("google_search_result")), + ], + pydantic.Field(alias="type"), + ] = "google_search_result" + + is_error: Optional[bool] = None + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["is_error", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + GoogleSearchResultDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/googlesearchresultstep.py b/google/genai/_gaos/types/interactions/googlesearchresultstep.py new file mode 100644 index 000000000..fdc81d7f7 --- /dev/null +++ b/google/genai/_gaos/types/interactions/googlesearchresultstep.py @@ -0,0 +1,87 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .googlesearchresult import GoogleSearchResult, GoogleSearchResultParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GoogleSearchResultStepParam(TypedDict): + r"""Google Search result step.""" + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + type: Literal["google_search_result"] + result: NotRequired[List[GoogleSearchResultParam]] + r"""The results of the Google Search.""" + is_error: NotRequired[bool] + r"""Whether the Google Search resulted in an error.""" + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + + +class GoogleSearchResultStep(BaseModel): + r"""Google Search result step.""" + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + + type: Annotated[ + Annotated[ + Literal["google_search_result"], + AfterValidator(validate_const("google_search_result")), + ], + pydantic.Field(alias="type"), + ] = "google_search_result" + + result: Optional[List[GoogleSearchResult]] = None + r"""The results of the Google Search.""" + + is_error: Optional[bool] = None + r"""Whether the Google Search resulted in an error.""" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["result", "is_error", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + GoogleSearchResultStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/groundingtoolcount.py b/google/genai/_gaos/types/interactions/groundingtoolcount.py new file mode 100644 index 000000000..613071b67 --- /dev/null +++ b/google/genai/_gaos/types/interactions/groundingtoolcount.py @@ -0,0 +1,69 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from pydantic import model_serializer +from typing import Literal, Optional, Union +from typing_extensions import NotRequired, TypedDict + + +GroundingToolCountType = Union[ + Literal[ + "google_search", + "google_maps", + "retrieval", + ], + UnrecognizedStr, +] +r"""The grounding tool type associated with the count.""" + + +class GroundingToolCountParam(TypedDict): + r"""The number of grounding tool counts.""" + + type: NotRequired[GroundingToolCountType] + r"""The grounding tool type associated with the count.""" + count: NotRequired[int] + r"""The number of grounding tool counts.""" + + +class GroundingToolCount(BaseModel): + r"""The number of grounding tool counts.""" + + type: Optional[GroundingToolCountType] = None + r"""The grounding tool type associated with the count.""" + + count: Optional[int] = None + r"""The number of grounding tool counts.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["type", "count"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/imageconfig.py b/google/genai/_gaos/types/interactions/imageconfig.py new file mode 100644 index 000000000..c121f30d8 --- /dev/null +++ b/google/genai/_gaos/types/interactions/imageconfig.py @@ -0,0 +1,86 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from pydantic import model_serializer +from typing import Literal, Optional, Union +from typing_extensions import NotRequired, TypedDict + + +ImageConfigAspectRatio = Union[ + Literal[ + "1:1", + "2:3", + "3:2", + "3:4", + "4:3", + "4:5", + "5:4", + "9:16", + "16:9", + "21:9", + "1:8", + "8:1", + "1:4", + "4:1", + ], + UnrecognizedStr, +] + + +ImageConfigImageSize = Union[ + Literal[ + "1K", + "2K", + "4K", + "512", + ], + UnrecognizedStr, +] + + +class ImageConfigParam(TypedDict): + r"""The configuration for image interaction.""" + + aspect_ratio: NotRequired[ImageConfigAspectRatio] + image_size: NotRequired[ImageConfigImageSize] + + +class ImageConfig(BaseModel): + r"""The configuration for image interaction.""" + + aspect_ratio: Optional[ImageConfigAspectRatio] = None + + image_size: Optional[ImageConfigImageSize] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["aspect_ratio", "image_size"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/imagecontent.py b/google/genai/_gaos/types/interactions/imagecontent.py new file mode 100644 index 000000000..2e40535ac --- /dev/null +++ b/google/genai/_gaos/types/interactions/imagecontent.py @@ -0,0 +1,104 @@ +# 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 __future__ import annotations +from .. import ( + Base64EncodedString, + Base64FileInput, + BaseModel, + UNSET_SENTINEL, + UnrecognizedStr, +) +from ...utils import validate_const +from .mediaresolution import MediaResolution +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +ImageContentMimeType = Union[ + Literal[ + "image/png", + "image/jpeg", + "image/webp", + "image/heic", + "image/heif", + "image/gif", + "image/bmp", + "image/tiff", + ], + UnrecognizedStr, +] +r"""The mime type of the image.""" + + +class ImageContentParam(TypedDict): + r"""An image content block.""" + + data: NotRequired[Union[str, Base64FileInput]] + r"""The image content.""" + mime_type: NotRequired[ImageContentMimeType] + r"""The mime type of the image.""" + resolution: NotRequired[MediaResolution] + type: Literal["image"] + uri: NotRequired[str] + r"""The URI of the image.""" + + +class ImageContent(BaseModel): + r"""An image content block.""" + + data: Optional[Base64EncodedString] = None + r"""The image content.""" + + mime_type: Optional[ImageContentMimeType] = None + r"""The mime type of the image.""" + + resolution: Optional[MediaResolution] = None + + type: Annotated[ + Annotated[Literal["image"], AfterValidator(validate_const("image"))], + pydantic.Field(alias="type"), + ] = "image" + + uri: Optional[str] = None + r"""The URI of the image.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["data", "mime_type", "resolution", "uri"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + ImageContent.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/imagedelta.py b/google/genai/_gaos/types/interactions/imagedelta.py new file mode 100644 index 000000000..4e3780ac9 --- /dev/null +++ b/google/genai/_gaos/types/interactions/imagedelta.py @@ -0,0 +1,87 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils import validate_const +from .mediaresolution import MediaResolution +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +ImageDeltaMimeType = Union[ + Literal[ + "image/png", + "image/jpeg", + "image/webp", + "image/heic", + "image/heif", + "image/gif", + "image/bmp", + "image/tiff", + ], + UnrecognizedStr, +] + + +class ImageDeltaTypedDict(TypedDict): + data: NotRequired[str] + mime_type: NotRequired[ImageDeltaMimeType] + resolution: NotRequired[MediaResolution] + type: Literal["image"] + uri: NotRequired[str] + + +class ImageDelta(BaseModel): + data: Optional[str] = None + + mime_type: Optional[ImageDeltaMimeType] = None + + resolution: Optional[MediaResolution] = None + + type: Annotated[ + Annotated[Literal["image"], AfterValidator(validate_const("image"))], + pydantic.Field(alias="type"), + ] = "image" + + uri: Optional[str] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["data", "mime_type", "resolution", "uri"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + ImageDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/imageresponseformat.py b/google/genai/_gaos/types/interactions/imageresponseformat.py new file mode 100644 index 000000000..55d5e440a --- /dev/null +++ b/google/genai/_gaos/types/interactions/imageresponseformat.py @@ -0,0 +1,131 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +ImageResponseFormatMimeType = Literal["image/jpeg",] +r"""The MIME type of the image output.""" + + +ImageResponseFormatDelivery = Union[ + Literal[ + "inline", + "uri", + ], + UnrecognizedStr, +] +r"""The delivery mode for the image output.""" + + +ImageResponseFormatAspectRatio = Union[ + Literal[ + "1:1", + "2:3", + "3:2", + "3:4", + "4:3", + "4:5", + "5:4", + "9:16", + "16:9", + "21:9", + "1:8", + "8:1", + "1:4", + "4:1", + ], + UnrecognizedStr, +] +r"""The aspect ratio for the image output.""" + + +ImageResponseFormatImageSize = Union[ + Literal[ + "512", + "1K", + "2K", + "4K", + ], + UnrecognizedStr, +] +r"""The size of the image output.""" + + +class ImageResponseFormatParam(TypedDict): + r"""Configuration for image output format.""" + + type: Literal["image"] + mime_type: NotRequired[ImageResponseFormatMimeType] + r"""The MIME type of the image output.""" + delivery: NotRequired[ImageResponseFormatDelivery] + r"""The delivery mode for the image output.""" + aspect_ratio: NotRequired[ImageResponseFormatAspectRatio] + r"""The aspect ratio for the image output.""" + image_size: NotRequired[ImageResponseFormatImageSize] + r"""The size of the image output.""" + + +class ImageResponseFormat(BaseModel): + r"""Configuration for image output format.""" + + type: Annotated[ + Annotated[Literal["image"], AfterValidator(validate_const("image"))], + pydantic.Field(alias="type"), + ] = "image" + + mime_type: Optional[ImageResponseFormatMimeType] = None + r"""The MIME type of the image output.""" + + delivery: Optional[ImageResponseFormatDelivery] = None + r"""The delivery mode for the image output.""" + + aspect_ratio: Optional[ImageResponseFormatAspectRatio] = None + r"""The aspect ratio for the image output.""" + + image_size: Optional[ImageResponseFormatImageSize] = None + r"""The size of the image output.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["mime_type", "delivery", "aspect_ratio", "image_size"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + ImageResponseFormat.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/interaction.py b/google/genai/_gaos/types/interactions/interaction.py new file mode 100644 index 000000000..bc6c172ab --- /dev/null +++ b/google/genai/_gaos/types/interactions/interaction.py @@ -0,0 +1,375 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils.unions import parse_open_union +from .agentoption import AgentOption +from .audiocontent import AudioContent, AudioContentParam +from .content import Content, ContentParam +from .deepresearchagentconfig import ( + DeepResearchAgentConfig, + DeepResearchAgentConfigParam, +) +from .dynamicagentconfig import DynamicAgentConfig, DynamicAgentConfigParam +from .environment import Environment, EnvironmentParam +from .generationconfig import GenerationConfig, GenerationConfigParam +from .imagecontent import ImageContent, ImageContentParam +from .interactionsinput import InteractionsInput, InteractionsInputParam +from .model import Model +from .responseformat import ResponseFormat, ResponseFormatParam +from .responsemodality import ResponseModality +from .servicetier import ServiceTier +from .step import Step, StepParam +from .tool import Tool, ToolParam +from .usage import Usage, UsageParam +from .videocontent import VideoContent, VideoContentParam +from .webhookconfig import WebhookConfig, WebhookConfigParam +from functools import partial +import pydantic +from pydantic import ConfigDict, model_serializer, model_validator +from pydantic.functional_validators import BeforeValidator +from typing import Any, List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict + + +InteractionEnvironmentTypedDict = TypeAliasType( + "InteractionEnvironmentTypedDict", Union[EnvironmentParam, str] +) +r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + + +InteractionEnvironment = TypeAliasType( + "InteractionEnvironment", Union[Environment, str] +) +r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + + +InteractionStatus = Union[ + Literal[ + "in_progress", + "requires_action", + "completed", + "failed", + "cancelled", + "incomplete", + ], + UnrecognizedStr, +] +r"""Required. Output only. The status of the interaction.""" + + +InteractionAgentConfigTypedDict = TypeAliasType( + "InteractionAgentConfigTypedDict", + Union[DynamicAgentConfigParam, DeepResearchAgentConfigParam], +) +r"""Configuration parameters for the agent interaction.""" + + +class UnknownInteractionAgentConfig(BaseModel): + r"""A InteractionAgentConfig variant the SDK doesn't recognize. Preserves the raw payload.""" + + type: Literal["UNKNOWN"] = "UNKNOWN" + raw: Any + is_unknown: Literal[True] = True + + model_config = ConfigDict(frozen=True) + + +_INTERACTION_AGENT_CONFIG_VARIANTS: dict[str, Any] = { + "dynamic": DynamicAgentConfig, + "deep-research": DeepResearchAgentConfig, +} + + +InteractionAgentConfig = Annotated[ + Union[DynamicAgentConfig, DeepResearchAgentConfig, UnknownInteractionAgentConfig], + BeforeValidator( + partial( + parse_open_union, + disc_key="type", + variants=_INTERACTION_AGENT_CONFIG_VARIANTS, + unknown_cls=UnknownInteractionAgentConfig, + union_name="InteractionAgentConfig", + ) + ), +] +r"""Configuration parameters for the agent interaction.""" + + +InteractionResponseFormatTypedDict = TypeAliasType( + "InteractionResponseFormatTypedDict", + Union[List[ResponseFormatParam], ResponseFormatParam], +) +r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + +InteractionResponseFormat = TypeAliasType( + "InteractionResponseFormat", Union[List[ResponseFormat], ResponseFormat] +) +r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + +_LEGACY_LYRIA_MODELS = frozenset({"lyria-3-pro-preview", "lyria-3-clip-preview"}) + + +class InteractionTypedDict(TypedDict): + r"""The Interaction resource.""" + + status: InteractionStatus + r"""Required. Output only. The status of the interaction.""" + model: NotRequired[Model] + r"""The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details.""" + agent: NotRequired[AgentOption] + r"""The agent to interact with.""" + created: NotRequired[str] + r"""Output only. The time at which the response was created in ISO 8601 format + (YYYY-MM-DDThh:mm:ssZ). + """ + environment: NotRequired[InteractionEnvironmentTypedDict] + r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + environment_id: NotRequired[str] + r"""Output only. The environment ID for the interaction. Only populated if environment + config is set in the request. + """ + id: NotRequired[str] + r"""Required. Output only. A unique identifier for the interaction completion.""" + previous_interaction_id: NotRequired[str] + r"""The ID of the previous interaction, if any.""" + response_mime_type: NotRequired[str] + r"""The mime type of the response. This is required if response_format is set.""" + response_modalities: NotRequired[List[ResponseModality]] + r"""The requested modalities of the response (TEXT, IMAGE, AUDIO).""" + role: NotRequired[str] + r"""Output only. The role of the interaction.""" + service_tier: NotRequired[ServiceTier] + steps: NotRequired[List[StepParam]] + r"""Output only. The steps that make up the interaction, when included in the response.""" + system_instruction: NotRequired[str] + r"""System instruction for the interaction.""" + tools: NotRequired[List[ToolParam]] + r"""A list of tool declarations the model may call during interaction.""" + updated: NotRequired[str] + r"""Output only. The time at which the response was last updated in ISO 8601 format + (YYYY-MM-DDThh:mm:ssZ). + """ + usage: NotRequired[UsageParam] + r"""Statistics on the interaction request's token usage.""" + webhook_config: NotRequired[WebhookConfigParam] + r"""Message for configuring webhook events for a request.""" + generation_config: NotRequired[GenerationConfigParam] + r"""Configuration parameters for model interactions.""" + agent_config: NotRequired[InteractionAgentConfigTypedDict] + r"""Configuration parameters for the agent interaction.""" + response_format: NotRequired[InteractionResponseFormatTypedDict] + r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + input: NotRequired[InteractionsInputParam] + r"""The input for the interaction.""" + outputs: NotRequired[List[ContentParam]] + r"""Output only. Legacy responses from the model.""" + output_text: NotRequired[str] + r"""Concatenated text from the last model output in response to the current request. + + Note: this is added by the SDK. + """ + output_image: NotRequired[ImageContentParam] + r"""An image content block.""" + output_audio: NotRequired[AudioContentParam] + r"""An audio content block.""" + output_video: NotRequired[VideoContentParam] + r"""A video content block.""" + + +class Interaction(BaseModel): + r"""The Interaction resource.""" + + status: InteractionStatus + r"""Required. Output only. The status of the interaction.""" + + model: Optional[Model] = None + r"""The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details.""" + + agent: Optional[AgentOption] = None + r"""The agent to interact with.""" + + created: Optional[str] = None + r"""Output only. The time at which the response was created in ISO 8601 format + (YYYY-MM-DDThh:mm:ssZ). + """ + + environment: Optional[InteractionEnvironment] = None + r"""The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID.""" + + environment_id: Optional[str] = None + r"""Output only. The environment ID for the interaction. Only populated if environment + config is set in the request. + """ + + id: Optional[str] = "" + r"""Required. Output only. A unique identifier for the interaction completion.""" + + previous_interaction_id: Optional[str] = None + r"""The ID of the previous interaction, if any.""" + + response_mime_type: Annotated[ + Optional[str], + pydantic.Field( + deprecated="warning: ** DEPRECATED ** - This will be removed in a future release, please migrate away from it as soon as possible." + ), + ] = None + r"""The mime type of the response. This is required if response_format is set.""" + + response_modalities: Optional[List[ResponseModality]] = None + r"""The requested modalities of the response (TEXT, IMAGE, AUDIO).""" + + role: Annotated[ + Optional[str], + pydantic.Field( + deprecated="warning: ** DEPRECATED ** - This will be removed in a future release, please migrate away from it as soon as possible." + ), + ] = None + r"""Output only. The role of the interaction.""" + + service_tier: Optional[ServiceTier] = None + + steps: Optional[List[Step]] = None + r"""Output only. The steps that make up the interaction, when included in the response.""" + + system_instruction: Optional[str] = None + r"""System instruction for the interaction.""" + + tools: Optional[List[Tool]] = None + r"""A list of tool declarations the model may call during interaction.""" + + updated: Optional[str] = None + r"""Output only. The time at which the response was last updated in ISO 8601 format + (YYYY-MM-DDThh:mm:ssZ). + """ + + usage: Optional[Usage] = None + r"""Statistics on the interaction request's token usage.""" + + webhook_config: Optional[WebhookConfig] = None + r"""Message for configuring webhook events for a request.""" + + generation_config: Optional[GenerationConfig] = None + r"""Configuration parameters for model interactions.""" + + agent_config: Optional[InteractionAgentConfig] = None + r"""Configuration parameters for the agent interaction.""" + + response_format: Optional[InteractionResponseFormat] = None + r"""Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field.""" + + input: Optional[InteractionsInput] = None + r"""The input for the interaction.""" + + outputs: Annotated[ + Optional[List[Content]], + pydantic.Field( + deprecated="warning: ** DEPRECATED ** - This will be removed in a future release, please migrate away from it as soon as possible." + ), + ] = None + r"""Output only. Legacy responses from the model.""" + + output_text: Optional[str] = None + r"""Concatenated text from the last model output in response to the current request. + + Note: this is added by the SDK. + """ + + output_image: Optional[ImageContent] = None + r"""An image content block.""" + + output_audio: Optional[AudioContent] = None + r"""An audio content block.""" + + output_video: Optional[VideoContent] = None + r"""A video content block.""" + + @classmethod + def _maybe_coerce_outputs(cls, data: Any) -> Any: + if not isinstance(data, dict): + return data + if ( + not isinstance(data.get("model"), str) + or data["model"] not in _LEGACY_LYRIA_MODELS + ): + return data + if "steps" in data: + return data + if "outputs" not in data: + return {**data, "steps": []} + outputs = data["outputs"] + if not isinstance(outputs, list): + return {**data, "steps": []} + + coerced = {**data} + coerced.pop("outputs") + coerced["steps"] = [{"type": "model_output", "content": outputs}] + return coerced + + @model_validator(mode="before") + @classmethod + def _coerce_outputs_to_steps(cls, data: Any) -> Any: + return cls._maybe_coerce_outputs(data) + + @model_serializer(mode="wrap") + def _serialize_model(self, handler): + optional_fields = set( + [ + "model", + "agent", + "created", + "environment", + "environment_id", + "id", + "previous_interaction_id", + "response_mime_type", + "response_modalities", + "role", + "service_tier", + "steps", + "system_instruction", + "tools", + "updated", + "usage", + "webhook_config", + "generation_config", + "agent_config", + "response_format", + "input", + "outputs", + "output_text", + "output_image", + "output_audio", + "output_video", + ] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/interactioncompletedevent.py b/google/genai/_gaos/types/interactions/interactioncompletedevent.py new file mode 100644 index 000000000..f9348c975 --- /dev/null +++ b/google/genai/_gaos/types/interactions/interactioncompletedevent.py @@ -0,0 +1,88 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .interactionsseeventinteraction import ( + InteractionSseEventInteraction, + InteractionSseEventInteractionTypedDict, +) +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class InteractionCompletedEventTypedDict(TypedDict): + interaction: InteractionSseEventInteractionTypedDict + r"""Partial interaction resource emitted by interaction lifecycle SSE events. + Streaming lifecycle payloads may omit fields that are only available on + full non-streaming Interaction responses. + + """ + event_id: NotRequired[str] + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + event_type: Literal["interaction.completed"] + + +class InteractionCompletedEvent(BaseModel): + interaction: InteractionSseEventInteraction + r"""Partial interaction resource emitted by interaction lifecycle SSE events. + Streaming lifecycle payloads may omit fields that are only available on + full non-streaming Interaction responses. + + """ + + event_id: Optional[str] = None + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: Annotated[ + Annotated[ + Literal["interaction.completed"], + AfterValidator(validate_const("interaction.completed")), + ], + pydantic.Field(alias="event_type"), + ] = "interaction.completed" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["event_id"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + InteractionCompletedEvent.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/interactioncreatedevent.py b/google/genai/_gaos/types/interactions/interactioncreatedevent.py new file mode 100644 index 000000000..cf9438fc6 --- /dev/null +++ b/google/genai/_gaos/types/interactions/interactioncreatedevent.py @@ -0,0 +1,88 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .interactionsseeventinteraction import ( + InteractionSseEventInteraction, + InteractionSseEventInteractionTypedDict, +) +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class InteractionCreatedEventTypedDict(TypedDict): + interaction: InteractionSseEventInteractionTypedDict + r"""Partial interaction resource emitted by interaction lifecycle SSE events. + Streaming lifecycle payloads may omit fields that are only available on + full non-streaming Interaction responses. + + """ + event_id: NotRequired[str] + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + event_type: Literal["interaction.created"] + + +class InteractionCreatedEvent(BaseModel): + interaction: InteractionSseEventInteraction + r"""Partial interaction resource emitted by interaction lifecycle SSE events. + Streaming lifecycle payloads may omit fields that are only available on + full non-streaming Interaction responses. + + """ + + event_id: Optional[str] = None + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: Annotated[ + Annotated[ + Literal["interaction.created"], + AfterValidator(validate_const("interaction.created")), + ], + pydantic.Field(alias="event_type"), + ] = "interaction.created" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["event_id"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + InteractionCreatedEvent.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/interactionsinput.py b/google/genai/_gaos/types/interactions/interactionsinput.py new file mode 100644 index 000000000..6b294d9dd --- /dev/null +++ b/google/genai/_gaos/types/interactions/interactionsinput.py @@ -0,0 +1,36 @@ +# 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 __future__ import annotations +from .content import Content, ContentParam +from .step import Step, StepParam +from typing import List, Union +from typing_extensions import TypeAliasType + + +InteractionsInputParam = TypeAliasType( + "InteractionsInputParam", + Union[str, List[StepParam], List[ContentParam], ContentParam], +) +r"""The input for the interaction.""" + + +InteractionsInput = TypeAliasType( + "InteractionsInput", Union[str, List[Step], List[Content], Content] +) +r"""The input for the interaction.""" diff --git a/google/genai/_gaos/types/interactions/interactionsseevent.py b/google/genai/_gaos/types/interactions/interactionsseevent.py new file mode 100644 index 000000000..a70cb9cf7 --- /dev/null +++ b/google/genai/_gaos/types/interactions/interactionsseevent.py @@ -0,0 +1,100 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils.unions import parse_open_union +from .errorevent import ErrorEvent, ErrorEventTypedDict +from .interactioncompletedevent import ( + InteractionCompletedEvent, + InteractionCompletedEventTypedDict, +) +from .interactioncreatedevent import ( + InteractionCreatedEvent, + InteractionCreatedEventTypedDict, +) +from .interactionstatusupdate import ( + InteractionStatusUpdate, + InteractionStatusUpdateTypedDict, +) +from .stepdelta import StepDelta, StepDeltaTypedDict +from .stepstart import StepStart, StepStartTypedDict +from .stepstop import StepStop, StepStopTypedDict +from functools import partial +from pydantic import ConfigDict +from pydantic.functional_validators import BeforeValidator +from typing import Any, Literal, Union +from typing_extensions import Annotated, TypeAliasType + + +InteractionSSEEventTypedDict = TypeAliasType( + "InteractionSSEEventTypedDict", + Union[ + InteractionCreatedEventTypedDict, + InteractionCompletedEventTypedDict, + ErrorEventTypedDict, + StepStopTypedDict, + InteractionStatusUpdateTypedDict, + StepStartTypedDict, + StepDeltaTypedDict, + ], +) + + +class UnknownInteractionSSEEvent(BaseModel): + r"""A InteractionSSEEvent variant the SDK doesn't recognize. Preserves the raw payload.""" + + event_type: Literal["UNKNOWN"] = "UNKNOWN" + raw: Any + is_unknown: Literal[True] = True + + model_config = ConfigDict(frozen=True) + + +_INTERACTION_SSE_EVENT_VARIANTS: dict[str, Any] = { + "interaction.created": InteractionCreatedEvent, + "interaction.completed": InteractionCompletedEvent, + "interaction.status_update": InteractionStatusUpdate, + "error": ErrorEvent, + "step.start": StepStart, + "step.delta": StepDelta, + "step.stop": StepStop, +} + + +InteractionSSEEvent = Annotated[ + Union[ + InteractionCreatedEvent, + InteractionCompletedEvent, + InteractionStatusUpdate, + ErrorEvent, + StepStart, + StepDelta, + StepStop, + UnknownInteractionSSEEvent, + ], + BeforeValidator( + partial( + parse_open_union, + disc_key="event_type", + variants=_INTERACTION_SSE_EVENT_VARIANTS, + unknown_cls=UnknownInteractionSSEEvent, + union_name="InteractionSSEEvent", + ) + ), +] diff --git a/google/genai/_gaos/types/interactions/interactionsseeventinteraction.py b/google/genai/_gaos/types/interactions/interactionsseeventinteraction.py new file mode 100644 index 000000000..38cd6352f --- /dev/null +++ b/google/genai/_gaos/types/interactions/interactionsseeventinteraction.py @@ -0,0 +1,132 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from .servicetier import ServiceTier +from .step import Step, StepParam +from .usage import Usage, UsageParam +from pydantic import model_serializer +from typing import List, Literal, Optional, Union +from typing_extensions import NotRequired, TypedDict + + +InteractionSseEventInteractionStatus = Union[ + Literal[ + "in_progress", + "requires_action", + "completed", + "failed", + "cancelled", + "incomplete", + ], + UnrecognizedStr, +] +r"""Required. Output only. The status of the interaction.""" + + +class InteractionSseEventInteractionTypedDict(TypedDict): + r"""Partial interaction resource emitted by interaction lifecycle SSE events. + Streaming lifecycle payloads may omit fields that are only available on + full non-streaming Interaction responses. + + """ + + id: str + r"""Required. Output only. A unique identifier for the interaction completion.""" + status: InteractionSseEventInteractionStatus + r"""Required. Output only. The status of the interaction.""" + object: NotRequired[str] + r"""Output only. The resource type.""" + model: NotRequired[str] + r"""The model that will complete your prompt.""" + agent: NotRequired[str] + r"""The agent to interact with.""" + created: NotRequired[str] + r"""Output only. The time at which the response was created in ISO 8601 format.""" + updated: NotRequired[str] + r"""Output only. The time at which the response was last updated in ISO 8601 format.""" + service_tier: NotRequired[ServiceTier] + usage: NotRequired[UsageParam] + r"""Statistics on the interaction request's token usage.""" + steps: NotRequired[List[StepParam]] + r"""Output only. The steps that make up the interaction, if included in this event.""" + + +class InteractionSseEventInteraction(BaseModel): + r"""Partial interaction resource emitted by interaction lifecycle SSE events. + Streaming lifecycle payloads may omit fields that are only available on + full non-streaming Interaction responses. + + """ + + id: str + r"""Required. Output only. A unique identifier for the interaction completion.""" + + status: InteractionSseEventInteractionStatus + r"""Required. Output only. The status of the interaction.""" + + object: Optional[str] = None + r"""Output only. The resource type.""" + + model: Optional[str] = None + r"""The model that will complete your prompt.""" + + agent: Optional[str] = None + r"""The agent to interact with.""" + + created: Optional[str] = None + r"""Output only. The time at which the response was created in ISO 8601 format.""" + + updated: Optional[str] = None + r"""Output only. The time at which the response was last updated in ISO 8601 format.""" + + service_tier: Optional[ServiceTier] = None + + usage: Optional[Usage] = None + r"""Statistics on the interaction request's token usage.""" + + steps: Optional[List[Step]] = None + r"""Output only. The steps that make up the interaction, if included in this event.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + [ + "object", + "model", + "agent", + "created", + "updated", + "service_tier", + "usage", + "steps", + ] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/interactionssestreamevent.py b/google/genai/_gaos/types/interactions/interactionssestreamevent.py new file mode 100644 index 000000000..208698c05 --- /dev/null +++ b/google/genai/_gaos/types/interactions/interactionssestreamevent.py @@ -0,0 +1,30 @@ +# 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 __future__ import annotations +from .. import BaseModel +from .interactionsseevent import InteractionSSEEvent, InteractionSSEEventTypedDict +from typing_extensions import TypedDict + + +class InteractionSSEStreamEventTypedDict(TypedDict): + data: InteractionSSEEventTypedDict + + +class InteractionSSEStreamEvent(BaseModel): + data: InteractionSSEEvent diff --git a/google/genai/_gaos/types/interactions/interactionstatusupdate.py b/google/genai/_gaos/types/interactions/interactionstatusupdate.py new file mode 100644 index 000000000..ea188a539 --- /dev/null +++ b/google/genai/_gaos/types/interactions/interactionstatusupdate.py @@ -0,0 +1,127 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils import validate_const +from .usage import Usage, UsageParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +InteractionStatusUpdateStatus = Union[ + Literal[ + "in_progress", + "requires_action", + "completed", + "failed", + "cancelled", + "incomplete", + "budget_exceeded", + ], + UnrecognizedStr, +] + + +class InteractionStatusUpdateMetadataTypedDict(TypedDict): + r"""Optional metadata accompanying ANY streamed event.""" + + total_usage: NotRequired[UsageParam] + r"""Statistics on the interaction request's token usage.""" + + +class InteractionStatusUpdateMetadata(BaseModel): + r"""Optional metadata accompanying ANY streamed event.""" + + total_usage: Optional[Usage] = None + r"""Statistics on the interaction request's token usage.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["total_usage"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class InteractionStatusUpdateTypedDict(TypedDict): + interaction_id: str + status: InteractionStatusUpdateStatus + event_type: Literal["interaction.status_update"] + event_id: NotRequired[str] + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + metadata: NotRequired[InteractionStatusUpdateMetadataTypedDict] + r"""Optional metadata accompanying ANY streamed event.""" + + +class InteractionStatusUpdate(BaseModel): + interaction_id: str + + status: InteractionStatusUpdateStatus + + event_type: Annotated[ + Annotated[ + Literal["interaction.status_update"], + AfterValidator(validate_const("interaction.status_update")), + ], + pydantic.Field(alias="event_type"), + ] = "interaction.status_update" + + event_id: Optional[str] = None + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + + metadata: Optional[InteractionStatusUpdateMetadata] = None + r"""Optional metadata accompanying ANY streamed event.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["event_id", "metadata"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + InteractionStatusUpdate.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/mcpserver.py b/google/genai/_gaos/types/interactions/mcpserver.py new file mode 100644 index 000000000..d9650d0d2 --- /dev/null +++ b/google/genai/_gaos/types/interactions/mcpserver.py @@ -0,0 +1,88 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .allowedtools import AllowedTools, AllowedToolsParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Dict, List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class MCPServerParam(TypedDict): + r"""A MCPServer is a server that can be called by the model to perform actions.""" + + type: Literal["mcp_server"] + name: NotRequired[str] + r"""The name of the MCPServer.""" + url: NotRequired[str] + r"""The full URL for the MCPServer endpoint. + Example: \"https://api.example.com/mcp\" + """ + headers: NotRequired[Dict[str, str]] + r"""Optional: Fields for authentication headers, timeouts, etc., if needed.""" + allowed_tools: NotRequired[List[AllowedToolsParam]] + r"""The allowed tools.""" + + +class MCPServer(BaseModel): + r"""A MCPServer is a server that can be called by the model to perform actions.""" + + type: Annotated[ + Annotated[Literal["mcp_server"], AfterValidator(validate_const("mcp_server"))], + pydantic.Field(alias="type"), + ] = "mcp_server" + + name: Optional[str] = None + r"""The name of the MCPServer.""" + + url: Optional[str] = None + r"""The full URL for the MCPServer endpoint. + Example: \"https://api.example.com/mcp\" + """ + + headers: Optional[Dict[str, str]] = None + r"""Optional: Fields for authentication headers, timeouts, etc., if needed.""" + + allowed_tools: Optional[List[AllowedTools]] = None + r"""The allowed tools.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["name", "url", "headers", "allowed_tools"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + MCPServer.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/mcpservertoolcalldelta.py b/google/genai/_gaos/types/interactions/mcpservertoolcalldelta.py new file mode 100644 index 000000000..83876109c --- /dev/null +++ b/google/genai/_gaos/types/interactions/mcpservertoolcalldelta.py @@ -0,0 +1,76 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Any, Dict, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class MCPServerToolCallDeltaTypedDict(TypedDict): + name: str + server_name: str + arguments: Dict[str, Any] + type: Literal["mcp_server_tool_call"] + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class MCPServerToolCallDelta(BaseModel): + name: str + + server_name: str + + arguments: Dict[str, Any] + + type: Annotated[ + Annotated[ + Literal["mcp_server_tool_call"], + AfterValidator(validate_const("mcp_server_tool_call")), + ], + pydantic.Field(alias="type"), + ] = "mcp_server_tool_call" + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + MCPServerToolCallDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/mcpservertoolcallstep.py b/google/genai/_gaos/types/interactions/mcpservertoolcallstep.py new file mode 100644 index 000000000..f7fbbb174 --- /dev/null +++ b/google/genai/_gaos/types/interactions/mcpservertoolcallstep.py @@ -0,0 +1,91 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Any, Dict, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class MCPServerToolCallStepParam(TypedDict): + r"""MCPServer tool call step.""" + + name: str + r"""Required. The name of the tool which was called.""" + server_name: str + r"""Required. The name of the used MCP server.""" + arguments: Dict[str, Any] + r"""Required. The JSON object of arguments for the function.""" + id: str + r"""Required. A unique ID for this specific tool call.""" + type: Literal["mcp_server_tool_call"] + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + + +class MCPServerToolCallStep(BaseModel): + r"""MCPServer tool call step.""" + + name: str + r"""Required. The name of the tool which was called.""" + + server_name: str + r"""Required. The name of the used MCP server.""" + + arguments: Dict[str, Any] + r"""Required. The JSON object of arguments for the function.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + + type: Annotated[ + Annotated[ + Literal["mcp_server_tool_call"], + AfterValidator(validate_const("mcp_server_tool_call")), + ], + pydantic.Field(alias="type"), + ] = "mcp_server_tool_call" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + MCPServerToolCallStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/mcpservertoolresultdelta.py b/google/genai/_gaos/types/interactions/mcpservertoolresultdelta.py new file mode 100644 index 000000000..4391b35a0 --- /dev/null +++ b/google/genai/_gaos/types/interactions/mcpservertoolresultdelta.py @@ -0,0 +1,97 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .content import Content, ContentParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict + + +class MCPServerToolResultDeltaResultTypedDict(TypedDict): + pass + + +class MCPServerToolResultDeltaResult(BaseModel): + pass + + +MCPServerToolResultDeltaResultUnionTypedDict = TypeAliasType( + "MCPServerToolResultDeltaResultUnionTypedDict", + Union[MCPServerToolResultDeltaResultTypedDict, List[ContentParam], str], +) + + +MCPServerToolResultDeltaResultUnion = TypeAliasType( + "MCPServerToolResultDeltaResultUnion", + Union[MCPServerToolResultDeltaResult, List[Content], str], +) + + +class MCPServerToolResultDeltaTypedDict(TypedDict): + result: MCPServerToolResultDeltaResultUnionTypedDict + type: Literal["mcp_server_tool_result"] + name: NotRequired[str] + server_name: NotRequired[str] + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class MCPServerToolResultDelta(BaseModel): + result: MCPServerToolResultDeltaResultUnion + + type: Annotated[ + Annotated[ + Literal["mcp_server_tool_result"], + AfterValidator(validate_const("mcp_server_tool_result")), + ], + pydantic.Field(alias="type"), + ] = "mcp_server_tool_result" + + name: Optional[str] = None + + server_name: Optional[str] = None + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["name", "server_name", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + MCPServerToolResultDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/mcpservertoolresultstep.py b/google/genai/_gaos/types/interactions/mcpservertoolresultstep.py new file mode 100644 index 000000000..a714357c8 --- /dev/null +++ b/google/genai/_gaos/types/interactions/mcpservertoolresultstep.py @@ -0,0 +1,114 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .content import Content, ContentParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict + + +class MCPServerToolResultStepResultParam(TypedDict): + pass + + +class MCPServerToolResultStepResult(BaseModel): + pass + + +MCPServerToolResultStepResultUnionParam = TypeAliasType( + "MCPServerToolResultStepResultUnionParam", + Union[MCPServerToolResultStepResultParam, List[ContentParam], str], +) +r"""The output from the MCP server call. Can be simple text or rich content.""" + + +MCPServerToolResultStepResultUnion = TypeAliasType( + "MCPServerToolResultStepResultUnion", + Union[MCPServerToolResultStepResult, List[Content], str], +) +r"""The output from the MCP server call. Can be simple text or rich content.""" + + +class MCPServerToolResultStepParam(TypedDict): + r"""MCPServer tool result step.""" + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + result: MCPServerToolResultStepResultUnionParam + r"""The output from the MCP server call. Can be simple text or rich content.""" + type: Literal["mcp_server_tool_result"] + name: NotRequired[str] + r"""Name of the tool which is called for this specific tool call.""" + server_name: NotRequired[str] + r"""The name of the used MCP server.""" + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + + +class MCPServerToolResultStep(BaseModel): + r"""MCPServer tool result step.""" + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + + result: MCPServerToolResultStepResultUnion + r"""The output from the MCP server call. Can be simple text or rich content.""" + + type: Annotated[ + Annotated[ + Literal["mcp_server_tool_result"], + AfterValidator(validate_const("mcp_server_tool_result")), + ], + pydantic.Field(alias="type"), + ] = "mcp_server_tool_result" + + name: Optional[str] = None + r"""Name of the tool which is called for this specific tool call.""" + + server_name: Optional[str] = None + r"""The name of the used MCP server.""" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["name", "server_name", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + MCPServerToolResultStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/mediaresolution.py b/google/genai/_gaos/types/interactions/mediaresolution.py new file mode 100644 index 000000000..24a236d78 --- /dev/null +++ b/google/genai/_gaos/types/interactions/mediaresolution.py @@ -0,0 +1,32 @@ +# 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 __future__ import annotations +from .. import UnrecognizedStr +from typing import Literal, Union + + +MediaResolution = Union[ + Literal[ + "low", + "medium", + "high", + "ultra_high", + ], + UnrecognizedStr, +] diff --git a/google/genai/_gaos/types/interactions/modalitytokens.py b/google/genai/_gaos/types/interactions/modalitytokens.py new file mode 100644 index 000000000..ebb14ff57 --- /dev/null +++ b/google/genai/_gaos/types/interactions/modalitytokens.py @@ -0,0 +1,57 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .responsemodality import ResponseModality +from pydantic import model_serializer +from typing import Optional +from typing_extensions import NotRequired, TypedDict + + +class ModalityTokensParam(TypedDict): + r"""The token count for a single response modality.""" + + modality: NotRequired[ResponseModality] + tokens: NotRequired[int] + r"""Number of tokens for the modality.""" + + +class ModalityTokens(BaseModel): + r"""The token count for a single response modality.""" + + modality: Optional[ResponseModality] = None + + tokens: Optional[int] = None + r"""Number of tokens for the modality.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["modality", "tokens"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/model.py b/google/genai/_gaos/types/interactions/model.py new file mode 100644 index 000000000..7273d7f2b --- /dev/null +++ b/google/genai/_gaos/types/interactions/model.py @@ -0,0 +1,69 @@ +# 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 __future__ import annotations +from .. import UnrecognizedStr +from typing import Literal, Union + + +Model = Union[ + Literal[ + # An agentic capability model designed for direct interface interaction, allowing Gemini to perceive and navigate digital environments. + "gemini-2.5-computer-use-preview-10-2025", + # Our first hybrid reasoning model which supports a 1M token context window and has thinking budgets. + "gemini-2.5-flash", + # Our native image generation model, optimized for speed, flexibility, and contextual understanding. Text input and output is priced the same as 2.5 Flash. + "gemini-2.5-flash-image", + # Our smallest and most cost effective model, built for at scale usage. + "gemini-2.5-flash-lite", + # The latest model based on Gemini 2.5 Flash lite optimized for cost-efficiency, high throughput and high quality. + "gemini-2.5-flash-lite-preview-09-2025", + # Our native audio models optimized for higher quality audio outputs with better pacing, voice naturalness, verbosity, and mood. + "gemini-2.5-flash-native-audio-preview-12-2025", + # The latest model based on the 2.5 Flash model. 2.5 Flash Preview is best for large scale processing, low-latency, high volume tasks that require thinking, and agentic use cases. + "gemini-2.5-flash-preview-09-2025", + # Our 2.5 Flash text-to-speech model optimized for powerful, low-latency controllable speech generation. + "gemini-2.5-flash-preview-tts", + # Our state-of-the-art multipurpose model, which excels at coding and complex reasoning tasks. + "gemini-2.5-pro", + # Our 2.5 Pro text-to-speech audio model optimized for powerful, low-latency speech generation for more natural outputs and easier to steer prompts. + "gemini-2.5-pro-preview-tts", + # Our most intelligent model built for speed, combining frontier intelligence with superior search and grounding. + "gemini-3-flash-preview", + # State-of-the-art image generation and editing model. + "gemini-3-pro-image-preview", + # Our most intelligent model with SOTA reasoning and multimodal understanding, and powerful agentic and vibe coding capabilities. + "gemini-3-pro-preview", + # Our latest SOTA reasoning model with unprecedented depth and nuance, and powerful multimodal understanding and coding capabilities. + "gemini-3.1-pro-preview", + # Pro-level visual intelligence with Flash-speed efficiency and reality-grounded generation capabilities. + "gemini-3.1-flash-image-preview", + # Our most cost-efficient model, optimized for high-volume agentic tasks, translation, and simple data processing. + "gemini-3.1-flash-lite", + # Our most cost-efficient model, optimized for high-volume agentic tasks, translation, and simple data processing. + "gemini-3.1-flash-lite-preview", + # Gemini 3.1 Flash TTS: Powerful, low-latency speech generation. Enjoy natural outputs, steerable prompts, and new expressive audio tags for precise narration control. + "gemini-3.1-flash-tts-preview", + # Our low-latency, music generation model optimized for high-fidelity audio clips and precise rhythmic control. + "lyria-3-clip-preview", + # Our advanced, full-song generative model with deep compositional understanding, optimized for precise structural control and complex transitions across diverse musical styles. + "lyria-3-pro-preview", + ], + UnrecognizedStr, +] +r"""The model that will complete your prompt.\n\nSee [models](https://ai.google.dev/gemini-api/docs/models) for additional details.""" diff --git a/google/genai/_gaos/types/interactions/modeloutputstep.py b/google/genai/_gaos/types/interactions/modeloutputstep.py new file mode 100644 index 000000000..80e04566b --- /dev/null +++ b/google/genai/_gaos/types/interactions/modeloutputstep.py @@ -0,0 +1,69 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .content import Content, ContentParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ModelOutputStepParam(TypedDict): + r"""Output generated by the model.""" + + type: Literal["model_output"] + content: NotRequired[List[ContentParam]] + + +class ModelOutputStep(BaseModel): + r"""Output generated by the model.""" + + type: Annotated[ + Annotated[ + Literal["model_output"], AfterValidator(validate_const("model_output")) + ], + pydantic.Field(alias="type"), + ] = "model_output" + + content: Optional[List[Content]] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["content"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + ModelOutputStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/placecitation.py b/google/genai/_gaos/types/interactions/placecitation.py new file mode 100644 index 000000000..ddbf084d8 --- /dev/null +++ b/google/genai/_gaos/types/interactions/placecitation.py @@ -0,0 +1,108 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .reviewsnippet import ReviewSnippet, ReviewSnippetParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class PlaceCitationParam(TypedDict): + r"""A place citation annotation.""" + + type: Literal["place_citation"] + place_id: NotRequired[str] + r"""The ID of the place, in `places/{place_id}` format.""" + name: NotRequired[str] + r"""Title of the place.""" + url: NotRequired[str] + r"""URI reference of the place.""" + review_snippets: NotRequired[List[ReviewSnippetParam]] + r"""Snippets of reviews that are used to generate answers about the + features of a given place in Google Maps. + """ + start_index: NotRequired[int] + r"""Start of segment of the response that is attributed to this source. + + Index indicates the start of the segment, measured in bytes. + """ + end_index: NotRequired[int] + r"""End of the attributed segment, exclusive.""" + + +class PlaceCitation(BaseModel): + r"""A place citation annotation.""" + + type: Annotated[ + Annotated[ + Literal["place_citation"], AfterValidator(validate_const("place_citation")) + ], + pydantic.Field(alias="type"), + ] = "place_citation" + + place_id: Optional[str] = None + r"""The ID of the place, in `places/{place_id}` format.""" + + name: Optional[str] = None + r"""Title of the place.""" + + url: Optional[str] = None + r"""URI reference of the place.""" + + review_snippets: Optional[List[ReviewSnippet]] = None + r"""Snippets of reviews that are used to generate answers about the + features of a given place in Google Maps. + """ + + start_index: Optional[int] = None + r"""Start of segment of the response that is attributed to this source. + + Index indicates the start of the segment, measured in bytes. + """ + + end_index: Optional[int] = None + r"""End of the attributed segment, exclusive.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + ["place_id", "name", "url", "review_snippets", "start_index", "end_index"] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + PlaceCitation.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/responseformat.py b/google/genai/_gaos/types/interactions/responseformat.py new file mode 100644 index 000000000..a2a798068 --- /dev/null +++ b/google/genai/_gaos/types/interactions/responseformat.py @@ -0,0 +1,41 @@ +# 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 __future__ import annotations +from .audioresponseformat import AudioResponseFormat, AudioResponseFormatParam +from .imageresponseformat import ImageResponseFormat, ImageResponseFormatParam +from .textresponseformat import TextResponseFormat, TextResponseFormatParam +from typing import Any, Dict, Union +from typing_extensions import TypeAliasType + + +ResponseFormatParam = TypeAliasType( + "ResponseFormatParam", + Union[ + TextResponseFormatParam, + AudioResponseFormatParam, + ImageResponseFormatParam, + Dict[str, Any], + ], +) + + +ResponseFormat = TypeAliasType( + "ResponseFormat", + Union[TextResponseFormat, AudioResponseFormat, ImageResponseFormat, Dict[str, Any]], +) diff --git a/google/genai/_gaos/types/interactions/responsemodality.py b/google/genai/_gaos/types/interactions/responsemodality.py new file mode 100644 index 000000000..ac8245dbf --- /dev/null +++ b/google/genai/_gaos/types/interactions/responsemodality.py @@ -0,0 +1,33 @@ +# 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 __future__ import annotations +from .. import UnrecognizedStr +from typing import Literal, Union + + +ResponseModality = Union[ + Literal[ + "text", + "image", + "audio", + "video", + "document", + ], + UnrecognizedStr, +] diff --git a/google/genai/_gaos/types/interactions/retrieval.py b/google/genai/_gaos/types/interactions/retrieval.py new file mode 100644 index 000000000..bb0c9d7ac --- /dev/null +++ b/google/genai/_gaos/types/interactions/retrieval.py @@ -0,0 +1,77 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .vertexaisearchconfig import VertexAISearchConfig, VertexAISearchConfigParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +RetrievalType = Literal["vertex_ai_search",] + + +class RetrievalParam(TypedDict): + r"""A tool that can be used by the model to retrieve files.""" + + retrieval_types: NotRequired[List[RetrievalType]] + r"""The types of file retrieval to enable.""" + type: Literal["retrieval"] + vertex_ai_search_config: NotRequired[VertexAISearchConfigParam] + r"""Used to specify configuration for VertexAISearch.""" + + +class Retrieval(BaseModel): + r"""A tool that can be used by the model to retrieve files.""" + + retrieval_types: Optional[List[RetrievalType]] = None + r"""The types of file retrieval to enable.""" + + type: Annotated[ + Annotated[Literal["retrieval"], AfterValidator(validate_const("retrieval"))], + pydantic.Field(alias="type"), + ] = "retrieval" + + vertex_ai_search_config: Optional[VertexAISearchConfig] = None + r"""Used to specify configuration for VertexAISearch.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["retrieval_types", "vertex_ai_search_config"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + Retrieval.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/reviewsnippet.py b/google/genai/_gaos/types/interactions/reviewsnippet.py new file mode 100644 index 000000000..a68fd0818 --- /dev/null +++ b/google/genai/_gaos/types/interactions/reviewsnippet.py @@ -0,0 +1,67 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import Optional +from typing_extensions import NotRequired, TypedDict + + +class ReviewSnippetParam(TypedDict): + r"""Encapsulates a snippet of a user review that answers a question about + the features of a specific place in Google Maps. + """ + + title: NotRequired[str] + r"""Title of the review.""" + url: NotRequired[str] + r"""A link that corresponds to the user review on Google Maps.""" + review_id: NotRequired[str] + r"""The ID of the review snippet.""" + + +class ReviewSnippet(BaseModel): + r"""Encapsulates a snippet of a user review that answers a question about + the features of a specific place in Google Maps. + """ + + title: Optional[str] = None + r"""Title of the review.""" + + url: Optional[str] = None + r"""A link that corresponds to the user review on Google Maps.""" + + review_id: Optional[str] = None + r"""The ID of the review snippet.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["title", "url", "review_id"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/servicetier.py b/google/genai/_gaos/types/interactions/servicetier.py new file mode 100644 index 000000000..97d60f87e --- /dev/null +++ b/google/genai/_gaos/types/interactions/servicetier.py @@ -0,0 +1,31 @@ +# 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 __future__ import annotations +from .. import UnrecognizedStr +from typing import Literal, Union + + +ServiceTier = Union[ + Literal[ + "flex", + "standard", + "priority", + ], + UnrecognizedStr, +] diff --git a/google/genai/_gaos/types/interactions/source.py b/google/genai/_gaos/types/interactions/source.py new file mode 100644 index 000000000..56ca2fe72 --- /dev/null +++ b/google/genai/_gaos/types/interactions/source.py @@ -0,0 +1,88 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from pydantic import model_serializer +from typing import Literal, Optional, Union +from typing_extensions import NotRequired, TypedDict + + +SourceType = Union[ + Literal[ + "gcs", + "inline", + "repository", + "skill_registry", + ], + UnrecognizedStr, +] + + +class SourceParam(TypedDict): + r"""A source to be mounted into the environment.""" + + type: NotRequired[SourceType] + source: NotRequired[str] + r"""The source of the environment. + For GCS, this is the GCS path. + For GitHub, this is the GitHub path. + """ + target: NotRequired[str] + r"""Where the source should appear in the environment.""" + content: NotRequired[str] + r"""The inline content if `type` is `INLINE`.""" + encoding: NotRequired[str] + r"""Optional encoding for inline content (e.g. `base64`).""" + + +class Source(BaseModel): + r"""A source to be mounted into the environment.""" + + type: Optional[SourceType] = None + + source: Optional[str] = None + r"""The source of the environment. + For GCS, this is the GCS path. + For GitHub, this is the GitHub path. + """ + + target: Optional[str] = None + r"""Where the source should appear in the environment.""" + + content: Optional[str] = None + r"""The inline content if `type` is `INLINE`.""" + + encoding: Optional[str] = None + r"""Optional encoding for inline content (e.g. `base64`).""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["type", "source", "target", "content", "encoding"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/speechconfig.py b/google/genai/_gaos/types/interactions/speechconfig.py new file mode 100644 index 000000000..c09e2c8a7 --- /dev/null +++ b/google/genai/_gaos/types/interactions/speechconfig.py @@ -0,0 +1,63 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import Optional +from typing_extensions import NotRequired, TypedDict + + +class SpeechConfigParam(TypedDict): + r"""The configuration for speech interaction.""" + + voice: NotRequired[str] + r"""The voice of the speaker.""" + language: NotRequired[str] + r"""The language of the speech.""" + speaker: NotRequired[str] + r"""The speaker's name, it should match the speaker name given in the prompt.""" + + +class SpeechConfig(BaseModel): + r"""The configuration for speech interaction.""" + + voice: Optional[str] = None + r"""The voice of the speaker.""" + + language: Optional[str] = None + r"""The language of the speech.""" + + speaker: Optional[str] = None + r"""The speaker's name, it should match the speaker name given in the prompt.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["voice", "language", "speaker"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/step.py b/google/genai/_gaos/types/interactions/step.py new file mode 100644 index 000000000..f1add0f9a --- /dev/null +++ b/google/genai/_gaos/types/interactions/step.py @@ -0,0 +1,139 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils.unions import parse_open_union +from .codeexecutioncallstep import CodeExecutionCallStep, CodeExecutionCallStepParam +from .codeexecutionresultstep import ( + CodeExecutionResultStep, + CodeExecutionResultStepParam, +) +from .filesearchcallstep import FileSearchCallStep, FileSearchCallStepParam +from .filesearchresultstep import FileSearchResultStep, FileSearchResultStepParam +from .functioncallstep import FunctionCallStep, FunctionCallStepParam +from .functionresultstep import FunctionResultStep, FunctionResultStepParam +from .googlemapscallstep import GoogleMapsCallStep, GoogleMapsCallStepParam +from .googlemapsresultstep import GoogleMapsResultStep, GoogleMapsResultStepParam +from .googlesearchcallstep import GoogleSearchCallStep, GoogleSearchCallStepParam +from .googlesearchresultstep import GoogleSearchResultStep, GoogleSearchResultStepParam +from .mcpservertoolcallstep import MCPServerToolCallStep, MCPServerToolCallStepParam +from .mcpservertoolresultstep import ( + MCPServerToolResultStep, + MCPServerToolResultStepParam, +) +from .modeloutputstep import ModelOutputStep, ModelOutputStepParam +from .thoughtstep import ThoughtStep, ThoughtStepParam +from .urlcontextcallstep import URLContextCallStep, URLContextCallStepParam +from .urlcontextresultstep import URLContextResultStep, URLContextResultStepParam +from .userinputstep import UserInputStep, UserInputStepParam +from functools import partial +from pydantic import ConfigDict +from pydantic.functional_validators import BeforeValidator +from typing import Any, Literal, Union +from typing_extensions import Annotated, TypeAliasType + + +StepParam = TypeAliasType( + "StepParam", + Union[ + ModelOutputStepParam, + UserInputStepParam, + FileSearchCallStepParam, + ThoughtStepParam, + FileSearchResultStepParam, + CodeExecutionCallStepParam, + URLContextCallStepParam, + GoogleMapsCallStepParam, + GoogleMapsResultStepParam, + GoogleSearchCallStepParam, + CodeExecutionResultStepParam, + URLContextResultStepParam, + GoogleSearchResultStepParam, + FunctionCallStepParam, + MCPServerToolCallStepParam, + FunctionResultStepParam, + MCPServerToolResultStepParam, + ], +) +r"""A step in the interaction.""" + + +class UnknownStep(BaseModel): + r"""A Step variant the SDK doesn't recognize. Preserves the raw payload.""" + + type: Literal["UNKNOWN"] = "UNKNOWN" + raw: Any + is_unknown: Literal[True] = True + + model_config = ConfigDict(frozen=True) + + +_STEP_VARIANTS: dict[str, Any] = { + "user_input": UserInputStep, + "model_output": ModelOutputStep, + "thought": ThoughtStep, + "function_call": FunctionCallStep, + "code_execution_call": CodeExecutionCallStep, + "url_context_call": URLContextCallStep, + "mcp_server_tool_call": MCPServerToolCallStep, + "google_search_call": GoogleSearchCallStep, + "file_search_call": FileSearchCallStep, + "google_maps_call": GoogleMapsCallStep, + "function_result": FunctionResultStep, + "code_execution_result": CodeExecutionResultStep, + "url_context_result": URLContextResultStep, + "google_search_result": GoogleSearchResultStep, + "mcp_server_tool_result": MCPServerToolResultStep, + "file_search_result": FileSearchResultStep, + "google_maps_result": GoogleMapsResultStep, +} + + +Step = Annotated[ + Union[ + UserInputStep, + ModelOutputStep, + ThoughtStep, + FunctionCallStep, + CodeExecutionCallStep, + URLContextCallStep, + MCPServerToolCallStep, + GoogleSearchCallStep, + FileSearchCallStep, + GoogleMapsCallStep, + FunctionResultStep, + CodeExecutionResultStep, + URLContextResultStep, + GoogleSearchResultStep, + MCPServerToolResultStep, + FileSearchResultStep, + GoogleMapsResultStep, + UnknownStep, + ], + BeforeValidator( + partial( + parse_open_union, + disc_key="type", + variants=_STEP_VARIANTS, + unknown_cls=UnknownStep, + union_name="Step", + ) + ), +] +r"""A step in the interaction.""" diff --git a/google/genai/_gaos/types/interactions/stepdelta.py b/google/genai/_gaos/types/interactions/stepdelta.py new file mode 100644 index 000000000..914b3bb53 --- /dev/null +++ b/google/genai/_gaos/types/interactions/stepdelta.py @@ -0,0 +1,81 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .stepdeltadata import StepDeltaData, StepDeltaDataTypedDict +from .stepdeltametadata import StepDeltaMetadata, StepDeltaMetadataTypedDict +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class StepDeltaTypedDict(TypedDict): + index: int + delta: StepDeltaDataTypedDict + event_type: Literal["step.delta"] + event_id: NotRequired[str] + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + metadata: NotRequired[StepDeltaMetadataTypedDict] + r"""Optional metadata accompanying ANY streamed event.""" + + +class StepDelta(BaseModel): + index: int + + delta: StepDeltaData + + event_type: Annotated[ + Annotated[Literal["step.delta"], AfterValidator(validate_const("step.delta"))], + pydantic.Field(alias="event_type"), + ] = "step.delta" + + event_id: Optional[str] = None + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + + metadata: Optional[StepDeltaMetadata] = None + r"""Optional metadata accompanying ANY streamed event.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["event_id", "metadata"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + StepDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/stepdeltadata.py b/google/genai/_gaos/types/interactions/stepdeltadata.py new file mode 100644 index 000000000..64e034085 --- /dev/null +++ b/google/genai/_gaos/types/interactions/stepdeltadata.py @@ -0,0 +1,166 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils.unions import parse_open_union +from .argumentsdelta import ArgumentsDelta, ArgumentsDeltaTypedDict +from .audiodelta import AudioDelta, AudioDeltaTypedDict +from .codeexecutioncalldelta import ( + CodeExecutionCallDelta, + CodeExecutionCallDeltaTypedDict, +) +from .codeexecutionresultdelta import ( + CodeExecutionResultDelta, + CodeExecutionResultDeltaTypedDict, +) +from .documentdelta import DocumentDelta, DocumentDeltaTypedDict +from .filesearchcalldelta import FileSearchCallDelta, FileSearchCallDeltaTypedDict +from .filesearchresultdelta import FileSearchResultDelta, FileSearchResultDeltaTypedDict +from .functionresultdelta import FunctionResultDelta, FunctionResultDeltaTypedDict +from .googlemapscalldelta import GoogleMapsCallDelta, GoogleMapsCallDeltaTypedDict +from .googlemapsresultdelta import GoogleMapsResultDelta, GoogleMapsResultDeltaTypedDict +from .googlesearchcalldelta import GoogleSearchCallDelta, GoogleSearchCallDeltaTypedDict +from .googlesearchresultdelta import ( + GoogleSearchResultDelta, + GoogleSearchResultDeltaTypedDict, +) +from .imagedelta import ImageDelta, ImageDeltaTypedDict +from .mcpservertoolcalldelta import ( + MCPServerToolCallDelta, + MCPServerToolCallDeltaTypedDict, +) +from .mcpservertoolresultdelta import ( + MCPServerToolResultDelta, + MCPServerToolResultDeltaTypedDict, +) +from .textannotationdelta import TextAnnotationDelta, TextAnnotationDeltaTypedDict +from .textdelta import TextDelta, TextDeltaTypedDict +from .thoughtsignaturedelta import ThoughtSignatureDelta, ThoughtSignatureDeltaTypedDict +from .thoughtsummarydelta import ThoughtSummaryDelta, ThoughtSummaryDeltaTypedDict +from .urlcontextcalldelta import URLContextCallDelta, URLContextCallDeltaTypedDict +from .urlcontextresultdelta import URLContextResultDelta, URLContextResultDeltaTypedDict +from .videodelta import VideoDelta, VideoDeltaTypedDict +from functools import partial +from pydantic import ConfigDict +from pydantic.functional_validators import BeforeValidator +from typing import Any, Literal, Union +from typing_extensions import Annotated, TypeAliasType + + +StepDeltaDataTypedDict = TypeAliasType( + "StepDeltaDataTypedDict", + Union[ + TextAnnotationDeltaTypedDict, + ArgumentsDeltaTypedDict, + ThoughtSummaryDeltaTypedDict, + ThoughtSignatureDeltaTypedDict, + TextDeltaTypedDict, + FileSearchCallDeltaTypedDict, + FileSearchResultDeltaTypedDict, + GoogleMapsCallDeltaTypedDict, + URLContextCallDeltaTypedDict, + GoogleMapsResultDeltaTypedDict, + CodeExecutionCallDeltaTypedDict, + GoogleSearchCallDeltaTypedDict, + DocumentDeltaTypedDict, + URLContextResultDeltaTypedDict, + GoogleSearchResultDeltaTypedDict, + CodeExecutionResultDeltaTypedDict, + MCPServerToolCallDeltaTypedDict, + MCPServerToolResultDeltaTypedDict, + FunctionResultDeltaTypedDict, + ImageDeltaTypedDict, + VideoDeltaTypedDict, + AudioDeltaTypedDict, + ], +) + + +class UnknownStepDeltaData(BaseModel): + r"""A StepDeltaData variant the SDK doesn't recognize. Preserves the raw payload.""" + + type: Literal["UNKNOWN"] = "UNKNOWN" + raw: Any + is_unknown: Literal[True] = True + + model_config = ConfigDict(frozen=True) + + +_STEP_DELTA_DATA_VARIANTS: dict[str, Any] = { + "arguments_delta": ArgumentsDelta, + "audio": AudioDelta, + "code_execution_call": CodeExecutionCallDelta, + "code_execution_result": CodeExecutionResultDelta, + "document": DocumentDelta, + "file_search_call": FileSearchCallDelta, + "file_search_result": FileSearchResultDelta, + "function_result": FunctionResultDelta, + "google_maps_call": GoogleMapsCallDelta, + "google_maps_result": GoogleMapsResultDelta, + "google_search_call": GoogleSearchCallDelta, + "google_search_result": GoogleSearchResultDelta, + "image": ImageDelta, + "mcp_server_tool_call": MCPServerToolCallDelta, + "mcp_server_tool_result": MCPServerToolResultDelta, + "text_annotation_delta": TextAnnotationDelta, + "text": TextDelta, + "thought_signature": ThoughtSignatureDelta, + "thought_summary": ThoughtSummaryDelta, + "url_context_call": URLContextCallDelta, + "url_context_result": URLContextResultDelta, + "video": VideoDelta, +} + + +StepDeltaData = Annotated[ + Union[ + ArgumentsDelta, + AudioDelta, + CodeExecutionCallDelta, + CodeExecutionResultDelta, + DocumentDelta, + FileSearchCallDelta, + FileSearchResultDelta, + FunctionResultDelta, + GoogleMapsCallDelta, + GoogleMapsResultDelta, + GoogleSearchCallDelta, + GoogleSearchResultDelta, + ImageDelta, + MCPServerToolCallDelta, + MCPServerToolResultDelta, + TextAnnotationDelta, + TextDelta, + ThoughtSignatureDelta, + ThoughtSummaryDelta, + URLContextCallDelta, + URLContextResultDelta, + VideoDelta, + UnknownStepDeltaData, + ], + BeforeValidator( + partial( + parse_open_union, + disc_key="type", + variants=_STEP_DELTA_DATA_VARIANTS, + unknown_cls=UnknownStepDeltaData, + union_name="StepDeltaData", + ) + ), +] diff --git a/google/genai/_gaos/types/interactions/stepdeltametadata.py b/google/genai/_gaos/types/interactions/stepdeltametadata.py new file mode 100644 index 000000000..44ae3bf0e --- /dev/null +++ b/google/genai/_gaos/types/interactions/stepdeltametadata.py @@ -0,0 +1,54 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .usage import Usage, UsageParam +from pydantic import model_serializer +from typing import Optional +from typing_extensions import NotRequired, TypedDict + + +class StepDeltaMetadataTypedDict(TypedDict): + r"""Optional metadata accompanying ANY streamed event.""" + + total_usage: NotRequired[UsageParam] + r"""Statistics on the interaction request's token usage.""" + + +class StepDeltaMetadata(BaseModel): + r"""Optional metadata accompanying ANY streamed event.""" + + total_usage: Optional[Usage] = None + r"""Statistics on the interaction request's token usage.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["total_usage"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/stepstart.py b/google/genai/_gaos/types/interactions/stepstart.py new file mode 100644 index 000000000..88f17c724 --- /dev/null +++ b/google/genai/_gaos/types/interactions/stepstart.py @@ -0,0 +1,113 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .step import Step, StepParam +from .usage import Usage, UsageParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class StepStartMetadataTypedDict(TypedDict): + r"""Optional metadata accompanying ANY streamed event.""" + + total_usage: NotRequired[UsageParam] + r"""Statistics on the interaction request's token usage.""" + + +class StepStartMetadata(BaseModel): + r"""Optional metadata accompanying ANY streamed event.""" + + total_usage: Optional[Usage] = None + r"""Statistics on the interaction request's token usage.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["total_usage"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class StepStartTypedDict(TypedDict): + index: int + step: StepParam + r"""A step in the interaction.""" + event_type: Literal["step.start"] + event_id: NotRequired[str] + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + metadata: NotRequired[StepStartMetadataTypedDict] + r"""Optional metadata accompanying ANY streamed event.""" + + +class StepStart(BaseModel): + index: int + + step: Step + r"""A step in the interaction.""" + + event_type: Annotated[ + Annotated[Literal["step.start"], AfterValidator(validate_const("step.start"))], + pydantic.Field(alias="event_type"), + ] = "step.start" + + event_id: Optional[str] = None + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + + metadata: Optional[StepStartMetadata] = None + r"""Optional metadata accompanying ANY streamed event.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["event_id", "metadata"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + StepStart.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/stepstop.py b/google/genai/_gaos/types/interactions/stepstop.py new file mode 100644 index 000000000..d077fc06b --- /dev/null +++ b/google/genai/_gaos/types/interactions/stepstop.py @@ -0,0 +1,107 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .usage import Usage, UsageParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class StepStopMetadataTypedDict(TypedDict): + r"""Optional metadata accompanying ANY streamed event.""" + + total_usage: NotRequired[UsageParam] + r"""Statistics on the interaction request's token usage.""" + + +class StepStopMetadata(BaseModel): + r"""Optional metadata accompanying ANY streamed event.""" + + total_usage: Optional[Usage] = None + r"""Statistics on the interaction request's token usage.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["total_usage"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class StepStopTypedDict(TypedDict): + index: int + event_type: Literal["step.stop"] + event_id: NotRequired[str] + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + metadata: NotRequired[StepStopMetadataTypedDict] + r"""Optional metadata accompanying ANY streamed event.""" + + +class StepStop(BaseModel): + index: int + + event_type: Annotated[ + Annotated[Literal["step.stop"], AfterValidator(validate_const("step.stop"))], + pydantic.Field(alias="event_type"), + ] = "step.stop" + + event_id: Optional[str] = None + r"""The event_id token to be used to resume the interaction stream, from + this event. + """ + + metadata: Optional[StepStopMetadata] = None + r"""Optional metadata accompanying ANY streamed event.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["event_id", "metadata"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + StepStop.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/textannotationdelta.py b/google/genai/_gaos/types/interactions/textannotationdelta.py new file mode 100644 index 000000000..9f629c531 --- /dev/null +++ b/google/genai/_gaos/types/interactions/textannotationdelta.py @@ -0,0 +1,68 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .annotation import Annotation, AnnotationParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class TextAnnotationDeltaTypedDict(TypedDict): + type: Literal["text_annotation_delta"] + annotations: NotRequired[List[AnnotationParam]] + r"""Citation information for model-generated content.""" + + +class TextAnnotationDelta(BaseModel): + type: Annotated[ + Annotated[ + Literal["text_annotation_delta"], + AfterValidator(validate_const("text_annotation_delta")), + ], + pydantic.Field(alias="type"), + ] = "text_annotation_delta" + + annotations: Optional[List[Annotation]] = None + r"""Citation information for model-generated content.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["annotations"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + TextAnnotationDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/textcontent.py b/google/genai/_gaos/types/interactions/textcontent.py new file mode 100644 index 000000000..894955e43 --- /dev/null +++ b/google/genai/_gaos/types/interactions/textcontent.py @@ -0,0 +1,74 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .annotation import Annotation, AnnotationParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class TextContentParam(TypedDict): + r"""A text content block.""" + + text: str + r"""Required. The text content.""" + type: Literal["text"] + annotations: NotRequired[List[AnnotationParam]] + r"""Citation information for model-generated content.""" + + +class TextContent(BaseModel): + r"""A text content block.""" + + text: str + r"""Required. The text content.""" + + type: Annotated[ + Annotated[Literal["text"], AfterValidator(validate_const("text"))], + pydantic.Field(alias="type"), + ] = "text" + + annotations: Optional[List[Annotation]] = None + r"""Citation information for model-generated content.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["annotations"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + TextContent.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/textdelta.py b/google/genai/_gaos/types/interactions/textdelta.py new file mode 100644 index 000000000..9e2d0b0fa --- /dev/null +++ b/google/genai/_gaos/types/interactions/textdelta.py @@ -0,0 +1,45 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils import validate_const +import pydantic +from pydantic.functional_validators import AfterValidator +from typing import Literal +from typing_extensions import Annotated, TypedDict + + +class TextDeltaTypedDict(TypedDict): + text: str + type: Literal["text"] + + +class TextDelta(BaseModel): + text: str + + type: Annotated[ + Annotated[Literal["text"], AfterValidator(validate_const("text"))], + pydantic.Field(alias="type"), + ] = "text" + + +try: + TextDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/textresponseformat.py b/google/genai/_gaos/types/interactions/textresponseformat.py new file mode 100644 index 000000000..72cc22977 --- /dev/null +++ b/google/genai/_gaos/types/interactions/textresponseformat.py @@ -0,0 +1,87 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Any, Dict, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +TextResponseFormatMimeType = Union[ + Literal[ + "application/json", + "text/plain", + ], + UnrecognizedStr, +] +r"""The MIME type of the text output.""" + + +class TextResponseFormatParam(TypedDict): + r"""Configuration for text output format.""" + + type: Literal["text"] + mime_type: NotRequired[TextResponseFormatMimeType] + r"""The MIME type of the text output.""" + schema_: NotRequired[Dict[str, Any]] + r"""The JSON schema that the output should conform to. Only applicable when + mime_type is application/json. + """ + + +class TextResponseFormat(BaseModel): + r"""Configuration for text output format.""" + + type: Annotated[ + Annotated[Literal["text"], AfterValidator(validate_const("text"))], + pydantic.Field(alias="type"), + ] = "text" + + mime_type: Optional[TextResponseFormatMimeType] = None + r"""The MIME type of the text output.""" + + schema_: Annotated[Optional[Dict[str, Any]], pydantic.Field(alias="schema")] = None + r"""The JSON schema that the output should conform to. Only applicable when + mime_type is application/json. + """ + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["mime_type", "schema"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + TextResponseFormat.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/thinkinglevel.py b/google/genai/_gaos/types/interactions/thinkinglevel.py new file mode 100644 index 000000000..52c98f9dd --- /dev/null +++ b/google/genai/_gaos/types/interactions/thinkinglevel.py @@ -0,0 +1,32 @@ +# 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 __future__ import annotations +from .. import UnrecognizedStr +from typing import Literal, Union + + +ThinkingLevel = Union[ + Literal[ + "minimal", + "low", + "medium", + "high", + ], + UnrecognizedStr, +] diff --git a/google/genai/_gaos/types/interactions/thinkingsummaries.py b/google/genai/_gaos/types/interactions/thinkingsummaries.py new file mode 100644 index 000000000..ac1b85fab --- /dev/null +++ b/google/genai/_gaos/types/interactions/thinkingsummaries.py @@ -0,0 +1,30 @@ +# 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 __future__ import annotations +from .. import UnrecognizedStr +from typing import Literal, Union + + +ThinkingSummaries = Union[ + Literal[ + "auto", + "none", + ], + UnrecognizedStr, +] diff --git a/google/genai/_gaos/types/interactions/thoughtsignaturedelta.py b/google/genai/_gaos/types/interactions/thoughtsignaturedelta.py new file mode 100644 index 000000000..14a89c16b --- /dev/null +++ b/google/genai/_gaos/types/interactions/thoughtsignaturedelta.py @@ -0,0 +1,67 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ThoughtSignatureDeltaTypedDict(TypedDict): + type: Literal["thought_signature"] + signature: NotRequired[str] + r"""Signature to match the backend source to be part of the generation.""" + + +class ThoughtSignatureDelta(BaseModel): + type: Annotated[ + Annotated[ + Literal["thought_signature"], + AfterValidator(validate_const("thought_signature")), + ], + pydantic.Field(alias="type"), + ] = "thought_signature" + + signature: Optional[str] = None + r"""Signature to match the backend source to be part of the generation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + ThoughtSignatureDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/thoughtstep.py b/google/genai/_gaos/types/interactions/thoughtstep.py new file mode 100644 index 000000000..cf5ca376c --- /dev/null +++ b/google/genai/_gaos/types/interactions/thoughtstep.py @@ -0,0 +1,74 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .content import Content, ContentParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ThoughtStepParam(TypedDict): + r"""A thought step.""" + + type: Literal["thought"] + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + summary: NotRequired[List[ContentParam]] + r"""A summary of the thought.""" + + +class ThoughtStep(BaseModel): + r"""A thought step.""" + + type: Annotated[ + Annotated[Literal["thought"], AfterValidator(validate_const("thought"))], + pydantic.Field(alias="type"), + ] = "thought" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + summary: Optional[List[Content]] = None + r"""A summary of the thought.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature", "summary"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + ThoughtStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/thoughtsummarycontent.py b/google/genai/_gaos/types/interactions/thoughtsummarycontent.py new file mode 100644 index 000000000..167d5b961 --- /dev/null +++ b/google/genai/_gaos/types/interactions/thoughtsummarycontent.py @@ -0,0 +1,63 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils.unions import parse_open_union +from .imagecontent import ImageContent, ImageContentParam +from .textcontent import TextContent, TextContentParam +from functools import partial +from pydantic import ConfigDict +from pydantic.functional_validators import BeforeValidator +from typing import Any, Literal, Union +from typing_extensions import Annotated, TypeAliasType + + +ThoughtSummaryContentTypedDict = TypeAliasType( + "ThoughtSummaryContentTypedDict", Union[TextContentParam, ImageContentParam] +) + + +class UnknownThoughtSummaryContent(BaseModel): + r"""A ThoughtSummaryContent variant the SDK doesn't recognize. Preserves the raw payload.""" + + type: Literal["UNKNOWN"] = "UNKNOWN" + raw: Any + is_unknown: Literal[True] = True + + model_config = ConfigDict(frozen=True) + + +_THOUGHT_SUMMARY_CONTENT_VARIANTS: dict[str, Any] = { + "image": ImageContent, + "text": TextContent, +} + + +ThoughtSummaryContent = Annotated[ + Union[ImageContent, TextContent, UnknownThoughtSummaryContent], + BeforeValidator( + partial( + parse_open_union, + disc_key="type", + variants=_THOUGHT_SUMMARY_CONTENT_VARIANTS, + unknown_cls=UnknownThoughtSummaryContent, + union_name="ThoughtSummaryContent", + ) + ), +] diff --git a/google/genai/_gaos/types/interactions/thoughtsummarydelta.py b/google/genai/_gaos/types/interactions/thoughtsummarydelta.py new file mode 100644 index 000000000..ce78c8008 --- /dev/null +++ b/google/genai/_gaos/types/interactions/thoughtsummarydelta.py @@ -0,0 +1,66 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .thoughtsummarycontent import ThoughtSummaryContent, ThoughtSummaryContentTypedDict +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ThoughtSummaryDeltaTypedDict(TypedDict): + content: NotRequired[ThoughtSummaryContentTypedDict] + type: Literal["thought_summary"] + + +class ThoughtSummaryDelta(BaseModel): + content: Optional[ThoughtSummaryContent] = None + + type: Annotated[ + Annotated[ + Literal["thought_summary"], + AfterValidator(validate_const("thought_summary")), + ], + pydantic.Field(alias="type"), + ] = "thought_summary" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["content"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + ThoughtSummaryDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/tool.py b/google/genai/_gaos/types/interactions/tool.py new file mode 100644 index 000000000..3c349da54 --- /dev/null +++ b/google/genai/_gaos/types/interactions/tool.py @@ -0,0 +1,101 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils.unions import parse_open_union +from .codeexecution import CodeExecution, CodeExecutionParam +from .computeruse import ComputerUse, ComputerUseParam +from .filesearch import FileSearch, FileSearchParam +from .function import Function, FunctionParam +from .googlemaps import GoogleMaps, GoogleMapsParam +from .googlesearch import GoogleSearch, GoogleSearchParam +from .mcpserver import MCPServer, MCPServerParam +from .retrieval import Retrieval, RetrievalParam +from .urlcontext import URLContext, URLContextParam +from functools import partial +from pydantic import ConfigDict +from pydantic.functional_validators import BeforeValidator +from typing import Any, Literal, Union +from typing_extensions import Annotated, TypeAliasType + + +ToolParam = TypeAliasType( + "ToolParam", + Union[ + CodeExecutionParam, + URLContextParam, + GoogleSearchParam, + ComputerUseParam, + RetrievalParam, + FileSearchParam, + FunctionParam, + GoogleMapsParam, + MCPServerParam, + ], +) +r"""A tool that can be used by the model.""" + + +class UnknownTool(BaseModel): + r"""A Tool variant the SDK doesn't recognize. Preserves the raw payload.""" + + type: Literal["UNKNOWN"] = "UNKNOWN" + raw: Any + is_unknown: Literal[True] = True + + model_config = ConfigDict(frozen=True) + + +_TOOL_VARIANTS: dict[str, Any] = { + "code_execution": CodeExecution, + "computer_use": ComputerUse, + "file_search": FileSearch, + "function": Function, + "google_maps": GoogleMaps, + "google_search": GoogleSearch, + "mcp_server": MCPServer, + "retrieval": Retrieval, + "url_context": URLContext, +} + + +Tool = Annotated[ + Union[ + CodeExecution, + ComputerUse, + FileSearch, + Function, + GoogleMaps, + GoogleSearch, + MCPServer, + Retrieval, + URLContext, + UnknownTool, + ], + BeforeValidator( + partial( + parse_open_union, + disc_key="type", + variants=_TOOL_VARIANTS, + unknown_cls=UnknownTool, + union_name="Tool", + ) + ), +] +r"""A tool that can be used by the model.""" diff --git a/google/genai/_gaos/types/interactions/toolchoiceconfig.py b/google/genai/_gaos/types/interactions/toolchoiceconfig.py new file mode 100644 index 000000000..d6ec51105 --- /dev/null +++ b/google/genai/_gaos/types/interactions/toolchoiceconfig.py @@ -0,0 +1,54 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .allowedtools import AllowedTools, AllowedToolsParam +from pydantic import model_serializer +from typing import Optional +from typing_extensions import NotRequired, TypedDict + + +class ToolChoiceConfigParam(TypedDict): + r"""The tool choice configuration containing allowed tools.""" + + allowed_tools: NotRequired[AllowedToolsParam] + r"""The configuration for allowed tools.""" + + +class ToolChoiceConfig(BaseModel): + r"""The tool choice configuration containing allowed tools.""" + + allowed_tools: Optional[AllowedTools] = None + r"""The configuration for allowed tools.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["allowed_tools"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/toolchoicetype.py b/google/genai/_gaos/types/interactions/toolchoicetype.py new file mode 100644 index 000000000..885b3d615 --- /dev/null +++ b/google/genai/_gaos/types/interactions/toolchoicetype.py @@ -0,0 +1,32 @@ +# 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 __future__ import annotations +from .. import UnrecognizedStr +from typing import Literal, Union + + +ToolChoiceType = Union[ + Literal[ + "auto", + "any", + "none", + "validated", + ], + UnrecognizedStr, +] diff --git a/google/genai/_gaos/types/interactions/urlcitation.py b/google/genai/_gaos/types/interactions/urlcitation.py new file mode 100644 index 000000000..6583eaaba --- /dev/null +++ b/google/genai/_gaos/types/interactions/urlcitation.py @@ -0,0 +1,91 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class URLCitationParam(TypedDict): + r"""A URL citation annotation.""" + + type: Literal["url_citation"] + url: NotRequired[str] + r"""The URL.""" + title: NotRequired[str] + r"""The title of the URL.""" + start_index: NotRequired[int] + r"""Start of segment of the response that is attributed to this source. + + Index indicates the start of the segment, measured in bytes. + """ + end_index: NotRequired[int] + r"""End of the attributed segment, exclusive.""" + + +class URLCitation(BaseModel): + r"""A URL citation annotation.""" + + type: Annotated[ + Annotated[ + Literal["url_citation"], AfterValidator(validate_const("url_citation")) + ], + pydantic.Field(alias="type"), + ] = "url_citation" + + url: Optional[str] = None + r"""The URL.""" + + title: Optional[str] = None + r"""The title of the URL.""" + + start_index: Optional[int] = None + r"""Start of segment of the response that is attributed to this source. + + Index indicates the start of the segment, measured in bytes. + """ + + end_index: Optional[int] = None + r"""End of the attributed segment, exclusive.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["url", "title", "start_index", "end_index"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + URLCitation.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/urlcontext.py b/google/genai/_gaos/types/interactions/urlcontext.py new file mode 100644 index 000000000..1957d4d58 --- /dev/null +++ b/google/genai/_gaos/types/interactions/urlcontext.py @@ -0,0 +1,48 @@ +# 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 __future__ import annotations +from .. import BaseModel +from ...utils import validate_const +import pydantic +from pydantic.functional_validators import AfterValidator +from typing import Literal +from typing_extensions import Annotated, TypedDict + + +class URLContextParam(TypedDict): + r"""A tool that can be used by the model to fetch URL context.""" + + type: Literal["url_context"] + + +class URLContext(BaseModel): + r"""A tool that can be used by the model to fetch URL context.""" + + type: Annotated[ + Annotated[ + Literal["url_context"], AfterValidator(validate_const("url_context")) + ], + pydantic.Field(alias="type"), + ] = "url_context" + + +try: + URLContext.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/urlcontextcallarguments.py b/google/genai/_gaos/types/interactions/urlcontextcallarguments.py new file mode 100644 index 000000000..afeefa382 --- /dev/null +++ b/google/genai/_gaos/types/interactions/urlcontextcallarguments.py @@ -0,0 +1,53 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class URLContextCallArgumentsTypedDict(TypedDict): + r"""The arguments to pass to the URL context.""" + + urls: NotRequired[List[str]] + r"""The URLs to fetch.""" + + +class URLContextCallArguments(BaseModel): + r"""The arguments to pass to the URL context.""" + + urls: Optional[List[str]] = None + r"""The URLs to fetch.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["urls"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/urlcontextcalldelta.py b/google/genai/_gaos/types/interactions/urlcontextcalldelta.py new file mode 100644 index 000000000..568e88fe2 --- /dev/null +++ b/google/genai/_gaos/types/interactions/urlcontextcalldelta.py @@ -0,0 +1,76 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .urlcontextcallarguments import ( + URLContextCallArguments, + URLContextCallArgumentsTypedDict, +) +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class URLContextCallDeltaTypedDict(TypedDict): + arguments: URLContextCallArgumentsTypedDict + r"""The arguments to pass to the URL context.""" + type: Literal["url_context_call"] + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class URLContextCallDelta(BaseModel): + arguments: URLContextCallArguments + r"""The arguments to pass to the URL context.""" + + type: Annotated[ + Annotated[ + Literal["url_context_call"], + AfterValidator(validate_const("url_context_call")), + ], + pydantic.Field(alias="type"), + ] = "url_context_call" + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + URLContextCallDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/urlcontextcallstep.py b/google/genai/_gaos/types/interactions/urlcontextcallstep.py new file mode 100644 index 000000000..fe9545182 --- /dev/null +++ b/google/genai/_gaos/types/interactions/urlcontextcallstep.py @@ -0,0 +1,111 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ArgumentsParam(TypedDict): + r"""Required. The arguments to pass to the URL context.""" + + urls: NotRequired[List[str]] + r"""The URLs to fetch.""" + + +class Arguments(BaseModel): + r"""Required. The arguments to pass to the URL context.""" + + urls: Optional[List[str]] = None + r"""The URLs to fetch.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["urls"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class URLContextCallStepParam(TypedDict): + r"""URL context call step.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + arguments: ArgumentsParam + r"""Required. The arguments to pass to the URL context.""" + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + type: Literal["url_context_call"] + + +class URLContextCallStep(BaseModel): + r"""URL context call step.""" + + id: str + r"""Required. A unique ID for this specific tool call.""" + + arguments: Arguments + r"""Required. The arguments to pass to the URL context.""" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + type: Annotated[ + Annotated[ + Literal["url_context_call"], + AfterValidator(validate_const("url_context_call")), + ], + pydantic.Field(alias="type"), + ] = "url_context_call" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + URLContextCallStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/urlcontextresult.py b/google/genai/_gaos/types/interactions/urlcontextresult.py new file mode 100644 index 000000000..4ba3e28df --- /dev/null +++ b/google/genai/_gaos/types/interactions/urlcontextresult.py @@ -0,0 +1,70 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from pydantic import model_serializer +from typing import Literal, Optional, Union +from typing_extensions import NotRequired, TypedDict + + +URLContextResultStatus = Union[ + Literal[ + "success", + "error", + "paywall", + "unsafe", + ], + UnrecognizedStr, +] +r"""The status of the URL retrieval.""" + + +class URLContextResultParam(TypedDict): + r"""The result of the URL context.""" + + url: NotRequired[str] + r"""The URL that was fetched.""" + status: NotRequired[URLContextResultStatus] + r"""The status of the URL retrieval.""" + + +class URLContextResult(BaseModel): + r"""The result of the URL context.""" + + url: Optional[str] = None + r"""The URL that was fetched.""" + + status: Optional[URLContextResultStatus] = None + r"""The status of the URL retrieval.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["url", "status"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/urlcontextresultdelta.py b/google/genai/_gaos/types/interactions/urlcontextresultdelta.py new file mode 100644 index 000000000..1c7d0fc55 --- /dev/null +++ b/google/genai/_gaos/types/interactions/urlcontextresultdelta.py @@ -0,0 +1,74 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .urlcontextresult import URLContextResult, URLContextResultParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class URLContextResultDeltaTypedDict(TypedDict): + result: List[URLContextResultParam] + type: Literal["url_context_result"] + is_error: NotRequired[bool] + signature: NotRequired[str] + r"""A signature hash for backend validation.""" + + +class URLContextResultDelta(BaseModel): + result: List[URLContextResult] + + type: Annotated[ + Annotated[ + Literal["url_context_result"], + AfterValidator(validate_const("url_context_result")), + ], + pydantic.Field(alias="type"), + ] = "url_context_result" + + is_error: Optional[bool] = None + + signature: Optional[str] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["is_error", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + URLContextResultDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/urlcontextresultstep.py b/google/genai/_gaos/types/interactions/urlcontextresultstep.py new file mode 100644 index 000000000..af91ce439 --- /dev/null +++ b/google/genai/_gaos/types/interactions/urlcontextresultstep.py @@ -0,0 +1,87 @@ +# 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 __future__ import annotations +from .. import Base64EncodedString, Base64FileInput, BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .urlcontextresult import URLContextResult, URLContextResultParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +class URLContextResultStepParam(TypedDict): + r"""URL context result step.""" + + result: List[URLContextResultParam] + r"""Required. The results of the URL context.""" + call_id: str + r"""Required. ID to match the ID from the function call block.""" + type: Literal["url_context_result"] + is_error: NotRequired[bool] + r"""Whether the URL context resulted in an error.""" + signature: NotRequired[Union[str, Base64FileInput]] + r"""A signature hash for backend validation.""" + + +class URLContextResultStep(BaseModel): + r"""URL context result step.""" + + result: List[URLContextResult] + r"""Required. The results of the URL context.""" + + call_id: str + r"""Required. ID to match the ID from the function call block.""" + + type: Annotated[ + Annotated[ + Literal["url_context_result"], + AfterValidator(validate_const("url_context_result")), + ], + pydantic.Field(alias="type"), + ] = "url_context_result" + + is_error: Optional[bool] = None + r"""Whether the URL context resulted in an error.""" + + signature: Optional[Base64EncodedString] = None + r"""A signature hash for backend validation.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["is_error", "signature"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + URLContextResultStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/usage.py b/google/genai/_gaos/types/interactions/usage.py new file mode 100644 index 000000000..e72499830 --- /dev/null +++ b/google/genai/_gaos/types/interactions/usage.py @@ -0,0 +1,123 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .groundingtoolcount import GroundingToolCount, GroundingToolCountParam +from .modalitytokens import ModalityTokens, ModalityTokensParam +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class UsageParam(TypedDict): + r"""Statistics on the interaction request's token usage.""" + + total_input_tokens: NotRequired[int] + r"""Number of tokens in the prompt (context).""" + input_tokens_by_modality: NotRequired[List[ModalityTokensParam]] + r"""A breakdown of input token usage by modality.""" + total_cached_tokens: NotRequired[int] + r"""Number of tokens in the cached part of the prompt (the cached content).""" + cached_tokens_by_modality: NotRequired[List[ModalityTokensParam]] + r"""A breakdown of cached token usage by modality.""" + total_output_tokens: NotRequired[int] + r"""Total number of tokens across all the generated responses.""" + output_tokens_by_modality: NotRequired[List[ModalityTokensParam]] + r"""A breakdown of output token usage by modality.""" + total_tool_use_tokens: NotRequired[int] + r"""Number of tokens present in tool-use prompt(s).""" + tool_use_tokens_by_modality: NotRequired[List[ModalityTokensParam]] + r"""A breakdown of tool-use token usage by modality.""" + total_thought_tokens: NotRequired[int] + r"""Number of tokens of thoughts for thinking models.""" + total_tokens: NotRequired[int] + r"""Total token count for the interaction request (prompt + responses + other + internal tokens). + """ + grounding_tool_count: NotRequired[List[GroundingToolCountParam]] + r"""Grounding tool count.""" + + +class Usage(BaseModel): + r"""Statistics on the interaction request's token usage.""" + + total_input_tokens: Optional[int] = None + r"""Number of tokens in the prompt (context).""" + + input_tokens_by_modality: Optional[List[ModalityTokens]] = None + r"""A breakdown of input token usage by modality.""" + + total_cached_tokens: Optional[int] = None + r"""Number of tokens in the cached part of the prompt (the cached content).""" + + cached_tokens_by_modality: Optional[List[ModalityTokens]] = None + r"""A breakdown of cached token usage by modality.""" + + total_output_tokens: Optional[int] = None + r"""Total number of tokens across all the generated responses.""" + + output_tokens_by_modality: Optional[List[ModalityTokens]] = None + r"""A breakdown of output token usage by modality.""" + + total_tool_use_tokens: Optional[int] = None + r"""Number of tokens present in tool-use prompt(s).""" + + tool_use_tokens_by_modality: Optional[List[ModalityTokens]] = None + r"""A breakdown of tool-use token usage by modality.""" + + total_thought_tokens: Optional[int] = None + r"""Number of tokens of thoughts for thinking models.""" + + total_tokens: Optional[int] = None + r"""Total token count for the interaction request (prompt + responses + other + internal tokens). + """ + + grounding_tool_count: Optional[List[GroundingToolCount]] = None + r"""Grounding tool count.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + [ + "total_input_tokens", + "input_tokens_by_modality", + "total_cached_tokens", + "cached_tokens_by_modality", + "total_output_tokens", + "output_tokens_by_modality", + "total_tool_use_tokens", + "tool_use_tokens_by_modality", + "total_thought_tokens", + "total_tokens", + "grounding_tool_count", + ] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/userinputstep.py b/google/genai/_gaos/types/interactions/userinputstep.py new file mode 100644 index 000000000..ea84c0d7d --- /dev/null +++ b/google/genai/_gaos/types/interactions/userinputstep.py @@ -0,0 +1,67 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from ...utils import validate_const +from .content import Content, ContentParam +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import List, Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class UserInputStepParam(TypedDict): + r"""Input provided by the user.""" + + content: NotRequired[List[ContentParam]] + type: Literal["user_input"] + + +class UserInputStep(BaseModel): + r"""Input provided by the user.""" + + content: Optional[List[Content]] = None + + type: Annotated[ + Annotated[Literal["user_input"], AfterValidator(validate_const("user_input"))], + pydantic.Field(alias="type"), + ] = "user_input" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["content"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + UserInputStep.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/vertexaisearchconfig.py b/google/genai/_gaos/types/interactions/vertexaisearchconfig.py new file mode 100644 index 000000000..8bff1b3fb --- /dev/null +++ b/google/genai/_gaos/types/interactions/vertexaisearchconfig.py @@ -0,0 +1,58 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class VertexAISearchConfigParam(TypedDict): + r"""Used to specify configuration for VertexAISearch.""" + + engine: NotRequired[str] + r"""Optional. Used to specify Vertex AI Search engine.""" + datastores: NotRequired[List[str]] + r"""Optional. Used to specify Vertex AI Search datastores.""" + + +class VertexAISearchConfig(BaseModel): + r"""Used to specify configuration for VertexAISearch.""" + + engine: Optional[str] = None + r"""Optional. Used to specify Vertex AI Search engine.""" + + datastores: Optional[List[str]] = None + r"""Optional. Used to specify Vertex AI Search datastores.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["engine", "datastores"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/interactions/videocontent.py b/google/genai/_gaos/types/interactions/videocontent.py new file mode 100644 index 000000000..f2a4edae6 --- /dev/null +++ b/google/genai/_gaos/types/interactions/videocontent.py @@ -0,0 +1,105 @@ +# 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 __future__ import annotations +from .. import ( + Base64EncodedString, + Base64FileInput, + BaseModel, + UNSET_SENTINEL, + UnrecognizedStr, +) +from ...utils import validate_const +from .mediaresolution import MediaResolution +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +VideoContentMimeType = Union[ + Literal[ + "video/mp4", + "video/mpeg", + "video/mpg", + "video/mov", + "video/avi", + "video/x-flv", + "video/webm", + "video/wmv", + "video/3gpp", + ], + UnrecognizedStr, +] +r"""The mime type of the video.""" + + +class VideoContentParam(TypedDict): + r"""A video content block.""" + + data: NotRequired[Union[str, Base64FileInput]] + r"""The video content.""" + mime_type: NotRequired[VideoContentMimeType] + r"""The mime type of the video.""" + resolution: NotRequired[MediaResolution] + type: Literal["video"] + uri: NotRequired[str] + r"""The URI of the video.""" + + +class VideoContent(BaseModel): + r"""A video content block.""" + + data: Optional[Base64EncodedString] = None + r"""The video content.""" + + mime_type: Optional[VideoContentMimeType] = None + r"""The mime type of the video.""" + + resolution: Optional[MediaResolution] = None + + type: Annotated[ + Annotated[Literal["video"], AfterValidator(validate_const("video"))], + pydantic.Field(alias="type"), + ] = "video" + + uri: Optional[str] = None + r"""The URI of the video.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["data", "mime_type", "resolution", "uri"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + VideoContent.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/videodelta.py b/google/genai/_gaos/types/interactions/videodelta.py new file mode 100644 index 000000000..444b5e6ec --- /dev/null +++ b/google/genai/_gaos/types/interactions/videodelta.py @@ -0,0 +1,88 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from ...utils import validate_const +from .mediaresolution import MediaResolution +import pydantic +from pydantic import model_serializer +from pydantic.functional_validators import AfterValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +VideoDeltaMimeType = Union[ + Literal[ + "video/mp4", + "video/mpeg", + "video/mpg", + "video/mov", + "video/avi", + "video/x-flv", + "video/webm", + "video/wmv", + "video/3gpp", + ], + UnrecognizedStr, +] + + +class VideoDeltaTypedDict(TypedDict): + data: NotRequired[str] + mime_type: NotRequired[VideoDeltaMimeType] + resolution: NotRequired[MediaResolution] + type: Literal["video"] + uri: NotRequired[str] + + +class VideoDelta(BaseModel): + data: Optional[str] = None + + mime_type: Optional[VideoDeltaMimeType] = None + + resolution: Optional[MediaResolution] = None + + type: Annotated[ + Annotated[Literal["video"], AfterValidator(validate_const("video"))], + pydantic.Field(alias="type"), + ] = "video" + + uri: Optional[str] = None + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["data", "mime_type", "resolution", "uri"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + VideoDelta.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/interactions/webhookconfig.py b/google/genai/_gaos/types/interactions/webhookconfig.py new file mode 100644 index 000000000..27a554e1d --- /dev/null +++ b/google/genai/_gaos/types/interactions/webhookconfig.py @@ -0,0 +1,66 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import Any, Dict, List, Optional +from typing_extensions import NotRequired, TypedDict + + +class WebhookConfigParam(TypedDict): + r"""Message for configuring webhook events for a request.""" + + uris: NotRequired[List[str]] + r"""Optional. If set, these webhook URIs will be used for webhook events instead of the + registered webhooks. + """ + user_metadata: NotRequired[Dict[str, Any]] + r"""Optional. The user metadata that will be returned on each event emission to the + webhooks. + """ + + +class WebhookConfig(BaseModel): + r"""Message for configuring webhook events for a request.""" + + uris: Optional[List[str]] = None + r"""Optional. If set, these webhook URIs will be used for webhook events instead of the + registered webhooks. + """ + + user_metadata: Optional[Dict[str, Any]] = None + r"""Optional. The user metadata that will be returned on each event emission to the + webhooks. + """ + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["uris", "user_metadata"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/security.py b/google/genai/_gaos/types/security.py new file mode 100644 index 000000000..b7492fe7a --- /dev/null +++ b/google/genai/_gaos/types/security.py @@ -0,0 +1,97 @@ +# 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 __future__ import annotations +from . import BaseModel, UNSET_SENTINEL +from ..utils import FieldMetadata, SecurityMetadata +import pydantic +from pydantic import model_serializer +from typing import Dict, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class SecurityTypedDict(TypedDict): + api_key: NotRequired[str] + r"""Gemini API key sent as x-goog-api-key.""" + access_token: NotRequired[str] + r"""OAuth access token sent as a bearer Authorization header.""" + default_headers: NotRequired[Dict[str, str]] + r"""Additional default headers to apply before request-specific headers and auth.""" + + +class Security(BaseModel): + api_key: Annotated[ + Optional[str], + pydantic.Field(alias="apiKey"), + FieldMetadata( + security=SecurityMetadata( + scheme=True, scheme_type="http", sub_type="custom", field_name="apiKey" + ) + ), + ] = None + r"""Gemini API key sent as x-goog-api-key.""" + + access_token: Annotated[ + Optional[str], + pydantic.Field(alias="accessToken"), + FieldMetadata( + security=SecurityMetadata( + scheme=True, + scheme_type="http", + sub_type="custom", + field_name="accessToken", + ) + ), + ] = None + r"""OAuth access token sent as a bearer Authorization header.""" + + default_headers: Annotated[ + Optional[Dict[str, str]], + pydantic.Field(alias="defaultHeaders"), + FieldMetadata( + security=SecurityMetadata( + scheme=True, + scheme_type="http", + sub_type="custom", + field_name="defaultHeaders", + ) + ), + ] = None + r"""Additional default headers to apply before request-specific headers and auth.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["apiKey", "accessToken", "defaultHeaders"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +try: + Security.model_rebuild() +except NameError: + pass diff --git a/google/genai/_gaos/types/webhooks/__init__.py b/google/genai/_gaos/types/webhooks/__init__.py new file mode 100644 index 000000000..f9340f8ff --- /dev/null +++ b/google/genai/_gaos/types/webhooks/__init__.py @@ -0,0 +1,112 @@ +# 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 typing import Any, TYPE_CHECKING + +from ...utils.dynamic_imports import lazy_getattr, lazy_dir + +if TYPE_CHECKING: + from .pingwebhookrequest import PingWebhookRequest, PingWebhookRequestParam + from .rotatesigningsecretrequest import ( + RevocationBehavior, + RotateSigningSecretRequest, + RotateSigningSecretRequestParam, + ) + from .signingsecret import SigningSecret, SigningSecretTypedDict + from .webhook import ( + Webhook, + WebhookInput, + WebhookInputParam, + WebhookState, + WebhookSubscribedEvent, + WebhookTypedDict, + ) + from .webhooklistresponse import WebhookListResponse, WebhookListResponseTypedDict + from .webhookpingresponse import WebhookPingResponse, WebhookPingResponseTypedDict + from .webhookrotatesigningsecretresponse import ( + WebhookRotateSigningSecretResponse, + WebhookRotateSigningSecretResponseTypedDict, + ) + from .webhookupdate import ( + WebhookUpdate, + WebhookUpdateParam, + WebhookUpdateState, + WebhookUpdateSubscribedEvent, + ) + +__all__ = [ + "PingWebhookRequest", + "PingWebhookRequestParam", + "RevocationBehavior", + "RotateSigningSecretRequest", + "RotateSigningSecretRequestParam", + "SigningSecret", + "SigningSecretTypedDict", + "Webhook", + "WebhookInput", + "WebhookInputParam", + "WebhookListResponse", + "WebhookListResponseTypedDict", + "WebhookPingResponse", + "WebhookPingResponseTypedDict", + "WebhookRotateSigningSecretResponse", + "WebhookRotateSigningSecretResponseTypedDict", + "WebhookState", + "WebhookSubscribedEvent", + "WebhookTypedDict", + "WebhookUpdate", + "WebhookUpdateParam", + "WebhookUpdateState", + "WebhookUpdateSubscribedEvent", +] + +_dynamic_imports: dict[str, str] = { + "PingWebhookRequest": ".pingwebhookrequest", + "PingWebhookRequestParam": ".pingwebhookrequest", + "RevocationBehavior": ".rotatesigningsecretrequest", + "RotateSigningSecretRequest": ".rotatesigningsecretrequest", + "RotateSigningSecretRequestParam": ".rotatesigningsecretrequest", + "SigningSecret": ".signingsecret", + "SigningSecretTypedDict": ".signingsecret", + "Webhook": ".webhook", + "WebhookInput": ".webhook", + "WebhookInputParam": ".webhook", + "WebhookState": ".webhook", + "WebhookSubscribedEvent": ".webhook", + "WebhookTypedDict": ".webhook", + "WebhookListResponse": ".webhooklistresponse", + "WebhookListResponseTypedDict": ".webhooklistresponse", + "WebhookPingResponse": ".webhookpingresponse", + "WebhookPingResponseTypedDict": ".webhookpingresponse", + "WebhookRotateSigningSecretResponse": ".webhookrotatesigningsecretresponse", + "WebhookRotateSigningSecretResponseTypedDict": ".webhookrotatesigningsecretresponse", + "WebhookUpdate": ".webhookupdate", + "WebhookUpdateParam": ".webhookupdate", + "WebhookUpdateState": ".webhookupdate", + "WebhookUpdateSubscribedEvent": ".webhookupdate", +} + + +def __getattr__(attr_name: str) -> Any: + return lazy_getattr( + attr_name, package=__package__, dynamic_imports=_dynamic_imports + ) + + +def __dir__(): + return lazy_dir(dynamic_imports=_dynamic_imports) diff --git a/google/genai/_gaos/types/webhooks/pingwebhookrequest.py b/google/genai/_gaos/types/webhooks/pingwebhookrequest.py new file mode 100644 index 000000000..a13e8e0ba --- /dev/null +++ b/google/genai/_gaos/types/webhooks/pingwebhookrequest.py @@ -0,0 +1,29 @@ +# 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 __future__ import annotations +from .. import BaseModel +from typing_extensions import TypedDict + + +class PingWebhookRequestParam(TypedDict): + r"""Request message for WebhookService.PingWebhook.""" + + +class PingWebhookRequest(BaseModel): + r"""Request message for WebhookService.PingWebhook.""" diff --git a/google/genai/_gaos/types/webhooks/rotatesigningsecretrequest.py b/google/genai/_gaos/types/webhooks/rotatesigningsecretrequest.py new file mode 100644 index 000000000..e5002c42a --- /dev/null +++ b/google/genai/_gaos/types/webhooks/rotatesigningsecretrequest.py @@ -0,0 +1,60 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import Literal, Optional +from typing_extensions import NotRequired, TypedDict + + +RevocationBehavior = Literal[ + "revoke_previous_secrets_after_h24", + "revoke_previous_secrets_immediately", +] +r"""Optional. The revocation behavior for previous signing secrets.""" + + +class RotateSigningSecretRequestParam(TypedDict): + r"""Request message for WebhookService.RotateSigningSecret.""" + + revocation_behavior: NotRequired[RevocationBehavior] + r"""Optional. The revocation behavior for previous signing secrets.""" + + +class RotateSigningSecretRequest(BaseModel): + r"""Request message for WebhookService.RotateSigningSecret.""" + + revocation_behavior: Optional[RevocationBehavior] = None + r"""Optional. The revocation behavior for previous signing secrets.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["revocation_behavior"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/webhooks/signingsecret.py b/google/genai/_gaos/types/webhooks/signingsecret.py new file mode 100644 index 000000000..7e862cadd --- /dev/null +++ b/google/genai/_gaos/types/webhooks/signingsecret.py @@ -0,0 +1,59 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from datetime import datetime +from pydantic import model_serializer +from typing import Optional +from typing_extensions import NotRequired, TypedDict + + +class SigningSecretTypedDict(TypedDict): + r"""Represents a signing secret used to verify webhook payloads.""" + + truncated_secret: NotRequired[str] + r"""Output only. The truncated version of the signing secret.""" + expire_time: NotRequired[datetime] + r"""Output only. The expiration date of the signing secret.""" + + +class SigningSecret(BaseModel): + r"""Represents a signing secret used to verify webhook payloads.""" + + truncated_secret: Optional[str] = None + r"""Output only. The truncated version of the signing secret.""" + + expire_time: Optional[datetime] = None + r"""Output only. The expiration date of the signing secret.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["truncated_secret", "expire_time"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/webhooks/webhook.py b/google/genai/_gaos/types/webhooks/webhook.py new file mode 100644 index 000000000..01fd0940c --- /dev/null +++ b/google/genai/_gaos/types/webhooks/webhook.py @@ -0,0 +1,213 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from .signingsecret import SigningSecret, SigningSecretTypedDict +from datetime import datetime +from pydantic import model_serializer +from typing import List, Literal, Optional, Union +from typing_extensions import NotRequired, TypedDict + + +WebhookSubscribedEvent = Union[ + Literal[ + # Batch processing finished successfully. + "batch.succeeded", + # Batch has not been processed within the 48h timeframe. + "batch.expired", + # Batch job failed. + "batch.failed", + # Interaction requires action (e.g., function calling). + "interaction.requires_action", + # Interaction completed successfully. + "interaction.completed", + # Interaction failed. + "interaction.failed", + # Video generation completed. + "video.generated", + ], + UnrecognizedStr, +] + + +WebhookState = Union[ + Literal[ + "enabled", + "disabled", + "disabled_due_to_failed_deliveries", + ], + UnrecognizedStr, +] +r"""Output only. The state of the webhook.""" + + +class WebhookTypedDict(TypedDict): + r"""A Webhook resource.""" + + uri: str + r"""Required. The URI to which webhook events will be sent.""" + subscribed_events: List[WebhookSubscribedEvent] + r"""Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + """ + name: NotRequired[str] + r"""Optional. The user-provided name of the webhook.""" + create_time: NotRequired[datetime] + r"""Output only. The timestamp when the webhook was created.""" + update_time: NotRequired[datetime] + r"""Output only. The timestamp when the webhook was last updated.""" + signing_secrets: NotRequired[List[SigningSecretTypedDict]] + r"""Output only. The signing secrets associated with this webhook.""" + state: NotRequired[WebhookState] + r"""Output only. The state of the webhook.""" + new_signing_secret: NotRequired[str] + r"""Output only. The new signing secret for the webhook. Only populated on create.""" + id: NotRequired[str] + r"""Output only. The ID of the webhook.""" + + +class Webhook(BaseModel): + r"""A Webhook resource.""" + + uri: str + r"""Required. The URI to which webhook events will be sent.""" + + subscribed_events: List[WebhookSubscribedEvent] + r"""Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + """ + + name: Optional[str] = None + r"""Optional. The user-provided name of the webhook.""" + + create_time: Optional[datetime] = None + r"""Output only. The timestamp when the webhook was created.""" + + update_time: Optional[datetime] = None + r"""Output only. The timestamp when the webhook was last updated.""" + + signing_secrets: Optional[List[SigningSecret]] = None + r"""Output only. The signing secrets associated with this webhook.""" + + state: Optional[WebhookState] = None + r"""Output only. The state of the webhook.""" + + new_signing_secret: Optional[str] = None + r"""Output only. The new signing secret for the webhook. Only populated on create.""" + + id: Optional[str] = None + r"""Output only. The ID of the webhook.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + [ + "name", + "create_time", + "update_time", + "signing_secrets", + "state", + "new_signing_secret", + "id", + ] + ) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + + +class WebhookInputParam(TypedDict): + r"""A Webhook resource.""" + + uri: str + r"""Required. The URI to which webhook events will be sent.""" + subscribed_events: List[WebhookSubscribedEvent] + r"""Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + """ + name: NotRequired[str] + r"""Optional. The user-provided name of the webhook.""" + + +class WebhookInput(BaseModel): + r"""A Webhook resource.""" + + uri: str + r"""Required. The URI to which webhook events will be sent.""" + + subscribed_events: List[WebhookSubscribedEvent] + r"""Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + """ + + name: Optional[str] = None + r"""Optional. The user-provided name of the webhook.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["name"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/webhooks/webhooklistresponse.py b/google/genai/_gaos/types/webhooks/webhooklistresponse.py new file mode 100644 index 000000000..f42ff488b --- /dev/null +++ b/google/genai/_gaos/types/webhooks/webhooklistresponse.py @@ -0,0 +1,63 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from .webhook import Webhook, WebhookTypedDict +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class WebhookListResponseTypedDict(TypedDict): + r"""Response message for WebhookService.ListWebhooks.""" + + webhooks: NotRequired[List[WebhookTypedDict]] + r"""The webhooks.""" + next_page_token: NotRequired[str] + r"""A token, which can be sent as `page_token` to retrieve the next page. + If this field is omitted, there are no subsequent pages. + """ + + +class WebhookListResponse(BaseModel): + r"""Response message for WebhookService.ListWebhooks.""" + + webhooks: Optional[List[Webhook]] = None + r"""The webhooks.""" + + next_page_token: Optional[str] = None + r"""A token, which can be sent as `page_token` to retrieve the next page. + If this field is omitted, there are no subsequent pages. + """ + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["webhooks", "next_page_token"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/webhooks/webhookpingresponse.py b/google/genai/_gaos/types/webhooks/webhookpingresponse.py new file mode 100644 index 000000000..a6fec1373 --- /dev/null +++ b/google/genai/_gaos/types/webhooks/webhookpingresponse.py @@ -0,0 +1,29 @@ +# 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 __future__ import annotations +from .. import BaseModel +from typing_extensions import TypedDict + + +class WebhookPingResponseTypedDict(TypedDict): + r"""Response message for WebhookService.PingWebhook.""" + + +class WebhookPingResponse(BaseModel): + r"""Response message for WebhookService.PingWebhook.""" diff --git a/google/genai/_gaos/types/webhooks/webhookrotatesigningsecretresponse.py b/google/genai/_gaos/types/webhooks/webhookrotatesigningsecretresponse.py new file mode 100644 index 000000000..514e6b45c --- /dev/null +++ b/google/genai/_gaos/types/webhooks/webhookrotatesigningsecretresponse.py @@ -0,0 +1,53 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import Optional +from typing_extensions import NotRequired, TypedDict + + +class WebhookRotateSigningSecretResponseTypedDict(TypedDict): + r"""Response message for WebhookService.RotateSigningSecret.""" + + secret: NotRequired[str] + r"""Output only. The newly generated signing secret.""" + + +class WebhookRotateSigningSecretResponse(BaseModel): + r"""Response message for WebhookService.RotateSigningSecret.""" + + secret: Optional[str] = None + r"""Output only. The newly generated signing secret.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["secret"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/types/webhooks/webhookupdate.py b/google/genai/_gaos/types/webhooks/webhookupdate.py new file mode 100644 index 000000000..48bc636c2 --- /dev/null +++ b/google/genai/_gaos/types/webhooks/webhookupdate.py @@ -0,0 +1,111 @@ +# 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 __future__ import annotations +from .. import BaseModel, UNSET_SENTINEL, UnrecognizedStr +from pydantic import model_serializer +from typing import List, Literal, Optional, Union +from typing_extensions import NotRequired, TypedDict + + +WebhookUpdateSubscribedEvent = Union[ + Literal[ + # Batch processing finished successfully. + "batch.succeeded", + # Batch has not been processed within the 48h timeframe. + "batch.expired", + # Batch job failed. + "batch.failed", + # Interaction requires action (e.g., function calling). + "interaction.requires_action", + # Interaction completed successfully. + "interaction.completed", + # Interaction failed. + "interaction.failed", + # Video generation completed. + "video.generated", + ], + UnrecognizedStr, +] + + +WebhookUpdateState = Literal[ + "enabled", + "disabled", + "disabled_due_to_failed_deliveries", +] +r"""Optional. The state of the webhook.""" + + +class WebhookUpdateParam(TypedDict): + name: NotRequired[str] + r"""Optional. The user-provided name of the webhook.""" + uri: NotRequired[str] + r"""Optional. The URI to which webhook events will be sent.""" + subscribed_events: NotRequired[List[WebhookUpdateSubscribedEvent]] + r"""Optional. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + """ + state: NotRequired[WebhookUpdateState] + r"""Optional. The state of the webhook.""" + + +class WebhookUpdate(BaseModel): + name: Optional[str] = None + r"""Optional. The user-provided name of the webhook.""" + + uri: Optional[str] = None + r"""Optional. The URI to which webhook events will be sent.""" + + subscribed_events: Optional[List[WebhookUpdateSubscribedEvent]] = None + r"""Optional. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + """ + + state: Optional[WebhookUpdateState] = None + r"""Optional. The state of the webhook.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["name", "uri", "subscribed_events", "state"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/google/genai/_gaos/utils/__init__.py b/google/genai/_gaos/utils/__init__.py new file mode 100644 index 000000000..810f90885 --- /dev/null +++ b/google/genai/_gaos/utils/__init__.py @@ -0,0 +1,197 @@ +# 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 typing import Any, TYPE_CHECKING, Callable, TypeVar +import asyncio + +from .dynamic_imports import lazy_getattr, lazy_dir + +_T = TypeVar("_T") + + +async def run_sync_in_thread(func: Callable[..., _T], *args) -> _T: + """Run a synchronous function in a thread pool to avoid blocking the event loop.""" + return await asyncio.to_thread(func, *args) + + +if TYPE_CHECKING: + from .annotations import get_discriminator + from .datetimes import parse_datetime + from .enums import OpenEnumMeta + from .unions import parse_open_union + from .headers import get_headers, get_response_headers + from .metadata import ( + FieldMetadata, + find_metadata, + FormMetadata, + HeaderMetadata, + MultipartFormMetadata, + PathParamMetadata, + QueryParamMetadata, + RequestMetadata, + SecurityMetadata, + ) + from .queryparams import get_query_params + from .retries import BackoffStrategy, Retries, retry, retry_async, RetryConfig + from .requestbodies import serialize_request_body, SerializedRequestBody + from .security import get_security, get_security_from_env + + from .serializers import ( + get_pydantic_model, + marshal_json, + unmarshal, + unmarshal_json, + serialize_decimal, + serialize_float, + serialize_int, + stream_to_text, + stream_to_text_async, + stream_to_bytes, + stream_to_bytes_async, + validate_const, + validate_decimal, + validate_float, + validate_int, + ) + from .url import generate_url, template_url, remove_suffix + from .values import ( + get_global_from_env, + match_content_type, + match_status_codes, + match_response, + cast_partial, + ) + from .logger import Logger, get_body_content, get_default_logger + +__all__ = [ + "BackoffStrategy", + "FieldMetadata", + "find_metadata", + "FormMetadata", + "generate_url", + "get_body_content", + "get_default_logger", + "get_discriminator", + "parse_datetime", + "get_global_from_env", + "get_headers", + "get_pydantic_model", + "get_query_params", + "get_response_headers", + "get_security", + "get_security_from_env", + "HeaderMetadata", + "Logger", + "marshal_json", + "match_content_type", + "match_status_codes", + "match_response", + "MultipartFormMetadata", + "OpenEnumMeta", + "parse_open_union", + "PathParamMetadata", + "QueryParamMetadata", + "remove_suffix", + "Retries", + "retry", + "retry_async", + "RetryConfig", + "RequestMetadata", + "SecurityMetadata", + "serialize_decimal", + "serialize_float", + "serialize_int", + "serialize_request_body", + "SerializedRequestBody", + "stream_to_text", + "stream_to_text_async", + "stream_to_bytes", + "stream_to_bytes_async", + "template_url", + "unmarshal", + "unmarshal_json", + "validate_decimal", + "validate_const", + "validate_float", + "validate_int", + "cast_partial", +] + +_dynamic_imports: dict[str, str] = { + "BackoffStrategy": ".retries", + "FieldMetadata": ".metadata", + "find_metadata": ".metadata", + "FormMetadata": ".metadata", + "generate_url": ".url", + "get_body_content": ".logger", + "get_default_logger": ".logger", + "get_discriminator": ".annotations", + "parse_datetime": ".datetimes", + "get_global_from_env": ".values", + "get_headers": ".headers", + "get_pydantic_model": ".serializers", + "get_query_params": ".queryparams", + "get_response_headers": ".headers", + "get_security": ".security", + "get_security_from_env": ".security", + "HeaderMetadata": ".metadata", + "Logger": ".logger", + "marshal_json": ".serializers", + "match_content_type": ".values", + "match_status_codes": ".values", + "match_response": ".values", + "MultipartFormMetadata": ".metadata", + "OpenEnumMeta": ".enums", + "parse_open_union": ".unions", + "PathParamMetadata": ".metadata", + "QueryParamMetadata": ".metadata", + "remove_suffix": ".url", + "Retries": ".retries", + "retry": ".retries", + "retry_async": ".retries", + "RetryConfig": ".retries", + "RequestMetadata": ".metadata", + "SecurityMetadata": ".metadata", + "serialize_decimal": ".serializers", + "serialize_float": ".serializers", + "serialize_int": ".serializers", + "serialize_request_body": ".requestbodies", + "SerializedRequestBody": ".requestbodies", + "stream_to_text": ".serializers", + "stream_to_text_async": ".serializers", + "stream_to_bytes": ".serializers", + "stream_to_bytes_async": ".serializers", + "template_url": ".url", + "unmarshal": ".serializers", + "unmarshal_json": ".serializers", + "validate_decimal": ".serializers", + "validate_const": ".serializers", + "validate_float": ".serializers", + "validate_int": ".serializers", + "cast_partial": ".values", +} + + +def __getattr__(attr_name: str) -> Any: + return lazy_getattr( + attr_name, package=__package__, dynamic_imports=_dynamic_imports + ) + + +def __dir__(): + return lazy_dir(dynamic_imports=_dynamic_imports) diff --git a/google/genai/_gaos/utils/annotations.py b/google/genai/_gaos/utils/annotations.py new file mode 100644 index 000000000..e3bae23a3 --- /dev/null +++ b/google/genai/_gaos/utils/annotations.py @@ -0,0 +1,95 @@ +# 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 enum import Enum +from typing import Any, Optional + + +def get_discriminator(model: Any, fieldname: str, key: str) -> str: + """ + Recursively search for the discriminator attribute in a model. + + Args: + model (Any): The model to search within. + fieldname (str): The name of the field to search for. + key (str): The key to search for in dictionaries. + + Returns: + str: The name of the discriminator attribute. + + Raises: + ValueError: If the discriminator attribute is not found. + """ + upper_fieldname = fieldname.upper() + + def get_field_discriminator(field: Any) -> Optional[str]: + """Search for the discriminator attribute in a given field.""" + + if isinstance(field, dict): + if key in field: + return f"{field[key]}" + + if hasattr(field, fieldname): + attr = getattr(field, fieldname) + if isinstance(attr, Enum): + return f"{attr.value}" + return f"{attr}" + + if hasattr(field, upper_fieldname): + attr = getattr(field, upper_fieldname) + if isinstance(attr, Enum): + return f"{attr.value}" + return f"{attr}" + + return None + + def search_nested_discriminator(obj: Any) -> Optional[str]: + """Recursively search for discriminator in nested structures.""" + # First try direct field lookup + discriminator = get_field_discriminator(obj) + if discriminator is not None: + return discriminator + + # If it's a dict, search in nested values + if isinstance(obj, dict): + for value in obj.values(): + if isinstance(value, list): + # Search in list items + for item in value: + nested_discriminator = search_nested_discriminator(item) + if nested_discriminator is not None: + return nested_discriminator + elif isinstance(value, dict): + # Search in nested dict + nested_discriminator = search_nested_discriminator(value) + if nested_discriminator is not None: + return nested_discriminator + + return None + + if isinstance(model, list): + for field in model: + discriminator = search_nested_discriminator(field) + if discriminator is not None: + return discriminator + + discriminator = search_nested_discriminator(model) + if discriminator is not None: + return discriminator + + raise ValueError(f"Could not find discriminator field {fieldname} in {model}") diff --git a/google/genai/_gaos/utils/datetimes.py b/google/genai/_gaos/utils/datetimes.py new file mode 100644 index 000000000..80b793b3e --- /dev/null +++ b/google/genai/_gaos/utils/datetimes.py @@ -0,0 +1,39 @@ +# 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 datetime import datetime +import sys + + +def parse_datetime(datetime_string: str) -> datetime: + """ + Convert a RFC 3339 / ISO 8601 formatted string into a datetime object. + Python versions 3.11 and later support parsing RFC 3339 directly with + datetime.fromisoformat(), but for earlier versions, this function + encapsulates the necessary extra logic. + """ + # Python 3.11 and later can parse RFC 3339 directly + if sys.version_info >= (3, 11): + return datetime.fromisoformat(datetime_string) + + # For Python 3.10 and earlier, a common ValueError is trailing 'Z' suffix, + # so fix that upfront. + if datetime_string.endswith("Z"): + datetime_string = datetime_string[:-1] + "+00:00" + + return datetime.fromisoformat(datetime_string) diff --git a/google/genai/_gaos/utils/dynamic_imports.py b/google/genai/_gaos/utils/dynamic_imports.py new file mode 100644 index 000000000..cf3fded2d --- /dev/null +++ b/google/genai/_gaos/utils/dynamic_imports.py @@ -0,0 +1,71 @@ +# 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 importlib import import_module, util +import builtins +import sys + + +def dynamic_import(package, modname, retries=3): + """Import a module relative to package, retrying on KeyError from half-initialized modules.""" + for attempt in range(retries): + try: + return import_module(modname, package) + except KeyError: + resolved = util.resolve_name(modname, package) + sys.modules.pop(resolved, None) + if attempt == retries - 1: + break + raise KeyError(f"Failed to import module '{modname}' after {retries} attempts") + + +def lazy_getattr(attr_name, *, package, dynamic_imports, sub_packages=None): + """Module-level __getattr__ that lazily loads from a dynamic_imports mapping. + + Args: + attr_name: The attribute being looked up. + package: The caller's __package__ (for relative imports). + dynamic_imports: Dict mapping attribute names to relative module paths. + sub_packages: Optional list of subpackage names to lazy-load. + """ + module_name = dynamic_imports.get(attr_name) + if module_name is not None: + try: + module = dynamic_import(package, module_name) + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + if sub_packages and attr_name in sub_packages: + return import_module(f".{attr_name}", package) + + raise AttributeError(f"module '{package}' has no attribute '{attr_name}'") + + +def lazy_dir(*, dynamic_imports, sub_packages=None): + """Module-level __dir__ that lists lazily-loadable attributes.""" + lazy_attrs = builtins.list(dynamic_imports.keys()) + if sub_packages: + lazy_attrs.extend(sub_packages) + return builtins.sorted(lazy_attrs) diff --git a/google/genai/_gaos/utils/enums.py b/google/genai/_gaos/utils/enums.py new file mode 100644 index 000000000..558b8cd4f --- /dev/null +++ b/google/genai/_gaos/utils/enums.py @@ -0,0 +1,150 @@ +# 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.""" + +import enum +import sys +from typing import Any + +from pydantic_core import core_schema + + +class OpenEnumMeta(enum.EnumMeta): + # The __call__ method `boundary` kwarg was added in 3.11 and must be present + # for pyright. Refer also: https://github.com/pylint-dev/pylint/issues/9622 + # pylint: disable=unexpected-keyword-arg + # The __call__ method `values` varg must be named for pyright. + # pylint: disable=keyword-arg-before-vararg + + if sys.version_info >= (3, 11): + def __call__( + cls, value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None + ): + # The `type` kwarg also happens to be a built-in that pylint flags as + # redeclared. Safe to ignore this lint rule with this scope. + # pylint: disable=redefined-builtin + + if names is not None: + return super().__call__( + value, + names=names, + *values, + module=module, + qualname=qualname, + type=type, + start=start, + boundary=boundary, + ) + + try: + return super().__call__( + value, + names=names, # pyright: ignore[reportArgumentType] + *values, + module=module, + qualname=qualname, + type=type, + start=start, + boundary=boundary, + ) + except ValueError: + return value + else: + def __call__( + cls, value, names=None, *, module=None, qualname=None, type=None, start=1 + ): + # The `type` kwarg also happens to be a built-in that pylint flags as + # redeclared. Safe to ignore this lint rule with this scope. + # pylint: disable=redefined-builtin + + if names is not None: + return super().__call__( + value, + names=names, + module=module, + qualname=qualname, + type=type, + start=start, + ) + + try: + return super().__call__( + value, + names=names, # pyright: ignore[reportArgumentType] + module=module, + qualname=qualname, + type=type, + start=start, + ) + except ValueError: + return value + + def __new__(mcs, name, bases, namespace, **kwargs): + cls = super().__new__(mcs, name, bases, namespace, **kwargs) + + # Add __get_pydantic_core_schema__ to make open enums work correctly + # in union discrimination. In strict mode (used by Pydantic for unions), + # only known enum values match. In lax mode, unknown values are accepted. + def __get_pydantic_core_schema__( + cls_inner: Any, _source_type: Any, _handler: Any + ) -> core_schema.CoreSchema: + # Create a validator that only accepts known enum values (for strict mode) + def validate_strict(v: Any) -> Any: + if isinstance(v, cls_inner): + return v + # Use the parent EnumMeta's __call__ which raises ValueError for unknown values + return enum.EnumMeta.__call__(cls_inner, v) + + # Create a lax validator that accepts unknown values + def validate_lax(v: Any) -> Any: + if isinstance(v, cls_inner): + return v + try: + return enum.EnumMeta.__call__(cls_inner, v) + except ValueError: + # Return the raw value for unknown enum values + return v + + # Determine the base type schema (str or int) + is_int_enum = False + for base in cls_inner.__mro__: + if base is int: + is_int_enum = True + break + if base is str: + break + + base_schema = ( + core_schema.int_schema() + if is_int_enum + else core_schema.str_schema() + ) + + # Use lax_or_strict_schema: + # - strict mode: only known enum values match (raises ValueError for unknown) + # - lax mode: accept any value, return enum member or raw value + return core_schema.lax_or_strict_schema( + lax_schema=core_schema.chain_schema( + [base_schema, core_schema.no_info_plain_validator_function(validate_lax)] + ), + strict_schema=core_schema.chain_schema( + [base_schema, core_schema.no_info_plain_validator_function(validate_strict)] + ), + ) + + setattr(cls, "__get_pydantic_core_schema__", classmethod(__get_pydantic_core_schema__)) + return cls diff --git a/google/genai/_gaos/utils/eventstreaming.py b/google/genai/_gaos/utils/eventstreaming.py new file mode 100644 index 000000000..88cc5c1c7 --- /dev/null +++ b/google/genai/_gaos/utils/eventstreaming.py @@ -0,0 +1,335 @@ +# 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.""" + +import re +import json +from dataclasses import dataclass, asdict +from typing import ( + Any, + Callable, + Generic, + TypeVar, + Optional, + Generator, + AsyncGenerator, + Tuple, +) +import httpx + +T = TypeVar("T") + + +class Stream(Generic[T]): + # Holds a reference to the SDK client to avoid it being garbage collected + # and cause termination of the underlying httpx client. + client_ref: Optional[object] + response: httpx.Response + generator: Generator[T, None, None] + _closed: bool + + def __init__( + self, + response: httpx.Response, + decoder: Callable[[str], T], + sentinel: Optional[str] = None, + client_ref: Optional[object] = None, + data_required: bool = True, + ): + self.response = response + self.generator = stream_events( + response, decoder, sentinel, data_required=data_required + ) + self.client_ref = client_ref + self._closed = False + + def __iter__(self): + return self + + def __next__(self): + if self._closed: + raise StopIteration + return next(self.generator) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def close(self): + self._closed = True + self.response.close() + + +class AsyncStream(Generic[T]): + # Holds a reference to the SDK client to avoid it being garbage collected + # and cause termination of the underlying httpx client. + client_ref: Optional[object] + response: httpx.Response + generator: AsyncGenerator[T, None] + _closed: bool + + def __init__( + self, + response: httpx.Response, + decoder: Callable[[str], T], + sentinel: Optional[str] = None, + client_ref: Optional[object] = None, + data_required: bool = True, + ): + self.response = response + self.generator = stream_events_async( + response, decoder, sentinel, data_required=data_required + ) + self.client_ref = client_ref + self._closed = False + + def __aiter__(self): + return self + + async def __anext__(self): + if self._closed: + raise StopAsyncIteration + return await self.generator.__anext__() + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.close() + + async def close(self): + self._closed = True + await self.response.aclose() + + +@dataclass +class ServerEvent: + id: Optional[str] = None + event: Optional[str] = None + data: Any = None + retry: Optional[int] = None + + +MESSAGE_BOUNDARIES = [ + b"\r\n\r\n", + b"\r\n\r", + b"\r\n\n", + b"\r\r\n", + b"\n\r\n", + b"\r\r", + b"\n\r", + b"\n\n", +] + +UTF8_BOM = b"\xef\xbb\xbf" + + +async def stream_events_async( + response: httpx.Response, + decoder: Callable[[str], T], + sentinel: Optional[str] = None, + data_required: bool = True, +) -> AsyncGenerator[T, None]: + try: + buffer = bytearray() + position = 0 + event_id: Optional[str] = None + async for chunk in response.aiter_bytes(): + if len(buffer) == 0 and chunk.startswith(UTF8_BOM): + chunk = chunk[len(UTF8_BOM) :] + buffer += chunk + for i in range(position, len(buffer)): + char = buffer[i : i + 1] + seq: Optional[bytes] = None + if char in [b"\r", b"\n"]: + for boundary in MESSAGE_BOUNDARIES: + seq = _peek_sequence(i, buffer, boundary) + if seq is not None: + break + if seq is None: + continue + + block = buffer[position:i] + position = i + len(seq) + event, discard, event_id = _parse_event( + raw=block, + decoder=decoder, + sentinel=sentinel, + event_id=event_id, + data_required=data_required, + ) + if event is not None: + yield event + if discard: + return + + if position > 0: + buffer = buffer[position:] + position = 0 + + event, discard, _ = _parse_event( + raw=buffer, + decoder=decoder, + sentinel=sentinel, + event_id=event_id, + data_required=data_required, + ) + if event is not None: + yield event + finally: + await response.aclose() + + +def stream_events( + response: httpx.Response, + decoder: Callable[[str], T], + sentinel: Optional[str] = None, + data_required: bool = True, +) -> Generator[T, None, None]: + try: + buffer = bytearray() + position = 0 + event_id: Optional[str] = None + for chunk in response.iter_bytes(): + if len(buffer) == 0 and chunk.startswith(UTF8_BOM): + chunk = chunk[len(UTF8_BOM) :] + buffer += chunk + for i in range(position, len(buffer)): + char = buffer[i : i + 1] + seq: Optional[bytes] = None + if char in [b"\r", b"\n"]: + for boundary in MESSAGE_BOUNDARIES: + seq = _peek_sequence(i, buffer, boundary) + if seq is not None: + break + if seq is None: + continue + + block = buffer[position:i] + position = i + len(seq) + event, discard, event_id = _parse_event( + raw=block, + decoder=decoder, + sentinel=sentinel, + event_id=event_id, + data_required=data_required, + ) + if event is not None: + yield event + if discard: + return + + if position > 0: + buffer = buffer[position:] + position = 0 + + event, discard, _ = _parse_event( + raw=buffer, + decoder=decoder, + sentinel=sentinel, + event_id=event_id, + data_required=data_required, + ) + if event is not None: + yield event + finally: + response.close() + + +def _parse_event( + *, + raw: bytearray, + decoder: Callable[[str], T], + sentinel: Optional[str] = None, + event_id: Optional[str] = None, + data_required: bool = True, +) -> Tuple[Optional[T], bool, Optional[str]]: + block = raw.decode() + lines = re.split(r"\r?\n|\r", block) + publish = False + event = ServerEvent() + data = "" + for line in lines: + if not line: + continue + + delim = line.find(":") + if delim == 0: + continue + + field = line + value = "" + if delim > 0: + field = line[0:delim] + value = line[delim + 1 :] if delim < len(line) - 1 else "" + if len(value) and value[0] == " ": + value = value[1:] + + if field == "event": + event.event = value + publish = True + elif field == "data": + data += value + "\n" + publish = True + elif field == "id": + publish = True + if "\x00" not in value: + event_id = value + elif field == "retry": + if value.isdigit(): + event.retry = int(value) + publish = True + + event.id = event_id + + if sentinel and data == f"{sentinel}\n": + return None, True, event_id + + # Skip data-less events when data is required + if not data and publish and data_required: + return None, False, event_id + + if data: + data = data[:-1] + try: + event.data = json.loads(data) + except json.JSONDecodeError: + event.data = data + + out = None + if publish: + out_dict = { + k: v + for k, v in asdict(event).items() + if v is not None or (k == "data" and data) + } + out = decoder(json.dumps(out_dict)) + + return out, False, event_id + + +def _peek_sequence(position: int, buffer: bytearray, sequence: bytes): + if len(sequence) > (len(buffer) - position): + return None + + for i, seq in enumerate(sequence): + if buffer[position + i] != seq: + return None + + return sequence diff --git a/google/genai/_gaos/utils/forms.py b/google/genai/_gaos/utils/forms.py new file mode 100644 index 000000000..cbf0a8674 --- /dev/null +++ b/google/genai/_gaos/utils/forms.py @@ -0,0 +1,250 @@ +# 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 typing import ( + Any, + Dict, + get_type_hints, + List, + Tuple, +) +from pydantic import BaseModel +from pydantic.fields import FieldInfo + +from .serializers import marshal_json + +from .metadata import ( + FormMetadata, + MultipartFormMetadata, + find_field_metadata, +) +from .values import _is_set, _val_to_string + + +def _populate_form( + field_name: str, + explode: bool, + obj: Any, + delimiter: str, + form: Dict[str, List[str]], +): + if not _is_set(obj): + return form + + if isinstance(obj, BaseModel): + items = [] + + obj_fields: Dict[str, FieldInfo] = obj.__class__.model_fields + for name in obj_fields: + obj_field = obj_fields[name] + obj_field_name = obj_field.alias if obj_field.alias is not None else name + if obj_field_name == "": + continue + + val = getattr(obj, name) + if not _is_set(val): + continue + + if explode: + form[obj_field_name] = [_val_to_string(val)] + else: + items.append(f"{obj_field_name}{delimiter}{_val_to_string(val)}") + + if len(items) > 0: + form[field_name] = [delimiter.join(items)] + elif isinstance(obj, Dict): + items = [] + for key, value in obj.items(): + if not _is_set(value): + continue + + if explode: + form[key] = [_val_to_string(value)] + else: + items.append(f"{key}{delimiter}{_val_to_string(value)}") + + if len(items) > 0: + form[field_name] = [delimiter.join(items)] + elif isinstance(obj, List): + items = [] + + for value in obj: + if not _is_set(value): + continue + + if explode: + if not field_name in form: + form[field_name] = [] + form[field_name].append(_val_to_string(value)) + else: + items.append(_val_to_string(value)) + + if len(items) > 0: + form[field_name] = [delimiter.join([str(item) for item in items])] + else: + form[field_name] = [_val_to_string(obj)] + + return form + + +def _extract_file_properties(file_obj: Any) -> Tuple[str, Any, Any]: + """Extract file name, content, and content type from a file object.""" + file_fields: Dict[str, FieldInfo] = file_obj.__class__.model_fields + + file_name = "" + content = None + content_type = None + + for file_field_name in file_fields: + file_field = file_fields[file_field_name] + + file_metadata = find_field_metadata(file_field, MultipartFormMetadata) + if file_metadata is None: + continue + + if file_metadata.content: + content = getattr(file_obj, file_field_name, None) + elif file_field_name == "content_type": + content_type = getattr(file_obj, file_field_name, None) + else: + file_name = getattr(file_obj, file_field_name) + + if file_name == "" or content is None: + raise ValueError("invalid multipart/form-data file") + + return file_name, content, content_type + + +def serialize_multipart_form( + media_type: str, request: Any +) -> Tuple[str, Dict[str, Any], List[Tuple[str, Any]]]: + form: Dict[str, Any] = {} + files: List[Tuple[str, Any]] = [] + + if not isinstance(request, BaseModel): + raise TypeError("invalid request body type") + + request_fields: Dict[str, FieldInfo] = request.__class__.model_fields + request_field_types = get_type_hints(request.__class__) + + for name in request_fields: + field = request_fields[name] + + val = getattr(request, name) + if not _is_set(val): + continue + + field_metadata = find_field_metadata(field, MultipartFormMetadata) + if not field_metadata: + continue + + f_name = field.alias if field.alias else name + + if field_metadata.file: + if isinstance(val, List): + # Handle array of files + array_field_name = f_name + for file_obj in val: + if not _is_set(file_obj): + continue + + file_name, content, content_type = _extract_file_properties( + file_obj + ) + + if content_type is not None: + files.append( + (array_field_name, (file_name, content, content_type)) + ) + else: + files.append((array_field_name, (file_name, content))) + else: + # Handle single file + file_name, content, content_type = _extract_file_properties(val) + + if content_type is not None: + files.append((f_name, (file_name, content, content_type))) + else: + files.append((f_name, (file_name, content))) + elif field_metadata.json: + files.append( + ( + f_name, + ( + None, + marshal_json(val, request_field_types[name]), + "application/json", + ), + ) + ) + else: + if isinstance(val, List): + values = [] + + for value in val: + if not _is_set(value): + continue + values.append(_val_to_string(value)) + + array_field_name = f_name + form[array_field_name] = values + else: + form[f_name] = _val_to_string(val) + return media_type, form, files + + +def serialize_form_data(data: Any) -> Dict[str, Any]: + form: Dict[str, List[str]] = {} + + if isinstance(data, BaseModel): + data_fields: Dict[str, FieldInfo] = data.__class__.model_fields + data_field_types = get_type_hints(data.__class__) + for name in data_fields: + field = data_fields[name] + + val = getattr(data, name) + if not _is_set(val): + continue + + metadata = find_field_metadata(field, FormMetadata) + if metadata is None: + continue + + f_name = field.alias if field.alias is not None else name + + if metadata.json: + form[f_name] = [marshal_json(val, data_field_types[name])] + else: + if metadata.style == "form": + _populate_form( + f_name, + metadata.explode, + val, + ",", + form, + ) + else: + raise ValueError(f"Invalid form style for field {name}") + elif isinstance(data, Dict): + for key, value in data.items(): + if _is_set(value): + form[key] = [_val_to_string(value)] + else: + raise TypeError(f"Invalid request body type {type(data)} for form data") + + return form diff --git a/google/genai/_gaos/utils/headers.py b/google/genai/_gaos/utils/headers.py new file mode 100644 index 000000000..502feb331 --- /dev/null +++ b/google/genai/_gaos/utils/headers.py @@ -0,0 +1,152 @@ +# 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 typing import ( + Any, + Dict, + List, + Optional, +) +from httpx import Headers +from pydantic import BaseModel +from pydantic.fields import FieldInfo + +from .metadata import ( + HeaderMetadata, + find_field_metadata, +) + +from .values import _is_set, _populate_from_globals, _val_to_string + + +def get_headers(headers_params: Any, gbls: Optional[Any] = None) -> Dict[str, str]: + headers: Dict[str, str] = {} + + globals_already_populated = [] + if _is_set(headers_params): + globals_already_populated = _populate_headers(headers_params, gbls, headers, []) + if _is_set(gbls): + _populate_headers(gbls, None, headers, globals_already_populated) + + return headers + + +def _populate_headers( + headers_params: Any, + gbls: Any, + header_values: Dict[str, str], + skip_fields: List[str], +) -> List[str]: + globals_already_populated: List[str] = [] + + if not isinstance(headers_params, BaseModel): + return globals_already_populated + + param_fields: Dict[str, FieldInfo] = headers_params.__class__.model_fields + for name in param_fields: + if name in skip_fields: + continue + + field = param_fields[name] + f_name = field.alias if field.alias is not None else name + + metadata = find_field_metadata(field, HeaderMetadata) + if metadata is None: + continue + + value, global_found = _populate_from_globals( + name, getattr(headers_params, name), HeaderMetadata, gbls + ) + if global_found: + globals_already_populated.append(name) + value = _serialize_header(metadata.explode, value) + + if value != "": + header_values[f_name] = value + + return globals_already_populated + + +def _serialize_header(explode: bool, obj: Any) -> str: + if not _is_set(obj): + return "" + + if isinstance(obj, BaseModel): + items = [] + obj_fields: Dict[str, FieldInfo] = obj.__class__.model_fields + for name in obj_fields: + obj_field = obj_fields[name] + obj_param_metadata = find_field_metadata(obj_field, HeaderMetadata) + + if not obj_param_metadata: + continue + + f_name = obj_field.alias if obj_field.alias is not None else name + + val = getattr(obj, name) + if not _is_set(val): + continue + + if explode: + items.append(f"{f_name}={_val_to_string(val)}") + else: + items.append(f_name) + items.append(_val_to_string(val)) + + if len(items) > 0: + return ",".join(items) + elif isinstance(obj, Dict): + items = [] + + for key, value in obj.items(): + if not _is_set(value): + continue + + if explode: + items.append(f"{key}={_val_to_string(value)}") + else: + items.append(key) + items.append(_val_to_string(value)) + + if len(items) > 0: + return ",".join([str(item) for item in items]) + elif isinstance(obj, List): + items = [] + + for value in obj: + if not _is_set(value): + continue + + items.append(_val_to_string(value)) + + if len(items) > 0: + return ",".join(items) + elif _is_set(obj): + return f"{_val_to_string(obj)}" + + return "" + + +def get_response_headers(headers: Headers) -> Dict[str, List[str]]: + res: Dict[str, List[str]] = {} + for k, v in headers.items(): + if not k in res: + res[k] = [] + + res[k].append(v) + return res diff --git a/google/genai/_gaos/utils/logger.py b/google/genai/_gaos/utils/logger.py new file mode 100644 index 000000000..3825a3720 --- /dev/null +++ b/google/genai/_gaos/utils/logger.py @@ -0,0 +1,43 @@ +# 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.""" + +import httpx +import logging +import os +from typing import Any, Protocol + + +class Logger(Protocol): + def debug(self, msg: str, *args: Any, **kwargs: Any) -> None: + pass + + +class NoOpLogger: + def debug(self, msg: str, *args: Any, **kwargs: Any) -> None: + pass + + +def get_body_content(req: httpx.Request) -> str: + return "" if not hasattr(req, "_content") else str(req.content) + + +def get_default_logger() -> Logger: + if os.getenv("GOOGLE_GENAI_DEBUG"): + logging.basicConfig(level=logging.DEBUG) + return logging.getLogger("google.genai") + return NoOpLogger() diff --git a/google/genai/_gaos/utils/metadata.py b/google/genai/_gaos/utils/metadata.py new file mode 100644 index 000000000..ae5f7ab74 --- /dev/null +++ b/google/genai/_gaos/utils/metadata.py @@ -0,0 +1,135 @@ +# 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 typing import Optional, Type, TypeVar, Union +from dataclasses import dataclass +from pydantic.fields import FieldInfo + + +T = TypeVar("T") + + +@dataclass +class SecurityMetadata: + option: bool = False + scheme: bool = False + scheme_type: Optional[str] = None + sub_type: Optional[str] = None + field_name: Optional[str] = None + composite: bool = False + + def get_field_name(self, default: str) -> str: + return self.field_name or default + + +@dataclass +class ParamMetadata: + serialization: Optional[str] = None + style: str = "simple" + explode: bool = False + + +@dataclass +class PathParamMetadata(ParamMetadata): + pass + + +@dataclass +class QueryParamMetadata(ParamMetadata): + style: str = "form" + explode: bool = True + + +@dataclass +class HeaderMetadata(ParamMetadata): + pass + + +@dataclass +class RequestMetadata: + media_type: str = "application/octet-stream" + + +@dataclass +class MultipartFormMetadata: + file: bool = False + content: bool = False + json: bool = False + + +@dataclass +class FormMetadata: + json: bool = False + style: str = "form" + explode: bool = True + + +class FieldMetadata: + security: Optional[SecurityMetadata] = None + path: Optional[PathParamMetadata] = None + query: Optional[QueryParamMetadata] = None + header: Optional[HeaderMetadata] = None + request: Optional[RequestMetadata] = None + form: Optional[FormMetadata] = None + multipart: Optional[MultipartFormMetadata] = None + + def __init__( + self, + security: Optional[SecurityMetadata] = None, + path: Optional[Union[PathParamMetadata, bool]] = None, + query: Optional[Union[QueryParamMetadata, bool]] = None, + header: Optional[Union[HeaderMetadata, bool]] = None, + request: Optional[Union[RequestMetadata, bool]] = None, + form: Optional[Union[FormMetadata, bool]] = None, + multipart: Optional[Union[MultipartFormMetadata, bool]] = None, + ): + self.security = security + self.path = PathParamMetadata() if isinstance(path, bool) else path + self.query = QueryParamMetadata() if isinstance(query, bool) else query + self.header = HeaderMetadata() if isinstance(header, bool) else header + self.request = RequestMetadata() if isinstance(request, bool) else request + self.form = FormMetadata() if isinstance(form, bool) else form + self.multipart = ( + MultipartFormMetadata() if isinstance(multipart, bool) else multipart + ) + + +def find_field_metadata(field_info: FieldInfo, metadata_type: Type[T]) -> Optional[T]: + metadata = find_metadata(field_info, FieldMetadata) + if not metadata: + return None + + fields = metadata.__dict__ + + for field in fields: + if isinstance(fields[field], metadata_type): + return fields[field] + + return None + + +def find_metadata(field_info: FieldInfo, metadata_type: Type[T]) -> Optional[T]: + metadata = field_info.metadata + if not metadata: + return None + + for md in metadata: + if isinstance(md, metadata_type): + return md + + return None diff --git a/google/genai/_gaos/utils/queryparams.py b/google/genai/_gaos/utils/queryparams.py new file mode 100644 index 000000000..e5befb27c --- /dev/null +++ b/google/genai/_gaos/utils/queryparams.py @@ -0,0 +1,233 @@ +# 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 typing import ( + Any, + Dict, + get_type_hints, + List, + Optional, +) + +from pydantic import BaseModel +from pydantic.fields import FieldInfo + +from .metadata import ( + QueryParamMetadata, + find_field_metadata, +) +from .values import ( + _get_serialized_params, + _is_set, + _populate_from_globals, + _val_to_string, +) +from .forms import _populate_form + + +def get_query_params( + query_params: Any, + gbls: Optional[Any] = None, + allow_empty_value: Optional[List[str]] = None, +) -> Dict[str, List[str]]: + params: Dict[str, List[str]] = {} + + globals_already_populated = _populate_query_params(query_params, gbls, params, [], allow_empty_value) + if _is_set(gbls): + _populate_query_params(gbls, None, params, globals_already_populated, allow_empty_value) + + return params + + +def _populate_query_params( + query_params: Any, + gbls: Any, + query_param_values: Dict[str, List[str]], + skip_fields: List[str], + allow_empty_value: Optional[List[str]] = None, +) -> List[str]: + globals_already_populated: List[str] = [] + + if not isinstance(query_params, BaseModel): + return globals_already_populated + + param_fields: Dict[str, FieldInfo] = query_params.__class__.model_fields + param_field_types = get_type_hints(query_params.__class__) + for name in param_fields: + if name in skip_fields: + continue + + field = param_fields[name] + + metadata = find_field_metadata(field, QueryParamMetadata) + if not metadata: + continue + + value = getattr(query_params, name) if _is_set(query_params) else None + + value, global_found = _populate_from_globals( + name, value, QueryParamMetadata, gbls + ) + if global_found: + globals_already_populated.append(name) + + f_name = field.alias if field.alias is not None else name + + allow_empty_set = set(allow_empty_value or []) + should_include_empty = f_name in allow_empty_set and ( + value is None or value == [] or value == "" + ) + + if should_include_empty: + query_param_values[f_name] = [""] + continue + + serialization = metadata.serialization + if serialization is not None: + serialized_parms = _get_serialized_params( + metadata, f_name, value, param_field_types[name] + ) + for key, value in serialized_parms.items(): + if key in query_param_values: + query_param_values[key].extend(value) + else: + query_param_values[key] = [value] + else: + style = metadata.style + if style == "deepObject": + _populate_deep_object_query_params(f_name, value, query_param_values) + elif style == "form": + _populate_delimited_query_params( + metadata, f_name, value, ",", query_param_values + ) + elif style == "pipeDelimited": + _populate_delimited_query_params( + metadata, f_name, value, "|", query_param_values + ) + else: + raise NotImplementedError( + f"query param style {style} not yet supported" + ) + + return globals_already_populated + + +def _populate_deep_object_query_params( + field_name: str, + obj: Any, + params: Dict[str, List[str]], +): + if not _is_set(obj): + return + + if isinstance(obj, BaseModel): + _populate_deep_object_query_params_basemodel(field_name, obj, params) + elif isinstance(obj, Dict): + _populate_deep_object_query_params_dict(field_name, obj, params) + + +def _populate_deep_object_query_params_basemodel( + prior_params_key: str, + obj: Any, + params: Dict[str, List[str]], +): + if not _is_set(obj) or not isinstance(obj, BaseModel): + return + + obj_fields: Dict[str, FieldInfo] = obj.__class__.model_fields + for name in obj_fields: + obj_field = obj_fields[name] + + f_name = obj_field.alias if obj_field.alias is not None else name + + params_key = f"{prior_params_key}[{f_name}]" + + obj_param_metadata = find_field_metadata(obj_field, QueryParamMetadata) + if not _is_set(obj_param_metadata): + continue + + obj_val = getattr(obj, name) + if not _is_set(obj_val): + continue + + if isinstance(obj_val, BaseModel): + _populate_deep_object_query_params_basemodel(params_key, obj_val, params) + elif isinstance(obj_val, Dict): + _populate_deep_object_query_params_dict(params_key, obj_val, params) + elif isinstance(obj_val, List): + _populate_deep_object_query_params_list(params_key, obj_val, params) + else: + params[params_key] = [_val_to_string(obj_val)] + + +def _populate_deep_object_query_params_dict( + prior_params_key: str, + value: Dict, + params: Dict[str, List[str]], +): + if not _is_set(value): + return + + for key, val in value.items(): + if not _is_set(val): + continue + + params_key = f"{prior_params_key}[{key}]" + + if isinstance(val, BaseModel): + _populate_deep_object_query_params_basemodel(params_key, val, params) + elif isinstance(val, Dict): + _populate_deep_object_query_params_dict(params_key, val, params) + elif isinstance(val, List): + _populate_deep_object_query_params_list(params_key, val, params) + else: + params[params_key] = [_val_to_string(val)] + + +def _populate_deep_object_query_params_list( + params_key: str, + value: List, + params: Dict[str, List[str]], +): + if not _is_set(value): + return + + for val in value: + if not _is_set(val): + continue + + if params.get(params_key) is None: + params[params_key] = [] + + params[params_key].append(_val_to_string(val)) + + +def _populate_delimited_query_params( + metadata: QueryParamMetadata, + field_name: str, + obj: Any, + delimiter: str, + query_param_values: Dict[str, List[str]], +): + _populate_form( + field_name, + metadata.explode, + obj, + delimiter, + query_param_values, + ) diff --git a/google/genai/_gaos/utils/requestbodies.py b/google/genai/_gaos/utils/requestbodies.py new file mode 100644 index 000000000..f62445e35 --- /dev/null +++ b/google/genai/_gaos/utils/requestbodies.py @@ -0,0 +1,92 @@ +# 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.""" + +import io +import json +from dataclasses import dataclass +import re +from typing import ( + Any, + Mapping, + Optional, +) + +from .forms import serialize_form_data, serialize_multipart_form + +from .serializers import marshal_json + +SERIALIZATION_METHOD_TO_CONTENT_TYPE = { + "json": "application/json", + "form": "application/x-www-form-urlencoded", + "multipart": "multipart/form-data", + "raw": "application/octet-stream", + "string": "text/plain", +} + + +@dataclass +class SerializedRequestBody: + media_type: Optional[str] = None + content: Optional[Any] = None + data: Optional[Any] = None + files: Optional[Any] = None + + +def serialize_request_body( + request_body: Any, + nullable: bool, + optional: bool, + serialization_method: str, + request_body_type, + extra_body: Optional[Mapping[str, Any]] = None, +) -> Optional[SerializedRequestBody]: + if request_body is None: + if not nullable and optional: + return None + + media_type = SERIALIZATION_METHOD_TO_CONTENT_TYPE[serialization_method] + + serialized_request_body = SerializedRequestBody(media_type) + + if re.match(r"^(application|text)\/([^+]+\+)*json.*", media_type) is not None: + serialized_request_body.content = marshal_json(request_body, request_body_type) + if extra_body is not None: + body = json.loads(serialized_request_body.content or "{}") + if not isinstance(body, dict): + raise TypeError("extra_body can only be merged into JSON object bodies") + body.update(extra_body) + serialized_request_body.content = json.dumps(body, separators=(",", ":")) + + elif re.match(r"^multipart\/.*", media_type) is not None: + ( + serialized_request_body.media_type, + serialized_request_body.data, + serialized_request_body.files, + ) = serialize_multipart_form(media_type, request_body) + elif re.match(r"^application\/x-www-form-urlencoded.*", media_type) is not None: + serialized_request_body.data = serialize_form_data(request_body) + elif isinstance(request_body, (bytes, bytearray, io.BytesIO, io.BufferedReader)): + serialized_request_body.content = request_body + elif isinstance(request_body, str): + serialized_request_body.content = request_body + else: + raise TypeError( + f"invalid request body type {type(request_body)} for mediaType {media_type}" + ) + + return serialized_request_body diff --git a/google/genai/_gaos/utils/response_helpers.py b/google/genai/_gaos/utils/response_helpers.py new file mode 100644 index 000000000..b7837cbbe --- /dev/null +++ b/google/genai/_gaos/utils/response_helpers.py @@ -0,0 +1,694 @@ +# 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 __future__ import annotations + +import functools +import json +from datetime import timedelta +from types import TracebackType +from typing import ( + Any, + AsyncIterator, + Awaitable, + Callable, + Dict, + Generic, + Iterator, + Literal, + Mapping, + Optional, + ParamSpec, + Tuple, + Type, + TypeVar, + Union, + cast, + get_args, + get_origin, + overload, +) + +import httpx +from pydantic import BaseModel +from typing_extensions import TypeAliasType + +from .._version import __response_mode_header__ +from .eventstreaming import Stream, AsyncStream +from .unmarshal_json_response import unmarshal_json_response + +P = ParamSpec("P") +T = TypeVar("T") +U = TypeVar("U") +ResponseT = TypeVar("ResponseT", bound="_APIResponseBase[Any]") +AsyncResponseT = TypeVar("AsyncResponseT", bound="_AsyncAPIResponseBase[Any]") + +ResponseMode = Literal["buffered", "raw_stream", "event_stream"] + +_DEFAULT_PARSE_KEY = object() +_PARSED_RESPONSE_MODE = "parsed" +_RAW_RESPONSE_MODE = "raw" +_STREAMING_RESPONSE_MODE = "streaming" + +# Internal marker header set by the raw/streaming response helpers and removed +# before the request is sent, so it never reaches the wire. +_RESPONSE_MODE_HEADER = __response_mode_header__.lower() + + +def _unwrap_type(t: Any) -> Any: + """Strip ``TypeAliasType('Name', T)`` and ``Annotated[T, ...]`` wrappers.""" + if isinstance(t, TypeAliasType): + t = t.__value__ + if get_origin(t) is not None and hasattr(t, "__metadata__"): + t = t.__origin__ + return t + + +def _event_stream_origin( + to: Any, +) -> Optional[ + Union[ + Type[Stream[Any]], + Type[AsyncStream[Any]], + ] +]: + """Resolve ``to`` to its underlying ``Stream`` / ``AsyncStream`` + subclass, or ``None`` if it isn't one. Accepts subclasses, generic + parametrizations (``Stream[T]``), ``Annotated[...]``, and + ``TypeAliasType``.""" + unwrapped = _unwrap_type(to) + origin = get_origin(unwrapped) or unwrapped + if isinstance(origin, type) and issubclass(origin, (Stream, AsyncStream)): + return origin + return None + + +def _extract_stream_chunk_type(to: Any) -> Optional[type]: + """Recover the chunk type ``T`` from ``Stream[T]`` or from a + pre-bound subclass ``class MyStream(Stream[T]): ...``.""" + unwrapped = _unwrap_type(to) + args = get_args(unwrapped) + if args: + return args[0] + for base in getattr(unwrapped, "__orig_bases__", ()): + base_args = get_args(base) + if base_args: + return base_args[0] + return None + + +def _can_honor_event_stream(to: Any, response: httpx.Response) -> bool: + """True when spec-declared ``event_stream`` is confirmed by caller intent + or wire Content-Type. ``to=None`` defers to the spec-driven default parser + and must not be demoted — preserves streaming semantics when the wire + Content-Type is unreliable (proxy strip, server misconfig).""" + if to is None: + return True + if _event_stream_origin(to) is not None: + return True + content_type = response.headers.get("content-type", "") + media_type = content_type.partition(";")[0].strip().lower() + return media_type == "text/event-stream" + + +def _build_event_stream( + *, + to: Any, + decoder: Optional[Callable[[str], Any]], + sentinel: Optional[str], + response: httpx.Response, + client_ref: Optional[object], + async_: bool, +) -> Any: + """Dispatch ``parse(to=Stream)`` to a typed SSE iterator.""" + + base_class = AsyncStream if async_ else Stream + origin = _event_stream_origin(to) + if origin is None or not issubclass(origin, base_class): + iter_methods = ( + "aiter_lines() / aiter_bytes()" if async_ else "iter_lines() / iter_bytes()" + ) + raise TypeError( + f"parse(to=...) on SSE responses must use {base_class.__name__}[T]; got {to!r}. Iterate via {iter_methods} for raw access." + ) + + if decoder is not None: + resolved_decoder: Callable[[str], Any] = decoder + else: + chunk_t = _extract_stream_chunk_type(to) + if chunk_t is None: + raise TypeError( + f"parse(to={base_class.__name__}[T]) requires a type parameter." + ) + if not (isinstance(chunk_t, type) and issubclass(chunk_t, BaseModel)): + raise TypeError( + f"parse(to={base_class.__name__}[T]) requires T to be a BaseModel when decoder= is not supplied; got {chunk_t!r}." + ) + + def _synthesized_decoder(raw: str, _t: Any = chunk_t) -> Any: + envelope = json.loads(raw) + if not isinstance(envelope, dict) or "data" not in envelope: + raise ValueError( + f"Synthesized SSE decoder expected an envelope of shape {{'data': ...}}, got {envelope!r}. Pass decoder= to parse(...) to handle non-standard envelopes." + ) + return _t.model_validate(envelope["data"]) + + resolved_decoder = _synthesized_decoder + + return origin( + response, + resolved_decoder, + sentinel=sentinel, + client_ref=client_ref, + ) + + +class _APIResponseBase(Generic[T]): + def __init__( + self, + *, + raw: httpx.Response, + parser: Callable[[httpx.Response], T], + mode: ResponseMode = "buffered", + client_ref: Optional[object] = None, + ) -> None: + self.http_response = raw + self._parser = parser + self._mode = mode + self._client_ref = client_ref + self._parsed_by_type: dict[Any, Any] = {} + + @property + def headers(self) -> httpx.Headers: + return self.http_response.headers + + @property + def http_request(self) -> httpx.Request: + return self.http_response.request + + @property + def status_code(self) -> int: + return self.http_response.status_code + + @property + def request_id(self) -> Optional[str]: + return self.http_response.headers.get("x-request-id") + + @property + def url(self) -> httpx.URL: + return self.http_response.url + + @property + def method(self) -> str: + return self.http_request.method + + @property + def http_version(self) -> str: + return self.http_response.http_version + + @property + def elapsed(self) -> timedelta: + return self.http_response.elapsed + + @property + def is_closed(self) -> bool: + return self.http_response.is_closed + + def _parse( + self, + *, + to: Optional[Type[U]] = None, + decoder: Optional[Callable[[str], Any]] = None, + sentinel: Optional[str] = None, + ) -> T | U: + mode = self._mode + if mode == "event_stream" and not _can_honor_event_stream( + to, self.http_response + ): + mode = "buffered" + if to is not None and mode == "event_stream": + return _build_event_stream( + to=to, + decoder=decoder, + sentinel=sentinel, + response=self.http_response, + client_ref=self._client_ref, + async_=False, + ) + parse_key = to or _DEFAULT_PARSE_KEY + if parse_key not in self._parsed_by_type: + # raw_stream / event_stream wrappers leave the body unread so the + # caller can iter_bytes/iter_lines; buffered mode eagerly reads. + body = None + if to is None: + if mode == "buffered": + self.read() + self._parsed_by_type[parse_key] = self._parser(self.http_response) + else: + body = self.text() + self._parsed_by_type[parse_key] = unmarshal_json_response( + to, self.http_response, body + ) + return self._parsed_by_type[parse_key] + + def read(self) -> bytes: + return self.http_response.read() + + def text(self) -> str: + self.read() + return self.http_response.text + + def json(self) -> Any: + self.read() + return self.http_response.json() + + def close(self) -> None: + self.http_response.close() + + def __enter__(self: ResponseT) -> ResponseT: + return self + + def __exit__( + self, + exc_type: Optional[type[BaseException]], + exc: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + self.close() + + def iter_bytes(self, chunk_size: Optional[int] = None) -> Iterator[bytes]: + yield from self.http_response.iter_bytes(chunk_size) + + def iter_text(self, chunk_size: Optional[int] = None) -> Iterator[str]: + yield from self.http_response.iter_text(chunk_size) + + def iter_lines(self) -> Iterator[str]: + yield from self.http_response.iter_lines() + + +class APIResponse(_APIResponseBase[T]): + @overload + def parse(self) -> T: ... + + @overload + def parse(self, *, to: Type[U]) -> U: ... + + @overload + def parse(self, *, to: Type[U], decoder: Callable[[str], Any]) -> U: ... + + @overload + def parse(self, *, to: Type[U], sentinel: str) -> U: ... + + @overload + def parse( + self, + *, + to: Type[U], + decoder: Callable[[str], Any], + sentinel: str, + ) -> U: ... + + def parse( + self, + *, + to: Optional[Type[U]] = None, + decoder: Optional[Callable[[str], Any]] = None, + sentinel: Optional[str] = None, + ) -> T | U: + """Decode the response body into a typed value. + Three modes, depending on the value provided for ``to``: + + 1. ``to=None`` (default) — runs the operation's generator-emitted + parser and returns ``T``. + + 2. ``to=`` on a non-SSE response — reads the body as + JSON and validates against the given Pydantic model. Returns an + instance of that model. + + 3. ``to=Stream[T]`` on an SSE response — constructs a typed + iterator over Server-Sent Events frames. Dispatch is driven by + the spec-derived ``mode="event_stream"`` set at wrapper construction, + not by the wire ``Content-Type`` header. + + SSE frame decoding + ------------------ + Each SSE frame is wrapped into a JSON envelope of shape + ``{"id"?, "event"?, "data"?, "retry"?}`` (None-valued fields + omitted) before reaching the decoder. The decoder receives the + serialized envelope as a single string per frame. + + * ``decoder=None`` — synthesizes a decoder that JSON-parses the + envelope, takes ``["data"]``, and validates as ``T`` via + ``T.model_validate(...)``. ``T`` must subclass + ``pydantic.BaseModel``. Correct when the wire ``data:`` payload + is itself an instance of ``T``. + + * ``decoder=`` — bypasses synthesis. The + callable receives the JSON-encoded envelope and returns ``T``. + Use when the wire payload has an outer wrapper that must be + peeled, when ``T`` is a discriminated union, or whenever the + synthesized mapping does not match the spec. + + * ``sentinel=`` — when an SSE ``data:`` line equals this + string verbatim, iteration terminates cleanly. Use for + protocols that mark end-of-stream with a literal token. + + Errors + ------ + * ``TypeError`` if ``to`` is supplied on an SSE response + (``mode="event_stream"``) and is neither ``Stream[T]`` + nor ``AsyncStream[T]``. + * ``TypeError`` if ``to`` is the right stream class but ``T`` is + not a ``BaseModel`` and ``decoder`` was not supplied. + * ``pydantic.ValidationError`` from the (synthesized or + user-supplied) decoder when the wire shape does not match + ``T``. + """ + return self._parse(to=to, decoder=decoder, sentinel=sentinel) + + +class StreamedAPIResponse(_APIResponseBase[T]): + def parse(self) -> T: + return self._parse() + + +class _AsyncAPIResponseBase(Generic[T]): + def __init__( + self, + *, + raw: httpx.Response, + parser: Callable[[httpx.Response], Awaitable[T]], + mode: ResponseMode = "buffered", + client_ref: Optional[object] = None, + ) -> None: + self.http_response = raw + self._parser = parser + self._mode = mode + self._client_ref = client_ref + self._parsed_by_type: dict[Any, Any] = {} + + @property + def headers(self) -> httpx.Headers: + return self.http_response.headers + + @property + def http_request(self) -> httpx.Request: + return self.http_response.request + + @property + def status_code(self) -> int: + return self.http_response.status_code + + @property + def request_id(self) -> Optional[str]: + return self.http_response.headers.get("x-request-id") + + @property + def url(self) -> httpx.URL: + return self.http_response.url + + @property + def method(self) -> str: + return self.http_request.method + + @property + def http_version(self) -> str: + return self.http_response.http_version + + @property + def elapsed(self) -> timedelta: + return self.http_response.elapsed + + @property + def is_closed(self) -> bool: + return self.http_response.is_closed + + async def _parse( + self, + *, + to: Optional[Type[U]] = None, + decoder: Optional[Callable[[str], Any]] = None, + sentinel: Optional[str] = None, + ) -> T | U: + mode = self._mode + if mode == "event_stream" and not _can_honor_event_stream( + to, self.http_response + ): + mode = "buffered" + if to is not None and mode == "event_stream": + return _build_event_stream( + to=to, + decoder=decoder, + sentinel=sentinel, + response=self.http_response, + client_ref=self._client_ref, + async_=True, + ) + parse_key = to or _DEFAULT_PARSE_KEY + if parse_key not in self._parsed_by_type: + # raw_stream / event_stream wrappers leave the body unread so the + # caller can iter_bytes/iter_lines; buffered mode eagerly reads. + body = None + if to is None: + if mode == "buffered": + await self.read() + self._parsed_by_type[parse_key] = await self._parser(self.http_response) + else: + body = await self.text() + self._parsed_by_type[parse_key] = unmarshal_json_response( + to, self.http_response, body + ) + return self._parsed_by_type[parse_key] + + async def read(self) -> bytes: + return await self.http_response.aread() + + async def text(self) -> str: + await self.read() + return self.http_response.text + + async def json(self) -> Any: + await self.read() + return self.http_response.json() + + async def close(self) -> None: + await self.http_response.aclose() + + async def __aenter__(self: AsyncResponseT) -> AsyncResponseT: + return self + + async def __aexit__( + self, + exc_type: Optional[type[BaseException]], + exc: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + await self.close() + + async def iter_bytes( + self, chunk_size: Optional[int] = None + ) -> AsyncIterator[bytes]: + async for chunk in self.http_response.aiter_bytes(chunk_size): + yield chunk + + async def iter_text(self, chunk_size: Optional[int] = None) -> AsyncIterator[str]: + async for chunk in self.http_response.aiter_text(chunk_size): + yield chunk + + async def iter_lines(self) -> AsyncIterator[str]: + async for chunk in self.http_response.aiter_lines(): + yield chunk + + +class AsyncAPIResponse(_AsyncAPIResponseBase[T]): + @overload + async def parse(self) -> T: ... + + @overload + async def parse(self, *, to: Type[U]) -> U: ... + + @overload + async def parse(self, *, to: Type[U], decoder: Callable[[str], Any]) -> U: ... + + @overload + async def parse(self, *, to: Type[U], sentinel: str) -> U: ... + + @overload + async def parse( + self, + *, + to: Type[U], + decoder: Callable[[str], Any], + sentinel: str, + ) -> U: ... + + async def parse( + self, + *, + to: Optional[Type[U]] = None, + decoder: Optional[Callable[[str], Any]] = None, + sentinel: Optional[str] = None, + ) -> T | U: + """Decode the response body into a typed value. + + Async sibling of ``APIResponse.parse``. Modes, decoder synthesis, + and ``sentinel`` semantics follow the same contract. For SSE + responses pass ``to=AsyncStream[T]`` (not ``Stream[T]``) + and iterate with ``async for``. + + Errors + ------ + * ``TypeError`` if ``to`` is supplied on an SSE response + (``mode="event_stream"``) and is neither ``Stream[T]`` + nor ``AsyncStream[T]``. + * ``TypeError`` if ``to`` is the right stream class but ``T`` is + not a ``BaseModel`` and ``decoder`` was not supplied. + * ``pydantic.ValidationError`` from the (synthesized or + user-supplied) decoder when the wire shape does not match + ``T``. + """ + return await self._parse(to=to, decoder=decoder, sentinel=sentinel) + + +class AsyncStreamedAPIResponse(_AsyncAPIResponseBase[T]): + async def parse(self) -> T: + return await self._parse() + + +class ResponseContextManager(Generic[ResponseT]): + def __init__(self, request_func: Callable[[], ResponseT]) -> None: + self._request_func = request_func + self._response: Optional[ResponseT] = None + + def __enter__(self) -> ResponseT: + self._response = self._request_func() + return self._response + + def __exit__( + self, + exc_type: Optional[type[BaseException]], + exc: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + if self._response is not None: + self._response.close() + + +class AsyncResponseContextManager(Generic[AsyncResponseT]): + def __init__(self, request_func: Callable[[], Awaitable[AsyncResponseT]]) -> None: + self._request_func = request_func + self._response: Optional[AsyncResponseT] = None + + async def __aenter__(self) -> AsyncResponseT: + self._response = await self._request_func() + return self._response + + async def __aexit__( + self, + exc_type: Optional[type[BaseException]], + exc: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + if self._response is not None: + await self._response.close() + + +def _set_response_mode(kwargs: Dict[str, Any], mode: str, header_kwarg: str) -> None: + headers = dict(kwargs.get(header_kwarg) or {}) + headers[_RESPONSE_MODE_HEADER] = mode + kwargs[header_kwarg] = headers + + +def consume_response_mode( + http_headers: Optional[Mapping[str, str]], +) -> Tuple[str, Optional[Mapping[str, str]]]: + """Read and remove the marker header, returning the response mode and the + remaining headers.""" + if http_headers is None: + return _PARSED_RESPONSE_MODE, None + mode = _PARSED_RESPONSE_MODE + remaining: Dict[str, str] = {} + for name, value in http_headers.items(): + if name.lower() == _RESPONSE_MODE_HEADER: + if value in (_RAW_RESPONSE_MODE, _STREAMING_RESPONSE_MODE): + mode = value + else: + remaining[name] = value + return mode, (remaining or None) + + +def to_raw_response_wrapper( + func: Callable[P, T], header_kwarg: str +) -> Callable[P, APIResponse[T]]: + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[T]: + _set_response_mode( + cast(Dict[str, Any], kwargs), _RAW_RESPONSE_MODE, header_kwarg + ) + return cast(APIResponse[T], func(*args, **kwargs)) + + return wrapped + + +def async_to_raw_response_wrapper( + func: Callable[P, Awaitable[T]], header_kwarg: str +) -> Callable[P, Awaitable[AsyncAPIResponse[T]]]: + @functools.wraps(func) + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[T]: + _set_response_mode( + cast(Dict[str, Any], kwargs), _RAW_RESPONSE_MODE, header_kwarg + ) + return cast(AsyncAPIResponse[T], await func(*args, **kwargs)) + + return wrapped + + +def to_streamed_response_wrapper( + func: Callable[P, T], header_kwarg: str +) -> Callable[P, ResponseContextManager[StreamedAPIResponse[T]]]: + @functools.wraps(func) + def wrapped( + *args: P.args, **kwargs: P.kwargs + ) -> ResponseContextManager[StreamedAPIResponse[T]]: + _set_response_mode( + cast(Dict[str, Any], kwargs), _STREAMING_RESPONSE_MODE, header_kwarg + ) + request_func = functools.partial(func, *args, **kwargs) + return ResponseContextManager( + cast(Callable[[], StreamedAPIResponse[T]], request_func) + ) + + return wrapped + + +def async_to_streamed_response_wrapper( + func: Callable[P, Awaitable[T]], header_kwarg: str +) -> Callable[P, AsyncResponseContextManager[AsyncStreamedAPIResponse[T]]]: + @functools.wraps(func) + def wrapped( + *args: P.args, **kwargs: P.kwargs + ) -> AsyncResponseContextManager[AsyncStreamedAPIResponse[T]]: + _set_response_mode( + cast(Dict[str, Any], kwargs), _STREAMING_RESPONSE_MODE, header_kwarg + ) + request_func = functools.partial(func, *args, **kwargs) + return AsyncResponseContextManager( + cast(Callable[[], Awaitable[AsyncStreamedAPIResponse[T]]], request_func) + ) + + return wrapped diff --git a/google/genai/_gaos/utils/retries.py b/google/genai/_gaos/utils/retries.py new file mode 100644 index 000000000..a07bb7955 --- /dev/null +++ b/google/genai/_gaos/utils/retries.py @@ -0,0 +1,287 @@ +# 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.""" + +import asyncio +import random +import time +from datetime import datetime +from email.utils import parsedate_to_datetime +from typing import List, Optional + +import httpx + + +class BackoffStrategy: + initial_interval: int + max_interval: int + exponent: float + max_elapsed_time: int + + def __init__( + self, + initial_interval: int, + max_interval: int, + exponent: float, + max_elapsed_time: int, + ): + self.initial_interval = initial_interval + self.max_interval = max_interval + self.exponent = exponent + self.max_elapsed_time = max_elapsed_time + + +class RetryConfig: + strategy: str + backoff: BackoffStrategy + retry_connection_errors: bool + + def __init__( + self, strategy: str, backoff: BackoffStrategy, retry_connection_errors: bool + ): + self.strategy = strategy + self.backoff = backoff + self.retry_connection_errors = retry_connection_errors + + +class Retries: + config: RetryConfig + status_codes: List[str] + + def __init__(self, config: RetryConfig, status_codes: List[str]): + self.config = config + self.status_codes = status_codes + + +class TemporaryError(Exception): + response: httpx.Response + retry_after: Optional[int] + + def __init__(self, response: httpx.Response): + self.response = response + self.retry_after = _parse_retry_after_header(response) + + +class PermanentError(Exception): + inner: Exception + + def __init__(self, inner: Exception): + self.inner = inner + + +def _parse_retry_after_header(response: httpx.Response) -> Optional[int]: + """Parse Retry-After header from response. + + Returns: + Retry interval in milliseconds, or None if header is missing or invalid. + """ + retry_after_header = response.headers.get("retry-after") + if not retry_after_header: + return None + + try: + seconds = float(retry_after_header) + return round(seconds * 1000) + except ValueError: + pass + + try: + retry_date = parsedate_to_datetime(retry_after_header) + delta = (retry_date - datetime.now(retry_date.tzinfo)).total_seconds() + return round(max(0, delta) * 1000) + except (ValueError, TypeError): + pass + + return None + + +def _get_sleep_interval( + exception: Exception, + initial_interval: int, + max_interval: int, + exponent: float, + retries: int, +) -> float: + """Get sleep interval for retry with exponential backoff. + + Args: + exception: The exception that triggered the retry. + initial_interval: Initial retry interval in milliseconds. + max_interval: Maximum retry interval in milliseconds. + exponent: Base for exponential backoff calculation. + retries: Current retry attempt count. + + Returns: + Sleep interval in seconds. + """ + if ( + isinstance(exception, TemporaryError) + and exception.retry_after is not None + and exception.retry_after > 0 + ): + return exception.retry_after / 1000 + + sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1) + return min(sleep, max_interval / 1000) + + +def retry(func, retries: Retries): + if retries.config.strategy == "backoff": + + def do_request() -> httpx.Response: + res: httpx.Response + try: + res = func() + + for code in retries.status_codes: + if "X" in code.upper(): + code_range = int(code[0]) + + status_major = res.status_code / 100 + + if code_range <= status_major < code_range + 1: + raise TemporaryError(res) + else: + parsed_code = int(code) + + if res.status_code == parsed_code: + raise TemporaryError(res) + except (httpx.NetworkError, httpx.TimeoutException) as exception: + if retries.config.retry_connection_errors: + raise + + raise PermanentError(exception) from exception + except TemporaryError: + raise + except Exception as exception: + raise PermanentError(exception) from exception + + return res + + return retry_with_backoff( + do_request, + retries.config.backoff.initial_interval, + retries.config.backoff.max_interval, + retries.config.backoff.exponent, + retries.config.backoff.max_elapsed_time, + ) + + return func() + + +async def retry_async(func, retries: Retries): + if retries.config.strategy == "backoff": + + async def do_request() -> httpx.Response: + res: httpx.Response + try: + res = await func() + + for code in retries.status_codes: + if "X" in code.upper(): + code_range = int(code[0]) + + status_major = res.status_code / 100 + + if code_range <= status_major < code_range + 1: + raise TemporaryError(res) + else: + parsed_code = int(code) + + if res.status_code == parsed_code: + raise TemporaryError(res) + except (httpx.NetworkError, httpx.TimeoutException) as exception: + if retries.config.retry_connection_errors: + raise + + raise PermanentError(exception) from exception + except TemporaryError: + raise + except Exception as exception: + raise PermanentError(exception) from exception + + return res + + return await retry_with_backoff_async( + do_request, + retries.config.backoff.initial_interval, + retries.config.backoff.max_interval, + retries.config.backoff.exponent, + retries.config.backoff.max_elapsed_time, + ) + + return await func() + + +def retry_with_backoff( + func, + initial_interval=500, + max_interval=60000, + exponent=1.5, + max_elapsed_time=3600000, +): + start = round(time.time() * 1000) + retries = 0 + + while True: + try: + return func() + except PermanentError as exception: + raise exception.inner + except Exception as exception: # pylint: disable=broad-exception-caught + now = round(time.time() * 1000) + if now - start > max_elapsed_time: + if isinstance(exception, TemporaryError): + return exception.response + + raise + + sleep = _get_sleep_interval( + exception, initial_interval, max_interval, exponent, retries + ) + time.sleep(sleep) + retries += 1 + + +async def retry_with_backoff_async( + func, + initial_interval=500, + max_interval=60000, + exponent=1.5, + max_elapsed_time=3600000, +): + start = round(time.time() * 1000) + retries = 0 + + while True: + try: + return await func() + except PermanentError as exception: + raise exception.inner + except Exception as exception: # pylint: disable=broad-exception-caught + now = round(time.time() * 1000) + if now - start > max_elapsed_time: + if isinstance(exception, TemporaryError): + return exception.response + + raise + + sleep = _get_sleep_interval( + exception, initial_interval, max_interval, exponent, retries + ) + await asyncio.sleep(sleep) + retries += 1 diff --git a/google/genai/_gaos/utils/security.py b/google/genai/_gaos/utils/security.py new file mode 100644 index 000000000..01c11f875 --- /dev/null +++ b/google/genai/_gaos/utils/security.py @@ -0,0 +1,237 @@ +# 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.""" + +import base64 + +from typing import ( + Any, + Dict, + List, + Optional, + Tuple, +) +from pydantic import BaseModel +from pydantic.fields import FieldInfo + +from .metadata import ( + SecurityMetadata, + find_field_metadata, +) +import os + + +def get_security( + security: Any, allowed_fields: Optional[List[str]] = None +) -> Tuple[Dict[str, str], Dict[str, List[str]]]: + headers: Dict[str, str] = {} + query_params: Dict[str, List[str]] = {} + + if security is None: + return headers, query_params + + if not isinstance(security, BaseModel): + raise TypeError("security must be a pydantic model") + + sec_fields: Dict[str, FieldInfo] = security.__class__.model_fields + sec_field_names = ( + list(sec_fields.keys()) if allowed_fields is None else allowed_fields + ) + + for name in sec_field_names: + if name not in sec_fields: + continue + + sec_field = sec_fields[name] + + value = getattr(security, name) + if value is None: + continue + + metadata = find_field_metadata(sec_field, SecurityMetadata) + if metadata is None: + continue + if metadata.option: + _parse_security_option(headers, query_params, value) + return headers, query_params + if metadata.scheme: + # Special case for basic auth or custom auth which could be a flattened model + if metadata.sub_type in ["basic", "custom"] and not isinstance( + value, BaseModel + ): + _parse_security_scheme(headers, query_params, metadata, name, security) + else: + _parse_security_scheme(headers, query_params, metadata, name, value) + + if not metadata.composite: + return headers, query_params + + return headers, query_params + + +def get_security_from_env(security: Any, security_class: Any) -> Optional[BaseModel]: + if security is not None: + return security + + if not issubclass(security_class, BaseModel): + raise TypeError("security_class must be a pydantic model class") + + security_dict: Any = {} + + if os.getenv("GOOGLE_GENAI_API_KEY"): + security_dict["api_key"] = os.getenv("GOOGLE_GENAI_API_KEY") + + if os.getenv("GOOGLE_GENAI_ACCESS_TOKEN"): + security_dict["access_token"] = os.getenv("GOOGLE_GENAI_ACCESS_TOKEN") + + if os.getenv("GOOGLE_GENAI_DEFAULT_HEADERS"): + security_dict["default_headers"] = os.getenv("GOOGLE_GENAI_DEFAULT_HEADERS") + + return security_class(**security_dict) if security_dict else None + + +def _parse_security_option( + headers: Dict[str, str], query_params: Dict[str, List[str]], option: Any +): + if not isinstance(option, BaseModel): + raise TypeError("security option must be a pydantic model") + + opt_fields: Dict[str, FieldInfo] = option.__class__.model_fields + + for name in opt_fields: + opt_field = opt_fields[name] + + metadata = find_field_metadata(opt_field, SecurityMetadata) + if metadata is None or not metadata.scheme: + continue + + value = getattr(option, name) + if ( + metadata.scheme_type == "http" + and metadata.sub_type == "basic" + and not isinstance(value, BaseModel) + ): + _parse_basic_auth_scheme(headers, option) + return + + _parse_security_scheme(headers, query_params, metadata, name, value) + + +def _parse_security_scheme( + headers: Dict[str, str], + query_params: Dict[str, List[str]], + scheme_metadata: SecurityMetadata, + field_name: str, + scheme: Any, +): + scheme_type = scheme_metadata.scheme_type + sub_type = scheme_metadata.sub_type + + if isinstance(scheme, BaseModel): + if scheme_type == "http": + if sub_type == "basic": + _parse_basic_auth_scheme(headers, scheme) + return + if sub_type == "custom": + return + + scheme_fields: Dict[str, FieldInfo] = scheme.__class__.model_fields + for name in scheme_fields: + scheme_field = scheme_fields[name] + + metadata = find_field_metadata(scheme_field, SecurityMetadata) + if metadata is None or metadata.field_name is None: + continue + + value = getattr(scheme, name) + + _parse_security_scheme_value( + headers, query_params, scheme_metadata, metadata, name, value + ) + else: + _parse_security_scheme_value( + headers, query_params, scheme_metadata, scheme_metadata, field_name, scheme + ) + + +def _parse_security_scheme_value( + headers: Dict[str, str], + query_params: Dict[str, List[str]], + scheme_metadata: SecurityMetadata, + security_metadata: SecurityMetadata, + field_name: str, + value: Any, +): + scheme_type = scheme_metadata.scheme_type + sub_type = scheme_metadata.sub_type + + header_name = security_metadata.get_field_name(field_name) + + if scheme_type == "apiKey": + if sub_type == "header": + headers[header_name] = value + elif sub_type == "query": + query_params[header_name] = [value] + else: + raise ValueError("sub type {sub_type} not supported") + elif scheme_type == "openIdConnect": + headers[header_name] = _apply_bearer(value) + elif scheme_type == "oauth2": + if sub_type != "client_credentials": + headers[header_name] = _apply_bearer(value) + elif scheme_type == "http": + if sub_type == "bearer": + headers[header_name] = _apply_bearer(value) + elif sub_type == "basic": + headers[header_name] = value + elif sub_type == "custom": + return + else: + raise ValueError("sub type {sub_type} not supported") + else: + raise ValueError("scheme type {scheme_type} not supported") + + +def _apply_bearer(token: str) -> str: + return token.lower().startswith("bearer ") and token or f"Bearer {token}" + + +def _parse_basic_auth_scheme(headers: Dict[str, str], scheme: Any): + username = "" + password = "" + + if not isinstance(scheme, BaseModel): + raise TypeError("basic auth scheme must be a pydantic model") + + scheme_fields: Dict[str, FieldInfo] = scheme.__class__.model_fields + for name in scheme_fields: + scheme_field = scheme_fields[name] + + metadata = find_field_metadata(scheme_field, SecurityMetadata) + if metadata is None or metadata.field_name is None: + continue + + field_name = metadata.field_name + value = getattr(scheme, name) + + if field_name == "username": + username = value + if field_name == "password": + password = value + + data = f"{username}:{password}".encode() + headers["Authorization"] = f"Basic {base64.b64encode(data).decode()}" diff --git a/google/genai/_gaos/utils/serializers.py b/google/genai/_gaos/utils/serializers.py new file mode 100644 index 000000000..3a7b81c5b --- /dev/null +++ b/google/genai/_gaos/utils/serializers.py @@ -0,0 +1,322 @@ +# 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 decimal import Decimal +import functools +import json +import typing +from typing import Any, Dict, Iterable, List, Mapping, Tuple, Union, get_args +import typing_extensions +from typing_extensions import get_origin + +import httpx +from pydantic import ConfigDict, create_model +from pydantic_core import from_json + +from ..types.basemodel import BaseModel, Nullable, OptionalNullable, Unset + + +def serialize_decimal(as_str: bool): + def serialize(d): + if d is None: + return None + if isinstance(d, Unset): + return d + + if not isinstance(d, Decimal): + raise ValueError("Expected Decimal object") + + return str(d) if as_str else float(d) + + return serialize + + +def validate_decimal(d): + if d is None: + return None + + if isinstance(d, (Decimal, Unset)): + return d + + if not isinstance(d, (str, int, float)): + raise ValueError("Expected string, int or float") + + return Decimal(str(d)) + + +def serialize_float(as_str: bool): + def serialize(f): + if f is None: + return None + if isinstance(f, Unset): + return f + + if not isinstance(f, float): + raise ValueError("Expected float") + + return str(f) if as_str else f + + return serialize + + +def validate_float(f): + if f is None: + return None + + if isinstance(f, (float, Unset)): + return f + + if not isinstance(f, str): + raise ValueError("Expected string") + + return float(f) + + +def serialize_int(as_str: bool): + def serialize(i): + if i is None: + return None + if isinstance(i, Unset): + return i + + if not isinstance(i, int): + raise ValueError("Expected int") + + return str(i) if as_str else i + + return serialize + + +def validate_int(b): + if b is None: + return None + + if isinstance(b, (int, Unset)): + return b + + if not isinstance(b, str): + raise ValueError("Expected string") + + return int(b) + + +def validate_const(v): + def validate(c): + if c is None: + return None + + if v != c: + raise ValueError(f"Expected {v}") + + return c + + return validate + + +def unmarshal_json(raw, typ: Any) -> Any: + return unmarshal(from_json(raw), typ, coerce_iterables=False) + + +def unmarshal(val, typ: Any, coerce_iterables: bool = True) -> Any: + if coerce_iterables: + val = _coerce_iterables_for_type(val, typ) + unmarshaller = create_model( + "Unmarshaller", + body=(typ, ...), + __config__=ConfigDict(populate_by_name=True, arbitrary_types_allowed=True), + ) + + m = unmarshaller(body=val) + + # pyright: ignore[reportAttributeAccessIssue] + return m.body # type: ignore + + +def marshal_json(val, typ): + if is_nullable(typ) and val is None: + return "null" + + marshaller = create_model( + "Marshaller", + body=(typ, ...), + __config__=ConfigDict(populate_by_name=True, arbitrary_types_allowed=True), + ) + + m = marshaller(body=val) + + d = m.model_dump(by_alias=True, mode="json", exclude_none=True) + + if len(d) == 0: + return "" + + return json.dumps(d[next(iter(d))], separators=(",", ":")) + + +def is_nullable(field): + origin = get_origin(field) + if origin is Nullable or origin is OptionalNullable: + return True + + if not origin is Union or type(None) not in get_args(field): + return False + + for arg in get_args(field): + if get_origin(arg) is Nullable or get_origin(arg) is OptionalNullable: + return True + + return False + + +def is_union(obj: object) -> bool: + """ + Returns True if the given object is a typing.Union or typing_extensions.Union. + """ + return any( + obj is typing_obj for typing_obj in _get_typing_objects_by_name_of("Union") + ) + + +def stream_to_text(stream: httpx.Response) -> str: + return "".join(stream.iter_text()) + + +async def stream_to_text_async(stream: httpx.Response) -> str: + return "".join([chunk async for chunk in stream.aiter_text()]) + + +def stream_to_bytes(stream: httpx.Response) -> bytes: + return stream.content + + +async def stream_to_bytes_async(stream: httpx.Response) -> bytes: + return await stream.aread() + + +def get_pydantic_model(data: Any, typ: Any) -> Any: + if not _contains_pydantic_model(data): + return unmarshal(data, typ) + + return _coerce_iterables_for_type(data, typ) + + +def _coerce_iterables_for_type(data: Any, typ: Any) -> Any: + if data is None or isinstance(data, (BaseModel, Unset)): + return data + + typ = _resolve_type_alias(typ) + origin = get_origin(typ) + + if _is_annotated_type(origin): + args = get_args(typ) + return _coerce_iterables_for_type(data, args[0]) if args else data + + if is_union(origin): + for arg in (arg for arg in get_args(typ) if arg is not type(None)): + coerced = _coerce_iterables_for_type(data, arg) + if coerced is not data: + return coerced + return data + + if _is_list_type(typ): + item_type = get_args(typ)[0] if get_args(typ) else Any + if isinstance(data, (str, bytes, bytearray, Mapping)): + return data + if isinstance(data, Iterable): + return [_coerce_iterables_for_type(item, item_type) for item in data] + return data + + if _is_mapping_type(typ): + value_type = get_args(typ)[1] if len(get_args(typ)) > 1 else Any + if isinstance(data, Mapping): + return { + key: _coerce_iterables_for_type(value, value_type) + for key, value in data.items() + } + return data + + if _is_pydantic_model_type(typ) and isinstance(data, Mapping): + coerced = None + for field_name, field in typ.model_fields.items(): + field_type = field.annotation + for key in (field_name, field.alias): + if key is not None and key in data: + value = data[key] if coerced is None else coerced[key] + coerced_value = _coerce_iterables_for_type(value, field_type) + if coerced_value is not value: + if coerced is None: + coerced = dict(data) + coerced[key] = coerced_value + return coerced if coerced is not None else data + + return data + + +def _resolve_type_alias(typ: Any) -> Any: + return getattr(typ, "__value__", typ) + + +def _is_annotated_type(origin: Any) -> bool: + return any( + origin is typing_obj + for typing_obj in _get_typing_objects_by_name_of("Annotated") + ) + + +def _is_list_type(typ: Any) -> bool: + typ = _resolve_type_alias(typ) + return typ is list or get_origin(typ) is list + + +def _is_mapping_type(typ: Any) -> bool: + typ = _resolve_type_alias(typ) + origin = get_origin(typ) + mapping_origin = get_origin(Mapping[Any, Any]) + return typ in (dict, Dict, Mapping) or origin in (dict, Mapping, mapping_origin) + + +def _is_pydantic_model_type(typ: Any) -> bool: + return isinstance(typ, type) and issubclass(typ, BaseModel) + + +def _contains_pydantic_model(data: Any) -> bool: + if isinstance(data, BaseModel): + return True + if isinstance(data, List): + return any(_contains_pydantic_model(item) for item in data) + if isinstance(data, Dict): + return any(_contains_pydantic_model(value) for value in data.values()) + + return False + + +@functools.cache +def _get_typing_objects_by_name_of(name: str) -> Tuple[Any, ...]: + """ + Get typing objects by name from typing and typing_extensions. + Reference: https://typing-extensions.readthedocs.io/en/latest/#runtime-use-of-types + """ + result = tuple( + getattr(module, name) + for module in (typing, typing_extensions) + if hasattr(module, name) + ) + if not result: + raise ValueError( + f"Neither typing nor typing_extensions has an object called {name!r}" + ) + return result diff --git a/google/genai/_gaos/utils/unions.py b/google/genai/_gaos/utils/unions.py new file mode 100644 index 000000000..e42cf3f97 --- /dev/null +++ b/google/genai/_gaos/utils/unions.py @@ -0,0 +1,57 @@ +# 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 typing import Any + +from pydantic import BaseModel, TypeAdapter, ValidationError + + +def parse_open_union( + v: Any, + *, + disc_key: str, + variants: dict[str, Any], + unknown_cls: type, + union_name: str, +) -> Any: + """Parse an open discriminated union value with forward-compatibility. + + Known discriminator values are dispatched to their variant types. + Unknown discriminator values — or known discriminator values whose + payload fails variant validation (e.g. a partial variant emitted by a + newer server) — produce an instance of the fallback class, preserving + the raw payload for inspection. + + Non-dict values and dicts missing the discriminator deliberately raise + instead of falling back, so pydantic can try sibling branches of an + enclosing union (e.g. None in Optional[...]). + """ + if isinstance(v, BaseModel): + return v + if not isinstance(v, dict) or disc_key not in v: + raise ValueError(f"{union_name}: expected object with '{disc_key}' field") + disc = v[disc_key] + variant_cls = variants.get(disc) + if variant_cls is not None: + try: + if isinstance(variant_cls, type) and issubclass(variant_cls, BaseModel): + return variant_cls.model_validate(v) + return TypeAdapter(variant_cls).validate_python(v) + except ValidationError: + return unknown_cls(raw=v) + return unknown_cls(raw=v) diff --git a/google/genai/_gaos/utils/unmarshal_json_response.py b/google/genai/_gaos/utils/unmarshal_json_response.py new file mode 100644 index 000000000..b3653f685 --- /dev/null +++ b/google/genai/_gaos/utils/unmarshal_json_response.py @@ -0,0 +1,54 @@ +# 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 typing import Any, Optional, Type, TypeVar, overload + +import httpx + +from .serializers import unmarshal_json +from .. import errors + +T = TypeVar("T") + + +@overload +def unmarshal_json_response( + typ: Type[T], http_res: httpx.Response, body: Optional[str] = None +) -> T: ... + + +@overload +def unmarshal_json_response( + typ: Any, http_res: httpx.Response, body: Optional[str] = None +) -> Any: ... + + +def unmarshal_json_response( + typ: Any, http_res: httpx.Response, body: Optional[str] = None +) -> Any: + if body is None: + body = http_res.text + try: + return unmarshal_json(body, typ) + except Exception as e: + raise errors.ResponseValidationError( + "Response validation failed", + http_res, + e, + body, + ) from e diff --git a/google/genai/_gaos/utils/url.py b/google/genai/_gaos/utils/url.py new file mode 100644 index 000000000..2daf6574c --- /dev/null +++ b/google/genai/_gaos/utils/url.py @@ -0,0 +1,171 @@ +# 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 decimal import Decimal +from typing import ( + Any, + Dict, + get_type_hints, + List, + Optional, + Union, + get_args, + get_origin, +) +from pydantic import BaseModel +from pydantic.fields import FieldInfo + +from .metadata import ( + PathParamMetadata, + find_field_metadata, +) +from .values import ( + _get_serialized_params, + _is_set, + _populate_from_globals, + _val_to_string, +) + + +def generate_url( + server_url: str, + path: str, + path_params: Any, + gbls: Optional[Any] = None, +) -> str: + path_param_values: Dict[str, str] = {} + + globals_already_populated = _populate_path_params( + path_params, gbls, path_param_values, [] + ) + if _is_set(gbls): + _populate_path_params(gbls, None, path_param_values, globals_already_populated) + + for key, value in path_param_values.items(): + path = path.replace("{" + key + "}", value, 1) + + return remove_suffix(server_url, "/") + path + + +def _populate_path_params( + path_params: Any, + gbls: Any, + path_param_values: Dict[str, str], + skip_fields: List[str], +) -> List[str]: + globals_already_populated: List[str] = [] + + if not isinstance(path_params, BaseModel): + return globals_already_populated + + path_param_fields: Dict[str, FieldInfo] = path_params.__class__.model_fields + path_param_field_types = get_type_hints(path_params.__class__) + for name in path_param_fields: + if name in skip_fields: + continue + + field = path_param_fields[name] + + param_metadata = find_field_metadata(field, PathParamMetadata) + if param_metadata is None: + continue + + param = getattr(path_params, name) if _is_set(path_params) else None + param, global_found = _populate_from_globals( + name, param, PathParamMetadata, gbls + ) + if global_found: + globals_already_populated.append(name) + + if not _is_set(param): + continue + + f_name = field.alias if field.alias is not None else name + serialization = param_metadata.serialization + if serialization is not None: + serialized_params = _get_serialized_params( + param_metadata, f_name, param, path_param_field_types[name] + ) + for key, value in serialized_params.items(): + path_param_values[key] = value + else: + pp_vals: List[str] = [] + if param_metadata.style == "simple": + if isinstance(param, List): + for pp_val in param: + if not _is_set(pp_val): + continue + pp_vals.append(_val_to_string(pp_val)) + path_param_values[f_name] = ",".join(pp_vals) + elif isinstance(param, Dict): + for pp_key in param: + if not _is_set(param[pp_key]): + continue + if param_metadata.explode: + pp_vals.append(f"{pp_key}={_val_to_string(param[pp_key])}") + else: + pp_vals.append(f"{pp_key},{_val_to_string(param[pp_key])}") + path_param_values[f_name] = ",".join(pp_vals) + elif not isinstance(param, (str, int, float, complex, bool, Decimal)): + param_fields: Dict[str, FieldInfo] = param.__class__.model_fields + for name in param_fields: + param_field = param_fields[name] + + param_value_metadata = find_field_metadata( + param_field, PathParamMetadata + ) + if param_value_metadata is None: + continue + + param_name = ( + param_field.alias if param_field.alias is not None else name + ) + + param_field_val = getattr(param, name) + if not _is_set(param_field_val): + continue + if param_metadata.explode: + pp_vals.append( + f"{param_name}={_val_to_string(param_field_val)}" + ) + else: + pp_vals.append( + f"{param_name},{_val_to_string(param_field_val)}" + ) + path_param_values[f_name] = ",".join(pp_vals) + elif _is_set(param): + path_param_values[f_name] = _val_to_string(param) + + return globals_already_populated + + +def is_optional(field): + return get_origin(field) is Union and type(None) in get_args(field) + + +def template_url(url_with_params: str, params: Dict[str, str]) -> str: + for key, value in params.items(): + url_with_params = url_with_params.replace("{" + key + "}", value) + + return url_with_params + + +def remove_suffix(input_string, suffix): + if suffix and input_string.endswith(suffix): + return input_string[: -len(suffix)] + return input_string diff --git a/google/genai/_gaos/utils/values.py b/google/genai/_gaos/utils/values.py new file mode 100644 index 000000000..c100b3933 --- /dev/null +++ b/google/genai/_gaos/utils/values.py @@ -0,0 +1,153 @@ +# 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 datetime import datetime +from enum import Enum +from email.message import Message +from functools import partial +import os +from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union, cast + +from httpx import Response +from pydantic import BaseModel +from pydantic.fields import FieldInfo + +from ..types.basemodel import Unset + +from .serializers import marshal_json + +from .metadata import ParamMetadata, find_field_metadata + + +def match_content_type(content_type: str, pattern: str) -> bool: + if pattern in (content_type, "*", "*/*"): + return True + + msg = Message() + msg["content-type"] = content_type + media_type = msg.get_content_type() + + if media_type == pattern: + return True + + parts = media_type.split("/") + if len(parts) == 2: + if pattern in (f"{parts[0]}/*", f"*/{parts[1]}"): + return True + + return False + + +def match_status_codes(status_codes: List[str], status_code: int) -> bool: + if "default" in status_codes: + return True + + for code in status_codes: + if code == str(status_code): + return True + + if code.endswith("XX") and code.startswith(str(status_code)[:1]): + return True + return False + + +T = TypeVar("T") + +def cast_partial(typ): + return partial(cast, typ) + +def get_global_from_env( + value: Optional[T], env_key: str, type_cast: Callable[[str], T] +) -> Optional[T]: + if value is not None: + return value + env_value = os.getenv(env_key) + if env_value is not None: + try: + return type_cast(env_value) + except ValueError: + pass + return None + + +def match_response( + response: Response, code: Union[str, List[str]], content_type: str +) -> bool: + codes = code if isinstance(code, list) else [code] + return match_status_codes(codes, response.status_code) and match_content_type( + response.headers.get("content-type", "application/octet-stream"), content_type + ) + + +def _populate_from_globals( + param_name: str, value: Any, param_metadata_type: type, gbls: Any +) -> Tuple[Any, bool]: + if gbls is None: + return value, False + + if not isinstance(gbls, BaseModel): + raise TypeError("globals must be a pydantic model") + + global_fields: Dict[str, FieldInfo] = gbls.__class__.model_fields + found = False + for name in global_fields: + field = global_fields[name] + if name is not param_name: + continue + + found = True + + if value is not None: + return value, True + + global_value = getattr(gbls, name) + + param_metadata = find_field_metadata(field, param_metadata_type) + if param_metadata is None: + return value, True + + return global_value, True + + return value, found + + +def _val_to_string(val) -> str: + if isinstance(val, bool): + return str(val).lower() + if isinstance(val, datetime): + return str(val.isoformat().replace("+00:00", "Z")) + if isinstance(val, Enum): + return str(val.value) + + return str(val) + + +def _get_serialized_params( + metadata: ParamMetadata, field_name: str, obj: Any, typ: type +) -> Dict[str, str]: + params: Dict[str, str] = {} + + serialization = metadata.serialization + if serialization == "json": + params[field_name] = marshal_json(obj, typ) + + return params + + +def _is_set(value: Any) -> bool: + return value is not None and not isinstance(value, Unset) diff --git a/google/genai/_gaos/webhooks.py b/google/genai/_gaos/webhooks.py new file mode 100644 index 000000000..64aa559b8 --- /dev/null +++ b/google/genai/_gaos/webhooks.py @@ -0,0 +1,2060 @@ +# 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 . import errors, models, types, utils +from ._hooks import HookContext +from .basesdk import AsyncBaseSDK, BaseSDK +from .types import OptionalNullable, UNSET, interactions, webhooks +from .types.webhooks import ( + pingwebhookrequest as webhooks_pingwebhookrequest, + rotatesigningsecretrequest as webhooks_rotatesigningsecretrequest, + webhook as webhooks_webhook, + webhookupdate as webhooks_webhookupdate, +) +from .utils import get_security_from_env, response_helpers +from .utils.unmarshal_json_response import unmarshal_json_response +import httpx +from typing import Any, Iterable, List, Mapping, Optional, Union, cast + + +class Webhooks(BaseSDK): + @property + def with_raw_response(self): + return WebhooksWithRawResponse(self) + + @property + def with_streaming_response(self): + return WebhooksWithStreamingResponse(self) + + def create( + self, + *, + uri: str, + subscribed_events: Iterable[webhooks_webhook.WebhookSubscribedEvent], + api_version: Optional[str] = None, + name: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.Webhook: + r"""Creates a new Webhook. + + :param uri: Required. The URI to which webhook events will be sent. + :param subscribed_events: Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + :param api_version: Which version of the API to use. + :param name: Optional. The user-provided name of the webhook. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.CreateWebhookRequest( + api_version=api_version, + body=webhooks.WebhookInput( + name=name, + uri=uri, + subscribed_events=utils.unmarshal( + subscribed_events, List[webhooks.WebhookSubscribedEvent] + ), + ), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="POST", + path="/{api_version}/webhooks", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=True, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.CreateWebhookGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body, + False, + False, + "json", + webhooks.WebhookInput, + extra_body=extra_body, + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(webhooks.Webhook, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="CreateWebhook", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def list( + self, + *, + api_version: Optional[str] = None, + page_size: Optional[int] = None, + page_token: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.WebhookListResponse: + r"""Lists all Webhooks. + + :param api_version: Which version of the API to use. + :param page_size: Optional. The maximum number of webhooks to return. The service may return fewer than + this value. If unspecified, at most 50 webhooks will be returned. + The maximum value is 1000. + :param page_token: Optional. A page token, received from a previous `ListWebhooks` call. + Provide this to retrieve the subsequent page. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.ListWebhooksRequest( + api_version=api_version, + page_size=page_size, + page_token=page_token, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="GET", + path="/{api_version}/webhooks", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.ListWebhooksGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(webhooks.WebhookListResponse, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="ListWebhooks", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def get( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.Webhook: + r"""Gets a specific Webhook. + + :param id: Required. The ID of the webhook to retrieve. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.GetWebhookRequest( + api_version=api_version, + id=id, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="GET", + path="/{api_version}/webhooks/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.GetWebhookGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(webhooks.Webhook, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="GetWebhook", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def update( + self, + id: str, + *, + api_version: Optional[str] = None, + update_mask: Optional[str] = None, + name: Optional[str] = None, + uri: Optional[str] = None, + subscribed_events: Optional[ + Iterable[webhooks_webhookupdate.WebhookUpdateSubscribedEvent] + ] = None, + state: Optional[webhooks_webhookupdate.WebhookUpdateState] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.Webhook: + r"""Updates an existing Webhook. + + :param id: Required. The ID of the webhook to update. + :param api_version: Which version of the API to use. + :param update_mask: Optional. The list of fields to update. + :param name: Optional. The user-provided name of the webhook. + :param uri: Optional. The URI to which webhook events will be sent. + :param subscribed_events: Optional. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + :param state: Optional. The state of the webhook. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.UpdateWebhookRequest( + api_version=api_version, + id=id, + update_mask=update_mask, + body=webhooks.WebhookUpdate( + name=name, + uri=uri, + subscribed_events=utils.unmarshal( + subscribed_events, + Optional[List[webhooks.WebhookUpdateSubscribedEvent]], + ), + state=state, + ), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="PATCH", + path="/{api_version}/webhooks/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.UpdateWebhookGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body if request is not None else None, + False, + True, + "json", + Optional[webhooks.WebhookUpdate], + extra_body=extra_body, + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(webhooks.Webhook, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="UpdateWebhook", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def delete( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Empty: + r"""Deletes a Webhook. + + :param id: Required. The ID of the webhook to delete. + Format: `{webhook_id}` + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.DeleteWebhookRequest( + api_version=api_version, + id=id, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="DELETE", + path="/{api_version}/webhooks/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.DeleteWebhookGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(interactions.Empty, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="DeleteWebhook", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def rotate_signing_secret( + self, + id: str, + *, + api_version: Optional[str] = None, + revocation_behavior: Optional[ + webhooks_rotatesigningsecretrequest.RevocationBehavior + ] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.WebhookRotateSigningSecretResponse: + r"""Generates a new signing secret for a Webhook. + + :param id: Required. The ID of the webhook for which to generate a signing secret. + Format: `{webhook_id}` + :param api_version: Which version of the API to use. + :param revocation_behavior: Optional. The revocation behavior for previous signing secrets. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.RotateSigningSecretRequest( + api_version=api_version, + id=id, + body=webhooks.RotateSigningSecretRequest( + revocation_behavior=revocation_behavior, + ), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="POST", + path="/{api_version}/webhooks/{id}:rotateSigningSecret", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.RotateSigningSecretGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body if request is not None else None, + False, + True, + "json", + Optional[webhooks.RotateSigningSecretRequest], + extra_body=extra_body, + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response( + webhooks.WebhookRotateSigningSecretResponse, http_res + ) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="RotateSigningSecret", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + def ping( + self, + id: str, + *, + api_version: Optional[str] = None, + body: Optional[ + Union[ + webhooks_pingwebhookrequest.PingWebhookRequest, + webhooks_pingwebhookrequest.PingWebhookRequestParam, + ] + ] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.WebhookPingResponse: + r"""Sends a ping event to a Webhook. + + :param id: Required. The ID of the webhook to ping. + Format: `{webhook_id}` + :param api_version: Which version of the API to use. + :param body: The request body. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.PingWebhookRequest( + api_version=api_version, + id=id, + body=utils.get_pydantic_model(body, Optional[webhooks.PingWebhookRequest]), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request( + method="POST", + path="/{api_version}/webhooks/{id}:ping", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.PingWebhookGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body if request is not None else None, + False, + True, + "json", + Optional[webhooks.PingWebhookRequest], + extra_body=extra_body, + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(webhooks.WebhookPingResponse, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="PingWebhook", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.StreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.APIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return _speakeasy_parse_response(http_res) + + +class WebhooksWithRawResponse: + def __init__(self, sdk: Webhooks) -> None: + self._sdk = sdk + self.create = response_helpers.to_raw_response_wrapper( + sdk.create, "extra_headers" + ) + self.list = response_helpers.to_raw_response_wrapper(sdk.list, "extra_headers") + self.get = response_helpers.to_raw_response_wrapper(sdk.get, "extra_headers") + self.update = response_helpers.to_raw_response_wrapper( + sdk.update, "extra_headers" + ) + self.delete = response_helpers.to_raw_response_wrapper( + sdk.delete, "extra_headers" + ) + self.rotate_signing_secret = response_helpers.to_raw_response_wrapper( + sdk.rotate_signing_secret, "extra_headers" + ) + self.ping = response_helpers.to_raw_response_wrapper(sdk.ping, "extra_headers") + + +class WebhooksWithStreamingResponse: + def __init__(self, sdk: Webhooks) -> None: + self._sdk = sdk + self.create = response_helpers.to_streamed_response_wrapper( + sdk.create, "extra_headers" + ) + self.list = response_helpers.to_streamed_response_wrapper( + sdk.list, "extra_headers" + ) + self.get = response_helpers.to_streamed_response_wrapper( + sdk.get, "extra_headers" + ) + self.update = response_helpers.to_streamed_response_wrapper( + sdk.update, "extra_headers" + ) + self.delete = response_helpers.to_streamed_response_wrapper( + sdk.delete, "extra_headers" + ) + self.rotate_signing_secret = response_helpers.to_streamed_response_wrapper( + sdk.rotate_signing_secret, "extra_headers" + ) + self.ping = response_helpers.to_streamed_response_wrapper( + sdk.ping, "extra_headers" + ) + + +class AsyncWebhooks(AsyncBaseSDK): + @property + def with_raw_response(self): + return AsyncWebhooksWithRawResponse(self) + + @property + def with_streaming_response(self): + return AsyncWebhooksWithStreamingResponse(self) + + async def create( + self, + *, + uri: str, + subscribed_events: Iterable[webhooks_webhook.WebhookSubscribedEvent], + api_version: Optional[str] = None, + name: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.Webhook: + r"""Creates a new Webhook. + + :param uri: Required. The URI to which webhook events will be sent. + :param subscribed_events: Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + :param api_version: Which version of the API to use. + :param name: Optional. The user-provided name of the webhook. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.CreateWebhookRequest( + api_version=api_version, + body=webhooks.WebhookInput( + name=name, + uri=uri, + subscribed_events=utils.unmarshal( + subscribed_events, List[webhooks.WebhookSubscribedEvent] + ), + ), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="POST", + path="/{api_version}/webhooks", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=True, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.CreateWebhookGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body, + False, + False, + "json", + webhooks.WebhookInput, + extra_body=extra_body, + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(webhooks.Webhook, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="CreateWebhook", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def list( + self, + *, + api_version: Optional[str] = None, + page_size: Optional[int] = None, + page_token: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.WebhookListResponse: + r"""Lists all Webhooks. + + :param api_version: Which version of the API to use. + :param page_size: Optional. The maximum number of webhooks to return. The service may return fewer than + this value. If unspecified, at most 50 webhooks will be returned. + The maximum value is 1000. + :param page_token: Optional. A page token, received from a previous `ListWebhooks` call. + Provide this to retrieve the subsequent page. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.ListWebhooksRequest( + api_version=api_version, + page_size=page_size, + page_token=page_token, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="GET", + path="/{api_version}/webhooks", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.ListWebhooksGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(webhooks.WebhookListResponse, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="ListWebhooks", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def get( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.Webhook: + r"""Gets a specific Webhook. + + :param id: Required. The ID of the webhook to retrieve. + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.GetWebhookRequest( + api_version=api_version, + id=id, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="GET", + path="/{api_version}/webhooks/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.GetWebhookGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(webhooks.Webhook, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="GetWebhook", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def update( + self, + id: str, + *, + api_version: Optional[str] = None, + update_mask: Optional[str] = None, + name: Optional[str] = None, + uri: Optional[str] = None, + subscribed_events: Optional[ + Iterable[webhooks_webhookupdate.WebhookUpdateSubscribedEvent] + ] = None, + state: Optional[webhooks_webhookupdate.WebhookUpdateState] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.Webhook: + r"""Updates an existing Webhook. + + :param id: Required. The ID of the webhook to update. + :param api_version: Which version of the API to use. + :param update_mask: Optional. The list of fields to update. + :param name: Optional. The user-provided name of the webhook. + :param uri: Optional. The URI to which webhook events will be sent. + :param subscribed_events: Optional. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + :param state: Optional. The state of the webhook. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.UpdateWebhookRequest( + api_version=api_version, + id=id, + update_mask=update_mask, + body=webhooks.WebhookUpdate( + name=name, + uri=uri, + subscribed_events=utils.unmarshal( + subscribed_events, + Optional[List[webhooks.WebhookUpdateSubscribedEvent]], + ), + state=state, + ), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="PATCH", + path="/{api_version}/webhooks/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.UpdateWebhookGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body if request is not None else None, + False, + True, + "json", + Optional[webhooks.WebhookUpdate], + extra_body=extra_body, + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(webhooks.Webhook, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="UpdateWebhook", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def delete( + self, + id: str, + *, + api_version: Optional[str] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> interactions.Empty: + r"""Deletes a Webhook. + + :param id: Required. The ID of the webhook to delete. + Format: `{webhook_id}` + :param api_version: Which version of the API to use. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.DeleteWebhookRequest( + api_version=api_version, + id=id, + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="DELETE", + path="/{api_version}/webhooks/{id}", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.DeleteWebhookGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(interactions.Empty, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="DeleteWebhook", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def rotate_signing_secret( + self, + id: str, + *, + api_version: Optional[str] = None, + revocation_behavior: Optional[ + webhooks_rotatesigningsecretrequest.RevocationBehavior + ] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.WebhookRotateSigningSecretResponse: + r"""Generates a new signing secret for a Webhook. + + :param id: Required. The ID of the webhook for which to generate a signing secret. + Format: `{webhook_id}` + :param api_version: Which version of the API to use. + :param revocation_behavior: Optional. The revocation behavior for previous signing secrets. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.RotateSigningSecretRequest( + api_version=api_version, + id=id, + body=webhooks.RotateSigningSecretRequest( + revocation_behavior=revocation_behavior, + ), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="POST", + path="/{api_version}/webhooks/{id}:rotateSigningSecret", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.RotateSigningSecretGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body if request is not None else None, + False, + True, + "json", + Optional[webhooks.RotateSigningSecretRequest], + extra_body=extra_body, + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response( + webhooks.WebhookRotateSigningSecretResponse, http_res + ) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="RotateSigningSecret", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + async def ping( + self, + id: str, + *, + api_version: Optional[str] = None, + body: Optional[ + Union[ + webhooks_pingwebhookrequest.PingWebhookRequest, + webhooks_pingwebhookrequest.PingWebhookRequestParam, + ] + ] = None, + extra_headers: Optional[Mapping[str, str]] = None, + extra_query: Optional[Mapping[str, Any]] = None, + extra_body: Optional[Mapping[str, Any]] = None, + timeout: Optional[Union[float, httpx.Timeout]] = None, + ) -> webhooks.WebhookPingResponse: + r"""Sends a ping event to a Webhook. + + :param id: Required. The ID of the webhook to ping. + Format: `{webhook_id}` + :param api_version: Which version of the API to use. + :param body: The request body. + :param extra_headers: Additional headers to set or replace on requests. + :param extra_query: Additional query parameters to append to requests. + :param extra_body: Additional JSON object fields to merge into request bodies. + :param timeout: Override the default request timeout configuration for this method in seconds + """ + base_url = None + url_variables = None + retries: OptionalNullable[utils.RetryConfig] = UNSET + server_url = None + http_headers = extra_headers + timeout_ms = self._coerce_timeout_ms(timeout) + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.PingWebhookRequest( + api_version=api_version, + id=id, + body=utils.get_pydantic_model(body, Optional[webhooks.PingWebhookRequest]), + ) + + _speakeasy_response_mode, http_headers = response_helpers.consume_response_mode( + http_headers + ) + req = self._build_request_async( + method="POST", + path="/{api_version}/webhooks/{id}:ping", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=True, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + extra_query_params=extra_query, + _globals=models.PingWebhookGlobals( + api_version=self.sdk_configuration.globals.api_version, + ), + security=self.sdk_configuration.security, + get_serialized_body=lambda: utils.serialize_request_body( + request.body if request is not None else None, + False, + True, + "json", + Optional[webhooks.PingWebhookRequest], + extra_body=extra_body, + ), + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(500, 8000, 2, 30000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["408", "409", "429", "5XX"]) + + async def _speakeasy_parse_response(http_res): + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.GenAiDefaultError( + "API error occurred", http_res, http_res_text + ) + if utils.match_response(http_res, "default", "application/json"): + return unmarshal_json_response(webhooks.WebhookPingResponse, http_res) + + raise errors.GenAiDefaultError("Unexpected response received", http_res) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="PingWebhook", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, types.Security + ), + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + stream=_speakeasy_response_mode == "streaming", + retry_config=retry_config, + ) + if _speakeasy_response_mode != "parsed": + _speakeasy_response_cls = ( + response_helpers.AsyncStreamedAPIResponse + if _speakeasy_response_mode == "streaming" + else response_helpers.AsyncAPIResponse + ) + return cast( + Any, + _speakeasy_response_cls( + raw=http_res, + parser=_speakeasy_parse_response, + mode="buffered", + client_ref=self, + ), + ) + return await _speakeasy_parse_response(http_res) + + +class AsyncWebhooksWithRawResponse: + def __init__(self, sdk: AsyncWebhooks) -> None: + self._sdk = sdk + self.create = response_helpers.async_to_raw_response_wrapper( + sdk.create, "extra_headers" + ) + self.list = response_helpers.async_to_raw_response_wrapper( + sdk.list, "extra_headers" + ) + self.get = response_helpers.async_to_raw_response_wrapper( + sdk.get, "extra_headers" + ) + self.update = response_helpers.async_to_raw_response_wrapper( + sdk.update, "extra_headers" + ) + self.delete = response_helpers.async_to_raw_response_wrapper( + sdk.delete, "extra_headers" + ) + self.rotate_signing_secret = response_helpers.async_to_raw_response_wrapper( + sdk.rotate_signing_secret, "extra_headers" + ) + self.ping = response_helpers.async_to_raw_response_wrapper( + sdk.ping, "extra_headers" + ) + + +class AsyncWebhooksWithStreamingResponse: + def __init__(self, sdk: AsyncWebhooks) -> None: + self._sdk = sdk + self.create = response_helpers.async_to_streamed_response_wrapper( + sdk.create, "extra_headers" + ) + self.list = response_helpers.async_to_streamed_response_wrapper( + sdk.list, "extra_headers" + ) + self.get = response_helpers.async_to_streamed_response_wrapper( + sdk.get, "extra_headers" + ) + self.update = response_helpers.async_to_streamed_response_wrapper( + sdk.update, "extra_headers" + ) + self.delete = response_helpers.async_to_streamed_response_wrapper( + sdk.delete, "extra_headers" + ) + self.rotate_signing_secret = ( + response_helpers.async_to_streamed_response_wrapper( + sdk.rotate_signing_secret, "extra_headers" + ) + ) + self.ping = response_helpers.async_to_streamed_response_wrapper( + sdk.ping, "extra_headers" + ) diff --git a/pyproject.toml b/pyproject.toml index 712bc7ccd..63bc5b436 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,8 @@ asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" [tool.mypy] -exclude = ["tests/", "_test_api_client\\.py"] +exclude = ["tests/", "_test_api_client\\.py", "_gaos/"] +explicit_package_bases = true plugins = ["pydantic.mypy"] # we are ignoring 'unused-ignore' because we run mypy on Python 3.9 - 3.13 and # some errors in _automatic_function_calling_util.py only apply in 3.10+ @@ -79,8 +80,18 @@ show_error_codes = true [[tool.mypy.overrides]] module = [ - "genai._interactions", - "genai._interactions.*" + "google.genai._gaos", + "google.genai._gaos.*" +] +# Auto-generated code in _gaos has type errors; ignore them but keep type info +# for imports. This will only affect the _gaos module. +ignore_errors = true + +# Remove after the switch. +[[tool.mypy.overrides]] +module = [ + "google.genai._interactions", + "google.genai._interactions.*" ] # 'redundant-cast' is safe to ignore. disable_error_code = [