diff --git a/agentplatform/_genai/client.py b/agentplatform/_genai/client.py index d04deba6d9..970330fba4 100644 --- a/agentplatform/_genai/client.py +++ b/agentplatform/_genai/client.py @@ -41,7 +41,9 @@ from agentplatform._genai import skills as skills_module from agentplatform._genai import live as live_module from agentplatform._genai import rag as rag_module - + from agentplatform._genai import ( + feedback_entries as feedback_entries_module, + ) _GENAI_MODULES_TELEMETRY_HEADER = "vertex-genai-modules" @@ -85,6 +87,7 @@ def __init__(self, api_client: genai_client.BaseApiClient): # type: ignore[name self._datasets: Optional[ModuleType] = None self._skills: Optional[ModuleType] = None self._rag: Optional[ModuleType] = None + self._feedback_entries: Optional[ModuleType] = None @property @_common.experimental_warning( @@ -166,6 +169,15 @@ def skills(self) -> "skills_module.AsyncSkills": ) return self._skills.AsyncSkills(self._api_client) # type: ignore[no-any-return] + @property + def feedback_entries(self) -> "feedback_entries_module.AsyncFeedbackEntries": + if self._feedback_entries is None: + self._feedback_entries = importlib.import_module( + ".feedback_entries", + __package__, + ) + return self._feedback_entries.AsyncFeedbackEntries(self._api_client) # type: ignore[no-any-return] + @property @_common.experimental_warning( "The Vertex SDK GenAI async rag module is experimental, " @@ -284,6 +296,7 @@ def __init__( self._datasets: Optional[ModuleType] = None self._skills: Optional[ModuleType] = None self._rag: Optional[ModuleType] = None + self._feedback_entries: Optional[ModuleType] = None @property def evals(self) -> "evals_module.Evals": @@ -390,6 +403,15 @@ def skills(self) -> "skills_module.Skills": ) return self._skills.Skills(self._api_client) # type: ignore[no-any-return] + @property + def feedback_entries(self) -> "feedback_entries_module.FeedbackEntries": + if self._feedback_entries is None: + self._feedback_entries = importlib.import_module( + ".feedback_entries", + __package__, + ) + return self._feedback_entries.FeedbackEntries(self._api_client) # type: ignore[no-any-return] + @property @_common.experimental_warning( "The Vertex SDK GenAI rag module is experimental, " diff --git a/agentplatform/_genai/feedback_entries.py b/agentplatform/_genai/feedback_entries.py new file mode 100644 index 0000000000..8237204f93 --- /dev/null +++ b/agentplatform/_genai/feedback_entries.py @@ -0,0 +1,1636 @@ +# 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. +# + +# Code generated by the Google Gen AI SDK generator DO NOT EDIT. + +import functools +import json +import logging +from typing import Any, AsyncIterator, Iterator, Optional, Union +from urllib.parse import urlencode + +from google.genai import _api_module +from google.genai import _common +from google.genai._common import get_value_by_path as getv +from google.genai._common import set_value_by_path as setv +from google.genai.pagers import AsyncPager, Pager + +from . import _agent_engines_utils +from . import types + +logger = logging.getLogger("agentplatform_genai.feedbackentries") + +logger.setLevel(logging.INFO) + + +def _CreateAgentEngineFeedbackEntryConfig_to_vertex( + from_object: Union[dict[str, Any], object], + parent_object: Optional[dict[str, Any]] = None, +) -> dict[str, Any]: + to_object: dict[str, Any] = {} + + if getv(from_object, ["feedback_labels"]) is not None: + setv( + parent_object, + ["feedbackLabels"], + getv(from_object, ["feedback_labels"]), + ) + + if getv(from_object, ["feedback_text"]) is not None: + setv(parent_object, ["feedbackText"], getv(from_object, ["feedback_text"])) + + if getv(from_object, ["user_id"]) is not None: + setv(parent_object, ["userId"], getv(from_object, ["user_id"])) + + if getv(from_object, ["source"]) is not None: + setv(parent_object, ["source"], getv(from_object, ["source"])) + + if getv(from_object, ["custom_metadata"]) is not None: + setv( + parent_object, + ["customMetadata"], + getv(from_object, ["custom_metadata"]), + ) + + return to_object + + +def _CreateAgentEngineFeedbackEntryRequestParameters_to_vertex( + from_object: Union[dict[str, Any], object], + parent_object: Optional[dict[str, Any]] = None, +) -> dict[str, Any]: + to_object: dict[str, Any] = {} + if getv(from_object, ["config"]) is not None: + _CreateAgentEngineFeedbackEntryConfig_to_vertex( + getv(from_object, ["config"]), to_object + ) + + if getv(from_object, ["session_id"]) is not None: + setv(to_object, ["sessionId"], getv(from_object, ["session_id"])) + + if getv(from_object, ["event_id"]) is not None: + setv(to_object, ["eventId"], getv(from_object, ["event_id"])) + + if getv(from_object, ["feedback_type"]) is not None: + setv(to_object, ["feedbackType"], getv(from_object, ["feedback_type"])) + + if getv(from_object, ["name"]) is not None: + setv(to_object, ["_url", "parent"], getv(from_object, ["name"])) + + return to_object + + +def _DeleteAgentEngineFeedbackEntryRequestParameters_to_vertex( + from_object: Union[dict[str, Any], object], + parent_object: Optional[dict[str, Any]] = None, +) -> dict[str, Any]: + to_object: dict[str, Any] = {} + if getv(from_object, ["name"]) is not None: + setv(to_object, ["_url", "name"], getv(from_object, ["name"])) + + return to_object + + +def _GetAgentEngineFeedbackOperationParameters_to_vertex( + from_object: Union[dict[str, Any], object], + parent_object: Optional[dict[str, Any]] = None, +) -> dict[str, Any]: + to_object: dict[str, Any] = {} + if getv(from_object, ["operation_name"]) is not None: + setv( + to_object, + ["_url", "operationName"], + getv(from_object, ["operation_name"]), + ) + + return to_object + + +def _GetAgentEngineFeedbackRequestParameters_to_vertex( + from_object: Union[dict[str, Any], object], + parent_object: Optional[dict[str, Any]] = None, +) -> dict[str, Any]: + to_object: dict[str, Any] = {} + if getv(from_object, ["name"]) is not None: + setv(to_object, ["_url", "name"], getv(from_object, ["name"])) + + return to_object + + +def _ListAgentEngineFeedbackEntriesConfig_to_vertex( + from_object: Union[dict[str, Any], object], + parent_object: Optional[dict[str, Any]] = None, +) -> dict[str, Any]: + to_object: dict[str, Any] = {} + + if getv(from_object, ["page_size"]) is not None: + setv( + parent_object, ["_query", "pageSize"], getv(from_object, ["page_size"]) + ) + + if getv(from_object, ["page_token"]) is not None: + setv( + parent_object, + ["_query", "pageToken"], + getv(from_object, ["page_token"]), + ) + + if getv(from_object, ["filter"]) is not None: + setv(parent_object, ["_query", "filter"], getv(from_object, ["filter"])) + + if getv(from_object, ["order_by"]) is not None: + setv(parent_object, ["_query", "orderBy"], getv(from_object, ["order_by"])) + + return to_object + + +def _ListAgentEngineFeedbackEntriesRequestParameters_to_vertex( + from_object: Union[dict[str, Any], object], + parent_object: Optional[dict[str, Any]] = None, +) -> dict[str, Any]: + to_object: dict[str, Any] = {} + if getv(from_object, ["parent"]) is not None: + setv(to_object, ["_url", "parent"], getv(from_object, ["parent"])) + + if getv(from_object, ["config"]) is not None: + _ListAgentEngineFeedbackEntriesConfig_to_vertex( + getv(from_object, ["config"]), to_object + ) + + return to_object + + +def _UpdateAgentEngineFeedbackEntryConfig_to_vertex( + from_object: Union[dict[str, Any], object], + parent_object: Optional[dict[str, Any]] = None, +) -> dict[str, Any]: + to_object: dict[str, Any] = {} + + if getv(from_object, ["update_mask"]) is not None: + setv( + parent_object, + ["_query", "updateMask"], + getv(from_object, ["update_mask"]), + ) + + if getv(from_object, ["feedback_labels"]) is not None: + setv( + parent_object, + ["feedbackLabels"], + getv(from_object, ["feedback_labels"]), + ) + + if getv(from_object, ["feedback_text"]) is not None: + setv(parent_object, ["feedbackText"], getv(from_object, ["feedback_text"])) + + if getv(from_object, ["user_id"]) is not None: + setv(parent_object, ["userId"], getv(from_object, ["user_id"])) + + if getv(from_object, ["source"]) is not None: + setv(parent_object, ["source"], getv(from_object, ["source"])) + + if getv(from_object, ["custom_metadata"]) is not None: + setv( + parent_object, + ["customMetadata"], + getv(from_object, ["custom_metadata"]), + ) + + if getv(from_object, ["feedback_type"]) is not None: + setv(parent_object, ["feedbackType"], getv(from_object, ["feedback_type"])) + + if getv(from_object, ["session_id"]) is not None: + setv(parent_object, ["sessionId"], getv(from_object, ["session_id"])) + + if getv(from_object, ["event_id"]) is not None: + setv(parent_object, ["eventId"], getv(from_object, ["event_id"])) + + return to_object + + +def _UpdateAgentEngineFeedbackEntryRequestParameters_to_vertex( + from_object: Union[dict[str, Any], object], + parent_object: Optional[dict[str, Any]] = None, +) -> dict[str, Any]: + to_object: dict[str, Any] = {} + if getv(from_object, ["name"]) is not None: + setv(to_object, ["_url", "name"], getv(from_object, ["name"])) + + if getv(from_object, ["config"]) is not None: + _UpdateAgentEngineFeedbackEntryConfig_to_vertex( + getv(from_object, ["config"]), to_object + ) + + return to_object + + +class FeedbackEntries(_api_module.BaseModule): + + def _create( + self, + *, + config: Optional[types.CreateAgentEngineFeedbackEntryConfigOrDict] = None, + session_id: str, + event_id: str, + feedback_type: types.FeedbackType, + name: str, + ) -> types.AgentEngineFeedbackEntryOperation: + parameter_model = types._CreateAgentEngineFeedbackEntryRequestParameters( + config=config, + session_id=session_id, + event_id=event_id, + feedback_type=feedback_type, + name=name, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _CreateAgentEngineFeedbackEntryRequestParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{parent}/feedbackEntries".format_map(request_url_dict) + else: + path = "{parent}/feedbackEntries" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = self._api_client.request( + "post", path, request_dict, http_options + ) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.AgentEngineFeedbackEntryOperation._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + def _delete( + self, + *, + name: str, + config: Optional[types.DeleteAgentEngineFeedbackEntryConfigOrDict] = None, + ) -> types.DeleteAgentEngineFeedbackEntryOperation: + parameter_model = types._DeleteAgentEngineFeedbackEntryRequestParameters( + name=name, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _DeleteAgentEngineFeedbackEntryRequestParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{name}".format_map(request_url_dict) + else: + path = "{name}" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = self._api_client.request( + "delete", path, request_dict, http_options + ) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.DeleteAgentEngineFeedbackEntryOperation._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + def get( + self, + *, + name: str, + config: Optional[types.GetAgentEngineFeedbackConfigOrDict] = None, + ) -> types.FeedbackEntry: + """Gets an agent engine feedback entry. + + Args: + name (str): Required. The name of the feedback entry to retrieve. + Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}/feedbackEntries/{feedback_entry_id}`. + config (GetAgentEngineFeedbackConfig): Optional. The configuration for + getting the feedback entry. + + Returns: + FeedbackEntry: The requested feedback entry. + """ + + parameter_model = types._GetAgentEngineFeedbackRequestParameters( + name=name, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _GetAgentEngineFeedbackRequestParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{name}".format_map(request_url_dict) + else: + path = "{name}" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = self._api_client.request("get", path, request_dict, http_options) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.FeedbackEntry._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + def _list( + self, + *, + parent: str, + config: Optional[types.ListAgentEngineFeedbackEntriesConfigOrDict] = None, + ) -> types.ListAgentEngineFeedbackEntriesResponse: + parameter_model = types._ListAgentEngineFeedbackEntriesRequestParameters( + parent=parent, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _ListAgentEngineFeedbackEntriesRequestParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{parent}/feedbackEntries".format_map(request_url_dict) + else: + path = "{parent}/feedbackEntries" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = self._api_client.request("get", path, request_dict, http_options) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.ListAgentEngineFeedbackEntriesResponse._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + def _update( + self, + *, + name: str, + config: Optional[types.UpdateAgentEngineFeedbackEntryConfigOrDict] = None, + ) -> types.AgentEngineFeedbackEntryOperation: + """Updates a Feedback Entry. + + Args: + name (str): Required. Name of the feedback entry to update. Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}/feedbackEntries/{feedback_entry_id}`. + feedback_type (shared.FeedbackType): Optional. The type of feedback + provided. + config (UpdateAgentEngineFeedbackEntryConfig): Optional. The + configuration for updating the feedback entry. + + Returns: + AgentEngineFeedbackEntryOperation: Operation for updating a Feedback + Entry. + """ + + parameter_model = types._UpdateAgentEngineFeedbackEntryRequestParameters( + name=name, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _UpdateAgentEngineFeedbackEntryRequestParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{name}".format_map(request_url_dict) + else: + path = "{name}" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = self._api_client.request( + "patch", path, request_dict, http_options + ) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.AgentEngineFeedbackEntryOperation._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + def _get_feedback_entry_operation( + self, + *, + operation_name: str, + config: Optional[types.GetAgentEngineOperationConfigOrDict] = None, + ) -> types.AgentEngineFeedbackEntryOperation: + parameter_model = types._GetAgentEngineFeedbackOperationParameters( + operation_name=operation_name, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _GetAgentEngineFeedbackOperationParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{operationName}".format_map(request_url_dict) + else: + path = "{operationName}" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = self._api_client.request("get", path, request_dict, http_options) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.AgentEngineFeedbackEntryOperation._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + def _get_delete_feedback_entry_operation( + self, + *, + operation_name: str, + config: Optional[types.GetAgentEngineOperationConfigOrDict] = None, + ) -> types.DeleteAgentEngineFeedbackEntryOperation: + parameter_model = types._GetAgentEngineFeedbackOperationParameters( + operation_name=operation_name, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _GetAgentEngineFeedbackOperationParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{operationName}".format_map(request_url_dict) + else: + path = "{operationName}" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = self._api_client.request("get", path, request_dict, http_options) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.DeleteAgentEngineFeedbackEntryOperation._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + def create( + self, + *, + name: str, + feedback_type: str, + session_id: str, + event_id: str, + config: Optional[types.CreateAgentEngineFeedbackEntryConfigOrDict] = None, + ) -> types.AgentEngineFeedbackEntryOperation: + """Creates a new feedback entry in the Agent Engine. + + Args: + name (str): Required. Resource name of the reasoning engine to create + the feedback entry in. Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}`. + feedback_type (FeedbackType): Required. The type of feedback provided. + session_id (str): Required. The ID of the session to which the feedback + relates to. + event_id (str): Required. The ID of the event to which the feedback + relates to. + config (CreateAgentEngineFeedbackEntryConfig): Optional. Additional + configurations for creating the feedback entry. + + Returns: + AgentEngineFeedbackEntryOperation: The operation for creating the + feedback entry. + """ + if config is None: + config = types.CreateAgentEngineFeedbackEntryConfig() + elif isinstance(config, dict): + config = types.CreateAgentEngineFeedbackEntryConfig.model_validate(config) + operation = self._create( + name=name, + session_id=session_id, + event_id=event_id, + feedback_type=feedback_type, + config=config, + ) + if config.wait_for_completion: + if not operation.done: + operation = _agent_engines_utils._await_operation( + operation_name=operation.name, + get_operation_fn=self._get_feedback_entry_operation, + poll_interval_seconds=0.5, + ) + if operation.error: + raise RuntimeError( + f"Failed to create feedback entry: {operation.error}" + ) + return operation + + def list( + self, + *, + parent: str, + config: Optional[types.ListAgentEngineFeedbackEntriesConfigOrDict] = None, + ) -> Iterator[types.FeedbackEntry]: + """ + Lists Agent Engine feedback entries. + + Args: + parent (str): Required. Resource name of the agent engine to list the + feedback entries from. Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}`. + config (ListAgentEngineFeedbackEntriesConfig): + Optional. The configuration for listing feedback entries. + + Returns: + Iterable[FeedbackEntry]: A pager of feedback entries. + """, + + return Pager( + "feedback_entries", + functools.partial(self._list, parent=parent), + self._list(parent=parent, config=config), + config, + ) + + def update( + self, + *, + name: str, + config: Optional[types.UpdateAgentEngineFeedbackEntryConfigOrDict] = None, + ) -> types.AgentEngineFeedbackEntryOperation: + """Updates a feedback entry in the Agent Engine. + + Args: + name (str): Required. Name of the Feedback Entry. Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}/feedbackEntries/{feedback_entry_id}`. + feedback_type (str): Optional. The type of feedback provided. + config (UpdateAgentEngineFeedbackEntryConfig): Optional. Additional + configurations for updating the feedback entry. + + Returns: + AgentEngineFeedbackEntryOperation: The operation for updating + the feedback entry. + """ + if config is None: + config = types.UpdateAgentEngineFeedbackEntryConfig() + elif isinstance(config, dict): + config = types.UpdateAgentEngineFeedbackEntryConfig.model_validate(config) + operation = self._update( + name=name, + config=config, + ) + if config.wait_for_completion: + if not operation.done: + operation = _agent_engines_utils._await_operation( + operation_name=operation.name, + get_operation_fn=self._get_feedback_entry_operation, + poll_interval_seconds=0.5, + ) + if operation.error: + raise RuntimeError( + f"Failed to update feedback entry: {operation.error}" + ) + return operation + + def delete( + self, + *, + name: str, + config: Optional[types.DeleteAgentEngineFeedbackEntryConfigOrDict] = None, + ) -> types.DeleteAgentEngineFeedbackEntryOperation: + """Deletes a feedback entry from the Agent Engine. + + Args: + name (str): Required. Name of the Feedback Entry to delete. Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}/feedbackEntries/{feedback_entry_id}`. + config (DeleteAgentEngineFeedbackEntryConfig): Optional. The + configuration for deleting the Feedback Entry. + + Returns: + DeleteAgentEngineFeedbackEntryOperation: The operation for deleting + the Feedback Entry. + """ + if config is None: + config = types.DeleteAgentEngineFeedbackEntryConfig() + elif isinstance(config, dict): + config = types.DeleteAgentEngineFeedbackEntryConfig.model_validate(config) + operation = self._delete( + name=name, + config=config, + ) + if config.wait_for_completion: + if not operation.done: + operation = _agent_engines_utils._await_operation( + operation_name=operation.name, + get_operation_fn=self._get_delete_feedback_entry_operation, + poll_interval_seconds=0.5, + ) + if operation.error: + raise RuntimeError( + f"Failed to delete feedback entry: {operation.error}" + ) + return operation + + +class AsyncFeedbackEntries(_api_module.BaseModule): + + async def _create( + self, + *, + config: Optional[types.CreateAgentEngineFeedbackEntryConfigOrDict] = None, + session_id: str, + event_id: str, + feedback_type: types.FeedbackType, + name: str, + ) -> types.AgentEngineFeedbackEntryOperation: + parameter_model = types._CreateAgentEngineFeedbackEntryRequestParameters( + config=config, + session_id=session_id, + event_id=event_id, + feedback_type=feedback_type, + name=name, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _CreateAgentEngineFeedbackEntryRequestParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{parent}/feedbackEntries".format_map(request_url_dict) + else: + path = "{parent}/feedbackEntries" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = await self._api_client.async_request( + "post", path, request_dict, http_options + ) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.AgentEngineFeedbackEntryOperation._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + async def _delete( + self, + *, + name: str, + config: Optional[types.DeleteAgentEngineFeedbackEntryConfigOrDict] = None, + ) -> types.DeleteAgentEngineFeedbackEntryOperation: + parameter_model = types._DeleteAgentEngineFeedbackEntryRequestParameters( + name=name, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _DeleteAgentEngineFeedbackEntryRequestParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{name}".format_map(request_url_dict) + else: + path = "{name}" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = await self._api_client.async_request( + "delete", path, request_dict, http_options + ) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.DeleteAgentEngineFeedbackEntryOperation._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + async def get( + self, + *, + name: str, + config: Optional[types.GetAgentEngineFeedbackConfigOrDict] = None, + ) -> types.FeedbackEntry: + """Gets an agent engine feedback entry. + + Args: + name (str): Required. The name of the feedback entry to retrieve. + Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}/feedbackEntries/{feedback_entry_id}`. + config (GetAgentEngineFeedbackConfig): Optional. The configuration for + getting the feedback entry. + + Returns: + FeedbackEntry: The requested feedback entry. + """ + + parameter_model = types._GetAgentEngineFeedbackRequestParameters( + name=name, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _GetAgentEngineFeedbackRequestParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{name}".format_map(request_url_dict) + else: + path = "{name}" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = await self._api_client.async_request( + "get", path, request_dict, http_options + ) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.FeedbackEntry._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + async def _list( + self, + *, + parent: str, + config: Optional[types.ListAgentEngineFeedbackEntriesConfigOrDict] = None, + ) -> types.ListAgentEngineFeedbackEntriesResponse: + parameter_model = types._ListAgentEngineFeedbackEntriesRequestParameters( + parent=parent, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _ListAgentEngineFeedbackEntriesRequestParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{parent}/feedbackEntries".format_map(request_url_dict) + else: + path = "{parent}/feedbackEntries" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = await self._api_client.async_request( + "get", path, request_dict, http_options + ) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.ListAgentEngineFeedbackEntriesResponse._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + async def _update( + self, + *, + name: str, + config: Optional[types.UpdateAgentEngineFeedbackEntryConfigOrDict] = None, + ) -> types.AgentEngineFeedbackEntryOperation: + """Updates a Feedback Entry. + + Args: + name (str): Required. Name of the feedback entry to update. Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}/feedbackEntries/{feedback_entry_id}`. + feedback_type (shared.FeedbackType): Optional. The type of feedback + provided. + config (UpdateAgentEngineFeedbackEntryConfig): Optional. The + configuration for updating the feedback entry. + + Returns: + AgentEngineFeedbackEntryOperation: Operation for updating a Feedback + Entry. + """ + + parameter_model = types._UpdateAgentEngineFeedbackEntryRequestParameters( + name=name, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _UpdateAgentEngineFeedbackEntryRequestParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{name}".format_map(request_url_dict) + else: + path = "{name}" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = await self._api_client.async_request( + "patch", path, request_dict, http_options + ) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.AgentEngineFeedbackEntryOperation._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + async def _get_feedback_entry_operation( + self, + *, + operation_name: str, + config: Optional[types.GetAgentEngineOperationConfigOrDict] = None, + ) -> types.AgentEngineFeedbackEntryOperation: + parameter_model = types._GetAgentEngineFeedbackOperationParameters( + operation_name=operation_name, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _GetAgentEngineFeedbackOperationParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{operationName}".format_map(request_url_dict) + else: + path = "{operationName}" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = await self._api_client.async_request( + "get", path, request_dict, http_options + ) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.AgentEngineFeedbackEntryOperation._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + async def _get_delete_feedback_entry_operation( + self, + *, + operation_name: str, + config: Optional[types.GetAgentEngineOperationConfigOrDict] = None, + ) -> types.DeleteAgentEngineFeedbackEntryOperation: + parameter_model = types._GetAgentEngineFeedbackOperationParameters( + operation_name=operation_name, + config=config, + ) + + request_url_dict: Optional[dict[str, str]] + if not self._api_client.vertexai: + raise ValueError( + "This method is only supported in Gemini Enterprise Agent Platform" + " mode, not in Gemini Developer API mode." + ) + else: + request_dict = _GetAgentEngineFeedbackOperationParameters_to_vertex( + parameter_model + ) + request_url_dict = request_dict.get("_url") + if request_url_dict: + path = "{operationName}".format_map(request_url_dict) + else: + path = "{operationName}" + + query_params = request_dict.get("_query") + if query_params: + path = f"{path}?{urlencode(query_params)}" + # TODO: remove the hack that pops config. + request_dict.pop("config", None) + + http_options: Optional[types.HttpOptions] = None + if ( + parameter_model.config is not None + and parameter_model.config.http_options is not None + ): + http_options = parameter_model.config.http_options + + request_dict = _common.convert_to_dict(request_dict) + request_dict = _common.encode_unserializable_types(request_dict) + + response = await self._api_client.async_request( + "get", path, request_dict, http_options + ) + + response_dict = {} if not response.body else json.loads(response.body) + + return_value = types.DeleteAgentEngineFeedbackEntryOperation._from_response( + response=response_dict, + kwargs=( + { + "config": { + "response_schema": getattr( + parameter_model.config, "response_schema", None + ), + "response_json_schema": getattr( + parameter_model.config, "response_json_schema", None + ), + "include_all_fields": getattr( + parameter_model.config, "include_all_fields", None + ), + } + } + if getattr(parameter_model, "config", None) + else {} + ), + ) + + self._api_client._verify_response(return_value) + return return_value + + async def create( + self, + *, + name: str, + feedback_type: types.FeedbackType, + session_id: str, + event_id: str, + config: Optional[types.CreateAgentEngineFeedbackEntryConfigOrDict] = None, + ) -> types.AgentEngineFeedbackEntryOperation: + """Creates a new feedback entry in the Agent Engine. + + Args: + name (str): Required. Resource name of the reasoning engine to create + the feedback entry in. Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}`. + feedback_type (FeedbackType): Required. The type of feedback provided. + session_id (str): Required. The ID of the session to which the feedback + relates to. + event_id (str): Required. The ID of the event to which the feedback + relates to. + config (CreateAgentEngineFeedbackEntryConfig): Optional. Additional + configurations for creating the feedback entry. + + Returns: + AgentEngineFeedbackEntryOperation: The operation for creating the + feedback entry. + """ + if config is None: + config = types.CreateAgentEngineFeedbackEntryConfig() + elif isinstance(config, dict): + config = types.CreateAgentEngineFeedbackEntryConfig.model_validate(config) + operation = await self._create( + name=name, + session_id=session_id, + event_id=event_id, + feedback_type=feedback_type, + config=config, + ) + if config.wait_for_completion: + if not operation.done: + operation = await _agent_engines_utils._await_async_operation( + operation_name=operation.name, + get_operation_fn=self._get_feedback_entry_operation, + poll_interval_seconds=0.5, + ) + if operation.error: + raise RuntimeError( + f"Failed to create feedback entry: {operation.error}" + ) + return operation + + async def list( + self, + *, + parent: str, + config: Optional[types.ListAgentEngineFeedbackEntriesConfigOrDict] = None, + ) -> AsyncIterator[types.FeedbackEntry]: + """ + Lists feedback entries in the Agent Engine. + + Args: + parent (str): Required. Resource name of the agent engine to list the + feedback entries from. Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}`. + config (ListAgentEngineFeedbackEntriesConfigOrDict): + Optional. The configuration for listing feedback entries. + + Returns: + AsyncPager[FeedbackEntry]: An async pager of feedback entries. + """, + + return AsyncPager( + "feedback_entries", + functools.partial(self._list, parent=parent), + await self._list(parent=parent, config=config), + config, + ) + + async def update( + self, + *, + name: str, + config: Optional[types.UpdateAgentEngineFeedbackEntryConfigOrDict] = None, + ) -> types.AgentEngineFeedbackEntryOperation: + """Updates a feedback entry in the Agent Engine. + + Args: + name (str): Required. Name of the Feedback Entry. Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}/feedbackEntries/{feedback_entry_id}`. + feedback_type (str): Optional. The type of feedback provided. + config (UpdateAgentEngineFeedbackEntryConfig): Optional. Additional + configurations for updating the feedback entry. + + Returns: + AgentEngineFeedbackEntryOperation: The operation for updating + the feedback entry. + """ + if config is None: + config = types.UpdateAgentEngineFeedbackEntryConfig() + elif isinstance(config, dict): + config = types.UpdateAgentEngineFeedbackEntryConfig.model_validate(config) + operation = await self._update( + name=name, + config=config, + ) + if config.wait_for_completion: + if not operation.done: + operation = await _agent_engines_utils._await_async_operation( + operation_name=operation.name, + get_operation_fn=self._get_feedback_entry_operation, + poll_interval_seconds=0.5, + ) + if operation.error: + raise RuntimeError("Error updating feedback entry.") + return operation + + async def delete( + self, + *, + name: str, + config: Optional[types.DeleteAgentEngineFeedbackEntryConfigOrDict] = None, + ) -> types.DeleteAgentEngineFeedbackEntryOperation: + """Deletes a feedback entry from the Agent Engine. + + Args: + name (str): Required. Name of the Feedback Entry to delete. Format: + `projects/{project}/locations/{location}/reasoningEngines/{resource_id}/feedbackEntries/{feedback_entry_id}`. + config (DeleteAgentEngineFeedbackEntryConfig): Optional. The + configuration for deleting the Feedback Entry. + + Returns: + DeleteAgentEngineFeedbackEntryOperation: The operation for deleting + the Feedback Entry. + """ + if config is None: + config = types.DeleteAgentEngineFeedbackEntryConfig() + elif isinstance(config, dict): + config = types.DeleteAgentEngineFeedbackEntryConfig.model_validate(config) + operation = await self._delete( + name=name, + config=config, + ) + if config.wait_for_completion: + if not operation.done: + operation = await _agent_engines_utils._await_async_operation( + operation_name=operation.name, + get_operation_fn=self._get_delete_feedback_entry_operation, + poll_interval_seconds=0.5, + ) + if operation.error: + raise RuntimeError( + f"Failed to delete feedback entry: {operation.error}" + ) + return operation diff --git a/agentplatform/_genai/types/__init__.py b/agentplatform/_genai/types/__init__.py index 8f4787820f..d6bf2b150e 100644 --- a/agentplatform/_genai/types/__init__.py +++ b/agentplatform/_genai/types/__init__.py @@ -29,6 +29,7 @@ from .common import _AssessDatasetParameters from .common import _CancelQueryJobAgentEngineRequestParameters from .common import _CheckQueryJobAgentEngineRequestParameters +from .common import _CreateAgentEngineFeedbackEntryRequestParameters from .common import _CreateAgentEngineMemoryRequestParameters from .common import _CreateAgentEngineRequestParameters from .common import _CreateAgentEngineSandboxRequestParameters @@ -47,6 +48,7 @@ from .common import _CreateSkillRequestParameters from .common import _CustomJobParameters from .common import _CustomJobParameters +from .common import _DeleteAgentEngineFeedbackEntryRequestParameters from .common import _DeleteAgentEngineMemoryRequestParameters from .common import _DeleteAgentEngineRequestParameters from .common import _DeleteAgentEngineRuntimeRevisionRequestParameters @@ -68,6 +70,9 @@ from .common import _GenerateInstanceRubricsRequest from .common import _GenerateLossClustersParameters from .common import _GenerateUserScenariosParameters +from .common import _GetAgentEngineFeedbackOperationParameters +from .common import _GetAgentEngineFeedbackOperationParameters +from .common import _GetAgentEngineFeedbackRequestParameters from .common import _GetAgentEngineGenerateMemoriesOperationParameters from .common import _GetAgentEngineMemoryOperationParameters from .common import _GetAgentEngineMemoryRequestParameters @@ -104,6 +109,7 @@ from .common import _GetSkillRequestParameters from .common import _GetSkillRevisionRequestParameters from .common import _IngestEventsRequestParameters +from .common import _ListAgentEngineFeedbackEntriesRequestParameters from .common import _ListAgentEngineMemoryRequestParameters from .common import _ListAgentEngineMemoryRevisionsRequestParameters from .common import _ListAgentEngineRequestParameters @@ -138,6 +144,7 @@ from .common import _RunQueryJobAgentEngineConfigDict from .common import _RunQueryJobAgentEngineConfigOrDict from .common import _RunQueryJobAgentEngineRequestParameters +from .common import _UpdateAgentEngineFeedbackEntryRequestParameters from .common import _UpdateAgentEngineMemoryRequestParameters from .common import _UpdateAgentEngineRequestParameters from .common import _UpdateAgentEngineSessionRequestParameters @@ -156,6 +163,9 @@ from .common import AgentEngineConfigDict from .common import AgentEngineConfigOrDict from .common import AgentEngineDict +from .common import AgentEngineFeedbackEntryOperation +from .common import AgentEngineFeedbackEntryOperationDict +from .common import AgentEngineFeedbackEntryOperationOrDict from .common import AgentEngineGenerateMemoriesOperation from .common import AgentEngineGenerateMemoriesOperationDict from .common import AgentEngineGenerateMemoriesOperationOrDict @@ -302,6 +312,9 @@ from .common import CreateAgentEngineConfig from .common import CreateAgentEngineConfigDict from .common import CreateAgentEngineConfigOrDict +from .common import CreateAgentEngineFeedbackEntryConfig +from .common import CreateAgentEngineFeedbackEntryConfigDict +from .common import CreateAgentEngineFeedbackEntryConfigOrDict from .common import CreateAgentEngineSandboxConfig from .common import CreateAgentEngineSandboxConfigDict from .common import CreateAgentEngineSandboxConfigOrDict @@ -375,6 +388,12 @@ from .common import DeleteAgentEngineConfig from .common import DeleteAgentEngineConfigDict from .common import DeleteAgentEngineConfigOrDict +from .common import DeleteAgentEngineFeedbackEntryConfig +from .common import DeleteAgentEngineFeedbackEntryConfigDict +from .common import DeleteAgentEngineFeedbackEntryConfigOrDict +from .common import DeleteAgentEngineFeedbackEntryOperation +from .common import DeleteAgentEngineFeedbackEntryOperationDict +from .common import DeleteAgentEngineFeedbackEntryOperationOrDict from .common import DeleteAgentEngineMemoryConfig from .common import DeleteAgentEngineMemoryConfigDict from .common import DeleteAgentEngineMemoryConfigOrDict @@ -580,6 +599,10 @@ from .common import FailedRubric from .common import FailedRubricDict from .common import FailedRubricOrDict +from .common import FeedbackEntry +from .common import FeedbackEntryDict +from .common import FeedbackEntryOrDict +from .common import FeedbackType from .common import FileStatus from .common import FileStatusDict from .common import FileStatusOrDict @@ -642,6 +665,9 @@ from .common import GetAgentEngineConfig from .common import GetAgentEngineConfigDict from .common import GetAgentEngineConfigOrDict +from .common import GetAgentEngineFeedbackConfig +from .common import GetAgentEngineFeedbackConfigDict +from .common import GetAgentEngineFeedbackConfigOrDict from .common import GetAgentEngineMemoryConfig from .common import GetAgentEngineMemoryConfigDict from .common import GetAgentEngineMemoryConfigOrDict @@ -751,6 +777,12 @@ from .common import ListAgentEngineConfig from .common import ListAgentEngineConfigDict from .common import ListAgentEngineConfigOrDict +from .common import ListAgentEngineFeedbackEntriesConfig +from .common import ListAgentEngineFeedbackEntriesConfigDict +from .common import ListAgentEngineFeedbackEntriesConfigOrDict +from .common import ListAgentEngineFeedbackEntriesResponse +from .common import ListAgentEngineFeedbackEntriesResponseDict +from .common import ListAgentEngineFeedbackEntriesResponseOrDict from .common import ListAgentEngineMemoryConfig from .common import ListAgentEngineMemoryConfigDict from .common import ListAgentEngineMemoryConfigOrDict @@ -1679,6 +1711,9 @@ from .common import UpdateAgentEngineConfig from .common import UpdateAgentEngineConfigDict from .common import UpdateAgentEngineConfigOrDict +from .common import UpdateAgentEngineFeedbackEntryConfig +from .common import UpdateAgentEngineFeedbackEntryConfigDict +from .common import UpdateAgentEngineFeedbackEntryConfigOrDict from .common import UpdateAgentEngineMemoryConfig from .common import UpdateAgentEngineMemoryConfigDict from .common import UpdateAgentEngineMemoryConfigOrDict @@ -3130,6 +3165,33 @@ "ListSkillRevisionsResponse", "ListSkillRevisionsResponseDict", "ListSkillRevisionsResponseOrDict", + "CreateAgentEngineFeedbackEntryConfig", + "CreateAgentEngineFeedbackEntryConfigDict", + "CreateAgentEngineFeedbackEntryConfigOrDict", + "FeedbackEntry", + "FeedbackEntryDict", + "FeedbackEntryOrDict", + "AgentEngineFeedbackEntryOperation", + "AgentEngineFeedbackEntryOperationDict", + "AgentEngineFeedbackEntryOperationOrDict", + "DeleteAgentEngineFeedbackEntryConfig", + "DeleteAgentEngineFeedbackEntryConfigDict", + "DeleteAgentEngineFeedbackEntryConfigOrDict", + "DeleteAgentEngineFeedbackEntryOperation", + "DeleteAgentEngineFeedbackEntryOperationDict", + "DeleteAgentEngineFeedbackEntryOperationOrDict", + "GetAgentEngineFeedbackConfig", + "GetAgentEngineFeedbackConfigDict", + "GetAgentEngineFeedbackConfigOrDict", + "ListAgentEngineFeedbackEntriesConfig", + "ListAgentEngineFeedbackEntriesConfigDict", + "ListAgentEngineFeedbackEntriesConfigOrDict", + "ListAgentEngineFeedbackEntriesResponse", + "ListAgentEngineFeedbackEntriesResponseDict", + "ListAgentEngineFeedbackEntriesResponseOrDict", + "UpdateAgentEngineFeedbackEntryConfig", + "UpdateAgentEngineFeedbackEntryConfigDict", + "UpdateAgentEngineFeedbackEntryConfigOrDict", "PromptOptimizerConfig", "PromptOptimizerConfigDict", "PromptOptimizerConfigOrDict", @@ -3242,6 +3304,7 @@ "Framework", "SkillState", "SkillSource", + "FeedbackType", "EvaluationItemType", "SamplingMethod", "EvaluationRunState", @@ -3394,6 +3457,13 @@ "_GetSkillOperationParameters", "_GetSkillRevisionRequestParameters", "_ListSkillRevisionsRequestParameters", + "_CreateAgentEngineFeedbackEntryRequestParameters", + "_DeleteAgentEngineFeedbackEntryRequestParameters", + "_GetAgentEngineFeedbackRequestParameters", + "_ListAgentEngineFeedbackEntriesRequestParameters", + "_UpdateAgentEngineFeedbackEntryRequestParameters", + "_GetAgentEngineFeedbackOperationParameters", + "_GetAgentEngineFeedbackOperationParameters", "evals", "agent_engines", "prompts", diff --git a/agentplatform/_genai/types/common.py b/agentplatform/_genai/types/common.py index e5638c98ef..9fb2194b44 100644 --- a/agentplatform/_genai/types/common.py +++ b/agentplatform/_genai/types/common.py @@ -421,14 +421,25 @@ class SkillState(_common.CaseInSensitiveEnum): class SkillSource(_common.CaseInSensitiveEnum): - """Output only. The source of the Skill.""" + """Output only. The source of the Skill.""" + + SKILL_SOURCE_UNSPECIFIED = "SKILL_SOURCE_UNSPECIFIED" + """The skill source is unspecified.""" + USER = "USER" + """The skill is created by a user.""" + SYSTEM = "SYSTEM" + """The skill is a system skill.""" + - SKILL_SOURCE_UNSPECIFIED = "SKILL_SOURCE_UNSPECIFIED" - """The skill source is unspecified.""" - USER = "USER" - """The skill is created by a user.""" - SYSTEM = "SYSTEM" - """The skill is a system skill.""" +class FeedbackType(_common.CaseInSensitiveEnum): + """The type of the feedback.""" + + FEEDBACK_TYPE_UNSPECIFIED = "FEEDBACK_TYPE_UNSPECIFIED" + """Default value.""" + THUMBS_UP = "THUMBS_UP" + """Indicates positive feedback (e.g., a "thumbs up").""" + THUMBS_DOWN = "THUMBS_DOWN" + """Indicates a thumbs down feedback (e.g., a "thumbs down").""" class EvaluationItemType(_common.CaseInSensitiveEnum): @@ -21635,6 +21646,654 @@ class ListSkillRevisionsResponseDict(TypedDict, total=False): ] +class CreateAgentEngineFeedbackEntryConfig(_common.BaseModel): + """Config for creating a Feedback Entry.""" + + http_options: Optional[genai_types.HttpOptions] = Field( + default=None, description="""Used to override HTTP request options.""" + ) + feedback_labels: Optional[list[str]] = Field( + default=None, + description="""Specific labels for feedback (non-factual, offensive, etc.).""", + ) + feedback_text: Optional[str] = Field( + default=None, + description="""Qualitative free-form comments provided by the user.""", + ) + user_id: Optional[str] = Field( + default=None, description="""User provided identifier.""" + ) + source: Optional[str] = Field( + default=None, + description="""Originating UI surface (e.g. 'ADK Web UI').""", + ) + custom_metadata: Optional[dict[str, str]] = Field( + default=None, + description=""" Additional key-value metadata associated with the feedback. Allows the collect data for which there is no dedicated field in the resource, ex. version, LLM temperature etc.""", + ) + wait_for_completion: Optional[bool] = Field( + default=True, + description="""Waits for the operation to complete before returning.""", + ) + + +class CreateAgentEngineFeedbackEntryConfigDict(TypedDict, total=False): + """Config for creating a Feedback Entry.""" + + http_options: Optional[genai_types.HttpOptionsDict] + """Used to override HTTP request options.""" + + feedback_labels: Optional[list[str]] + """Specific labels for feedback (non-factual, offensive, etc.).""" + + feedback_text: Optional[str] + """Qualitative free-form comments provided by the user.""" + + user_id: Optional[str] + """User provided identifier.""" + + source: Optional[str] + """Originating UI surface (e.g. 'ADK Web UI').""" + + custom_metadata: Optional[dict[str, str]] + """ Additional key-value metadata associated with the feedback. Allows the collect data for which there is no dedicated field in the resource, ex. version, LLM temperature etc.""" + + wait_for_completion: Optional[bool] + """Waits for the operation to complete before returning.""" + + +CreateAgentEngineFeedbackEntryConfigOrDict = Union[ + CreateAgentEngineFeedbackEntryConfig, + CreateAgentEngineFeedbackEntryConfigDict, +] + + +class _CreateAgentEngineFeedbackEntryRequestParameters(_common.BaseModel): + """Parameters for creating a Feedback Entry.""" + + config: Optional[CreateAgentEngineFeedbackEntryConfig] = Field( + default=None, description="""""" + ) + session_id: Optional[str] = Field( + default=None, + description="""The ID of the session to which the feedback relates to.""", + ) + event_id: Optional[str] = Field( + default=None, + description="""The ID of the event to which the feedback relates to.""", + ) + feedback_type: Optional[FeedbackType] = Field( + default=None, description="""The type of feedback provided.""" + ) + name: Optional[str] = Field( + default=None, + description="""Resource name of the reasoning engine to create the feedback entry in.""", + ) + + +class _CreateAgentEngineFeedbackEntryRequestParametersDict( + TypedDict, total=False +): + """Parameters for creating a Feedback Entry.""" + + config: Optional[CreateAgentEngineFeedbackEntryConfigDict] + """""" + + session_id: Optional[str] + """The ID of the session to which the feedback relates to.""" + + event_id: Optional[str] + """The ID of the event to which the feedback relates to.""" + + feedback_type: Optional[FeedbackType] + """The type of feedback provided.""" + + name: Optional[str] + """Resource name of the reasoning engine to create the feedback entry in.""" + + +_CreateAgentEngineFeedbackEntryRequestParametersOrDict = Union[ + _CreateAgentEngineFeedbackEntryRequestParameters, + _CreateAgentEngineFeedbackEntryRequestParametersDict, +] + + +class FeedbackEntry(_common.BaseModel): + """A feedback entry.""" + + create_time: Optional[datetime.datetime] = Field( + default=None, + description="""Output only. Timestamp when the feedback entry was created.""", + ) + custom_metadata: Optional[dict[str, str]] = Field( + default=None, + description="""Optional. Additional key-value metadata associated with the feedback. Allows the collect data for which there is no dedicated field in the resource, ex. version, LLM temperature etc.""", + ) + event_id: Optional[str] = Field( + default=None, + description="""Required. The ID of the event to which the feedback relates to.""", + ) + feedback_labels: Optional[list[str]] = Field( + default=None, + description="""Optional. Specific labels for feedback (non-factual, offensive, etc.).""", + ) + feedback_text: Optional[str] = Field( + default=None, + description="""Optional. Qualitative free-form comments provided by the user.""", + ) + feedback_type: Optional[FeedbackType] = Field( + default=None, description="""Required. The type of feedback provided.""" + ) + name: Optional[str] = Field( + default=None, + description="""Identifier. The resource name of the feedback entry. Format: 'projects/{project}/locations/{location}/reasoningEngines/{reasoning_engine}/feedbackEntries/{feedback_entry}'.""", + ) + session_id: Optional[str] = Field( + default=None, + description="""Required. The ID of the session to which the feedback relates to.""", + ) + source: Optional[str] = Field( + default=None, + description="""Optional. Originating UI surface (e.g. 'ADK Web UI').""", + ) + update_time: Optional[datetime.datetime] = Field( + default=None, + description="""Output only. Timestamp when the feedback entry was last updated.""", + ) + user_id: Optional[str] = Field( + default=None, description="""Optional. User provided identifier.""" + ) + + +class FeedbackEntryDict(TypedDict, total=False): + """A feedback entry.""" + + create_time: Optional[datetime.datetime] + """Output only. Timestamp when the feedback entry was created.""" + + custom_metadata: Optional[dict[str, str]] + """Optional. Additional key-value metadata associated with the feedback. Allows the collect data for which there is no dedicated field in the resource, ex. version, LLM temperature etc.""" + + event_id: Optional[str] + """Required. The ID of the event to which the feedback relates to.""" + + feedback_labels: Optional[list[str]] + """Optional. Specific labels for feedback (non-factual, offensive, etc.).""" + + feedback_text: Optional[str] + """Optional. Qualitative free-form comments provided by the user.""" + + feedback_type: Optional[FeedbackType] + """Required. The type of feedback provided.""" + + name: Optional[str] + """Identifier. The resource name of the feedback entry. Format: 'projects/{project}/locations/{location}/reasoningEngines/{reasoning_engine}/feedbackEntries/{feedback_entry}'.""" + + session_id: Optional[str] + """Required. The ID of the session to which the feedback relates to.""" + + source: Optional[str] + """Optional. Originating UI surface (e.g. 'ADK Web UI').""" + + update_time: Optional[datetime.datetime] + """Output only. Timestamp when the feedback entry was last updated.""" + + user_id: Optional[str] + """Optional. User provided identifier.""" + + +FeedbackEntryOrDict = Union[FeedbackEntry, FeedbackEntryDict] + + +class AgentEngineFeedbackEntryOperation(_common.BaseModel): + """Operation that has an agent engine feedback entry as a response.""" + + name: Optional[str] = Field( + default=None, + description="""The server-assigned name, which is only unique within the same service that originally returns it. If you use the default HTTP mapping, the `name` should be a resource name ending with `operations/{unique_id}`.""", + ) + metadata: Optional[dict[str, Any]] = Field( + default=None, + description="""Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time. Some services might not provide such metadata. Any method that returns a long-running operation should document the metadata type, if any.""", + ) + done: Optional[bool] = Field( + default=None, + description="""If the value is `false`, it means the operation is still in progress. If `true`, the operation is completed, and either `error` or `response` is available.""", + ) + error: Optional[dict[str, Any]] = Field( + default=None, + description="""The error result of the operation in case of failure or cancellation.""", + ) + response: Optional[FeedbackEntry] = Field( + default=None, description="""The Agent Engine Feedback Entry.""" + ) + + +class AgentEngineFeedbackEntryOperationDict(TypedDict, total=False): + """Operation that has an agent engine feedback entry as a response.""" + + name: Optional[str] + """The server-assigned name, which is only unique within the same service that originally returns it. If you use the default HTTP mapping, the `name` should be a resource name ending with `operations/{unique_id}`.""" + + metadata: Optional[dict[str, Any]] + """Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time. Some services might not provide such metadata. Any method that returns a long-running operation should document the metadata type, if any.""" + + done: Optional[bool] + """If the value is `false`, it means the operation is still in progress. If `true`, the operation is completed, and either `error` or `response` is available.""" + + error: Optional[dict[str, Any]] + """The error result of the operation in case of failure or cancellation.""" + + response: Optional[FeedbackEntryDict] + """The Agent Engine Feedback Entry.""" + + +AgentEngineFeedbackEntryOperationOrDict = Union[ + AgentEngineFeedbackEntryOperation, AgentEngineFeedbackEntryOperationDict +] + + +class DeleteAgentEngineFeedbackEntryConfig(_common.BaseModel): + """Config for deleting a Feedback Entry.""" + + http_options: Optional[genai_types.HttpOptions] = Field( + default=None, description="""Used to override HTTP request options.""" + ) + wait_for_completion: Optional[bool] = Field( + default=True, + description="""Waits for the operation to complete before returning.""", + ) + + +class DeleteAgentEngineFeedbackEntryConfigDict(TypedDict, total=False): + """Config for deleting a Feedback Entry.""" + + http_options: Optional[genai_types.HttpOptionsDict] + """Used to override HTTP request options.""" + + wait_for_completion: Optional[bool] + """Waits for the operation to complete before returning.""" + + +DeleteAgentEngineFeedbackEntryConfigOrDict = Union[ + DeleteAgentEngineFeedbackEntryConfig, + DeleteAgentEngineFeedbackEntryConfigDict, +] + + +class _DeleteAgentEngineFeedbackEntryRequestParameters(_common.BaseModel): + """Parameters for deleting a Feedback Entry.""" + + name: Optional[str] = Field( + default=None, description="""Name of the feedback entry to delete.""" + ) + config: Optional[DeleteAgentEngineFeedbackEntryConfig] = Field( + default=None, description="""""" + ) + + +class _DeleteAgentEngineFeedbackEntryRequestParametersDict( + TypedDict, total=False +): + """Parameters for deleting a Feedback Entry.""" + + name: Optional[str] + """Name of the feedback entry to delete.""" + + config: Optional[DeleteAgentEngineFeedbackEntryConfigDict] + """""" + + +_DeleteAgentEngineFeedbackEntryRequestParametersOrDict = Union[ + _DeleteAgentEngineFeedbackEntryRequestParameters, + _DeleteAgentEngineFeedbackEntryRequestParametersDict, +] + + +class DeleteAgentEngineFeedbackEntryOperation(_common.BaseModel): + """Operation for deleting a Feedback Entry.""" + + name: Optional[str] = Field( + default=None, + description="""The server-assigned name, which is only unique within the same service that originally returns it. If you use the default HTTP mapping, the `name` should be a resource name ending with `operations/{unique_id}`.""", + ) + metadata: Optional[dict[str, Any]] = Field( + default=None, + description="""Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time. Some services might not provide such metadata. Any method that returns a long-running operation should document the metadata type, if any.""", + ) + done: Optional[bool] = Field( + default=None, + description="""If the value is `false`, it means the operation is still in progress. If `true`, the operation is completed, and either `error` or `response` is available.""", + ) + error: Optional[dict[str, Any]] = Field( + default=None, + description="""The error result of the operation in case of failure or cancellation.""", + ) + + +class DeleteAgentEngineFeedbackEntryOperationDict(TypedDict, total=False): + """Operation for deleting a Feedback Entry.""" + + name: Optional[str] + """The server-assigned name, which is only unique within the same service that originally returns it. If you use the default HTTP mapping, the `name` should be a resource name ending with `operations/{unique_id}`.""" + + metadata: Optional[dict[str, Any]] + """Service-specific metadata associated with the operation. It typically contains progress information and common metadata such as create time. Some services might not provide such metadata. Any method that returns a long-running operation should document the metadata type, if any.""" + + done: Optional[bool] + """If the value is `false`, it means the operation is still in progress. If `true`, the operation is completed, and either `error` or `response` is available.""" + + error: Optional[dict[str, Any]] + """The error result of the operation in case of failure or cancellation.""" + + +DeleteAgentEngineFeedbackEntryOperationOrDict = Union[ + DeleteAgentEngineFeedbackEntryOperation, + DeleteAgentEngineFeedbackEntryOperationDict, +] + + +class GetAgentEngineFeedbackConfig(_common.BaseModel): + """Config for getting a Feedback Entry.""" + + http_options: Optional[genai_types.HttpOptions] = Field( + default=None, description="""Used to override HTTP request options.""" + ) + + +class GetAgentEngineFeedbackConfigDict(TypedDict, total=False): + """Config for getting a Feedback Entry.""" + + http_options: Optional[genai_types.HttpOptionsDict] + """Used to override HTTP request options.""" + + +GetAgentEngineFeedbackConfigOrDict = Union[ + GetAgentEngineFeedbackConfig, GetAgentEngineFeedbackConfigDict +] + + +class _GetAgentEngineFeedbackRequestParameters(_common.BaseModel): + """Parameters for getting Agent Engine Feedback.""" + + name: Optional[str] = Field( + default=None, + description="""The name of the feedback entry to retrieve.""", + ) + config: Optional[GetAgentEngineFeedbackConfig] = Field( + default=None, description="""""" + ) + + +class _GetAgentEngineFeedbackRequestParametersDict(TypedDict, total=False): + """Parameters for getting Agent Engine Feedback.""" + + name: Optional[str] + """The name of the feedback entry to retrieve.""" + + config: Optional[GetAgentEngineFeedbackConfigDict] + """""" + + +_GetAgentEngineFeedbackRequestParametersOrDict = Union[ + _GetAgentEngineFeedbackRequestParameters, + _GetAgentEngineFeedbackRequestParametersDict, +] + + +class ListAgentEngineFeedbackEntriesConfig(_common.BaseModel): + """Config for listing feedback entries.""" + + http_options: Optional[genai_types.HttpOptions] = Field( + default=None, description="""Used to override HTTP request options.""" + ) + page_size: Optional[int] = Field(default=None, description="""""") + page_token: Optional[str] = Field(default=None, description="""""") + filter: Optional[str] = Field( + default=None, + description="""An expression for filtering the results of the request.""", + ) + order_by: Optional[str] = Field( + default=None, + description="""A comma-separated list of fields to order by.""", + ) + + +class ListAgentEngineFeedbackEntriesConfigDict(TypedDict, total=False): + """Config for listing feedback entries.""" + + http_options: Optional[genai_types.HttpOptionsDict] + """Used to override HTTP request options.""" + + page_size: Optional[int] + """""" + + page_token: Optional[str] + """""" + + filter: Optional[str] + """An expression for filtering the results of the request.""" + + order_by: Optional[str] + """A comma-separated list of fields to order by.""" + + +ListAgentEngineFeedbackEntriesConfigOrDict = Union[ + ListAgentEngineFeedbackEntriesConfig, + ListAgentEngineFeedbackEntriesConfigDict, +] + + +class _ListAgentEngineFeedbackEntriesRequestParameters(_common.BaseModel): + """Parameters for listing feedback entries.""" + + parent: Optional[str] = Field( + default=None, + description="""Resource name of the reasoning engine to list the feedback entries from.""", + ) + config: Optional[ListAgentEngineFeedbackEntriesConfig] = Field( + default=None, description="""""" + ) + + +class _ListAgentEngineFeedbackEntriesRequestParametersDict( + TypedDict, total=False +): + """Parameters for listing feedback entries.""" + + parent: Optional[str] + """Resource name of the reasoning engine to list the feedback entries from.""" + + config: Optional[ListAgentEngineFeedbackEntriesConfigDict] + """""" + + +_ListAgentEngineFeedbackEntriesRequestParametersOrDict = Union[ + _ListAgentEngineFeedbackEntriesRequestParameters, + _ListAgentEngineFeedbackEntriesRequestParametersDict, +] + + +class ListAgentEngineFeedbackEntriesResponse(_common.BaseModel): + """Response for listing feedback entries.""" + + sdk_http_response: Optional[genai_types.HttpResponse] = Field( + default=None, description="""Used to retain the full HTTP response.""" + ) + next_page_token: Optional[str] = Field(default=None, description="""""") + feedback_entries: Optional[list[FeedbackEntry]] = Field( + default=None, description="""List of feedback entries.""" + ) + + +class ListAgentEngineFeedbackEntriesResponseDict(TypedDict, total=False): + """Response for listing feedback entries.""" + + sdk_http_response: Optional[genai_types.HttpResponseDict] + """Used to retain the full HTTP response.""" + + next_page_token: Optional[str] + """""" + + feedback_entries: Optional[list[FeedbackEntryDict]] + """List of feedback entries.""" + + +ListAgentEngineFeedbackEntriesResponseOrDict = Union[ + ListAgentEngineFeedbackEntriesResponse, + ListAgentEngineFeedbackEntriesResponseDict, +] + + +class UpdateAgentEngineFeedbackEntryConfig(_common.BaseModel): + """Config for updating a Feedback Entry.""" + + http_options: Optional[genai_types.HttpOptions] = Field( + default=None, description="""Used to override HTTP request options.""" + ) + update_mask: Optional[str] = Field( + default=None, + description="""The update mask to apply. For the `FieldMask` definition, see + https://protobuf.dev/reference/protobuf/google.protobuf/#field-mask.""", + ) + feedback_labels: Optional[list[str]] = Field( + default=None, + description="""Specific labels for feedback (non-factual, offensive, etc.).""", + ) + feedback_text: Optional[str] = Field( + default=None, + description="""Qualitative free-form comments provided by the user.""", + ) + user_id: Optional[str] = Field( + default=None, description="""User provided identifier.""" + ) + source: Optional[str] = Field( + default=None, + description="""Originating UI surface (e.g. 'ADK Web UI').""", + ) + custom_metadata: Optional[dict[str, str]] = Field( + default=None, + description=""" Additional key-value metadata associated with the feedback. Allows the collect data for which there is no dedicated field in the resource, ex. version, LLM temperature etc.""", + ) + feedback_type: Optional[FeedbackType] = Field( + default=None, description="""The type of feedback provided.""" + ) + wait_for_completion: Optional[bool] = Field( + default=True, + description="""Waits for the operation to complete before returning.""", + ) + session_id: Optional[str] = Field( + default=None, + description="""The ID of the session to which the feedback relates to.""", + ) + event_id: Optional[str] = Field( + default=None, + description="""The ID of the event to which the feedback relates to.""", + ) + + +class UpdateAgentEngineFeedbackEntryConfigDict(TypedDict, total=False): + """Config for updating a Feedback Entry.""" + + http_options: Optional[genai_types.HttpOptionsDict] + """Used to override HTTP request options.""" + + update_mask: Optional[str] + """The update mask to apply. For the `FieldMask` definition, see + https://protobuf.dev/reference/protobuf/google.protobuf/#field-mask.""" + + feedback_labels: Optional[list[str]] + """Specific labels for feedback (non-factual, offensive, etc.).""" + + feedback_text: Optional[str] + """Qualitative free-form comments provided by the user.""" + + user_id: Optional[str] + """User provided identifier.""" + + source: Optional[str] + """Originating UI surface (e.g. 'ADK Web UI').""" + + custom_metadata: Optional[dict[str, str]] + """ Additional key-value metadata associated with the feedback. Allows the collect data for which there is no dedicated field in the resource, ex. version, LLM temperature etc.""" + + feedback_type: Optional[FeedbackType] + """The type of feedback provided.""" + + wait_for_completion: Optional[bool] + """Waits for the operation to complete before returning.""" + + session_id: Optional[str] + """The ID of the session to which the feedback relates to.""" + + event_id: Optional[str] + """The ID of the event to which the feedback relates to.""" + + +UpdateAgentEngineFeedbackEntryConfigOrDict = Union[ + UpdateAgentEngineFeedbackEntryConfig, + UpdateAgentEngineFeedbackEntryConfigDict, +] + + +class _UpdateAgentEngineFeedbackEntryRequestParameters(_common.BaseModel): + """Parameters for updating a Feedback Entry.""" + + name: Optional[str] = Field( + default=None, description="""Name of the Feedback Entry.""" + ) + config: Optional[UpdateAgentEngineFeedbackEntryConfig] = Field( + default=None, description="""Config for updating a Feedback Entry.""" + ) + + +class _UpdateAgentEngineFeedbackEntryRequestParametersDict( + TypedDict, total=False +): + """Parameters for updating a Feedback Entry.""" + + name: Optional[str] + """Name of the Feedback Entry.""" + + config: Optional[UpdateAgentEngineFeedbackEntryConfigDict] + """Config for updating a Feedback Entry.""" + + +_UpdateAgentEngineFeedbackEntryRequestParametersOrDict = Union[ + _UpdateAgentEngineFeedbackEntryRequestParameters, + _UpdateAgentEngineFeedbackEntryRequestParametersDict, +] + + +class _GetAgentEngineFeedbackOperationParameters(_common.BaseModel): + """Parameters for getting an operation with a feedback entry as a response.""" + + operation_name: Optional[str] = Field( + default=None, + description="""The server-assigned name for the operation.""", + ) + config: Optional[GetAgentEngineOperationConfig] = Field( + default=None, + description="""Used to override the default configuration.""", + ) + + +class _GetAgentEngineFeedbackOperationParametersDict(TypedDict, total=False): + """Parameters for getting an operation with a feedback entry as a response.""" + + operation_name: Optional[str] + """The server-assigned name for the operation.""" + + config: Optional[GetAgentEngineOperationConfigDict] + """Used to override the default configuration.""" + + +_GetAgentEngineFeedbackOperationParametersOrDict = Union[ + _GetAgentEngineFeedbackOperationParameters, + _GetAgentEngineFeedbackOperationParametersDict, +] + + class PromptOptimizerConfig(_common.BaseModel): """VAPO Prompt Optimizer Config.""" diff --git a/tests/unit/agentplatform/genai/replays/test_create_feedback_entry.py b/tests/unit/agentplatform/genai/replays/test_create_feedback_entry.py new file mode 100644 index 0000000000..617e623746 --- /dev/null +++ b/tests/unit/agentplatform/genai/replays/test_create_feedback_entry.py @@ -0,0 +1,118 @@ +# 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. +# +# pylint: disable=protected-access,bad-continuation,missing-function-docstring + +from agentplatform._genai import types +from tests.unit.agentplatform.genai.replays import pytest_helper +from google.genai._api_client import HttpOptions +import pytest + +pytestmark = pytest_helper.setup( + file=__file__, + globals_for_file=globals(), + test_method="feedback_entries.create", + http_options=HttpOptions( + api_version="v1beta1", + ), +) + +pytest_plugins = ("pytest_asyncio",) + + +def test_create(client): + agent_engine = client.agent_engines.create() + assert isinstance(agent_engine, types.AgentEngine) + assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + + try: + operation = client.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_123", + event_id="event_456", + feedback_type=types.FeedbackType.THUMBS_UP, + config=types.CreateAgentEngineFeedbackEntryConfig( + feedback_text="Great response!", + feedback_labels=["incomplete", "inaccurate"], + user_id="user_789", + source="Test IDE", + custom_metadata={"key1": "val1", "key2": "val2"}, + ), + ) + assert isinstance(operation, types.AgentEngineFeedbackEntryOperation) + assert operation.done + + assert operation.response is not None + assert isinstance(operation.response, types.FeedbackEntry) + assert operation.response.feedback_type == types.FeedbackType.THUMBS_UP + assert operation.response.feedback_text == "Great response!" + assert operation.response.session_id == "session_123" + assert operation.response.event_id == "event_456" + assert operation.response.source == "Test IDE" + assert operation.response.user_id == "user_789" + assert operation.response.feedback_labels == ["incomplete", "inaccurate"] + assert operation.response.custom_metadata == { + "key1": "val1", + "key2": "val2", + } + finally: + # Clean up resources. + client.agent_engines.delete( + name=agent_engine.api_resource.name, + force=True, + ) + + +@pytest.mark.asyncio +async def test_create_async(client): + agent_engine = client.agent_engines.create() + assert isinstance(agent_engine, types.AgentEngine) + assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + + try: + operation = await client.aio.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_123", + event_id="event_456", + feedback_type=types.FeedbackType.THUMBS_UP, + config=types.CreateAgentEngineFeedbackEntryConfig( + feedback_text="Great response!", + feedback_labels=["incomplete", "inaccurate"], + user_id="user_789", + source="Test IDE", + custom_metadata={"key1": "val1", "key2": "val2"}, + ), + ) + assert isinstance(operation, types.AgentEngineFeedbackEntryOperation) + assert operation.done + + assert operation.response is not None + assert isinstance(operation.response, types.FeedbackEntry) + assert operation.response.feedback_type == types.FeedbackType.THUMBS_UP + assert operation.response.feedback_text == "Great response!" + assert operation.response.session_id == "session_123" + assert operation.response.event_id == "event_456" + assert operation.response.source == "Test IDE" + assert operation.response.user_id == "user_789" + assert operation.response.feedback_labels == ["incomplete", "inaccurate"] + assert operation.response.custom_metadata == { + "key1": "val1", + "key2": "val2", + } + finally: + # Clean up resources. + await client.aio.agent_engines.delete( + name=agent_engine.api_resource.name, + force=True, + ) diff --git a/tests/unit/agentplatform/genai/replays/test_delete_feedback_entry.py b/tests/unit/agentplatform/genai/replays/test_delete_feedback_entry.py new file mode 100644 index 0000000000..af286b9fa8 --- /dev/null +++ b/tests/unit/agentplatform/genai/replays/test_delete_feedback_entry.py @@ -0,0 +1,98 @@ +# 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. +# +# pylint: disable=protected-access,bad-continuation,missing-function-docstring + +from agentplatform._genai import types +from tests.unit.agentplatform.genai.replays import pytest_helper +from google.genai import errors +from google.genai._api_client import HttpOptions +import pytest + +pytestmark = pytest_helper.setup( + file=__file__, + globals_for_file=globals(), + test_method="feedback_entries.delete", + http_options=HttpOptions( + api_version="v1beta1", + ), +) + +pytest_plugins = ("pytest_asyncio",) + + +def test_delete(client): + agent_engine = client.agent_engines.create() + assert isinstance(agent_engine, types.AgentEngine) + assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + + try: + create_operation = client.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_123", + event_id="event_456", + feedback_type=types.FeedbackType.THUMBS_UP, + ) + assert create_operation.done + + delete_operation = client.feedback_entries.delete( + name=create_operation.response.name + ) + assert isinstance( + delete_operation, + types.DeleteAgentEngineFeedbackEntryOperation, + ) + assert delete_operation.done + + with pytest.raises(errors.ClientError, match="404 NOT_FOUND"): + client.feedback_entries.get(name=create_operation.response.name) + finally: + # Clean up resources. + client.agent_engines.delete( + name=agent_engine.api_resource.name, + force=True, + ) + + +@pytest.mark.asyncio +async def test_delete_async(client): + agent_engine = client.agent_engines.create() + assert isinstance(agent_engine, types.AgentEngine) + assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + + try: + create_operation = await client.aio.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_123", + event_id="event_456", + feedback_type=types.FeedbackType.THUMBS_UP, + ) + assert create_operation.done + + delete_operation = await client.aio.feedback_entries.delete( + name=create_operation.response.name + ) + assert isinstance( + delete_operation, + types.DeleteAgentEngineFeedbackEntryOperation, + ) + + with pytest.raises(errors.ClientError, match="404 NOT_FOUND"): + await client.aio.feedback_entries.get(name=create_operation.response.name) + finally: + # Clean up resources. + await client.aio.agent_engines.delete( + name=agent_engine.api_resource.name, + force=True, + ) diff --git a/tests/unit/agentplatform/genai/replays/test_get_feedback_entry.py b/tests/unit/agentplatform/genai/replays/test_get_feedback_entry.py new file mode 100644 index 0000000000..1d94a8556e --- /dev/null +++ b/tests/unit/agentplatform/genai/replays/test_get_feedback_entry.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. +# +# pylint: disable=protected-access,bad-continuation,missing-function-docstring + +from agentplatform._genai import types +from tests.unit.agentplatform.genai.replays import pytest_helper +from google.genai._api_client import HttpOptions +import pytest + +pytestmark = pytest_helper.setup( + file=__file__, + globals_for_file=globals(), + test_method="feedback_entries.get", + http_options=HttpOptions( + api_version="v1beta1", + ), +) + +pytest_plugins = ("pytest_asyncio",) + + +def test_get(client): + agent_engine = client.agent_engines.create() + assert isinstance(agent_engine, types.AgentEngine) + assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + + try: + operation = client.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_123", + event_id="event_456", + feedback_type=types.FeedbackType.THUMBS_UP, + config=types.CreateAgentEngineFeedbackEntryConfig( + feedback_text="Great response!", + feedback_labels=["incomplete", "inaccurate"], + user_id="user_789", + source="Test IDE", + custom_metadata={"key1": "val1", "key2": "val2"}, + ), + ) + assert isinstance(operation, types.AgentEngineFeedbackEntryOperation) + assert operation.done + + feedback = client.feedback_entries.get(name=operation.response.name) + + assert isinstance(feedback, types.FeedbackEntry) + assert feedback.feedback_type == types.FeedbackType.THUMBS_UP + assert feedback.feedback_text == "Great response!" + assert feedback.session_id == "session_123" + assert feedback.event_id == "event_456" + assert feedback.source == "Test IDE" + assert feedback.user_id == "user_789" + assert feedback.feedback_labels == ["incomplete", "inaccurate"] + assert feedback.custom_metadata == {"key1": "val1", "key2": "val2"} + finally: + # Clean up resources. + client.agent_engines.delete(name=agent_engine.api_resource.name, force=True) + + +@pytest.mark.asyncio +async def test_get_async(client): + agent_engine = client.agent_engines.create() + assert isinstance(agent_engine, types.AgentEngine) + assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + + try: + operation = await client.aio.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_123", + event_id="event_456", + feedback_type=types.FeedbackType.THUMBS_UP, + config=types.CreateAgentEngineFeedbackEntryConfig( + feedback_text="Great response!", + feedback_labels=["incomplete", "inaccurate"], + user_id="user_789", + source="Test IDE", + custom_metadata={"key1": "val1", "key2": "val2"}, + ), + ) + assert isinstance(operation, types.AgentEngineFeedbackEntryOperation) + assert operation.done + + feedback = await client.aio.feedback_entries.get( + name=operation.response.name + ) + + assert isinstance(feedback, types.FeedbackEntry) + assert feedback.feedback_type == types.FeedbackType.THUMBS_UP + assert feedback.feedback_text == "Great response!" + assert feedback.session_id == "session_123" + assert feedback.event_id == "event_456" + assert feedback.source == "Test IDE" + assert feedback.user_id == "user_789" + assert feedback.feedback_labels == ["incomplete", "inaccurate"] + assert feedback.custom_metadata == {"key1": "val1", "key2": "val2"} + finally: + # Clean up resources. + await client.aio.agent_engines.delete( + name=agent_engine.api_resource.name, force=True + ) diff --git a/tests/unit/agentplatform/genai/replays/test_list_feedback_entries.py b/tests/unit/agentplatform/genai/replays/test_list_feedback_entries.py new file mode 100644 index 0000000000..e16a759963 --- /dev/null +++ b/tests/unit/agentplatform/genai/replays/test_list_feedback_entries.py @@ -0,0 +1,194 @@ +# 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. +# +# pylint: disable=protected-access,bad-continuation,missing-function-docstring + +from agentplatform._genai import types +from tests.unit.agentplatform.genai.replays import pytest_helper +from google.genai._api_client import HttpOptions +import pytest + +pytestmark = pytest_helper.setup( + file=__file__, + globals_for_file=globals(), + test_method="feedback_entries.list", + http_options=HttpOptions( + api_version="v1beta1", + ), +) + +pytest_plugins = ("pytest_asyncio",) + + +def test_list(client): + agent_engine = client.agent_engines.create() + assert isinstance(agent_engine, types.AgentEngine) + assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + + try: + # Create THUMBS_UP feedback entry. + operation_up = client.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_123", + event_id="event_456", + feedback_type=types.FeedbackType.THUMBS_UP, + config=types.CreateAgentEngineFeedbackEntryConfig( + feedback_text="Great response!", + feedback_labels=["incomplete", "inaccurate"], + user_id="user_789", + source="Test IDE", + custom_metadata={"key1": "val1", "key2": "val2"}, + ), + ) + assert isinstance(operation_up, types.AgentEngineFeedbackEntryOperation) + assert operation_up.done + + # Create THUMBS_DOWN feedback entry. + operation_down = client.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_abc", + event_id="event_xyz", + feedback_type=types.FeedbackType.THUMBS_DOWN, + config=types.CreateAgentEngineFeedbackEntryConfig( + feedback_text="Response was incorrect.", + feedback_labels=["off_topic", "hallucination"], + user_id="user_789", + source="Test IDE", + custom_metadata={"key_a": "val_a"}, + ), + ) + assert isinstance(operation_down, types.AgentEngineFeedbackEntryOperation) + assert operation_down.done + + # List and verify the feedback entries. + response = client.feedback_entries.list( + parent=agent_engine.api_resource.name + ) + feedback_entries = list(response) + assert len(feedback_entries) == 2 + + thumbs_up_entry = next( + f + for f in feedback_entries + if f.feedback_type == types.FeedbackType.THUMBS_UP + ) + thumbs_down_entry = next( + f + for f in feedback_entries + if f.feedback_type == types.FeedbackType.THUMBS_DOWN + ) + + assert thumbs_up_entry.feedback_text == "Great response!" + assert thumbs_up_entry.user_id == "user_789" + assert thumbs_up_entry.source == "Test IDE" + assert thumbs_up_entry.session_id == "session_123" + assert thumbs_up_entry.event_id == "event_456" + assert thumbs_up_entry.feedback_labels == ["incomplete", "inaccurate"] + assert thumbs_up_entry.custom_metadata == {"key1": "val1", "key2": "val2"} + + assert thumbs_down_entry.feedback_text == "Response was incorrect." + assert thumbs_down_entry.user_id == "user_789" + assert thumbs_down_entry.source == "Test IDE" + assert thumbs_down_entry.session_id == "session_abc" + assert thumbs_down_entry.event_id == "event_xyz" + assert thumbs_down_entry.feedback_labels == ["off_topic", "hallucination"] + assert thumbs_down_entry.custom_metadata == {"key_a": "val_a"} + finally: + # Clean up resources. + client.agent_engines.delete( + name=agent_engine.api_resource.name, + force=True, + ) + + +@pytest.mark.asyncio +async def test_list_async(client): + agent_engine = client.agent_engines.create() + assert isinstance(agent_engine, types.AgentEngine) + assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + + try: + # Create THUMBS_UP feedback entry. + operation_up = await client.aio.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_123", + event_id="event_456", + feedback_type=types.FeedbackType.THUMBS_UP, + config=types.CreateAgentEngineFeedbackEntryConfig( + feedback_text="Great response!", + feedback_labels=["incomplete", "inaccurate"], + user_id="user_789", + source="Test IDE", + custom_metadata={"key1": "val1", "key2": "val2"}, + ), + ) + assert isinstance(operation_up, types.AgentEngineFeedbackEntryOperation) + assert operation_up.done + + # Create THUMBS_DOWN feedback entry. + operation_down = await client.aio.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_abc", + event_id="event_xyz", + feedback_type=types.FeedbackType.THUMBS_DOWN, + config=types.CreateAgentEngineFeedbackEntryConfig( + feedback_text="Response was incorrect.", + feedback_labels=["off_topic", "hallucination"], + user_id="user_789", + source="Test IDE", + custom_metadata={"key_a": "val_a"}, + ), + ) + assert isinstance(operation_down, types.AgentEngineFeedbackEntryOperation) + assert operation_down.done + + # List and verify the feedback entries. + response = await client.aio.feedback_entries.list( + parent=agent_engine.api_resource.name + ) + feedback_entries = [f async for f in response] + assert len(feedback_entries) == 2 + + thumbs_up_entry = next( + f + for f in feedback_entries + if f.feedback_type == types.FeedbackType.THUMBS_UP + ) + thumbs_down_entry = next( + f + for f in feedback_entries + if f.feedback_type == types.FeedbackType.THUMBS_DOWN + ) + + assert thumbs_up_entry.feedback_text == "Great response!" + assert thumbs_up_entry.user_id == "user_789" + assert thumbs_up_entry.source == "Test IDE" + assert thumbs_up_entry.session_id == "session_123" + assert thumbs_up_entry.event_id == "event_456" + assert thumbs_up_entry.feedback_labels == ["incomplete", "inaccurate"] + assert thumbs_up_entry.custom_metadata == {"key1": "val1", "key2": "val2"} + + assert thumbs_down_entry.feedback_text == "Response was incorrect." + assert thumbs_down_entry.user_id == "user_789" + assert thumbs_down_entry.source == "Test IDE" + assert thumbs_down_entry.session_id == "session_abc" + assert thumbs_down_entry.event_id == "event_xyz" + assert thumbs_down_entry.feedback_labels == ["off_topic", "hallucination"] + assert thumbs_down_entry.custom_metadata == {"key_a": "val_a"} + finally: + # Clean up resources. + await client.aio.agent_engines.delete( + name=agent_engine.api_resource.name, + force=True, + ) diff --git a/tests/unit/agentplatform/genai/replays/test_update_feedback_entry.py b/tests/unit/agentplatform/genai/replays/test_update_feedback_entry.py new file mode 100644 index 0000000000..6bf7bccd66 --- /dev/null +++ b/tests/unit/agentplatform/genai/replays/test_update_feedback_entry.py @@ -0,0 +1,216 @@ +# 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. +# +# pylint: disable=protected-access,bad-continuation,missing-function-docstring + +from agentplatform._genai import types +from tests.unit.agentplatform.genai.replays import pytest_helper +from google.genai._api_client import HttpOptions +import pytest + +pytestmark = pytest_helper.setup( + file=__file__, + globals_for_file=globals(), + test_method="feedback_entries.update", + http_options=HttpOptions( + api_version="v1beta1", + ), +) + +pytest_plugins = ("pytest_asyncio",) + + +def test_update(client): + agent_engine = client.agent_engines.create() + assert isinstance(agent_engine, types.AgentEngine) + assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + + try: + operation = client.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_123", + event_id="event_456", + feedback_type=types.FeedbackType.THUMBS_UP, + config=types.CreateAgentEngineFeedbackEntryConfig( + feedback_text="Great response!", + feedback_labels=["incomplete"], + user_id="user_789", + source="Test IDE", + custom_metadata={"key1": "val1", "key2": "val2"}, + ), + ) + assert isinstance(operation, types.AgentEngineFeedbackEntryOperation) + assert operation.done + + assert operation.response is not None + assert isinstance(operation.response, types.FeedbackEntry) + + feedback_entry = operation.response + + update_operation = client.feedback_entries.update( + name=feedback_entry.name, + config=types.UpdateAgentEngineFeedbackEntryConfig( + update_mask="feedbackType,feedbackText,feedbackLabels,userId,source,customMetadata", + feedback_type=types.FeedbackType.THUMBS_DOWN, + feedback_text="Actually bad response!", + feedback_labels=["inaccurate", "too_slow"], + user_id="user_new_999", + source="New Surface", + custom_metadata={"key1": "val1", "key2": "val2"}, + ), + ) + + assert isinstance(update_operation, types.AgentEngineFeedbackEntryOperation) + assert update_operation.done + + updated_feedback = update_operation.response + assert updated_feedback is not None + assert isinstance(updated_feedback, types.FeedbackEntry) + assert updated_feedback.feedback_type == types.FeedbackType.THUMBS_DOWN + assert updated_feedback.feedback_text == "Actually bad response!" + assert updated_feedback.user_id == "user_new_999" + assert updated_feedback.source == "New Surface" + assert updated_feedback.feedback_labels == ["inaccurate", "too_slow"] + assert updated_feedback.custom_metadata == {"key1": "val1", "key2": "val2"} + assert updated_feedback.session_id == "session_123" + assert updated_feedback.event_id == "event_456" + + # Second update: change only session_id and event_id via a scoped mask. + session_update_operation = client.feedback_entries.update( + name=feedback_entry.name, + config=types.UpdateAgentEngineFeedbackEntryConfig( + update_mask="sessionId,eventId", + session_id="session_new_456", + event_id="event_new_789", + ), + ) + + assert isinstance( + session_update_operation, types.AgentEngineFeedbackEntryOperation + ) + assert session_update_operation.done + + session_updated_feedback = session_update_operation.response + assert session_updated_feedback is not None + assert isinstance(session_updated_feedback, types.FeedbackEntry) + assert session_updated_feedback.session_id == "session_new_456" + assert session_updated_feedback.event_id == "event_new_789" + # Fields from the first update are preserved. + assert ( + session_updated_feedback.feedback_type == types.FeedbackType.THUMBS_DOWN + ) + assert session_updated_feedback.feedback_labels == [ + "inaccurate", + "too_slow", + ] + + finally: + # Clean up resources. + client.agent_engines.delete( + name=agent_engine.api_resource.name, + force=True, + ) + + +@pytest.mark.asyncio +async def test_update_async(client): + agent_engine = client.agent_engines.create() + assert isinstance(agent_engine, types.AgentEngine) + assert isinstance(agent_engine.api_resource, types.ReasoningEngine) + + try: + operation = await client.aio.feedback_entries.create( + name=agent_engine.api_resource.name, + session_id="session_123", + event_id="event_456", + feedback_type=types.FeedbackType.THUMBS_UP, + config=types.CreateAgentEngineFeedbackEntryConfig( + feedback_text="Great response!", + feedback_labels=["incomplete"], + user_id="user_789", + source="Test IDE", + custom_metadata={"key1": "val1", "key2": "val2"}, + ), + ) + assert isinstance(operation, types.AgentEngineFeedbackEntryOperation) + assert operation.done + + assert operation.response is not None + assert isinstance(operation.response, types.FeedbackEntry) + + feedback_entry = operation.response + + update_operation = await client.aio.feedback_entries.update( + name=feedback_entry.name, + config=types.UpdateAgentEngineFeedbackEntryConfig( + update_mask="feedbackType,feedbackText,feedbackLabels,userId,source,customMetadata", + feedback_type=types.FeedbackType.THUMBS_DOWN, + feedback_text="Actually bad response!", + feedback_labels=["inaccurate", "too_slow"], + user_id="user_new_999", + source="New Surface", + custom_metadata={"key1": "val1", "key2": "val2"}, + ), + ) + + assert isinstance(update_operation, types.AgentEngineFeedbackEntryOperation) + assert update_operation.done + + updated_feedback = update_operation.response + assert updated_feedback is not None + assert isinstance(updated_feedback, types.FeedbackEntry) + assert updated_feedback.feedback_type == types.FeedbackType.THUMBS_DOWN + assert updated_feedback.feedback_text == "Actually bad response!" + assert updated_feedback.user_id == "user_new_999" + assert updated_feedback.source == "New Surface" + assert updated_feedback.feedback_labels == ["inaccurate", "too_slow"] + assert updated_feedback.custom_metadata == {"key1": "val1", "key2": "val2"} + assert updated_feedback.session_id == "session_123" + assert updated_feedback.event_id == "event_456" + + # Second update: change only session_id and event_id via a scoped mask. + session_update_operation = await client.aio.feedback_entries.update( + name=feedback_entry.name, + config=types.UpdateAgentEngineFeedbackEntryConfig( + update_mask="sessionId,eventId", + session_id="session_new_456", + event_id="event_new_789", + ), + ) + + assert isinstance( + session_update_operation, types.AgentEngineFeedbackEntryOperation + ) + assert session_update_operation.done + + session_updated_feedback = session_update_operation.response + assert session_updated_feedback is not None + assert isinstance(session_updated_feedback, types.FeedbackEntry) + assert session_updated_feedback.session_id == "session_new_456" + assert session_updated_feedback.event_id == "event_new_789" + # Fields from the first update are preserved. + assert ( + session_updated_feedback.feedback_type == types.FeedbackType.THUMBS_DOWN + ) + assert session_updated_feedback.feedback_labels == [ + "inaccurate", + "too_slow", + ] + + finally: + # Clean up resources. + await client.aio.agent_engines.delete( + name=agent_engine.api_resource.name, + force=True, + )