diff --git a/google/genai/_interactions/_client.py b/google/genai/_interactions/_client.py index 05c6d7fca..76d7e9343 100644 --- a/google/genai/_interactions/_client.py +++ b/google/genai/_interactions/_client.py @@ -35,7 +35,7 @@ RequestOptions, not_given, ) -from ._utils import is_given +from ._utils import is_given, is_mapping_t from ._compat import cached_property from ._models import FinalRequestOptions from ._version import __version__ @@ -348,7 +348,6 @@ def __init__( if base_url is None: base_url = f"https://generativelanguage.googleapis.com" - self.client_adapter = client_adapter super().__init__( version=__version__, diff --git a/google/genai/_interactions/_files.py b/google/genai/_interactions/_files.py index 7f209ee4e..f8603936d 100644 --- a/google/genai/_interactions/_files.py +++ b/google/genai/_interactions/_files.py @@ -115,7 +115,7 @@ async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles elif is_sequence_t(files): files = [(key, await _async_transform_file(file)) for key, file in files] else: - raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") return files diff --git a/google/genai/_interactions/_models.py b/google/genai/_interactions/_models.py index f09924cc5..8a7c4b672 100644 --- a/google/genai/_interactions/_models.py +++ b/google/genai/_interactions/_models.py @@ -41,7 +41,9 @@ ClassVar, Protocol, Required, + Annotated, ParamSpec, + TypeAlias, TypedDict, TypeGuard, final, @@ -95,7 +97,15 @@ from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: + from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler + from pydantic_core import CoreSchema, core_schema from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema +else: + try: + from pydantic_core import CoreSchema, core_schema + except ImportError: + CoreSchema = None + core_schema = None __all__ = ["BaseModel", "GenericModel"] @@ -412,6 +422,76 @@ def model_dump_json( ) +class _EagerIterable(list[_T], Generic[_T]): + """ + Accepts any Iterable[T] input (including generators), consumes it + eagerly, and validates all items upfront. + + Validation preserves the original container type where possible + (e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON) + always emits a list — round-tripping through model_dump() will not + restore the original container type. + """ + + @classmethod + def __get_pydantic_core_schema__( + cls, + source_type: Any, + handler: GetCoreSchemaHandler, + ) -> CoreSchema: + (item_type,) = get_args(source_type) or (Any,) + item_schema: CoreSchema = handler.generate_schema(item_type) + list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema) + + return core_schema.no_info_wrap_validator_function( + cls._validate, + list_of_items_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + cls._serialize, + info_arg=False, + ), + ) + + @staticmethod + def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any: + original_type: type[Any] = type(v) + + # Normalize to list so list_schema can validate each item + if isinstance(v, list): + items: list[_T] = v + else: + try: + items = list(v) + except TypeError as e: + raise TypeError("Value is not iterable") from e + + # Validate items against the inner schema + validated: list[_T] = handler(items) + + # Reconstruct original container type + if original_type is list: + return validated + # str(list) produces the list's repr, not a string built from items, + # so skip reconstruction for str and its subclasses. + if issubclass(original_type, str): + return validated + try: + return original_type(validated) + except (TypeError, ValueError): + # If the type cannot be reconstructed, just return the validated list + return validated + + @staticmethod + def _serialize(v: Iterable[_T]) -> list[_T]: + """Always serialize as a list so Pydantic's JSON encoder is happy.""" + if isinstance(v, list): + return v + return list(v) + + +EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable] + + def _construct_field(value: object, field: FieldInfo, key: str) -> object: if value is None: return field_get_default(field) diff --git a/google/genai/_interactions/types/__init__.py b/google/genai/_interactions/types/__init__.py index 9a1f90467..a6bc7d722 100644 --- a/google/genai/_interactions/types/__init__.py +++ b/google/genai/_interactions/types/__init__.py @@ -106,6 +106,7 @@ from .url_context_result_step import URLContextResultStep as URLContextResultStep from .webhook_delete_response import WebhookDeleteResponse as WebhookDeleteResponse from .code_execution_call_step import CodeExecutionCallStep as CodeExecutionCallStep +from .code_mender_agent_config import CodeMenderAgentConfig as CodeMenderAgentConfig from .function_call_step_param import FunctionCallStepParam as FunctionCallStepParam from .tool_choice_config_param import ToolChoiceConfigParam as ToolChoiceConfigParam from .google_search_result_step import GoogleSearchResultStep as GoogleSearchResultStep @@ -134,6 +135,7 @@ from .google_search_call_step_param import GoogleSearchCallStepParam as GoogleSearchCallStepParam from .url_context_result_step_param import URLContextResultStepParam as URLContextResultStepParam from .code_execution_call_step_param import CodeExecutionCallStepParam as CodeExecutionCallStepParam +from .code_mender_agent_config_param import CodeMenderAgentConfigParam as CodeMenderAgentConfigParam from .google_search_result_step_param import GoogleSearchResultStepParam as GoogleSearchResultStepParam from .mcp_server_tool_call_step_param import MCPServerToolCallStepParam as MCPServerToolCallStepParam from .code_execution_result_step_param import CodeExecutionResultStepParam as CodeExecutionResultStepParam diff --git a/google/genai/_interactions/types/agent_list_params.py b/google/genai/_interactions/types/agent_list_params.py index 54db37f6f..af944d617 100644 --- a/google/genai/_interactions/types/agent_list_params.py +++ b/google/genai/_interactions/types/agent_list_params.py @@ -17,9 +17,7 @@ from __future__ import annotations -from typing_extensions import Annotated, TypedDict - -from .._utils import PropertyInfo +from typing_extensions import TypedDict __all__ = ["AgentListParams"] @@ -27,8 +25,8 @@ class AgentListParams(TypedDict, total=False): api_version: str - page_size: Annotated[int, PropertyInfo(alias="pageSize")] + page_size: int - page_token: Annotated[str, PropertyInfo(alias="pageToken")] + page_token: str parent: str diff --git a/google/genai/_interactions/types/agent_list_response.py b/google/genai/_interactions/types/agent_list_response.py index 845e917b2..fabad2175 100644 --- a/google/genai/_interactions/types/agent_list_response.py +++ b/google/genai/_interactions/types/agent_list_response.py @@ -17,8 +17,6 @@ from typing import List, Optional -from pydantic import Field as FieldInfo - from .agent import Agent from .._models import BaseModel @@ -28,4 +26,4 @@ class AgentListResponse(BaseModel): agents: Optional[List[Agent]] = None - next_page_token: Optional[str] = FieldInfo(alias="nextPageToken", default=None) + next_page_token: Optional[str] = None diff --git a/google/genai/_interactions/types/code_mender_agent_config.py b/google/genai/_interactions/types/code_mender_agent_config.py new file mode 100644 index 000000000..67822916a --- /dev/null +++ b/google/genai/_interactions/types/code_mender_agent_config.py @@ -0,0 +1,132 @@ +# Copyright 2025 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. +# + +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = [ + "CodeMenderAgentConfig", + "FindRequest", + "FindRequestSourceFile", + "FixRequest", + "FixRequestSourceFile", + "SessionConfig", +] + + +class FindRequestSourceFile(BaseModel): + """Content of a single file in the codebase.""" + + content: Optional[str] = None + """The UTF-8 encoded text content of the file.""" + + path: Optional[str] = None + """The relative path of the file from the project root.""" + + +class FindRequest(BaseModel): + """Parameters for finding vulnerabilities.""" + + description: Optional[str] = None + """ + Additional context or custom instructions provided by the user to guide the + vulnerability analysis. + """ + + finding_id: Optional[str] = None + """The identifier of a specific finding to verify. + + This is primarily used in VERIFY mode to focus the agent's execution-based + validation on a single vulnerability. + """ + + source_files: Optional[List[FindRequestSourceFile]] = None + """A list of source files to provide as context for the scan.""" + + +class FixRequestSourceFile(BaseModel): + """Content of a single file in the codebase.""" + + content: Optional[str] = None + """The UTF-8 encoded text content of the file.""" + + path: Optional[str] = None + """The relative path of the file from the project root.""" + + +class FixRequest(BaseModel): + """Parameters for fixing vulnerabilities.""" + + description: Optional[str] = None + """ + Additional context or custom instructions provided by the user to guide the + patch generation process. + """ + + finding_id: Optional[str] = None + """The identifier of the specific security finding to be remediated. + + This ID maps to a previously discovered vulnerability. + """ + + source_files: Optional[List[FixRequestSourceFile]] = None + """A list of source files providing context for the remediation. + + These files are typically the ones containing the identified vulnerability. + """ + + +class SessionConfig(BaseModel): + """ + Optional session-specific configurations to override default agent + behavior. + """ + + max_rounds: Optional[int] = None + """ + The maximum number of interaction rounds the agent is allowed to perform before + reaching a timeout. + """ + + pipeline_mode: Optional[Literal["scan", "verify"]] = None + """The pipeline mode of a CodeMender session. + + It can only be used for a find session. + """ + + topology: Optional[str] = None + """The cognitive architecture or "thinking" topology used by the agent (e.g. + + "default", "deep"). + """ + + +class CodeMenderAgentConfig(BaseModel): + """Configuration for the CodeMender agent.""" + + type: Literal["code-mender"] + + find_request: Optional[FindRequest] = None + """Parameters for finding vulnerabilities.""" + + fix_request: Optional[FixRequest] = None + """Parameters for fixing vulnerabilities.""" + + session_config: Optional[SessionConfig] = None + """Optional session-specific configurations to override default agent behavior.""" diff --git a/google/genai/_interactions/types/code_mender_agent_config_param.py b/google/genai/_interactions/types/code_mender_agent_config_param.py new file mode 100644 index 000000000..37cf8b4a4 --- /dev/null +++ b/google/genai/_interactions/types/code_mender_agent_config_param.py @@ -0,0 +1,132 @@ +# Copyright 2025 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. +# + +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = [ + "CodeMenderAgentConfigParam", + "FindRequest", + "FindRequestSourceFile", + "FixRequest", + "FixRequestSourceFile", + "SessionConfig", +] + + +class FindRequestSourceFile(TypedDict, total=False): + """Content of a single file in the codebase.""" + + content: str + """The UTF-8 encoded text content of the file.""" + + path: str + """The relative path of the file from the project root.""" + + +class FindRequest(TypedDict, total=False): + """Parameters for finding vulnerabilities.""" + + description: str + """ + Additional context or custom instructions provided by the user to guide the + vulnerability analysis. + """ + + finding_id: str + """The identifier of a specific finding to verify. + + This is primarily used in VERIFY mode to focus the agent's execution-based + validation on a single vulnerability. + """ + + source_files: Iterable[FindRequestSourceFile] + """A list of source files to provide as context for the scan.""" + + +class FixRequestSourceFile(TypedDict, total=False): + """Content of a single file in the codebase.""" + + content: str + """The UTF-8 encoded text content of the file.""" + + path: str + """The relative path of the file from the project root.""" + + +class FixRequest(TypedDict, total=False): + """Parameters for fixing vulnerabilities.""" + + description: str + """ + Additional context or custom instructions provided by the user to guide the + patch generation process. + """ + + finding_id: str + """The identifier of the specific security finding to be remediated. + + This ID maps to a previously discovered vulnerability. + """ + + source_files: Iterable[FixRequestSourceFile] + """A list of source files providing context for the remediation. + + These files are typically the ones containing the identified vulnerability. + """ + + +class SessionConfig(TypedDict, total=False): + """ + Optional session-specific configurations to override default agent + behavior. + """ + + max_rounds: int + """ + The maximum number of interaction rounds the agent is allowed to perform before + reaching a timeout. + """ + + pipeline_mode: Literal["scan", "verify"] + """The pipeline mode of a CodeMender session. + + It can only be used for a find session. + """ + + topology: str + """The cognitive architecture or "thinking" topology used by the agent (e.g. + + "default", "deep"). + """ + + +class CodeMenderAgentConfigParam(TypedDict, total=False): + """Configuration for the CodeMender agent.""" + + type: Required[Literal["code-mender"]] + + find_request: FindRequest + """Parameters for finding vulnerabilities.""" + + fix_request: FixRequest + """Parameters for fixing vulnerabilities.""" + + session_config: SessionConfig + """Optional session-specific configurations to override default agent behavior.""" diff --git a/google/genai/_interactions/types/interaction.py b/google/genai/_interactions/types/interaction.py index 505d1f63a..f66dee907 100644 --- a/google/genai/_interactions/types/interaction.py +++ b/google/genai/_interactions/types/interaction.py @@ -41,12 +41,13 @@ from .text_response_format import TextResponseFormat from .audio_response_format import AudioResponseFormat from .image_response_format import ImageResponseFormat +from .code_mender_agent_config import CodeMenderAgentConfig from .deep_research_agent_config import DeepResearchAgentConfig __all__ = ["Interaction", "AgentConfig", "Environment", "Input", "ResponseFormat", "ResponseFormatResponseFormatList"] AgentConfig: TypeAlias = Annotated[ - Union[DynamicAgentConfig, DeepResearchAgentConfig], PropertyInfo(discriminator="type") + Union[DynamicAgentConfig, DeepResearchAgentConfig, CodeMenderAgentConfig], PropertyInfo(discriminator="type") ] Environment: TypeAlias = Union[str, environment.Environment] diff --git a/google/genai/_interactions/types/interaction_create_params.py b/google/genai/_interactions/types/interaction_create_params.py index 61a7761a5..3fa289867 100644 --- a/google/genai/_interactions/types/interaction_create_params.py +++ b/google/genai/_interactions/types/interaction_create_params.py @@ -36,6 +36,7 @@ from .text_response_format_param import TextResponseFormatParam from .audio_response_format_param import AudioResponseFormatParam from .image_response_format_param import ImageResponseFormatParam +from .code_mender_agent_config_param import CodeMenderAgentConfigParam from .deep_research_agent_config_param import DeepResearchAgentConfigParam __all__ = [ @@ -202,7 +203,7 @@ class BaseCreateAgentInteractionParams(TypedDict, total=False): """ -AgentConfig: TypeAlias = Union[DynamicAgentConfigParam, DeepResearchAgentConfigParam] +AgentConfig: TypeAlias = Union[DynamicAgentConfigParam, DeepResearchAgentConfigParam, CodeMenderAgentConfigParam] class CreateModelInteractionParamsNonStreaming(BaseCreateModelInteractionParams, total=False):