diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index b8edda51c..32b3c4865 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,4 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:2e247c7bf5154df7f98cce087a20ca7605e236340c7d6d1a14447e5c06791bd6 + digest: sha256:9bc5fa3b62b091f60614c08a7fb4fd1d3e1678e326f34dd66ce1eefb5dc3267b +# created: 2023-05-25T14:56:16.294623272Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 66a2172a7..3b8d7ee81 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -419,9 +419,9 @@ readme-renderer==37.3 \ --hash=sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273 \ --hash=sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343 # via twine -requests==2.28.1 \ - --hash=sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983 \ - --hash=sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349 +requests==2.31.0 \ + --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ + --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 # via # gcp-releasetool # google-api-core diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 02bd31d35..8428ab92e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.21.0" + ".": "2.22.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index e5c83085f..1740a0db3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ [1]: https://pypi.org/project/dialogflow/#history +## [2.22.0](https://github.com/googleapis/python-dialogflow/compare/v2.21.0...v2.22.0) (2023-05-25) + + +### Features + +* Add baseline model configuration for conversation summarization ([#636](https://github.com/googleapis/python-dialogflow/issues/636)) ([c0a030e](https://github.com/googleapis/python-dialogflow/commit/c0a030ee32e07b3d3e3bdfe738b4754eda4dbc64)) +* Added debug info for StreamingDetectIntent ([06fea2a](https://github.com/googleapis/python-dialogflow/commit/06fea2a9b5b8f7c4c5ec7dd9881b7f55de9ea149)) +* Added GenerateStatelessSummary method ([06fea2a](https://github.com/googleapis/python-dialogflow/commit/06fea2a9b5b8f7c4c5ec7dd9881b7f55de9ea149)) +* Extended StreamingListCallCompanionEvents timeout to 600 seconds ([06fea2a](https://github.com/googleapis/python-dialogflow/commit/06fea2a9b5b8f7c4c5ec7dd9881b7f55de9ea149)) + ## [2.21.0](https://github.com/googleapis/python-dialogflow/compare/v2.20.0...v2.21.0) (2023-03-23) diff --git a/google/cloud/dialogflow/__init__.py b/google/cloud/dialogflow/__init__.py index f19db2a3f..05927f351 100644 --- a/google/cloud/dialogflow/__init__.py +++ b/google/cloud/dialogflow/__init__.py @@ -147,6 +147,8 @@ Conversation, ConversationPhoneNumber, CreateConversationRequest, + GenerateStatelessSummaryRequest, + GenerateStatelessSummaryResponse, GetConversationRequest, ListConversationsRequest, ListConversationsResponse, @@ -322,6 +324,7 @@ UpdateParticipantRequest, ) from google.cloud.dialogflow_v2.types.session import ( + CloudConversationDebuggingInfo, DetectIntentRequest, DetectIntentResponse, EventInput, @@ -443,6 +446,8 @@ "Conversation", "ConversationPhoneNumber", "CreateConversationRequest", + "GenerateStatelessSummaryRequest", + "GenerateStatelessSummaryResponse", "GetConversationRequest", "ListConversationsRequest", "ListConversationsResponse", @@ -595,6 +600,7 @@ "SuggestSmartRepliesRequest", "SuggestSmartRepliesResponse", "UpdateParticipantRequest", + "CloudConversationDebuggingInfo", "DetectIntentRequest", "DetectIntentResponse", "EventInput", diff --git a/google/cloud/dialogflow/gapic_version.py b/google/cloud/dialogflow/gapic_version.py index e546bae05..03d6d0200 100644 --- a/google/cloud/dialogflow/gapic_version.py +++ b/google/cloud/dialogflow/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.0" # {x-release-please-version} +__version__ = "2.22.0" # {x-release-please-version} diff --git a/google/cloud/dialogflow_v2/__init__.py b/google/cloud/dialogflow_v2/__init__.py index 2afbcb115..9d5ce68a5 100644 --- a/google/cloud/dialogflow_v2/__init__.py +++ b/google/cloud/dialogflow_v2/__init__.py @@ -100,6 +100,8 @@ Conversation, ConversationPhoneNumber, CreateConversationRequest, + GenerateStatelessSummaryRequest, + GenerateStatelessSummaryResponse, GetConversationRequest, ListConversationsRequest, ListConversationsResponse, @@ -273,6 +275,7 @@ UpdateParticipantRequest, ) from .types.session import ( + CloudConversationDebuggingInfo, DetectIntentRequest, DetectIntentResponse, EventInput, @@ -353,6 +356,7 @@ "BatchUpdateIntentsResponse", "ClearSuggestionFeatureConfigOperationMetadata", "ClearSuggestionFeatureConfigRequest", + "CloudConversationDebuggingInfo", "CompleteConversationRequest", "Context", "ContextsClient", @@ -424,6 +428,8 @@ "FulfillmentsClient", "GcsDestination", "GcsSources", + "GenerateStatelessSummaryRequest", + "GenerateStatelessSummaryResponse", "GetAgentRequest", "GetContextRequest", "GetConversationDatasetRequest", diff --git a/google/cloud/dialogflow_v2/gapic_metadata.json b/google/cloud/dialogflow_v2/gapic_metadata.json index 59e9b0fa5..7df043d2d 100644 --- a/google/cloud/dialogflow_v2/gapic_metadata.json +++ b/google/cloud/dialogflow_v2/gapic_metadata.json @@ -704,6 +704,11 @@ "create_conversation" ] }, + "GenerateStatelessSummary": { + "methods": [ + "generate_stateless_summary" + ] + }, "GetConversation": { "methods": [ "get_conversation" @@ -739,6 +744,11 @@ "create_conversation" ] }, + "GenerateStatelessSummary": { + "methods": [ + "generate_stateless_summary" + ] + }, "GetConversation": { "methods": [ "get_conversation" @@ -774,6 +784,11 @@ "create_conversation" ] }, + "GenerateStatelessSummary": { + "methods": [ + "generate_stateless_summary" + ] + }, "GetConversation": { "methods": [ "get_conversation" diff --git a/google/cloud/dialogflow_v2/gapic_version.py b/google/cloud/dialogflow_v2/gapic_version.py index e546bae05..03d6d0200 100644 --- a/google/cloud/dialogflow_v2/gapic_version.py +++ b/google/cloud/dialogflow_v2/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.0" # {x-release-please-version} +__version__ = "2.22.0" # {x-release-please-version} diff --git a/google/cloud/dialogflow_v2/services/conversations/async_client.py b/google/cloud/dialogflow_v2/services/conversations/async_client.py index 51f288674..104f66cb0 100644 --- a/google/cloud/dialogflow_v2/services/conversations/async_client.py +++ b/google/cloud/dialogflow_v2/services/conversations/async_client.py @@ -66,18 +66,36 @@ class ConversationsAsyncClient: DEFAULT_ENDPOINT = ConversationsClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = ConversationsClient.DEFAULT_MTLS_ENDPOINT + agent_path = staticmethod(ConversationsClient.agent_path) + parse_agent_path = staticmethod(ConversationsClient.parse_agent_path) answer_record_path = staticmethod(ConversationsClient.answer_record_path) parse_answer_record_path = staticmethod( ConversationsClient.parse_answer_record_path ) conversation_path = staticmethod(ConversationsClient.conversation_path) parse_conversation_path = staticmethod(ConversationsClient.parse_conversation_path) + conversation_model_path = staticmethod(ConversationsClient.conversation_model_path) + parse_conversation_model_path = staticmethod( + ConversationsClient.parse_conversation_model_path + ) conversation_profile_path = staticmethod( ConversationsClient.conversation_profile_path ) parse_conversation_profile_path = staticmethod( ConversationsClient.parse_conversation_profile_path ) + cx_security_settings_path = staticmethod( + ConversationsClient.cx_security_settings_path + ) + parse_cx_security_settings_path = staticmethod( + ConversationsClient.parse_cx_security_settings_path + ) + document_path = staticmethod(ConversationsClient.document_path) + parse_document_path = staticmethod(ConversationsClient.parse_document_path) + knowledge_base_path = staticmethod(ConversationsClient.knowledge_base_path) + parse_knowledge_base_path = staticmethod( + ConversationsClient.parse_knowledge_base_path + ) message_path = staticmethod(ConversationsClient.message_path) parse_message_path = staticmethod(ConversationsClient.parse_message_path) common_billing_account_path = staticmethod( @@ -939,6 +957,104 @@ async def sample_suggest_conversation_summary(): # Done; return the response. return response + async def generate_stateless_summary( + self, + request: Optional[ + Union[conversation.GenerateStatelessSummaryRequest, dict] + ] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.GenerateStatelessSummaryResponse: + r"""Generates and returns a summary for a conversation + that does not have a resource created for it. + + .. code-block:: python + + # This snippet has been automatically generated and should be regarded as a + # code template only. + # It will require modifications to work: + # - It may require correct/in-range values for request initialization. + # - It may require specifying regional endpoints when creating the service + # client as shown in: + # https://googleapis.dev/python/google-api-core/latest/client_options.html + from google.cloud import dialogflow_v2 + + async def sample_generate_stateless_summary(): + # Create a client + client = dialogflow_v2.ConversationsAsyncClient() + + # Initialize request argument(s) + stateless_conversation = dialogflow_v2.MinimalConversation() + stateless_conversation.messages.content = "content_value" + stateless_conversation.parent = "parent_value" + + conversation_profile = dialogflow_v2.ConversationProfile() + conversation_profile.display_name = "display_name_value" + + request = dialogflow_v2.GenerateStatelessSummaryRequest( + stateless_conversation=stateless_conversation, + conversation_profile=conversation_profile, + ) + + # Make the request + response = await client.generate_stateless_summary(request=request) + + # Handle the response + print(response) + + Args: + request (Optional[Union[google.cloud.dialogflow_v2.types.GenerateStatelessSummaryRequest, dict]]): + The request object. The request message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2.Conversations.GenerateStatelessSummary]. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.GenerateStatelessSummaryResponse: + The response message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2.Conversations.GenerateStatelessSummary]. + + """ + # Create or coerce a protobuf request object. + request = conversation.GenerateStatelessSummaryRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.generate_stateless_summary, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + ( + ( + "stateless_conversation.parent", + request.stateless_conversation.parent, + ), + ) + ), + ) + + # Send the request. + response = await rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Done; return the response. + return response + async def list_operations( self, request: Optional[operations_pb2.ListOperationsRequest] = None, diff --git a/google/cloud/dialogflow_v2/services/conversations/client.py b/google/cloud/dialogflow_v2/services/conversations/client.py index 3c4d75083..c79a2e49d 100644 --- a/google/cloud/dialogflow_v2/services/conversations/client.py +++ b/google/cloud/dialogflow_v2/services/conversations/client.py @@ -183,6 +183,21 @@ def transport(self) -> ConversationsTransport: """ return self._transport + @staticmethod + def agent_path( + project: str, + ) -> str: + """Returns a fully-qualified agent string.""" + return "projects/{project}/agent".format( + project=project, + ) + + @staticmethod + def parse_agent_path(path: str) -> Dict[str, str]: + """Parses a agent path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/agent$", path) + return m.groupdict() if m else {} + @staticmethod def answer_record_path( project: str, @@ -221,6 +236,28 @@ def parse_conversation_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def conversation_model_path( + project: str, + location: str, + conversation_model: str, + ) -> str: + """Returns a fully-qualified conversation_model string.""" + return "projects/{project}/locations/{location}/conversationModels/{conversation_model}".format( + project=project, + location=location, + conversation_model=conversation_model, + ) + + @staticmethod + def parse_conversation_model_path(path: str) -> Dict[str, str]: + """Parses a conversation_model path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/conversationModels/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + @staticmethod def conversation_profile_path( project: str, @@ -241,6 +278,69 @@ def parse_conversation_profile_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def cx_security_settings_path( + project: str, + location: str, + security_settings: str, + ) -> str: + """Returns a fully-qualified cx_security_settings string.""" + return "projects/{project}/locations/{location}/securitySettings/{security_settings}".format( + project=project, + location=location, + security_settings=security_settings, + ) + + @staticmethod + def parse_cx_security_settings_path(path: str) -> Dict[str, str]: + """Parses a cx_security_settings path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/securitySettings/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def document_path( + project: str, + knowledge_base: str, + document: str, + ) -> str: + """Returns a fully-qualified document string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, + knowledge_base=knowledge_base, + document=document, + ) + + @staticmethod + def parse_document_path(path: str) -> Dict[str, str]: + """Parses a document path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?P.+?)/documents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def knowledge_base_path( + project: str, + knowledge_base: str, + ) -> str: + """Returns a fully-qualified knowledge_base string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}".format( + project=project, + knowledge_base=knowledge_base, + ) + + @staticmethod + def parse_knowledge_base_path(path: str) -> Dict[str, str]: + """Parses a knowledge_base path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?P.+?)$", path + ) + return m.groupdict() if m else {} + @staticmethod def message_path( project: str, @@ -1216,6 +1316,107 @@ def sample_suggest_conversation_summary(): # Done; return the response. return response + def generate_stateless_summary( + self, + request: Optional[ + Union[conversation.GenerateStatelessSummaryRequest, dict] + ] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.GenerateStatelessSummaryResponse: + r"""Generates and returns a summary for a conversation + that does not have a resource created for it. + + .. code-block:: python + + # This snippet has been automatically generated and should be regarded as a + # code template only. + # It will require modifications to work: + # - It may require correct/in-range values for request initialization. + # - It may require specifying regional endpoints when creating the service + # client as shown in: + # https://googleapis.dev/python/google-api-core/latest/client_options.html + from google.cloud import dialogflow_v2 + + def sample_generate_stateless_summary(): + # Create a client + client = dialogflow_v2.ConversationsClient() + + # Initialize request argument(s) + stateless_conversation = dialogflow_v2.MinimalConversation() + stateless_conversation.messages.content = "content_value" + stateless_conversation.parent = "parent_value" + + conversation_profile = dialogflow_v2.ConversationProfile() + conversation_profile.display_name = "display_name_value" + + request = dialogflow_v2.GenerateStatelessSummaryRequest( + stateless_conversation=stateless_conversation, + conversation_profile=conversation_profile, + ) + + # Make the request + response = client.generate_stateless_summary(request=request) + + # Handle the response + print(response) + + Args: + request (Union[google.cloud.dialogflow_v2.types.GenerateStatelessSummaryRequest, dict]): + The request object. The request message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2.Conversations.GenerateStatelessSummary]. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.GenerateStatelessSummaryResponse: + The response message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2.Conversations.GenerateStatelessSummary]. + + """ + # Create or coerce a protobuf request object. + # Minor optimization to avoid making a copy if the user passes + # in a conversation.GenerateStatelessSummaryRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.GenerateStatelessSummaryRequest): + request = conversation.GenerateStatelessSummaryRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.generate_stateless_summary + ] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + ( + ( + "stateless_conversation.parent", + request.stateless_conversation.parent, + ), + ) + ), + ) + + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Done; return the response. + return response + def __enter__(self) -> "ConversationsClient": return self diff --git a/google/cloud/dialogflow_v2/services/conversations/transports/base.py b/google/cloud/dialogflow_v2/services/conversations/transports/base.py index f20905fc5..489cbc248 100644 --- a/google/cloud/dialogflow_v2/services/conversations/transports/base.py +++ b/google/cloud/dialogflow_v2/services/conversations/transports/base.py @@ -158,6 +158,11 @@ def _prep_wrapped_messages(self, client_info): default_timeout=None, client_info=client_info, ), + self.generate_stateless_summary: gapic_v1.method.wrap_method( + self.generate_stateless_summary, + default_timeout=None, + client_info=client_info, + ), } def close(self): @@ -232,6 +237,18 @@ def suggest_conversation_summary( ]: raise NotImplementedError() + @property + def generate_stateless_summary( + self, + ) -> Callable[ + [conversation.GenerateStatelessSummaryRequest], + Union[ + conversation.GenerateStatelessSummaryResponse, + Awaitable[conversation.GenerateStatelessSummaryResponse], + ], + ]: + raise NotImplementedError() + @property def list_operations( self, diff --git a/google/cloud/dialogflow_v2/services/conversations/transports/grpc.py b/google/cloud/dialogflow_v2/services/conversations/transports/grpc.py index eae586e4d..478995b50 100644 --- a/google/cloud/dialogflow_v2/services/conversations/transports/grpc.py +++ b/google/cloud/dialogflow_v2/services/conversations/transports/grpc.py @@ -428,6 +428,36 @@ def suggest_conversation_summary( ) return self._stubs["suggest_conversation_summary"] + @property + def generate_stateless_summary( + self, + ) -> Callable[ + [conversation.GenerateStatelessSummaryRequest], + conversation.GenerateStatelessSummaryResponse, + ]: + r"""Return a callable for the generate stateless summary method over gRPC. + + Generates and returns a summary for a conversation + that does not have a resource created for it. + + Returns: + Callable[[~.GenerateStatelessSummaryRequest], + ~.GenerateStatelessSummaryResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "generate_stateless_summary" not in self._stubs: + self._stubs["generate_stateless_summary"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/GenerateStatelessSummary", + request_serializer=conversation.GenerateStatelessSummaryRequest.serialize, + response_deserializer=conversation.GenerateStatelessSummaryResponse.deserialize, + ) + return self._stubs["generate_stateless_summary"] + def close(self): self.grpc_channel.close() diff --git a/google/cloud/dialogflow_v2/services/conversations/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/conversations/transports/grpc_asyncio.py index 46bcb1ead..6dfafa063 100644 --- a/google/cloud/dialogflow_v2/services/conversations/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2/services/conversations/transports/grpc_asyncio.py @@ -435,6 +435,36 @@ def suggest_conversation_summary( ) return self._stubs["suggest_conversation_summary"] + @property + def generate_stateless_summary( + self, + ) -> Callable[ + [conversation.GenerateStatelessSummaryRequest], + Awaitable[conversation.GenerateStatelessSummaryResponse], + ]: + r"""Return a callable for the generate stateless summary method over gRPC. + + Generates and returns a summary for a conversation + that does not have a resource created for it. + + Returns: + Callable[[~.GenerateStatelessSummaryRequest], + Awaitable[~.GenerateStatelessSummaryResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "generate_stateless_summary" not in self._stubs: + self._stubs["generate_stateless_summary"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/GenerateStatelessSummary", + request_serializer=conversation.GenerateStatelessSummaryRequest.serialize, + response_deserializer=conversation.GenerateStatelessSummaryResponse.deserialize, + ) + return self._stubs["generate_stateless_summary"] + def close(self): return self.grpc_channel.close() diff --git a/google/cloud/dialogflow_v2/services/conversations/transports/rest.py b/google/cloud/dialogflow_v2/services/conversations/transports/rest.py index 077977472..4bb1f566d 100644 --- a/google/cloud/dialogflow_v2/services/conversations/transports/rest.py +++ b/google/cloud/dialogflow_v2/services/conversations/transports/rest.py @@ -82,6 +82,14 @@ def post_create_conversation(self, response): logging.log(f"Received response: {response}") return response + def pre_generate_stateless_summary(self, request, metadata): + logging.log(f"Received request: {request}") + return request, metadata + + def post_generate_stateless_summary(self, response): + logging.log(f"Received response: {response}") + return response + def pre_get_conversation(self, request, metadata): logging.log(f"Received request: {request}") return request, metadata @@ -166,6 +174,29 @@ def post_create_conversation( """ return response + def pre_generate_stateless_summary( + self, + request: conversation.GenerateStatelessSummaryRequest, + metadata: Sequence[Tuple[str, str]], + ) -> Tuple[conversation.GenerateStatelessSummaryRequest, Sequence[Tuple[str, str]]]: + """Pre-rpc interceptor for generate_stateless_summary + + Override in a subclass to manipulate the request or metadata + before they are sent to the Conversations server. + """ + return request, metadata + + def post_generate_stateless_summary( + self, response: conversation.GenerateStatelessSummaryResponse + ) -> conversation.GenerateStatelessSummaryResponse: + """Post-rpc interceptor for generate_stateless_summary + + Override in a subclass to manipulate the response + after it is returned by the Conversations server but before + it is returned to user code. + """ + return response + def pre_get_conversation( self, request: conversation.GetConversationRequest, @@ -691,6 +722,112 @@ def __call__( resp = self._interceptor.post_create_conversation(resp) return resp + class _GenerateStatelessSummary(ConversationsRestStub): + def __hash__(self): + return hash("GenerateStatelessSummary") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + def __call__( + self, + request: conversation.GenerateStatelessSummaryRequest, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Optional[float] = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.GenerateStatelessSummaryResponse: + r"""Call the generate stateless + summary method over HTTP. + + Args: + request (~.conversation.GenerateStatelessSummaryRequest): + The request object. The request message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2.Conversations.GenerateStatelessSummary]. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.conversation.GenerateStatelessSummaryResponse: + The response message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2.Conversations.GenerateStatelessSummary]. + + """ + + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v2/{stateless_conversation.parent=projects/*}/suggestions:generateStatelessSummary", + "body": "*", + }, + { + "method": "post", + "uri": "/v2/{stateless_conversation.parent=projects/*/locations/*}/suggestions:generateStatelessSummary", + "body": "*", + }, + ] + request, metadata = self._interceptor.pre_generate_stateless_summary( + request, metadata + ) + pb_request = conversation.GenerateStatelessSummaryRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], + including_default_value_fields=False, + use_integers_for_enums=True, + ) + uri = transcoded_request["uri"] + method = transcoded_request["method"] + + # Jsonify the query params + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + including_default_value_fields=False, + use_integers_for_enums=True, + ) + ) + query_params.update(self._get_unset_required_fields(query_params)) + + query_params["$alt"] = "json;enum-encoding=int" + + # Send the request + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(self._session, method)( + "{host}{uri}".format(host=self._host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + raise core_exceptions.from_http_response(response) + + # Return the response + resp = conversation.GenerateStatelessSummaryResponse() + pb_resp = conversation.GenerateStatelessSummaryResponse.pb(resp) + + json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_generate_stateless_summary(resp) + return resp + class _GetConversation(ConversationsRestStub): def __hash__(self): return hash("GetConversation") @@ -1105,6 +1242,17 @@ def create_conversation( # In C++ this would require a dynamic_cast return self._CreateConversation(self._session, self._host, self._interceptor) # type: ignore + @property + def generate_stateless_summary( + self, + ) -> Callable[ + [conversation.GenerateStatelessSummaryRequest], + conversation.GenerateStatelessSummaryResponse, + ]: + # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here. + # In C++ this would require a dynamic_cast + return self._GenerateStatelessSummary(self._session, self._host, self._interceptor) # type: ignore + @property def get_conversation( self, diff --git a/google/cloud/dialogflow_v2/types/__init__.py b/google/cloud/dialogflow_v2/types/__init__.py index 8dd374de7..d13358d81 100644 --- a/google/cloud/dialogflow_v2/types/__init__.py +++ b/google/cloud/dialogflow_v2/types/__init__.py @@ -66,6 +66,8 @@ Conversation, ConversationPhoneNumber, CreateConversationRequest, + GenerateStatelessSummaryRequest, + GenerateStatelessSummaryResponse, GetConversationRequest, ListConversationsRequest, ListConversationsResponse, @@ -235,6 +237,7 @@ UpdateParticipantRequest, ) from .session import ( + CloudConversationDebuggingInfo, DetectIntentRequest, DetectIntentResponse, EventInput, @@ -315,6 +318,8 @@ "Conversation", "ConversationPhoneNumber", "CreateConversationRequest", + "GenerateStatelessSummaryRequest", + "GenerateStatelessSummaryResponse", "GetConversationRequest", "ListConversationsRequest", "ListConversationsResponse", @@ -467,6 +472,7 @@ "SuggestSmartRepliesRequest", "SuggestSmartRepliesResponse", "UpdateParticipantRequest", + "CloudConversationDebuggingInfo", "DetectIntentRequest", "DetectIntentResponse", "EventInput", diff --git a/google/cloud/dialogflow_v2/types/answer_record.py b/google/cloud/dialogflow_v2/types/answer_record.py index 7a32d8302..62fc4a635 100644 --- a/google/cloud/dialogflow_v2/types/answer_record.py +++ b/google/cloud/dialogflow_v2/types/answer_record.py @@ -222,6 +222,9 @@ class AnswerFeedback(proto.Message): clicked (bool): Indicates whether the answer/item was clicked by the human agent or not. Default to false. + For knowledge search, the answer record is + considered to be clicked if the answer was + copied or any URI was clicked. click_time (google.protobuf.timestamp_pb2.Timestamp): Time when the answer/item was clicked. displayed (bool): diff --git a/google/cloud/dialogflow_v2/types/audio_config.py b/google/cloud/dialogflow_v2/types/audio_config.py index 7299f67b2..b10f6221d 100644 --- a/google/cloud/dialogflow_v2/types/audio_config.py +++ b/google/cloud/dialogflow_v2/types/audio_config.py @@ -419,7 +419,13 @@ class InputAudioConfig(proto.Message): standard version of the specified model. Refer to `Cloud Speech API documentation `__ - for more details. + for more details. If you specify a model, the following + models typically have the best performance: + + - phone_call (best for Agent Assist and telephony) + - latest_short (best for Dialogflow non-telephony) + - command_and_search (best for very short utterances and + commands) model_variant (google.cloud.dialogflow_v2.types.SpeechModelVariant): Which variant of the [Speech model][google.cloud.dialogflow.v2.InputAudioConfig.model] to @@ -442,6 +448,9 @@ class InputAudioConfig(proto.Message): [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent]. If ``false`` and recognition doesn't return any result, trigger ``NO_SPEECH_RECOGNIZED`` event to Dialogflow agent. + enable_automatic_punctuation (bool): + Enable automatic punctuation option at the + speech backend. """ audio_encoding: "AudioEncoding" = proto.Field( @@ -487,6 +496,10 @@ class InputAudioConfig(proto.Message): proto.BOOL, number=14, ) + enable_automatic_punctuation: bool = proto.Field( + proto.BOOL, + number=17, + ) class VoiceSelectionParams(proto.Message): diff --git a/google/cloud/dialogflow_v2/types/conversation.py b/google/cloud/dialogflow_v2/types/conversation.py index b5dd0c6dc..25ce02c72 100644 --- a/google/cloud/dialogflow_v2/types/conversation.py +++ b/google/cloud/dialogflow_v2/types/conversation.py @@ -20,6 +20,9 @@ from google.protobuf import timestamp_pb2 # type: ignore import proto # type: ignore +from google.cloud.dialogflow_v2.types import ( + conversation_profile as gcd_conversation_profile, +) from google.cloud.dialogflow_v2.types import participant __protobuf__ = proto.module( @@ -36,6 +39,8 @@ "ConversationPhoneNumber", "SuggestConversationSummaryRequest", "SuggestConversationSummaryResponse", + "GenerateStatelessSummaryRequest", + "GenerateStatelessSummaryResponse", }, ) @@ -523,4 +528,137 @@ class Summary(proto.Message): ) +class GenerateStatelessSummaryRequest(proto.Message): + r"""The request message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2.Conversations.GenerateStatelessSummary]. + + Attributes: + stateless_conversation (google.cloud.dialogflow_v2.types.GenerateStatelessSummaryRequest.MinimalConversation): + Required. The conversation to suggest a + summary for. + conversation_profile (google.cloud.dialogflow_v2.types.ConversationProfile): + Required. A ConversationProfile containing information + required for Summary generation. Required fields: + {language_code, security_settings} Optional fields: + {agent_assistant_config} + latest_message (str): + The name of the latest conversation message + used as context for generating a Summary. If + empty, the latest message of the conversation + will be used. The format is specific to the user + and the names of the messages provided. + max_context_size (int): + Max number of messages prior to and including + [latest_message] to use as context when compiling the + suggestion. By default 500 and at most 1000. + """ + + class MinimalConversation(proto.Message): + r"""The minimum amount of information required to generate a + Summary without having a Conversation resource created. + + Attributes: + messages (MutableSequence[google.cloud.dialogflow_v2.types.Message]): + Required. The messages that the Summary will be generated + from. It is expected that this message content is already + redacted and does not contain any PII. Required fields: + {content, language_code, participant, participant_role} + Optional fields: {send_time} If send_time is not provided, + then the messages must be provided in chronological order. + parent (str): + Required. The parent resource to charge for the Summary's + generation. Format: + ``projects//locations/``. + """ + + messages: MutableSequence[participant.Message] = proto.RepeatedField( + proto.MESSAGE, + number=1, + message=participant.Message, + ) + parent: str = proto.Field( + proto.STRING, + number=2, + ) + + stateless_conversation: MinimalConversation = proto.Field( + proto.MESSAGE, + number=1, + message=MinimalConversation, + ) + conversation_profile: gcd_conversation_profile.ConversationProfile = proto.Field( + proto.MESSAGE, + number=2, + message=gcd_conversation_profile.ConversationProfile, + ) + latest_message: str = proto.Field( + proto.STRING, + number=3, + ) + max_context_size: int = proto.Field( + proto.INT32, + number=4, + ) + + +class GenerateStatelessSummaryResponse(proto.Message): + r"""The response message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2.Conversations.GenerateStatelessSummary]. + + Attributes: + summary (google.cloud.dialogflow_v2.types.GenerateStatelessSummaryResponse.Summary): + Generated summary. + latest_message (str): + The name of the latest conversation message + used as context for compiling suggestion. The + format is specific to the user and the names of + the messages provided. + context_size (int): + Number of messages prior to and including + [last_conversation_message][] used to compile the + suggestion. It may be smaller than the + [GenerateStatelessSummaryRequest.context_size][] field in + the request if there weren't that many messages in the + conversation. + """ + + class Summary(proto.Message): + r"""Generated summary for a conversation. + + Attributes: + text (str): + The summary content that is concatenated into + one string. + text_sections (MutableMapping[str, str]): + The summary content that is divided into + sections. The key is the section's name and the + value is the section's content. There is no + specific format for the key or value. + """ + + text: str = proto.Field( + proto.STRING, + number=1, + ) + text_sections: MutableMapping[str, str] = proto.MapField( + proto.STRING, + proto.STRING, + number=2, + ) + + summary: Summary = proto.Field( + proto.MESSAGE, + number=1, + message=Summary, + ) + latest_message: str = proto.Field( + proto.STRING, + number=2, + ) + context_size: int = proto.Field( + proto.INT32, + number=3, + ) + + __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/conversation_profile.py b/google/cloud/dialogflow_v2/types/conversation_profile.py index b2e759763..6c98c0e89 100644 --- a/google/cloud/dialogflow_v2/types/conversation_profile.py +++ b/google/cloud/dialogflow_v2/types/conversation_profile.py @@ -671,12 +671,22 @@ class ConversationModelConfig(proto.Message): model (str): Conversation model resource name. Format: ``projects//conversationModels/``. + baseline_model_version (str): + Version of current baseline model. It will be ignored if + [model][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.ConversationModelConfig.model] + is set. Valid versions are: Article Suggestion baseline + model: - 0.9 - 1.0 (default) Summarization baseline model: - + 1.0 """ model: str = proto.Field( proto.STRING, number=1, ) + baseline_model_version: str = proto.Field( + proto.STRING, + number=8, + ) class ConversationProcessConfig(proto.Message): r"""Config to process conversation. diff --git a/google/cloud/dialogflow_v2/types/participant.py b/google/cloud/dialogflow_v2/types/participant.py index d56ed1cd7..0a5d550ae 100644 --- a/google/cloud/dialogflow_v2/types/participant.py +++ b/google/cloud/dialogflow_v2/types/participant.py @@ -694,6 +694,9 @@ class StreamingAnalyzeContentRequest(proto.Message): response even if some ``Fulfillment``\ s in Dialogflow virtual agent have been configured to return partial responses. + enable_debugging_info (bool): + If true, ``StreamingAnalyzeContentResponse.debugging_info`` + will get populated. """ participant: str = proto.Field( @@ -752,6 +755,10 @@ class StreamingAnalyzeContentRequest(proto.Message): proto.BOOL, number=12, ) + enable_debugging_info: bool = proto.Field( + proto.BOOL, + number=19, + ) class StreamingAnalyzeContentResponse(proto.Message): @@ -826,6 +833,10 @@ class StreamingAnalyzeContentResponse(proto.Message): [HumanAgentAssistantConfig.end_user_suggestion_config][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.end_user_suggestion_config]. dtmf_parameters (google.cloud.dialogflow_v2.types.DtmfParameters): Indicates the parameters of DTMF. + debugging_info (google.cloud.dialogflow_v2.types.CloudConversationDebuggingInfo): + Debugging info that would get populated when + ``StreamingAnalyzeContentRequest.enable_debugging_info`` is + set to true. """ recognition_result: session.StreamingRecognitionResult = proto.Field( @@ -871,6 +882,11 @@ class StreamingAnalyzeContentResponse(proto.Message): number=10, message="DtmfParameters", ) + debugging_info: session.CloudConversationDebuggingInfo = proto.Field( + proto.MESSAGE, + number=11, + message=session.CloudConversationDebuggingInfo, + ) class SuggestArticlesRequest(proto.Message): diff --git a/google/cloud/dialogflow_v2/types/session.py b/google/cloud/dialogflow_v2/types/session.py index 7d311be4a..bf4268f50 100644 --- a/google/cloud/dialogflow_v2/types/session.py +++ b/google/cloud/dialogflow_v2/types/session.py @@ -38,6 +38,7 @@ "QueryInput", "QueryResult", "StreamingDetectIntentRequest", + "CloudConversationDebuggingInfo", "StreamingDetectIntentResponse", "StreamingRecognitionResult", "TextInput", @@ -638,6 +639,9 @@ class StreamingDetectIntentRequest(proto.Message): ``query_input`` was set to a streaming input audio config. The complete audio over all streaming messages must not exceed 1 minute. + enable_debugging_info (bool): + if true, ``StreamingDetectIntentResponse.debugging_info`` + will get populated. """ session: str = proto.Field( @@ -672,6 +676,157 @@ class StreamingDetectIntentRequest(proto.Message): proto.BYTES, number=6, ) + enable_debugging_info: bool = proto.Field( + proto.BOOL, + number=8, + ) + + +class CloudConversationDebuggingInfo(proto.Message): + r"""Cloud conversation info for easier debugging. It will get populated + in ``StreamingDetectIntentResponse`` or + ``StreamingAnalyzeContentResponse`` when the flag + ``enable_debugging_info`` is set to true in corresponding requests. + + Attributes: + audio_data_chunks (int): + Number of input audio data chunks in + streaming requests. + result_end_time_offset (google.protobuf.duration_pb2.Duration): + Time offset of the end of speech utterance + relative to the beginning of the first audio + chunk. + first_audio_duration (google.protobuf.duration_pb2.Duration): + Duration of first audio chunk. + single_utterance (bool): + Whether client used single utterance mode. + speech_partial_results_end_times (MutableSequence[google.protobuf.duration_pb2.Duration]): + Time offsets of the speech partial results + relative to the beginning of the stream. + speech_final_results_end_times (MutableSequence[google.protobuf.duration_pb2.Duration]): + Time offsets of the speech final results (is_final=true) + relative to the beginning of the stream. + partial_responses (int): + Total number of partial responses. + speaker_id_passive_latency_ms_offset (int): + Time offset of Speaker ID stream close time + relative to the Speech stream close time in + milliseconds. Only meaningful for conversations + involving passive verification. + bargein_event_triggered (bool): + Whether a barge-in event is triggered in this + request. + speech_single_utterance (bool): + Whether speech uses single utterance mode. + dtmf_partial_results_times (MutableSequence[google.protobuf.duration_pb2.Duration]): + Time offsets of the DTMF partial results + relative to the beginning of the stream. + dtmf_final_results_times (MutableSequence[google.protobuf.duration_pb2.Duration]): + Time offsets of the DTMF final results + relative to the beginning of the stream. + single_utterance_end_time_offset (google.protobuf.duration_pb2.Duration): + Time offset of the end-of-single-utterance + signal relative to the beginning of the stream. + no_speech_timeout (google.protobuf.duration_pb2.Duration): + No speech timeout settings observed at + runtime. + is_input_text (bool): + Whether the streaming terminates with an + injected text query. + client_half_close_time_offset (google.protobuf.duration_pb2.Duration): + Client half close time in terms of input + audio duration. + client_half_close_streaming_time_offset (google.protobuf.duration_pb2.Duration): + Client half close time in terms of API + streaming duration. + """ + + audio_data_chunks: int = proto.Field( + proto.INT32, + number=1, + ) + result_end_time_offset: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=2, + message=duration_pb2.Duration, + ) + first_audio_duration: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=3, + message=duration_pb2.Duration, + ) + single_utterance: bool = proto.Field( + proto.BOOL, + number=5, + ) + speech_partial_results_end_times: MutableSequence[ + duration_pb2.Duration + ] = proto.RepeatedField( + proto.MESSAGE, + number=6, + message=duration_pb2.Duration, + ) + speech_final_results_end_times: MutableSequence[ + duration_pb2.Duration + ] = proto.RepeatedField( + proto.MESSAGE, + number=7, + message=duration_pb2.Duration, + ) + partial_responses: int = proto.Field( + proto.INT32, + number=8, + ) + speaker_id_passive_latency_ms_offset: int = proto.Field( + proto.INT32, + number=9, + ) + bargein_event_triggered: bool = proto.Field( + proto.BOOL, + number=10, + ) + speech_single_utterance: bool = proto.Field( + proto.BOOL, + number=11, + ) + dtmf_partial_results_times: MutableSequence[ + duration_pb2.Duration + ] = proto.RepeatedField( + proto.MESSAGE, + number=12, + message=duration_pb2.Duration, + ) + dtmf_final_results_times: MutableSequence[ + duration_pb2.Duration + ] = proto.RepeatedField( + proto.MESSAGE, + number=13, + message=duration_pb2.Duration, + ) + single_utterance_end_time_offset: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=14, + message=duration_pb2.Duration, + ) + no_speech_timeout: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=15, + message=duration_pb2.Duration, + ) + is_input_text: bool = proto.Field( + proto.BOOL, + number=16, + ) + client_half_close_time_offset: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=17, + message=duration_pb2.Duration, + ) + client_half_close_streaming_time_offset: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=18, + message=duration_pb2.Duration, + ) class StreamingDetectIntentResponse(proto.Message): @@ -716,6 +871,10 @@ class StreamingDetectIntentResponse(proto.Message): output_audio_config (google.cloud.dialogflow_v2.types.OutputAudioConfig): The config used by the speech synthesizer to generate the output audio. + debugging_info (google.cloud.dialogflow_v2.types.CloudConversationDebuggingInfo): + Debugging info that would get populated when + ``StreamingDetectIntentRequest.enable_debugging_info`` is + set to true. """ response_id: str = proto.Field( @@ -746,6 +905,11 @@ class StreamingDetectIntentResponse(proto.Message): number=6, message=gcd_audio_config.OutputAudioConfig, ) + debugging_info: "CloudConversationDebuggingInfo" = proto.Field( + proto.MESSAGE, + number=8, + message="CloudConversationDebuggingInfo", + ) class StreamingRecognitionResult(proto.Message): diff --git a/google/cloud/dialogflow_v2beta1/__init__.py b/google/cloud/dialogflow_v2beta1/__init__.py index c582f06e9..42086d763 100644 --- a/google/cloud/dialogflow_v2beta1/__init__.py +++ b/google/cloud/dialogflow_v2beta1/__init__.py @@ -98,6 +98,8 @@ ConversationPhoneNumber, CreateConversationRequest, CreateMessageRequest, + GenerateStatelessSummaryRequest, + GenerateStatelessSummaryResponse, GetConversationRequest, ListConversationsRequest, ListConversationsResponse, @@ -237,6 +239,7 @@ UpdateParticipantRequest, ) from .types.session import ( + CloudConversationDebuggingInfo, DetectIntentRequest, DetectIntentResponse, EventInput, @@ -319,6 +322,7 @@ "BatchUpdateIntentsResponse", "ClearSuggestionFeatureConfigOperationMetadata", "ClearSuggestionFeatureConfigRequest", + "CloudConversationDebuggingInfo", "CompileSuggestionRequest", "CompileSuggestionResponse", "CompleteConversationRequest", @@ -374,6 +378,8 @@ "GcsDestination", "GcsSource", "GcsSources", + "GenerateStatelessSummaryRequest", + "GenerateStatelessSummaryResponse", "GetAgentRequest", "GetAnswerRecordRequest", "GetContextRequest", diff --git a/google/cloud/dialogflow_v2beta1/gapic_metadata.json b/google/cloud/dialogflow_v2beta1/gapic_metadata.json index 62c0128c9..43a0d4c96 100644 --- a/google/cloud/dialogflow_v2beta1/gapic_metadata.json +++ b/google/cloud/dialogflow_v2beta1/gapic_metadata.json @@ -476,6 +476,11 @@ "create_conversation" ] }, + "GenerateStatelessSummary": { + "methods": [ + "generate_stateless_summary" + ] + }, "GetConversation": { "methods": [ "get_conversation" @@ -516,6 +521,11 @@ "create_conversation" ] }, + "GenerateStatelessSummary": { + "methods": [ + "generate_stateless_summary" + ] + }, "GetConversation": { "methods": [ "get_conversation" @@ -556,6 +566,11 @@ "create_conversation" ] }, + "GenerateStatelessSummary": { + "methods": [ + "generate_stateless_summary" + ] + }, "GetConversation": { "methods": [ "get_conversation" diff --git a/google/cloud/dialogflow_v2beta1/gapic_version.py b/google/cloud/dialogflow_v2beta1/gapic_version.py index e546bae05..03d6d0200 100644 --- a/google/cloud/dialogflow_v2beta1/gapic_version.py +++ b/google/cloud/dialogflow_v2beta1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.0" # {x-release-please-version} +__version__ = "2.22.0" # {x-release-please-version} diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/async_client.py b/google/cloud/dialogflow_v2beta1/services/conversations/async_client.py index 63052482e..d4f1cf112 100644 --- a/google/cloud/dialogflow_v2beta1/services/conversations/async_client.py +++ b/google/cloud/dialogflow_v2beta1/services/conversations/async_client.py @@ -66,18 +66,36 @@ class ConversationsAsyncClient: DEFAULT_ENDPOINT = ConversationsClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = ConversationsClient.DEFAULT_MTLS_ENDPOINT + agent_path = staticmethod(ConversationsClient.agent_path) + parse_agent_path = staticmethod(ConversationsClient.parse_agent_path) answer_record_path = staticmethod(ConversationsClient.answer_record_path) parse_answer_record_path = staticmethod( ConversationsClient.parse_answer_record_path ) conversation_path = staticmethod(ConversationsClient.conversation_path) parse_conversation_path = staticmethod(ConversationsClient.parse_conversation_path) + conversation_model_path = staticmethod(ConversationsClient.conversation_model_path) + parse_conversation_model_path = staticmethod( + ConversationsClient.parse_conversation_model_path + ) conversation_profile_path = staticmethod( ConversationsClient.conversation_profile_path ) parse_conversation_profile_path = staticmethod( ConversationsClient.parse_conversation_profile_path ) + cx_security_settings_path = staticmethod( + ConversationsClient.cx_security_settings_path + ) + parse_cx_security_settings_path = staticmethod( + ConversationsClient.parse_cx_security_settings_path + ) + document_path = staticmethod(ConversationsClient.document_path) + parse_document_path = staticmethod(ConversationsClient.parse_document_path) + knowledge_base_path = staticmethod(ConversationsClient.knowledge_base_path) + parse_knowledge_base_path = staticmethod( + ConversationsClient.parse_knowledge_base_path + ) message_path = staticmethod(ConversationsClient.message_path) parse_message_path = staticmethod(ConversationsClient.parse_message_path) common_billing_account_path = staticmethod( @@ -1049,6 +1067,104 @@ async def sample_suggest_conversation_summary(): # Done; return the response. return response + async def generate_stateless_summary( + self, + request: Optional[ + Union[conversation.GenerateStatelessSummaryRequest, dict] + ] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.GenerateStatelessSummaryResponse: + r"""Generates and returns a summary for a conversation + that does not have a resource created for it. + + .. code-block:: python + + # This snippet has been automatically generated and should be regarded as a + # code template only. + # It will require modifications to work: + # - It may require correct/in-range values for request initialization. + # - It may require specifying regional endpoints when creating the service + # client as shown in: + # https://googleapis.dev/python/google-api-core/latest/client_options.html + from google.cloud import dialogflow_v2beta1 + + async def sample_generate_stateless_summary(): + # Create a client + client = dialogflow_v2beta1.ConversationsAsyncClient() + + # Initialize request argument(s) + stateless_conversation = dialogflow_v2beta1.MinimalConversation() + stateless_conversation.messages.content = "content_value" + stateless_conversation.parent = "parent_value" + + conversation_profile = dialogflow_v2beta1.ConversationProfile() + conversation_profile.display_name = "display_name_value" + + request = dialogflow_v2beta1.GenerateStatelessSummaryRequest( + stateless_conversation=stateless_conversation, + conversation_profile=conversation_profile, + ) + + # Make the request + response = await client.generate_stateless_summary(request=request) + + # Handle the response + print(response) + + Args: + request (Optional[Union[google.cloud.dialogflow_v2beta1.types.GenerateStatelessSummaryRequest, dict]]): + The request object. The request message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2beta1.Conversations.GenerateStatelessSummary]. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.GenerateStatelessSummaryResponse: + The response message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2beta1.Conversations.GenerateStatelessSummary]. + + """ + # Create or coerce a protobuf request object. + request = conversation.GenerateStatelessSummaryRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.generate_stateless_summary, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + ( + ( + "stateless_conversation.parent", + request.stateless_conversation.parent, + ), + ) + ), + ) + + # Send the request. + response = await rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Done; return the response. + return response + async def list_operations( self, request: Optional[operations_pb2.ListOperationsRequest] = None, diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/client.py b/google/cloud/dialogflow_v2beta1/services/conversations/client.py index f241a6030..f9127fee6 100644 --- a/google/cloud/dialogflow_v2beta1/services/conversations/client.py +++ b/google/cloud/dialogflow_v2beta1/services/conversations/client.py @@ -183,6 +183,21 @@ def transport(self) -> ConversationsTransport: """ return self._transport + @staticmethod + def agent_path( + project: str, + ) -> str: + """Returns a fully-qualified agent string.""" + return "projects/{project}/agent".format( + project=project, + ) + + @staticmethod + def parse_agent_path(path: str) -> Dict[str, str]: + """Parses a agent path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/agent$", path) + return m.groupdict() if m else {} + @staticmethod def answer_record_path( project: str, @@ -221,6 +236,28 @@ def parse_conversation_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def conversation_model_path( + project: str, + location: str, + conversation_model: str, + ) -> str: + """Returns a fully-qualified conversation_model string.""" + return "projects/{project}/locations/{location}/conversationModels/{conversation_model}".format( + project=project, + location=location, + conversation_model=conversation_model, + ) + + @staticmethod + def parse_conversation_model_path(path: str) -> Dict[str, str]: + """Parses a conversation_model path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/conversationModels/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + @staticmethod def conversation_profile_path( project: str, @@ -241,6 +278,69 @@ def parse_conversation_profile_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def cx_security_settings_path( + project: str, + location: str, + security_settings: str, + ) -> str: + """Returns a fully-qualified cx_security_settings string.""" + return "projects/{project}/locations/{location}/securitySettings/{security_settings}".format( + project=project, + location=location, + security_settings=security_settings, + ) + + @staticmethod + def parse_cx_security_settings_path(path: str) -> Dict[str, str]: + """Parses a cx_security_settings path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/securitySettings/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def document_path( + project: str, + knowledge_base: str, + document: str, + ) -> str: + """Returns a fully-qualified document string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, + knowledge_base=knowledge_base, + document=document, + ) + + @staticmethod + def parse_document_path(path: str) -> Dict[str, str]: + """Parses a document path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?P.+?)/documents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def knowledge_base_path( + project: str, + knowledge_base: str, + ) -> str: + """Returns a fully-qualified knowledge_base string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}".format( + project=project, + knowledge_base=knowledge_base, + ) + + @staticmethod + def parse_knowledge_base_path(path: str) -> Dict[str, str]: + """Parses a knowledge_base path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?P.+?)$", path + ) + return m.groupdict() if m else {} + @staticmethod def message_path( project: str, @@ -1326,6 +1426,107 @@ def sample_suggest_conversation_summary(): # Done; return the response. return response + def generate_stateless_summary( + self, + request: Optional[ + Union[conversation.GenerateStatelessSummaryRequest, dict] + ] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.GenerateStatelessSummaryResponse: + r"""Generates and returns a summary for a conversation + that does not have a resource created for it. + + .. code-block:: python + + # This snippet has been automatically generated and should be regarded as a + # code template only. + # It will require modifications to work: + # - It may require correct/in-range values for request initialization. + # - It may require specifying regional endpoints when creating the service + # client as shown in: + # https://googleapis.dev/python/google-api-core/latest/client_options.html + from google.cloud import dialogflow_v2beta1 + + def sample_generate_stateless_summary(): + # Create a client + client = dialogflow_v2beta1.ConversationsClient() + + # Initialize request argument(s) + stateless_conversation = dialogflow_v2beta1.MinimalConversation() + stateless_conversation.messages.content = "content_value" + stateless_conversation.parent = "parent_value" + + conversation_profile = dialogflow_v2beta1.ConversationProfile() + conversation_profile.display_name = "display_name_value" + + request = dialogflow_v2beta1.GenerateStatelessSummaryRequest( + stateless_conversation=stateless_conversation, + conversation_profile=conversation_profile, + ) + + # Make the request + response = client.generate_stateless_summary(request=request) + + # Handle the response + print(response) + + Args: + request (Union[google.cloud.dialogflow_v2beta1.types.GenerateStatelessSummaryRequest, dict]): + The request object. The request message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2beta1.Conversations.GenerateStatelessSummary]. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.GenerateStatelessSummaryResponse: + The response message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2beta1.Conversations.GenerateStatelessSummary]. + + """ + # Create or coerce a protobuf request object. + # Minor optimization to avoid making a copy if the user passes + # in a conversation.GenerateStatelessSummaryRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.GenerateStatelessSummaryRequest): + request = conversation.GenerateStatelessSummaryRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.generate_stateless_summary + ] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + ( + ( + "stateless_conversation.parent", + request.stateless_conversation.parent, + ), + ) + ), + ) + + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Done; return the response. + return response + def __enter__(self) -> "ConversationsClient": return self diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/transports/base.py b/google/cloud/dialogflow_v2beta1/services/conversations/transports/base.py index 5d68a9904..40dc5295e 100644 --- a/google/cloud/dialogflow_v2beta1/services/conversations/transports/base.py +++ b/google/cloud/dialogflow_v2beta1/services/conversations/transports/base.py @@ -163,6 +163,11 @@ def _prep_wrapped_messages(self, client_info): default_timeout=None, client_info=client_info, ), + self.generate_stateless_summary: gapic_v1.method.wrap_method( + self.generate_stateless_summary, + default_timeout=None, + client_info=client_info, + ), } def close(self): @@ -249,6 +254,18 @@ def suggest_conversation_summary( ]: raise NotImplementedError() + @property + def generate_stateless_summary( + self, + ) -> Callable[ + [conversation.GenerateStatelessSummaryRequest], + Union[ + conversation.GenerateStatelessSummaryResponse, + Awaitable[conversation.GenerateStatelessSummaryResponse], + ], + ]: + raise NotImplementedError() + @property def list_operations( self, diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc.py index ea4563dad..1e0b757e5 100644 --- a/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc.py +++ b/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc.py @@ -459,6 +459,36 @@ def suggest_conversation_summary( ) return self._stubs["suggest_conversation_summary"] + @property + def generate_stateless_summary( + self, + ) -> Callable[ + [conversation.GenerateStatelessSummaryRequest], + conversation.GenerateStatelessSummaryResponse, + ]: + r"""Return a callable for the generate stateless summary method over gRPC. + + Generates and returns a summary for a conversation + that does not have a resource created for it. + + Returns: + Callable[[~.GenerateStatelessSummaryRequest], + ~.GenerateStatelessSummaryResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "generate_stateless_summary" not in self._stubs: + self._stubs["generate_stateless_summary"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/GenerateStatelessSummary", + request_serializer=conversation.GenerateStatelessSummaryRequest.serialize, + response_deserializer=conversation.GenerateStatelessSummaryResponse.deserialize, + ) + return self._stubs["generate_stateless_summary"] + def close(self): self.grpc_channel.close() diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc_asyncio.py index cd5599103..c28b21c92 100644 --- a/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc_asyncio.py @@ -466,6 +466,36 @@ def suggest_conversation_summary( ) return self._stubs["suggest_conversation_summary"] + @property + def generate_stateless_summary( + self, + ) -> Callable[ + [conversation.GenerateStatelessSummaryRequest], + Awaitable[conversation.GenerateStatelessSummaryResponse], + ]: + r"""Return a callable for the generate stateless summary method over gRPC. + + Generates and returns a summary for a conversation + that does not have a resource created for it. + + Returns: + Callable[[~.GenerateStatelessSummaryRequest], + Awaitable[~.GenerateStatelessSummaryResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "generate_stateless_summary" not in self._stubs: + self._stubs["generate_stateless_summary"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/GenerateStatelessSummary", + request_serializer=conversation.GenerateStatelessSummaryRequest.serialize, + response_deserializer=conversation.GenerateStatelessSummaryResponse.deserialize, + ) + return self._stubs["generate_stateless_summary"] + def close(self): return self.grpc_channel.close() diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/transports/rest.py b/google/cloud/dialogflow_v2beta1/services/conversations/transports/rest.py index 2684c880a..39517b656 100644 --- a/google/cloud/dialogflow_v2beta1/services/conversations/transports/rest.py +++ b/google/cloud/dialogflow_v2beta1/services/conversations/transports/rest.py @@ -90,6 +90,14 @@ def post_create_conversation(self, response): logging.log(f"Received response: {response}") return response + def pre_generate_stateless_summary(self, request, metadata): + logging.log(f"Received request: {request}") + return request, metadata + + def post_generate_stateless_summary(self, response): + logging.log(f"Received response: {response}") + return response + def pre_get_conversation(self, request, metadata): logging.log(f"Received request: {request}") return request, metadata @@ -197,6 +205,29 @@ def post_create_conversation( """ return response + def pre_generate_stateless_summary( + self, + request: conversation.GenerateStatelessSummaryRequest, + metadata: Sequence[Tuple[str, str]], + ) -> Tuple[conversation.GenerateStatelessSummaryRequest, Sequence[Tuple[str, str]]]: + """Pre-rpc interceptor for generate_stateless_summary + + Override in a subclass to manipulate the request or metadata + before they are sent to the Conversations server. + """ + return request, metadata + + def post_generate_stateless_summary( + self, response: conversation.GenerateStatelessSummaryResponse + ) -> conversation.GenerateStatelessSummaryResponse: + """Post-rpc interceptor for generate_stateless_summary + + Override in a subclass to manipulate the response + after it is returned by the Conversations server but before + it is returned to user code. + """ + return response + def pre_get_conversation( self, request: conversation.GetConversationRequest, @@ -827,6 +858,112 @@ def __call__( resp = self._interceptor.post_create_conversation(resp) return resp + class _GenerateStatelessSummary(ConversationsRestStub): + def __hash__(self): + return hash("GenerateStatelessSummary") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + def __call__( + self, + request: conversation.GenerateStatelessSummaryRequest, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Optional[float] = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.GenerateStatelessSummaryResponse: + r"""Call the generate stateless + summary method over HTTP. + + Args: + request (~.conversation.GenerateStatelessSummaryRequest): + The request object. The request message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2beta1.Conversations.GenerateStatelessSummary]. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.conversation.GenerateStatelessSummaryResponse: + The response message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2beta1.Conversations.GenerateStatelessSummary]. + + """ + + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v2beta1/{stateless_conversation.parent=projects/*}/suggestions:generateStatelessSummary", + "body": "*", + }, + { + "method": "post", + "uri": "/v2beta1/{stateless_conversation.parent=projects/*/locations/*}/suggestions:generateStatelessSummary", + "body": "*", + }, + ] + request, metadata = self._interceptor.pre_generate_stateless_summary( + request, metadata + ) + pb_request = conversation.GenerateStatelessSummaryRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], + including_default_value_fields=False, + use_integers_for_enums=True, + ) + uri = transcoded_request["uri"] + method = transcoded_request["method"] + + # Jsonify the query params + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + including_default_value_fields=False, + use_integers_for_enums=True, + ) + ) + query_params.update(self._get_unset_required_fields(query_params)) + + query_params["$alt"] = "json;enum-encoding=int" + + # Send the request + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(self._session, method)( + "{host}{uri}".format(host=self._host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + raise core_exceptions.from_http_response(response) + + # Return the response + resp = conversation.GenerateStatelessSummaryResponse() + pb_resp = conversation.GenerateStatelessSummaryResponse.pb(resp) + + json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_generate_stateless_summary(resp) + return resp + class _GetConversation(ConversationsRestStub): def __hash__(self): return hash("GetConversation") @@ -1252,6 +1389,17 @@ def create_conversation( # In C++ this would require a dynamic_cast return self._CreateConversation(self._session, self._host, self._interceptor) # type: ignore + @property + def generate_stateless_summary( + self, + ) -> Callable[ + [conversation.GenerateStatelessSummaryRequest], + conversation.GenerateStatelessSummaryResponse, + ]: + # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here. + # In C++ this would require a dynamic_cast + return self._GenerateStatelessSummary(self._session, self._host, self._interceptor) # type: ignore + @property def get_conversation( self, diff --git a/google/cloud/dialogflow_v2beta1/types/__init__.py b/google/cloud/dialogflow_v2beta1/types/__init__.py index 3c648e501..5a3de47cd 100644 --- a/google/cloud/dialogflow_v2beta1/types/__init__.py +++ b/google/cloud/dialogflow_v2beta1/types/__init__.py @@ -72,6 +72,8 @@ ConversationPhoneNumber, CreateConversationRequest, CreateMessageRequest, + GenerateStatelessSummaryRequest, + GenerateStatelessSummaryResponse, GetConversationRequest, ListConversationsRequest, ListConversationsResponse, @@ -207,6 +209,7 @@ UpdateParticipantRequest, ) from .session import ( + CloudConversationDebuggingInfo, DetectIntentRequest, DetectIntentResponse, EventInput, @@ -294,6 +297,8 @@ "ConversationPhoneNumber", "CreateConversationRequest", "CreateMessageRequest", + "GenerateStatelessSummaryRequest", + "GenerateStatelessSummaryResponse", "GetConversationRequest", "ListConversationsRequest", "ListConversationsResponse", @@ -417,6 +422,7 @@ "SuggestSmartRepliesRequest", "SuggestSmartRepliesResponse", "UpdateParticipantRequest", + "CloudConversationDebuggingInfo", "DetectIntentRequest", "DetectIntentResponse", "EventInput", diff --git a/google/cloud/dialogflow_v2beta1/types/answer_record.py b/google/cloud/dialogflow_v2beta1/types/answer_record.py index bf877b683..da3e8701c 100644 --- a/google/cloud/dialogflow_v2beta1/types/answer_record.py +++ b/google/cloud/dialogflow_v2beta1/types/answer_record.py @@ -159,6 +159,9 @@ class AnswerFeedback(proto.Message): clicked (bool): Indicates whether the answer/item was clicked by the human agent or not. Default to false. + For knowledge search, the answer record is + considered to be clicked if the answer was + copied or any URI was clicked. click_time (google.protobuf.timestamp_pb2.Timestamp): Time when the answer/item was clicked. displayed (bool): diff --git a/google/cloud/dialogflow_v2beta1/types/audio_config.py b/google/cloud/dialogflow_v2beta1/types/audio_config.py index 50e3c9bb3..44da36729 100644 --- a/google/cloud/dialogflow_v2beta1/types/audio_config.py +++ b/google/cloud/dialogflow_v2beta1/types/audio_config.py @@ -387,6 +387,15 @@ class BargeInConfig(proto.Message): those two phases. The durations are measured in terms of the audio length fromt the the start of the input audio. + The flow goes like below: + + --> Time + + without speech detection \| utterance only \| utterance or no-speech + event \| \| +-------------+ \| +------------+ \| +---------------+ + ----------+ no barge-in +-|-+ barge-in +-|-+ normal period + +----------- +-------------+ \| +------------+ \| +---------------+ + No-speech event is a response with END_OF_UTTERANCE without any transcript following up. @@ -469,7 +478,13 @@ class InputAudioConfig(proto.Message): standard version of the specified model. Refer to `Cloud Speech API documentation `__ - for more details. + for more details. If you specify a model, the following + models typically have the best performance: + + - phone_call (best for Agent Assist and telephony) + - latest_short (best for Dialogflow non-telephony) + - command_and_search (best for very short utterances and + commands) model_variant (google.cloud.dialogflow_v2beta1.types.SpeechModelVariant): Which variant of the [Speech model][google.cloud.dialogflow.v2beta1.InputAudioConfig.model] @@ -495,6 +510,9 @@ class InputAudioConfig(proto.Message): barge_in_config (google.cloud.dialogflow_v2beta1.types.BargeInConfig): Configuration of barge-in behavior during the streaming of input audio. + enable_automatic_punctuation (bool): + Enable automatic punctuation option at the + speech backend. """ audio_encoding: "AudioEncoding" = proto.Field( @@ -545,6 +563,10 @@ class InputAudioConfig(proto.Message): number=15, message="BargeInConfig", ) + enable_automatic_punctuation: bool = proto.Field( + proto.BOOL, + number=17, + ) class VoiceSelectionParams(proto.Message): diff --git a/google/cloud/dialogflow_v2beta1/types/conversation.py b/google/cloud/dialogflow_v2beta1/types/conversation.py index d4fce9112..84b62be17 100644 --- a/google/cloud/dialogflow_v2beta1/types/conversation.py +++ b/google/cloud/dialogflow_v2beta1/types/conversation.py @@ -20,6 +20,9 @@ from google.protobuf import timestamp_pb2 # type: ignore import proto # type: ignore +from google.cloud.dialogflow_v2beta1.types import ( + conversation_profile as gcd_conversation_profile, +) from google.cloud.dialogflow_v2beta1.types import participant __protobuf__ = proto.module( @@ -39,6 +42,8 @@ "ListMessagesResponse", "SuggestConversationSummaryRequest", "SuggestConversationSummaryResponse", + "GenerateStatelessSummaryRequest", + "GenerateStatelessSummaryResponse", }, ) @@ -595,4 +600,137 @@ class Summary(proto.Message): ) +class GenerateStatelessSummaryRequest(proto.Message): + r"""The request message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2beta1.Conversations.GenerateStatelessSummary]. + + Attributes: + stateless_conversation (google.cloud.dialogflow_v2beta1.types.GenerateStatelessSummaryRequest.MinimalConversation): + Required. The conversation to suggest a + summary for. + conversation_profile (google.cloud.dialogflow_v2beta1.types.ConversationProfile): + Required. A ConversationProfile containing information + required for Summary generation. Required fields: + {language_code, security_settings} Optional fields: + {agent_assistant_config} + latest_message (str): + The name of the latest conversation message + used as context for generating a Summary. If + empty, the latest message of the conversation + will be used. The format is specific to the user + and the names of the messages provided. + max_context_size (int): + Max number of messages prior to and including + [latest_message] to use as context when compiling the + suggestion. By default 500 and at most 1000. + """ + + class MinimalConversation(proto.Message): + r"""The minimum amount of information required to generate a + Summary without having a Conversation resource created. + + Attributes: + messages (MutableSequence[google.cloud.dialogflow_v2beta1.types.Message]): + Required. The messages that the Summary will be generated + from. It is expected that this message content is already + redacted and does not contain any PII. Required fields: + {content, language_code, participant, participant_role} + Optional fields: {send_time} If send_time is not provided, + then the messages must be provided in chronological order. + parent (str): + Required. The parent resource to charge for the Summary's + generation. Format: + ``projects//locations/``. + """ + + messages: MutableSequence[participant.Message] = proto.RepeatedField( + proto.MESSAGE, + number=1, + message=participant.Message, + ) + parent: str = proto.Field( + proto.STRING, + number=2, + ) + + stateless_conversation: MinimalConversation = proto.Field( + proto.MESSAGE, + number=1, + message=MinimalConversation, + ) + conversation_profile: gcd_conversation_profile.ConversationProfile = proto.Field( + proto.MESSAGE, + number=2, + message=gcd_conversation_profile.ConversationProfile, + ) + latest_message: str = proto.Field( + proto.STRING, + number=3, + ) + max_context_size: int = proto.Field( + proto.INT32, + number=4, + ) + + +class GenerateStatelessSummaryResponse(proto.Message): + r"""The response message for + [Conversations.GenerateStatelessSummary][google.cloud.dialogflow.v2beta1.Conversations.GenerateStatelessSummary]. + + Attributes: + summary (google.cloud.dialogflow_v2beta1.types.GenerateStatelessSummaryResponse.Summary): + Generated summary. + latest_message (str): + The name of the latest conversation message + used as context for compiling suggestion. The + format is specific to the user and the names of + the messages provided. + context_size (int): + Number of messages prior to and including + [last_conversation_message][] used to compile the + suggestion. It may be smaller than the + [GenerateStatelessSummaryRequest.context_size][] field in + the request if there weren't that many messages in the + conversation. + """ + + class Summary(proto.Message): + r"""Generated summary for a conversation. + + Attributes: + text (str): + The summary content that is concatenated into + one string. + text_sections (MutableMapping[str, str]): + The summary content that is divided into + sections. The key is the section's name and the + value is the section's content. There is no + specific format for the key or value. + """ + + text: str = proto.Field( + proto.STRING, + number=1, + ) + text_sections: MutableMapping[str, str] = proto.MapField( + proto.STRING, + proto.STRING, + number=2, + ) + + summary: Summary = proto.Field( + proto.MESSAGE, + number=1, + message=Summary, + ) + latest_message: str = proto.Field( + proto.STRING, + number=2, + ) + context_size: int = proto.Field( + proto.INT32, + number=3, + ) + + __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2beta1/types/conversation_profile.py b/google/cloud/dialogflow_v2beta1/types/conversation_profile.py index 5dd28be44..b950ee11b 100644 --- a/google/cloud/dialogflow_v2beta1/types/conversation_profile.py +++ b/google/cloud/dialogflow_v2beta1/types/conversation_profile.py @@ -526,12 +526,22 @@ class ConversationModelConfig(proto.Message): model (str): Conversation model resource name. Format: ``projects//conversationModels/``. + baseline_model_version (str): + Version of current baseline model. It will be ignored if + [model][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.ConversationModelConfig.model] + is set. Valid versions are: Article Suggestion baseline + model: - 0.9 - 1.0 (default) Summarization baseline model: - + 1.0 """ model: str = proto.Field( proto.STRING, number=1, ) + baseline_model_version: str = proto.Field( + proto.STRING, + number=8, + ) class ConversationProcessConfig(proto.Message): r"""Config to process conversation. diff --git a/google/cloud/dialogflow_v2beta1/types/participant.py b/google/cloud/dialogflow_v2beta1/types/participant.py index cc52ee917..64280bd36 100644 --- a/google/cloud/dialogflow_v2beta1/types/participant.py +++ b/google/cloud/dialogflow_v2beta1/types/participant.py @@ -1033,6 +1033,9 @@ class StreamingAnalyzeContentRequest(proto.Message): response even if some ``Fulfillment``\ s in Dialogflow virtual agent have been configured to return partial responses. + enable_debugging_info (bool): + if true, ``StreamingAnalyzeContentResponse.debugging_info`` + will get populated. """ participant: str = proto.Field( @@ -1095,6 +1098,10 @@ class StreamingAnalyzeContentRequest(proto.Message): proto.BOOL, number=12, ) + enable_debugging_info: bool = proto.Field( + proto.BOOL, + number=19, + ) class StreamingAnalyzeContentResponse(proto.Message): @@ -1169,6 +1176,10 @@ class StreamingAnalyzeContentResponse(proto.Message): [HumanAgentAssistantConfig.end_user_suggestion_config][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.end_user_suggestion_config]. dtmf_parameters (google.cloud.dialogflow_v2beta1.types.DtmfParameters): Indicates the parameters of DTMF. + debugging_info (google.cloud.dialogflow_v2beta1.types.CloudConversationDebuggingInfo): + Debugging info that would get populated when + ``StreamingAnalyzeContentRequest.enable_debugging_info`` is + set to true. """ recognition_result: session.StreamingRecognitionResult = proto.Field( @@ -1214,6 +1225,11 @@ class StreamingAnalyzeContentResponse(proto.Message): number=10, message="DtmfParameters", ) + debugging_info: session.CloudConversationDebuggingInfo = proto.Field( + proto.MESSAGE, + number=11, + message=session.CloudConversationDebuggingInfo, + ) class AnnotatedMessagePart(proto.Message): diff --git a/google/cloud/dialogflow_v2beta1/types/session.py b/google/cloud/dialogflow_v2beta1/types/session.py index 78469185c..1122adc92 100644 --- a/google/cloud/dialogflow_v2beta1/types/session.py +++ b/google/cloud/dialogflow_v2beta1/types/session.py @@ -40,6 +40,7 @@ "QueryResult", "KnowledgeAnswers", "StreamingDetectIntentRequest", + "CloudConversationDebuggingInfo", "StreamingDetectIntentResponse", "StreamingRecognitionResult", "TextInput", @@ -796,6 +797,9 @@ class StreamingDetectIntentRequest(proto.Message): ``query_input`` was set to a streaming input audio config. The complete audio over all streaming messages must not exceed 1 minute. + enable_debugging_info (bool): + If true, ``StreamingDetectIntentResponse.debugging_info`` + will get populated. """ session: str = proto.Field( @@ -830,6 +834,157 @@ class StreamingDetectIntentRequest(proto.Message): proto.BYTES, number=6, ) + enable_debugging_info: bool = proto.Field( + proto.BOOL, + number=8, + ) + + +class CloudConversationDebuggingInfo(proto.Message): + r"""Cloud conversation info for easier debugging. It will get populated + in ``StreamingDetectIntentResponse`` or + ``StreamingAnalyzeContentResponse`` when the flag + ``enable_debugging_info`` is set to true in corresponding requests. + + Attributes: + audio_data_chunks (int): + Number of input audio data chunks in + streaming requests. + result_end_time_offset (google.protobuf.duration_pb2.Duration): + Time offset of the end of speech utterance + relative to the beginning of the first audio + chunk. + first_audio_duration (google.protobuf.duration_pb2.Duration): + Duration of first audio chunk. + single_utterance (bool): + Whether client used single utterance mode. + speech_partial_results_end_times (MutableSequence[google.protobuf.duration_pb2.Duration]): + Time offsets of the speech partial results + relative to the beginning of the stream. + speech_final_results_end_times (MutableSequence[google.protobuf.duration_pb2.Duration]): + Time offsets of the speech final results (is_final=true) + relative to the beginning of the stream. + partial_responses (int): + Total number of partial responses. + speaker_id_passive_latency_ms_offset (int): + Time offset of Speaker ID stream close time + relative to the Speech stream close time in + milliseconds. Only meaningful for conversations + involving passive verification. + bargein_event_triggered (bool): + Whether a barge-in event is triggered in this + request. + speech_single_utterance (bool): + Whether speech uses single utterance mode. + dtmf_partial_results_times (MutableSequence[google.protobuf.duration_pb2.Duration]): + Time offsets of the DTMF partial results + relative to the beginning of the stream. + dtmf_final_results_times (MutableSequence[google.protobuf.duration_pb2.Duration]): + Time offsets of the DTMF final results + relative to the beginning of the stream. + single_utterance_end_time_offset (google.protobuf.duration_pb2.Duration): + Time offset of the end-of-single-utterance + signal relative to the beginning of the stream. + no_speech_timeout (google.protobuf.duration_pb2.Duration): + No speech timeout settings observed at + runtime. + is_input_text (bool): + Whether the streaming terminates with an + injected text query. + client_half_close_time_offset (google.protobuf.duration_pb2.Duration): + Client half close time in terms of input + audio duration. + client_half_close_streaming_time_offset (google.protobuf.duration_pb2.Duration): + Client half close time in terms of API + streaming duration. + """ + + audio_data_chunks: int = proto.Field( + proto.INT32, + number=1, + ) + result_end_time_offset: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=2, + message=duration_pb2.Duration, + ) + first_audio_duration: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=3, + message=duration_pb2.Duration, + ) + single_utterance: bool = proto.Field( + proto.BOOL, + number=5, + ) + speech_partial_results_end_times: MutableSequence[ + duration_pb2.Duration + ] = proto.RepeatedField( + proto.MESSAGE, + number=6, + message=duration_pb2.Duration, + ) + speech_final_results_end_times: MutableSequence[ + duration_pb2.Duration + ] = proto.RepeatedField( + proto.MESSAGE, + number=7, + message=duration_pb2.Duration, + ) + partial_responses: int = proto.Field( + proto.INT32, + number=8, + ) + speaker_id_passive_latency_ms_offset: int = proto.Field( + proto.INT32, + number=9, + ) + bargein_event_triggered: bool = proto.Field( + proto.BOOL, + number=10, + ) + speech_single_utterance: bool = proto.Field( + proto.BOOL, + number=11, + ) + dtmf_partial_results_times: MutableSequence[ + duration_pb2.Duration + ] = proto.RepeatedField( + proto.MESSAGE, + number=12, + message=duration_pb2.Duration, + ) + dtmf_final_results_times: MutableSequence[ + duration_pb2.Duration + ] = proto.RepeatedField( + proto.MESSAGE, + number=13, + message=duration_pb2.Duration, + ) + single_utterance_end_time_offset: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=14, + message=duration_pb2.Duration, + ) + no_speech_timeout: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=15, + message=duration_pb2.Duration, + ) + is_input_text: bool = proto.Field( + proto.BOOL, + number=16, + ) + client_half_close_time_offset: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=17, + message=duration_pb2.Duration, + ) + client_half_close_streaming_time_offset: duration_pb2.Duration = proto.Field( + proto.MESSAGE, + number=18, + message=duration_pb2.Duration, + ) class StreamingDetectIntentResponse(proto.Message): @@ -890,6 +1045,10 @@ class StreamingDetectIntentResponse(proto.Message): output_audio_config (google.cloud.dialogflow_v2beta1.types.OutputAudioConfig): The config used by the speech synthesizer to generate the output audio. + debugging_info (google.cloud.dialogflow_v2beta1.types.CloudConversationDebuggingInfo): + Debugging info that would get populated when + ``StreamingDetectIntentRequest.enable_debugging_info`` is + set to true. """ response_id: str = proto.Field( @@ -925,6 +1084,11 @@ class StreamingDetectIntentResponse(proto.Message): number=6, message=gcd_audio_config.OutputAudioConfig, ) + debugging_info: "CloudConversationDebuggingInfo" = proto.Field( + proto.MESSAGE, + number=8, + message="CloudConversationDebuggingInfo", + ) class StreamingRecognitionResult(proto.Message): @@ -1029,11 +1193,7 @@ class MessageType(proto.Enum): Message contains a (possibly partial) transcript. DTMF_DIGITS (3): - Message contains DTMF digits. When the client - gets the message, it should stop sending - additional data, half-close the gRPC connection, - and wait for any additional results until the - server closes the gRPC. connection. + Message contains DTMF digits. END_OF_SINGLE_UTTERANCE (2): Event indicates that the server has detected the end of the user's speech utterance and expects no additional speech. diff --git a/samples/generated_samples/dialogflow_v2_generated_conversations_generate_stateless_summary_async.py b/samples/generated_samples/dialogflow_v2_generated_conversations_generate_stateless_summary_async.py new file mode 100644 index 000000000..0f1580256 --- /dev/null +++ b/samples/generated_samples/dialogflow_v2_generated_conversations_generate_stateless_summary_async.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 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. +# +# Generated code. DO NOT EDIT! +# +# Snippet for GenerateStatelessSummary +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-dialogflow + + +# [START dialogflow_v2_generated_Conversations_GenerateStatelessSummary_async] +# This snippet has been automatically generated and should be regarded as a +# code template only. +# It will require modifications to work: +# - It may require correct/in-range values for request initialization. +# - It may require specifying regional endpoints when creating the service +# client as shown in: +# https://googleapis.dev/python/google-api-core/latest/client_options.html +from google.cloud import dialogflow_v2 + + +async def sample_generate_stateless_summary(): + # Create a client + client = dialogflow_v2.ConversationsAsyncClient() + + # Initialize request argument(s) + stateless_conversation = dialogflow_v2.MinimalConversation() + stateless_conversation.messages.content = "content_value" + stateless_conversation.parent = "parent_value" + + conversation_profile = dialogflow_v2.ConversationProfile() + conversation_profile.display_name = "display_name_value" + + request = dialogflow_v2.GenerateStatelessSummaryRequest( + stateless_conversation=stateless_conversation, + conversation_profile=conversation_profile, + ) + + # Make the request + response = await client.generate_stateless_summary(request=request) + + # Handle the response + print(response) + +# [END dialogflow_v2_generated_Conversations_GenerateStatelessSummary_async] diff --git a/samples/generated_samples/dialogflow_v2_generated_conversations_generate_stateless_summary_sync.py b/samples/generated_samples/dialogflow_v2_generated_conversations_generate_stateless_summary_sync.py new file mode 100644 index 000000000..0d4d24249 --- /dev/null +++ b/samples/generated_samples/dialogflow_v2_generated_conversations_generate_stateless_summary_sync.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 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. +# +# Generated code. DO NOT EDIT! +# +# Snippet for GenerateStatelessSummary +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-dialogflow + + +# [START dialogflow_v2_generated_Conversations_GenerateStatelessSummary_sync] +# This snippet has been automatically generated and should be regarded as a +# code template only. +# It will require modifications to work: +# - It may require correct/in-range values for request initialization. +# - It may require specifying regional endpoints when creating the service +# client as shown in: +# https://googleapis.dev/python/google-api-core/latest/client_options.html +from google.cloud import dialogflow_v2 + + +def sample_generate_stateless_summary(): + # Create a client + client = dialogflow_v2.ConversationsClient() + + # Initialize request argument(s) + stateless_conversation = dialogflow_v2.MinimalConversation() + stateless_conversation.messages.content = "content_value" + stateless_conversation.parent = "parent_value" + + conversation_profile = dialogflow_v2.ConversationProfile() + conversation_profile.display_name = "display_name_value" + + request = dialogflow_v2.GenerateStatelessSummaryRequest( + stateless_conversation=stateless_conversation, + conversation_profile=conversation_profile, + ) + + # Make the request + response = client.generate_stateless_summary(request=request) + + # Handle the response + print(response) + +# [END dialogflow_v2_generated_Conversations_GenerateStatelessSummary_sync] diff --git a/samples/generated_samples/dialogflow_v2beta1_generated_conversations_generate_stateless_summary_async.py b/samples/generated_samples/dialogflow_v2beta1_generated_conversations_generate_stateless_summary_async.py new file mode 100644 index 000000000..f43051c40 --- /dev/null +++ b/samples/generated_samples/dialogflow_v2beta1_generated_conversations_generate_stateless_summary_async.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 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. +# +# Generated code. DO NOT EDIT! +# +# Snippet for GenerateStatelessSummary +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-dialogflow + + +# [START dialogflow_v2beta1_generated_Conversations_GenerateStatelessSummary_async] +# This snippet has been automatically generated and should be regarded as a +# code template only. +# It will require modifications to work: +# - It may require correct/in-range values for request initialization. +# - It may require specifying regional endpoints when creating the service +# client as shown in: +# https://googleapis.dev/python/google-api-core/latest/client_options.html +from google.cloud import dialogflow_v2beta1 + + +async def sample_generate_stateless_summary(): + # Create a client + client = dialogflow_v2beta1.ConversationsAsyncClient() + + # Initialize request argument(s) + stateless_conversation = dialogflow_v2beta1.MinimalConversation() + stateless_conversation.messages.content = "content_value" + stateless_conversation.parent = "parent_value" + + conversation_profile = dialogflow_v2beta1.ConversationProfile() + conversation_profile.display_name = "display_name_value" + + request = dialogflow_v2beta1.GenerateStatelessSummaryRequest( + stateless_conversation=stateless_conversation, + conversation_profile=conversation_profile, + ) + + # Make the request + response = await client.generate_stateless_summary(request=request) + + # Handle the response + print(response) + +# [END dialogflow_v2beta1_generated_Conversations_GenerateStatelessSummary_async] diff --git a/samples/generated_samples/dialogflow_v2beta1_generated_conversations_generate_stateless_summary_sync.py b/samples/generated_samples/dialogflow_v2beta1_generated_conversations_generate_stateless_summary_sync.py new file mode 100644 index 000000000..adda02afa --- /dev/null +++ b/samples/generated_samples/dialogflow_v2beta1_generated_conversations_generate_stateless_summary_sync.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 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. +# +# Generated code. DO NOT EDIT! +# +# Snippet for GenerateStatelessSummary +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-dialogflow + + +# [START dialogflow_v2beta1_generated_Conversations_GenerateStatelessSummary_sync] +# This snippet has been automatically generated and should be regarded as a +# code template only. +# It will require modifications to work: +# - It may require correct/in-range values for request initialization. +# - It may require specifying regional endpoints when creating the service +# client as shown in: +# https://googleapis.dev/python/google-api-core/latest/client_options.html +from google.cloud import dialogflow_v2beta1 + + +def sample_generate_stateless_summary(): + # Create a client + client = dialogflow_v2beta1.ConversationsClient() + + # Initialize request argument(s) + stateless_conversation = dialogflow_v2beta1.MinimalConversation() + stateless_conversation.messages.content = "content_value" + stateless_conversation.parent = "parent_value" + + conversation_profile = dialogflow_v2beta1.ConversationProfile() + conversation_profile.display_name = "display_name_value" + + request = dialogflow_v2beta1.GenerateStatelessSummaryRequest( + stateless_conversation=stateless_conversation, + conversation_profile=conversation_profile, + ) + + # Make the request + response = client.generate_stateless_summary(request=request) + + # Handle the response + print(response) + +# [END dialogflow_v2beta1_generated_Conversations_GenerateStatelessSummary_sync] diff --git a/samples/generated_samples/snippet_metadata_google.cloud.dialogflow.v2.json b/samples/generated_samples/snippet_metadata_google.cloud.dialogflow.v2.json index 1c7632304..0b80ee5b1 100644 --- a/samples/generated_samples/snippet_metadata_google.cloud.dialogflow.v2.json +++ b/samples/generated_samples/snippet_metadata_google.cloud.dialogflow.v2.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-dialogflow", - "version": "2.21.0" + "version": "2.22.0" }, "snippets": [ { @@ -6483,6 +6483,159 @@ ], "title": "dialogflow_v2_generated_conversations_create_conversation_sync.py" }, + { + "canonical": true, + "clientMethod": { + "async": true, + "client": { + "fullName": "google.cloud.dialogflow_v2.ConversationsAsyncClient", + "shortName": "ConversationsAsyncClient" + }, + "fullName": "google.cloud.dialogflow_v2.ConversationsAsyncClient.generate_stateless_summary", + "method": { + "fullName": "google.cloud.dialogflow.v2.Conversations.GenerateStatelessSummary", + "service": { + "fullName": "google.cloud.dialogflow.v2.Conversations", + "shortName": "Conversations" + }, + "shortName": "GenerateStatelessSummary" + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.dialogflow_v2.types.GenerateStatelessSummaryRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.cloud.dialogflow_v2.types.GenerateStatelessSummaryResponse", + "shortName": "generate_stateless_summary" + }, + "description": "Sample for GenerateStatelessSummary", + "file": "dialogflow_v2_generated_conversations_generate_stateless_summary_async.py", + "language": "PYTHON", + "origin": "API_DEFINITION", + "regionTag": "dialogflow_v2_generated_Conversations_GenerateStatelessSummary_async", + "segments": [ + { + "end": 59, + "start": 27, + "type": "FULL" + }, + { + "end": 59, + "start": 27, + "type": "SHORT" + }, + { + "end": 40, + "start": 38, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 53, + "start": 41, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 56, + "start": 54, + "type": "REQUEST_EXECUTION" + }, + { + "end": 60, + "start": 57, + "type": "RESPONSE_HANDLING" + } + ], + "title": "dialogflow_v2_generated_conversations_generate_stateless_summary_async.py" + }, + { + "canonical": true, + "clientMethod": { + "client": { + "fullName": "google.cloud.dialogflow_v2.ConversationsClient", + "shortName": "ConversationsClient" + }, + "fullName": "google.cloud.dialogflow_v2.ConversationsClient.generate_stateless_summary", + "method": { + "fullName": "google.cloud.dialogflow.v2.Conversations.GenerateStatelessSummary", + "service": { + "fullName": "google.cloud.dialogflow.v2.Conversations", + "shortName": "Conversations" + }, + "shortName": "GenerateStatelessSummary" + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.dialogflow_v2.types.GenerateStatelessSummaryRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.cloud.dialogflow_v2.types.GenerateStatelessSummaryResponse", + "shortName": "generate_stateless_summary" + }, + "description": "Sample for GenerateStatelessSummary", + "file": "dialogflow_v2_generated_conversations_generate_stateless_summary_sync.py", + "language": "PYTHON", + "origin": "API_DEFINITION", + "regionTag": "dialogflow_v2_generated_Conversations_GenerateStatelessSummary_sync", + "segments": [ + { + "end": 59, + "start": 27, + "type": "FULL" + }, + { + "end": 59, + "start": 27, + "type": "SHORT" + }, + { + "end": 40, + "start": 38, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 53, + "start": 41, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 56, + "start": 54, + "type": "REQUEST_EXECUTION" + }, + { + "end": 60, + "start": 57, + "type": "RESPONSE_HANDLING" + } + ], + "title": "dialogflow_v2_generated_conversations_generate_stateless_summary_sync.py" + }, { "canonical": true, "clientMethod": { diff --git a/samples/generated_samples/snippet_metadata_google.cloud.dialogflow.v2beta1.json b/samples/generated_samples/snippet_metadata_google.cloud.dialogflow.v2beta1.json index c3eb5577d..5bb5fc085 100644 --- a/samples/generated_samples/snippet_metadata_google.cloud.dialogflow.v2beta1.json +++ b/samples/generated_samples/snippet_metadata_google.cloud.dialogflow.v2beta1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-dialogflow", - "version": "2.21.0" + "version": "2.22.0" }, "snippets": [ { @@ -4543,6 +4543,159 @@ ], "title": "dialogflow_v2beta1_generated_conversations_create_conversation_sync.py" }, + { + "canonical": true, + "clientMethod": { + "async": true, + "client": { + "fullName": "google.cloud.dialogflow_v2beta1.ConversationsAsyncClient", + "shortName": "ConversationsAsyncClient" + }, + "fullName": "google.cloud.dialogflow_v2beta1.ConversationsAsyncClient.generate_stateless_summary", + "method": { + "fullName": "google.cloud.dialogflow.v2beta1.Conversations.GenerateStatelessSummary", + "service": { + "fullName": "google.cloud.dialogflow.v2beta1.Conversations", + "shortName": "Conversations" + }, + "shortName": "GenerateStatelessSummary" + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.dialogflow_v2beta1.types.GenerateStatelessSummaryRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.cloud.dialogflow_v2beta1.types.GenerateStatelessSummaryResponse", + "shortName": "generate_stateless_summary" + }, + "description": "Sample for GenerateStatelessSummary", + "file": "dialogflow_v2beta1_generated_conversations_generate_stateless_summary_async.py", + "language": "PYTHON", + "origin": "API_DEFINITION", + "regionTag": "dialogflow_v2beta1_generated_Conversations_GenerateStatelessSummary_async", + "segments": [ + { + "end": 59, + "start": 27, + "type": "FULL" + }, + { + "end": 59, + "start": 27, + "type": "SHORT" + }, + { + "end": 40, + "start": 38, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 53, + "start": 41, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 56, + "start": 54, + "type": "REQUEST_EXECUTION" + }, + { + "end": 60, + "start": 57, + "type": "RESPONSE_HANDLING" + } + ], + "title": "dialogflow_v2beta1_generated_conversations_generate_stateless_summary_async.py" + }, + { + "canonical": true, + "clientMethod": { + "client": { + "fullName": "google.cloud.dialogflow_v2beta1.ConversationsClient", + "shortName": "ConversationsClient" + }, + "fullName": "google.cloud.dialogflow_v2beta1.ConversationsClient.generate_stateless_summary", + "method": { + "fullName": "google.cloud.dialogflow.v2beta1.Conversations.GenerateStatelessSummary", + "service": { + "fullName": "google.cloud.dialogflow.v2beta1.Conversations", + "shortName": "Conversations" + }, + "shortName": "GenerateStatelessSummary" + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.dialogflow_v2beta1.types.GenerateStatelessSummaryRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.cloud.dialogflow_v2beta1.types.GenerateStatelessSummaryResponse", + "shortName": "generate_stateless_summary" + }, + "description": "Sample for GenerateStatelessSummary", + "file": "dialogflow_v2beta1_generated_conversations_generate_stateless_summary_sync.py", + "language": "PYTHON", + "origin": "API_DEFINITION", + "regionTag": "dialogflow_v2beta1_generated_Conversations_GenerateStatelessSummary_sync", + "segments": [ + { + "end": 59, + "start": 27, + "type": "FULL" + }, + { + "end": 59, + "start": 27, + "type": "SHORT" + }, + { + "end": 40, + "start": 38, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 53, + "start": 41, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 56, + "start": 54, + "type": "REQUEST_EXECUTION" + }, + { + "end": 60, + "start": 57, + "type": "RESPONSE_HANDLING" + } + ], + "title": "dialogflow_v2beta1_generated_conversations_generate_stateless_summary_sync.py" + }, { "canonical": true, "clientMethod": { diff --git a/scripts/fixup_dialogflow_v2_keywords.py b/scripts/fixup_dialogflow_v2_keywords.py index 0822b56f0..a38f4dd49 100644 --- a/scripts/fixup_dialogflow_v2_keywords.py +++ b/scripts/fixup_dialogflow_v2_keywords.py @@ -80,6 +80,7 @@ class dialogflowCallTransformer(cst.CSTTransformer): 'detect_intent': ('session', 'query_input', 'query_params', 'output_audio_config', 'output_audio_config_mask', 'input_audio', ), 'export_agent': ('parent', 'agent_uri', ), 'export_document': ('name', 'gcs_destination', 'export_full_content', 'smart_messaging_partial_update', ), + 'generate_stateless_summary': ('stateless_conversation', 'conversation_profile', 'latest_message', 'max_context_size', ), 'get_agent': ('parent', ), 'get_context': ('name', ), 'get_conversation': ('name', ), @@ -122,8 +123,8 @@ class dialogflowCallTransformer(cst.CSTTransformer): 'search_agents': ('parent', 'page_size', 'page_token', ), 'set_agent': ('agent', 'update_mask', ), 'set_suggestion_feature_config': ('conversation_profile', 'participant_role', 'suggestion_feature_config', ), - 'streaming_analyze_content': ('participant', 'audio_config', 'text_config', 'reply_audio_config', 'input_audio', 'input_text', 'input_dtmf', 'query_params', 'assist_query_params', 'cx_parameters', 'enable_partial_automated_agent_reply', ), - 'streaming_detect_intent': ('session', 'query_input', 'query_params', 'single_utterance', 'output_audio_config', 'output_audio_config_mask', 'input_audio', ), + 'streaming_analyze_content': ('participant', 'audio_config', 'text_config', 'reply_audio_config', 'input_audio', 'input_text', 'input_dtmf', 'query_params', 'assist_query_params', 'cx_parameters', 'enable_partial_automated_agent_reply', 'enable_debugging_info', ), + 'streaming_detect_intent': ('session', 'query_input', 'query_params', 'single_utterance', 'output_audio_config', 'output_audio_config_mask', 'input_audio', 'enable_debugging_info', ), 'suggest_articles': ('parent', 'latest_message', 'context_size', 'assist_query_params', ), 'suggest_conversation_summary': ('conversation', 'latest_message', 'context_size', 'assist_query_params', ), 'suggest_faq_answers': ('parent', 'latest_message', 'context_size', 'assist_query_params', ), diff --git a/scripts/fixup_dialogflow_v2beta1_keywords.py b/scripts/fixup_dialogflow_v2beta1_keywords.py index 5f973a106..bb10e1a55 100644 --- a/scripts/fixup_dialogflow_v2beta1_keywords.py +++ b/scripts/fixup_dialogflow_v2beta1_keywords.py @@ -75,6 +75,7 @@ class dialogflowCallTransformer(cst.CSTTransformer): 'delete_version': ('name', ), 'detect_intent': ('session', 'query_input', 'query_params', 'output_audio_config', 'output_audio_config_mask', 'input_audio', ), 'export_agent': ('parent', 'agent_uri', ), + 'generate_stateless_summary': ('stateless_conversation', 'conversation_profile', 'latest_message', 'max_context_size', ), 'get_agent': ('parent', ), 'get_answer_record': ('name', ), 'get_context': ('name', ), @@ -112,8 +113,8 @@ class dialogflowCallTransformer(cst.CSTTransformer): 'search_agents': ('parent', 'page_size', 'page_token', ), 'set_agent': ('agent', 'update_mask', ), 'set_suggestion_feature_config': ('conversation_profile', 'participant_role', 'suggestion_feature_config', ), - 'streaming_analyze_content': ('participant', 'audio_config', 'text_config', 'reply_audio_config', 'input_audio', 'input_text', 'input_dtmf', 'query_params', 'assist_query_params', 'cx_parameters', 'cx_current_page', 'enable_partial_automated_agent_reply', ), - 'streaming_detect_intent': ('session', 'query_input', 'query_params', 'single_utterance', 'output_audio_config', 'output_audio_config_mask', 'input_audio', ), + 'streaming_analyze_content': ('participant', 'audio_config', 'text_config', 'reply_audio_config', 'input_audio', 'input_text', 'input_dtmf', 'query_params', 'assist_query_params', 'cx_parameters', 'cx_current_page', 'enable_partial_automated_agent_reply', 'enable_debugging_info', ), + 'streaming_detect_intent': ('session', 'query_input', 'query_params', 'single_utterance', 'output_audio_config', 'output_audio_config_mask', 'input_audio', 'enable_debugging_info', ), 'suggest_articles': ('parent', 'latest_message', 'context_size', 'assist_query_params', ), 'suggest_conversation_summary': ('conversation', 'latest_message', 'context_size', 'assist_query_params', ), 'suggest_faq_answers': ('parent', 'latest_message', 'context_size', 'assist_query_params', ), diff --git a/tests/unit/gapic/dialogflow_v2/test_conversation_profiles.py b/tests/unit/gapic/dialogflow_v2/test_conversation_profiles.py index 7c56ecab6..e8c2c60e2 100644 --- a/tests/unit/gapic/dialogflow_v2/test_conversation_profiles.py +++ b/tests/unit/gapic/dialogflow_v2/test_conversation_profiles.py @@ -3501,7 +3501,10 @@ def test_create_conversation_profile_rest(request_type): "drop_ivr_messages": True, }, }, - "conversation_model_config": {"model": "model_value"}, + "conversation_model_config": { + "model": "model_value", + "baseline_model_version": "baseline_model_version_value", + }, "conversation_process_config": {"recent_sentences_count": 2352}, } ], @@ -3777,7 +3780,10 @@ def test_create_conversation_profile_rest_bad_request( "drop_ivr_messages": True, }, }, - "conversation_model_config": {"model": "model_value"}, + "conversation_model_config": { + "model": "model_value", + "baseline_model_version": "baseline_model_version_value", + }, "conversation_process_config": {"recent_sentences_count": 2352}, } ], @@ -3952,7 +3958,10 @@ def test_update_conversation_profile_rest(request_type): "drop_ivr_messages": True, }, }, - "conversation_model_config": {"model": "model_value"}, + "conversation_model_config": { + "model": "model_value", + "baseline_model_version": "baseline_model_version_value", + }, "conversation_process_config": {"recent_sentences_count": 2352}, } ], @@ -4229,7 +4238,10 @@ def test_update_conversation_profile_rest_bad_request( "drop_ivr_messages": True, }, }, - "conversation_model_config": {"model": "model_value"}, + "conversation_model_config": { + "model": "model_value", + "baseline_model_version": "baseline_model_version_value", + }, "conversation_process_config": {"recent_sentences_count": 2352}, } ], diff --git a/tests/unit/gapic/dialogflow_v2/test_conversations.py b/tests/unit/gapic/dialogflow_v2/test_conversations.py index eb75ae541..c6a968097 100644 --- a/tests/unit/gapic/dialogflow_v2/test_conversations.py +++ b/tests/unit/gapic/dialogflow_v2/test_conversations.py @@ -36,6 +36,7 @@ from google.longrunning import operations_pb2 from google.oauth2 import service_account from google.protobuf import json_format +from google.protobuf import struct_pb2 # type: ignore from google.protobuf import timestamp_pb2 # type: ignore import grpc from grpc.experimental import aio @@ -51,9 +52,10 @@ pagers, transports, ) +from google.cloud.dialogflow_v2.types import conversation_profile, participant, session +from google.cloud.dialogflow_v2.types import audio_config from google.cloud.dialogflow_v2.types import conversation from google.cloud.dialogflow_v2.types import conversation as gcd_conversation -from google.cloud.dialogflow_v2.types import participant def client_cert_source_callback(): @@ -2627,6 +2629,171 @@ async def test_suggest_conversation_summary_flattened_error_async(): ) +@pytest.mark.parametrize( + "request_type", + [ + conversation.GenerateStatelessSummaryRequest, + dict, + ], +) +def test_generate_stateless_summary(request_type, transport: str = "grpc"): + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.generate_stateless_summary), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.GenerateStatelessSummaryResponse( + latest_message="latest_message_value", + context_size=1311, + ) + response = client.generate_stateless_summary(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == conversation.GenerateStatelessSummaryRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.GenerateStatelessSummaryResponse) + assert response.latest_message == "latest_message_value" + assert response.context_size == 1311 + + +def test_generate_stateless_summary_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.generate_stateless_summary), "__call__" + ) as call: + client.generate_stateless_summary() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == conversation.GenerateStatelessSummaryRequest() + + +@pytest.mark.asyncio +async def test_generate_stateless_summary_async( + transport: str = "grpc_asyncio", + request_type=conversation.GenerateStatelessSummaryRequest, +): + client = ConversationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.generate_stateless_summary), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.GenerateStatelessSummaryResponse( + latest_message="latest_message_value", + context_size=1311, + ) + ) + response = await client.generate_stateless_summary(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == conversation.GenerateStatelessSummaryRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.GenerateStatelessSummaryResponse) + assert response.latest_message == "latest_message_value" + assert response.context_size == 1311 + + +@pytest.mark.asyncio +async def test_generate_stateless_summary_async_from_dict(): + await test_generate_stateless_summary_async(request_type=dict) + + +def test_generate_stateless_summary_field_headers(): + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = conversation.GenerateStatelessSummaryRequest() + + request.stateless_conversation.parent = "parent_value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.generate_stateless_summary), "__call__" + ) as call: + call.return_value = conversation.GenerateStatelessSummaryResponse() + client.generate_stateless_summary(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "stateless_conversation.parent=parent_value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_generate_stateless_summary_field_headers_async(): + client = ConversationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = conversation.GenerateStatelessSummaryRequest() + + request.stateless_conversation.parent = "parent_value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.generate_stateless_summary), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.GenerateStatelessSummaryResponse() + ) + await client.generate_stateless_summary(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "stateless_conversation.parent=parent_value", + ) in kw["metadata"] + + @pytest.mark.parametrize( "request_type", [ @@ -4452,6 +4619,228 @@ def test_suggest_conversation_summary_rest_error(): ) +@pytest.mark.parametrize( + "request_type", + [ + conversation.GenerateStatelessSummaryRequest, + dict, + ], +) +def test_generate_stateless_summary_rest(request_type): + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # send a request that will satisfy transcoding + request_init = {"stateless_conversation": {"parent": "projects/sample1"}} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = conversation.GenerateStatelessSummaryResponse( + latest_message="latest_message_value", + context_size=1311, + ) + + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 200 + pb_return_value = conversation.GenerateStatelessSummaryResponse.pb(return_value) + json_return_value = json_format.MessageToJson(pb_return_value) + + response_value._content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.generate_stateless_summary(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.GenerateStatelessSummaryResponse) + assert response.latest_message == "latest_message_value" + assert response.context_size == 1311 + + +def test_generate_stateless_summary_rest_required_fields( + request_type=conversation.GenerateStatelessSummaryRequest, +): + transport_class = transports.ConversationsRestTransport + + request_init = {} + request = request_type(**request_init) + pb_request = request_type.pb(request) + jsonified_request = json.loads( + json_format.MessageToJson( + pb_request, + including_default_value_fields=False, + use_integers_for_enums=False, + ) + ) + + # verify fields with default values are dropped + + unset_fields = transport_class( + credentials=ga_credentials.AnonymousCredentials() + ).generate_stateless_summary._get_unset_required_fields(jsonified_request) + jsonified_request.update(unset_fields) + + # verify required fields with default values are now present + + unset_fields = transport_class( + credentials=ga_credentials.AnonymousCredentials() + ).generate_stateless_summary._get_unset_required_fields(jsonified_request) + jsonified_request.update(unset_fields) + + # verify required fields with non-default values are left alone + + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type(**request_init) + + # Designate an appropriate value for the returned response. + return_value = conversation.GenerateStatelessSummaryResponse() + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # We need to mock transcode() because providing default values + # for required fields will fail the real version if the http_options + # expect actual values for those fields. + with mock.patch.object(path_template, "transcode") as transcode: + # A uri without fields and an empty body will force all the + # request fields to show up in the query_params. + pb_request = request_type.pb(request) + transcode_result = { + "uri": "v1/sample_method", + "method": "post", + "query_params": pb_request, + } + transcode_result["body"] = pb_request + transcode.return_value = transcode_result + + response_value = Response() + response_value.status_code = 200 + + pb_return_value = conversation.GenerateStatelessSummaryResponse.pb( + return_value + ) + json_return_value = json_format.MessageToJson(pb_return_value) + + response_value._content = json_return_value.encode("UTF-8") + req.return_value = response_value + + response = client.generate_stateless_summary(request) + + expected_params = [("$alt", "json;enum-encoding=int")] + actual_params = req.call_args.kwargs["params"] + assert expected_params == actual_params + + +def test_generate_stateless_summary_rest_unset_required_fields(): + transport = transports.ConversationsRestTransport( + credentials=ga_credentials.AnonymousCredentials + ) + + unset_fields = transport.generate_stateless_summary._get_unset_required_fields({}) + assert set(unset_fields) == ( + set(()) + & set( + ( + "statelessConversation", + "conversationProfile", + ) + ) + ) + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_generate_stateless_summary_rest_interceptors(null_interceptor): + transport = transports.ConversationsRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.ConversationsRestInterceptor(), + ) + client = ConversationsClient(transport=transport) + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.ConversationsRestInterceptor, "post_generate_stateless_summary" + ) as post, mock.patch.object( + transports.ConversationsRestInterceptor, "pre_generate_stateless_summary" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = conversation.GenerateStatelessSummaryRequest.pb( + conversation.GenerateStatelessSummaryRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = Response() + req.return_value.status_code = 200 + req.return_value.request = PreparedRequest() + req.return_value._content = ( + conversation.GenerateStatelessSummaryResponse.to_json( + conversation.GenerateStatelessSummaryResponse() + ) + ) + + request = conversation.GenerateStatelessSummaryRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = conversation.GenerateStatelessSummaryResponse() + + client.generate_stateless_summary( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_generate_stateless_summary_rest_bad_request( + transport: str = "rest", request_type=conversation.GenerateStatelessSummaryRequest +): + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # send a request that will satisfy transcoding + request_init = {"stateless_conversation": {"parent": "projects/sample1"}} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.generate_stateless_summary(request) + + +def test_generate_stateless_summary_rest_error(): + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + def test_credentials_transport_error(): # It is an error to provide credentials and a transport instance. transport = transports.ConversationsGrpcTransport( @@ -4597,6 +4986,7 @@ def test_conversations_base_transport(): "complete_conversation", "list_messages", "suggest_conversation_summary", + "generate_stateless_summary", "get_location", "list_locations", "get_operation", @@ -4890,6 +5280,9 @@ def test_conversations_client_transport_session_collision(transport_name): session1 = client1.transport.suggest_conversation_summary._session session2 = client2.transport.suggest_conversation_summary._session assert session1 != session2 + session1 = client1.transport.generate_stateless_summary._session + session2 = client2.transport.generate_stateless_summary._session + assert session1 != session2 def test_conversations_grpc_transport_channel(): @@ -5016,9 +5409,29 @@ def test_conversations_transport_channel_mtls_with_adc(transport_class): assert transport.grpc_channel == mock_grpc_channel -def test_answer_record_path(): +def test_agent_path(): project = "squid" - answer_record = "clam" + expected = "projects/{project}/agent".format( + project=project, + ) + actual = ConversationsClient.agent_path(project) + assert expected == actual + + +def test_parse_agent_path(): + expected = { + "project": "clam", + } + path = ConversationsClient.agent_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_agent_path(path) + assert expected == actual + + +def test_answer_record_path(): + project = "whelk" + answer_record = "octopus" expected = "projects/{project}/answerRecords/{answer_record}".format( project=project, answer_record=answer_record, @@ -5029,8 +5442,8 @@ def test_answer_record_path(): def test_parse_answer_record_path(): expected = { - "project": "whelk", - "answer_record": "octopus", + "project": "oyster", + "answer_record": "nudibranch", } path = ConversationsClient.answer_record_path(**expected) @@ -5040,8 +5453,8 @@ def test_parse_answer_record_path(): def test_conversation_path(): - project = "oyster" - conversation = "nudibranch" + project = "cuttlefish" + conversation = "mussel" expected = "projects/{project}/conversations/{conversation}".format( project=project, conversation=conversation, @@ -5052,8 +5465,8 @@ def test_conversation_path(): def test_parse_conversation_path(): expected = { - "project": "cuttlefish", - "conversation": "mussel", + "project": "winkle", + "conversation": "nautilus", } path = ConversationsClient.conversation_path(**expected) @@ -5062,9 +5475,37 @@ def test_parse_conversation_path(): assert expected == actual +def test_conversation_model_path(): + project = "scallop" + location = "abalone" + conversation_model = "squid" + expected = "projects/{project}/locations/{location}/conversationModels/{conversation_model}".format( + project=project, + location=location, + conversation_model=conversation_model, + ) + actual = ConversationsClient.conversation_model_path( + project, location, conversation_model + ) + assert expected == actual + + +def test_parse_conversation_model_path(): + expected = { + "project": "clam", + "location": "whelk", + "conversation_model": "octopus", + } + path = ConversationsClient.conversation_model_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_conversation_model_path(path) + assert expected == actual + + def test_conversation_profile_path(): - project = "winkle" - conversation_profile = "nautilus" + project = "oyster" + conversation_profile = "nudibranch" expected = "projects/{project}/conversationProfiles/{conversation_profile}".format( project=project, conversation_profile=conversation_profile, @@ -5077,8 +5518,8 @@ def test_conversation_profile_path(): def test_parse_conversation_profile_path(): expected = { - "project": "scallop", - "conversation_profile": "abalone", + "project": "cuttlefish", + "conversation_profile": "mussel", } path = ConversationsClient.conversation_profile_path(**expected) @@ -5087,6 +5528,83 @@ def test_parse_conversation_profile_path(): assert expected == actual +def test_cx_security_settings_path(): + project = "winkle" + location = "nautilus" + security_settings = "scallop" + expected = "projects/{project}/locations/{location}/securitySettings/{security_settings}".format( + project=project, + location=location, + security_settings=security_settings, + ) + actual = ConversationsClient.cx_security_settings_path( + project, location, security_settings + ) + assert expected == actual + + +def test_parse_cx_security_settings_path(): + expected = { + "project": "abalone", + "location": "squid", + "security_settings": "clam", + } + path = ConversationsClient.cx_security_settings_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_cx_security_settings_path(path) + assert expected == actual + + +def test_document_path(): + project = "whelk" + knowledge_base = "octopus" + document = "oyster" + expected = "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, + knowledge_base=knowledge_base, + document=document, + ) + actual = ConversationsClient.document_path(project, knowledge_base, document) + assert expected == actual + + +def test_parse_document_path(): + expected = { + "project": "nudibranch", + "knowledge_base": "cuttlefish", + "document": "mussel", + } + path = ConversationsClient.document_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_document_path(path) + assert expected == actual + + +def test_knowledge_base_path(): + project = "winkle" + knowledge_base = "nautilus" + expected = "projects/{project}/knowledgeBases/{knowledge_base}".format( + project=project, + knowledge_base=knowledge_base, + ) + actual = ConversationsClient.knowledge_base_path(project, knowledge_base) + assert expected == actual + + +def test_parse_knowledge_base_path(): + expected = { + "project": "scallop", + "knowledge_base": "abalone", + } + path = ConversationsClient.knowledge_base_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_knowledge_base_path(path) + assert expected == actual + + def test_message_path(): project = "squid" conversation = "clam" diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_conversation_profiles.py b/tests/unit/gapic/dialogflow_v2beta1/test_conversation_profiles.py index ce88ebba3..3c472f27e 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_conversation_profiles.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_conversation_profiles.py @@ -3507,7 +3507,10 @@ def test_create_conversation_profile_rest(request_type): "drop_ivr_messages": True, }, }, - "conversation_model_config": {"model": "model_value"}, + "conversation_model_config": { + "model": "model_value", + "baseline_model_version": "baseline_model_version_value", + }, "conversation_process_config": {"recent_sentences_count": 2352}, } ], @@ -3783,7 +3786,10 @@ def test_create_conversation_profile_rest_bad_request( "drop_ivr_messages": True, }, }, - "conversation_model_config": {"model": "model_value"}, + "conversation_model_config": { + "model": "model_value", + "baseline_model_version": "baseline_model_version_value", + }, "conversation_process_config": {"recent_sentences_count": 2352}, } ], @@ -3959,7 +3965,10 @@ def test_update_conversation_profile_rest(request_type): "drop_ivr_messages": True, }, }, - "conversation_model_config": {"model": "model_value"}, + "conversation_model_config": { + "model": "model_value", + "baseline_model_version": "baseline_model_version_value", + }, "conversation_process_config": {"recent_sentences_count": 2352}, } ], @@ -4236,7 +4245,10 @@ def test_update_conversation_profile_rest_bad_request( "drop_ivr_messages": True, }, }, - "conversation_model_config": {"model": "model_value"}, + "conversation_model_config": { + "model": "model_value", + "baseline_model_version": "baseline_model_version_value", + }, "conversation_process_config": {"recent_sentences_count": 2352}, } ], diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_conversations.py b/tests/unit/gapic/dialogflow_v2beta1/test_conversations.py index 23e1177b9..337ae7f2c 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_conversations.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_conversations.py @@ -52,9 +52,14 @@ pagers, transports, ) +from google.cloud.dialogflow_v2beta1.types import ( + conversation_profile, + participant, + session, +) from google.cloud.dialogflow_v2beta1.types import conversation as gcd_conversation +from google.cloud.dialogflow_v2beta1.types import audio_config from google.cloud.dialogflow_v2beta1.types import conversation -from google.cloud.dialogflow_v2beta1.types import participant, session def client_cert_source_callback(): @@ -2869,6 +2874,171 @@ async def test_suggest_conversation_summary_flattened_error_async(): ) +@pytest.mark.parametrize( + "request_type", + [ + conversation.GenerateStatelessSummaryRequest, + dict, + ], +) +def test_generate_stateless_summary(request_type, transport: str = "grpc"): + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.generate_stateless_summary), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.GenerateStatelessSummaryResponse( + latest_message="latest_message_value", + context_size=1311, + ) + response = client.generate_stateless_summary(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == conversation.GenerateStatelessSummaryRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.GenerateStatelessSummaryResponse) + assert response.latest_message == "latest_message_value" + assert response.context_size == 1311 + + +def test_generate_stateless_summary_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.generate_stateless_summary), "__call__" + ) as call: + client.generate_stateless_summary() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == conversation.GenerateStatelessSummaryRequest() + + +@pytest.mark.asyncio +async def test_generate_stateless_summary_async( + transport: str = "grpc_asyncio", + request_type=conversation.GenerateStatelessSummaryRequest, +): + client = ConversationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.generate_stateless_summary), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.GenerateStatelessSummaryResponse( + latest_message="latest_message_value", + context_size=1311, + ) + ) + response = await client.generate_stateless_summary(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == conversation.GenerateStatelessSummaryRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.GenerateStatelessSummaryResponse) + assert response.latest_message == "latest_message_value" + assert response.context_size == 1311 + + +@pytest.mark.asyncio +async def test_generate_stateless_summary_async_from_dict(): + await test_generate_stateless_summary_async(request_type=dict) + + +def test_generate_stateless_summary_field_headers(): + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = conversation.GenerateStatelessSummaryRequest() + + request.stateless_conversation.parent = "parent_value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.generate_stateless_summary), "__call__" + ) as call: + call.return_value = conversation.GenerateStatelessSummaryResponse() + client.generate_stateless_summary(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "stateless_conversation.parent=parent_value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_generate_stateless_summary_field_headers_async(): + client = ConversationsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = conversation.GenerateStatelessSummaryRequest() + + request.stateless_conversation.parent = "parent_value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.generate_stateless_summary), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.GenerateStatelessSummaryResponse() + ) + await client.generate_stateless_summary(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "stateless_conversation.parent=parent_value", + ) in kw["metadata"] + + @pytest.mark.parametrize( "request_type", [ @@ -4972,6 +5142,228 @@ def test_suggest_conversation_summary_rest_error(): ) +@pytest.mark.parametrize( + "request_type", + [ + conversation.GenerateStatelessSummaryRequest, + dict, + ], +) +def test_generate_stateless_summary_rest(request_type): + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # send a request that will satisfy transcoding + request_init = {"stateless_conversation": {"parent": "projects/sample1"}} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = conversation.GenerateStatelessSummaryResponse( + latest_message="latest_message_value", + context_size=1311, + ) + + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 200 + pb_return_value = conversation.GenerateStatelessSummaryResponse.pb(return_value) + json_return_value = json_format.MessageToJson(pb_return_value) + + response_value._content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.generate_stateless_summary(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.GenerateStatelessSummaryResponse) + assert response.latest_message == "latest_message_value" + assert response.context_size == 1311 + + +def test_generate_stateless_summary_rest_required_fields( + request_type=conversation.GenerateStatelessSummaryRequest, +): + transport_class = transports.ConversationsRestTransport + + request_init = {} + request = request_type(**request_init) + pb_request = request_type.pb(request) + jsonified_request = json.loads( + json_format.MessageToJson( + pb_request, + including_default_value_fields=False, + use_integers_for_enums=False, + ) + ) + + # verify fields with default values are dropped + + unset_fields = transport_class( + credentials=ga_credentials.AnonymousCredentials() + ).generate_stateless_summary._get_unset_required_fields(jsonified_request) + jsonified_request.update(unset_fields) + + # verify required fields with default values are now present + + unset_fields = transport_class( + credentials=ga_credentials.AnonymousCredentials() + ).generate_stateless_summary._get_unset_required_fields(jsonified_request) + jsonified_request.update(unset_fields) + + # verify required fields with non-default values are left alone + + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type(**request_init) + + # Designate an appropriate value for the returned response. + return_value = conversation.GenerateStatelessSummaryResponse() + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # We need to mock transcode() because providing default values + # for required fields will fail the real version if the http_options + # expect actual values for those fields. + with mock.patch.object(path_template, "transcode") as transcode: + # A uri without fields and an empty body will force all the + # request fields to show up in the query_params. + pb_request = request_type.pb(request) + transcode_result = { + "uri": "v1/sample_method", + "method": "post", + "query_params": pb_request, + } + transcode_result["body"] = pb_request + transcode.return_value = transcode_result + + response_value = Response() + response_value.status_code = 200 + + pb_return_value = conversation.GenerateStatelessSummaryResponse.pb( + return_value + ) + json_return_value = json_format.MessageToJson(pb_return_value) + + response_value._content = json_return_value.encode("UTF-8") + req.return_value = response_value + + response = client.generate_stateless_summary(request) + + expected_params = [("$alt", "json;enum-encoding=int")] + actual_params = req.call_args.kwargs["params"] + assert expected_params == actual_params + + +def test_generate_stateless_summary_rest_unset_required_fields(): + transport = transports.ConversationsRestTransport( + credentials=ga_credentials.AnonymousCredentials + ) + + unset_fields = transport.generate_stateless_summary._get_unset_required_fields({}) + assert set(unset_fields) == ( + set(()) + & set( + ( + "statelessConversation", + "conversationProfile", + ) + ) + ) + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_generate_stateless_summary_rest_interceptors(null_interceptor): + transport = transports.ConversationsRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.ConversationsRestInterceptor(), + ) + client = ConversationsClient(transport=transport) + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.ConversationsRestInterceptor, "post_generate_stateless_summary" + ) as post, mock.patch.object( + transports.ConversationsRestInterceptor, "pre_generate_stateless_summary" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = conversation.GenerateStatelessSummaryRequest.pb( + conversation.GenerateStatelessSummaryRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = Response() + req.return_value.status_code = 200 + req.return_value.request = PreparedRequest() + req.return_value._content = ( + conversation.GenerateStatelessSummaryResponse.to_json( + conversation.GenerateStatelessSummaryResponse() + ) + ) + + request = conversation.GenerateStatelessSummaryRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = conversation.GenerateStatelessSummaryResponse() + + client.generate_stateless_summary( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_generate_stateless_summary_rest_bad_request( + transport: str = "rest", request_type=conversation.GenerateStatelessSummaryRequest +): + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # send a request that will satisfy transcoding + request_init = {"stateless_conversation": {"parent": "projects/sample1"}} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.generate_stateless_summary(request) + + +def test_generate_stateless_summary_rest_error(): + client = ConversationsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + def test_credentials_transport_error(): # It is an error to provide credentials and a transport instance. transport = transports.ConversationsGrpcTransport( @@ -5118,6 +5510,7 @@ def test_conversations_base_transport(): "batch_create_messages", "list_messages", "suggest_conversation_summary", + "generate_stateless_summary", "get_location", "list_locations", "get_operation", @@ -5414,6 +5807,9 @@ def test_conversations_client_transport_session_collision(transport_name): session1 = client1.transport.suggest_conversation_summary._session session2 = client2.transport.suggest_conversation_summary._session assert session1 != session2 + session1 = client1.transport.generate_stateless_summary._session + session2 = client2.transport.generate_stateless_summary._session + assert session1 != session2 def test_conversations_grpc_transport_channel(): @@ -5540,9 +5936,29 @@ def test_conversations_transport_channel_mtls_with_adc(transport_class): assert transport.grpc_channel == mock_grpc_channel -def test_answer_record_path(): +def test_agent_path(): project = "squid" - answer_record = "clam" + expected = "projects/{project}/agent".format( + project=project, + ) + actual = ConversationsClient.agent_path(project) + assert expected == actual + + +def test_parse_agent_path(): + expected = { + "project": "clam", + } + path = ConversationsClient.agent_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_agent_path(path) + assert expected == actual + + +def test_answer_record_path(): + project = "whelk" + answer_record = "octopus" expected = "projects/{project}/answerRecords/{answer_record}".format( project=project, answer_record=answer_record, @@ -5553,8 +5969,8 @@ def test_answer_record_path(): def test_parse_answer_record_path(): expected = { - "project": "whelk", - "answer_record": "octopus", + "project": "oyster", + "answer_record": "nudibranch", } path = ConversationsClient.answer_record_path(**expected) @@ -5564,8 +5980,8 @@ def test_parse_answer_record_path(): def test_conversation_path(): - project = "oyster" - conversation = "nudibranch" + project = "cuttlefish" + conversation = "mussel" expected = "projects/{project}/conversations/{conversation}".format( project=project, conversation=conversation, @@ -5576,8 +5992,8 @@ def test_conversation_path(): def test_parse_conversation_path(): expected = { - "project": "cuttlefish", - "conversation": "mussel", + "project": "winkle", + "conversation": "nautilus", } path = ConversationsClient.conversation_path(**expected) @@ -5586,9 +6002,37 @@ def test_parse_conversation_path(): assert expected == actual +def test_conversation_model_path(): + project = "scallop" + location = "abalone" + conversation_model = "squid" + expected = "projects/{project}/locations/{location}/conversationModels/{conversation_model}".format( + project=project, + location=location, + conversation_model=conversation_model, + ) + actual = ConversationsClient.conversation_model_path( + project, location, conversation_model + ) + assert expected == actual + + +def test_parse_conversation_model_path(): + expected = { + "project": "clam", + "location": "whelk", + "conversation_model": "octopus", + } + path = ConversationsClient.conversation_model_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_conversation_model_path(path) + assert expected == actual + + def test_conversation_profile_path(): - project = "winkle" - conversation_profile = "nautilus" + project = "oyster" + conversation_profile = "nudibranch" expected = "projects/{project}/conversationProfiles/{conversation_profile}".format( project=project, conversation_profile=conversation_profile, @@ -5601,8 +6045,8 @@ def test_conversation_profile_path(): def test_parse_conversation_profile_path(): expected = { - "project": "scallop", - "conversation_profile": "abalone", + "project": "cuttlefish", + "conversation_profile": "mussel", } path = ConversationsClient.conversation_profile_path(**expected) @@ -5611,6 +6055,83 @@ def test_parse_conversation_profile_path(): assert expected == actual +def test_cx_security_settings_path(): + project = "winkle" + location = "nautilus" + security_settings = "scallop" + expected = "projects/{project}/locations/{location}/securitySettings/{security_settings}".format( + project=project, + location=location, + security_settings=security_settings, + ) + actual = ConversationsClient.cx_security_settings_path( + project, location, security_settings + ) + assert expected == actual + + +def test_parse_cx_security_settings_path(): + expected = { + "project": "abalone", + "location": "squid", + "security_settings": "clam", + } + path = ConversationsClient.cx_security_settings_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_cx_security_settings_path(path) + assert expected == actual + + +def test_document_path(): + project = "whelk" + knowledge_base = "octopus" + document = "oyster" + expected = "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, + knowledge_base=knowledge_base, + document=document, + ) + actual = ConversationsClient.document_path(project, knowledge_base, document) + assert expected == actual + + +def test_parse_document_path(): + expected = { + "project": "nudibranch", + "knowledge_base": "cuttlefish", + "document": "mussel", + } + path = ConversationsClient.document_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_document_path(path) + assert expected == actual + + +def test_knowledge_base_path(): + project = "winkle" + knowledge_base = "nautilus" + expected = "projects/{project}/knowledgeBases/{knowledge_base}".format( + project=project, + knowledge_base=knowledge_base, + ) + actual = ConversationsClient.knowledge_base_path(project, knowledge_base) + assert expected == actual + + +def test_parse_knowledge_base_path(): + expected = { + "project": "scallop", + "knowledge_base": "abalone", + } + path = ConversationsClient.knowledge_base_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_knowledge_base_path(path) + assert expected == actual + + def test_message_path(): project = "squid" conversation = "clam"