// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package google.cloud.retail.v2alpha;

import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/cloud/retail/v2alpha/common.proto";
import "google/cloud/retail/v2alpha/search_service.proto";

option csharp_namespace = "Google.Cloud.Retail.V2Alpha";
option go_package = "cloud.google.com/go/retail/apiv2alpha/retailpb;retailpb";
option java_multiple_files = true;
option java_outer_classname = "ConversationalSearchServiceProto";
option java_package = "com.google.cloud.retail.v2alpha";
option objc_class_prefix = "RETAIL";
option php_namespace = "Google\\Cloud\\Retail\\V2alpha";
option ruby_package = "Google::Cloud::Retail::V2alpha";

// Service for retail conversational search.
//
// This feature is only available for users who have Retail Conversational
// Search enabled. Enable Retail Conversational Search on Cloud Console
// before using this feature.
service ConversationalSearchService {
  option (google.api.default_host) = "retail.googleapis.com";
  option (google.api.oauth_scopes) =
      "https://www.googleapis.com/auth/cloud-platform";

  // Performs a conversational search.
  //
  // This feature is only available for users who have Conversational Search
  // enabled.
  rpc ConversationalSearch(ConversationalSearchRequest)
      returns (stream ConversationalSearchResponse) {
    option (google.api.http) = {
      post: "/v2alpha/{placement=projects/*/locations/*/catalogs/*/placements/*}:conversationalSearch"
      body: "*"
      additional_bindings {
        post: "/v2alpha/{placement=projects/*/locations/*/catalogs/*/servingConfigs/*}:conversationalSearch"
        body: "*"
      }
    };
  }
}

// Request message for
// [ConversationalSearchService.ConversationalSearch][google.cloud.retail.v2alpha.ConversationalSearchService.ConversationalSearch]
// method.
message ConversationalSearchRequest {
  // Search parameters.
  message SearchParams {
    // Optional. The filter string to restrict search results.
    //
    // The syntax of the filter string is the same as
    // [SearchRequest.filter][google.cloud.retail.v2alpha.SearchRequest.filter].
    string filter = 1 [(google.api.field_behavior) = OPTIONAL];

    // Optional. The canonical filter string to restrict search results.
    //
    // The syntax of the canonical filter string is the same as
    // [SearchRequest.canonical_filter][google.cloud.retail.v2alpha.SearchRequest.canonical_filter].
    string canonical_filter = 2 [(google.api.field_behavior) = OPTIONAL];

    // Optional. The sort string to specify the sorting of search results.
    //
    // The syntax of the sort string is the same as
    // [SearchRequest.sort][].
    string sort_by = 3 [(google.api.field_behavior) = OPTIONAL];

    // Optional. The boost spec to specify the boosting of search results.
    //
    // The syntax of the boost spec is the same as
    // [SearchRequest.boost_spec][google.cloud.retail.v2alpha.SearchRequest.boost_spec].
    SearchRequest.BoostSpec boost_spec = 4
        [(google.api.field_behavior) = OPTIONAL];
  }

  // This field specifies the current user answer during the conversational
  // filtering search. This can be either user selected from suggested answers
  // or user input plain text.
  message UserAnswer {
    // This field specifies the selected answers during the conversational
    // search.
    message SelectedAnswer {
      // Optional. This field specifies the selected answer which is a attribute
      // key-value.
      ProductAttributeValue product_attribute_value = 1
          [(google.api.field_behavior) = OPTIONAL];
    }

    // This field specifies the type of user answer.
    oneof type {
      // This field specifies the incremental input text from the user during
      // the conversational search.
      string text_answer = 1;

      // Optional. This field specifies the selected answer during the
      // conversational search. This should be a subset of
      // [ConversationalSearchResponse.followup_question.suggested_answers][].
      SelectedAnswer selected_answer = 2
          [(google.api.field_behavior) = OPTIONAL];
    }
  }

  // This field specifies all conversational filtering related parameters
  // addition to conversational retail search.
  message ConversationalFilteringSpec {
    // Enum to control Conversational Filtering mode.
    enum Mode {
      // Default value.
      MODE_UNSPECIFIED = 0;

      // Enabled Conversational Filtering without default Conversational Search.
      CONVERSATIONAL_FILTER_ONLY = 3;
    }

    // Optional. This field is deprecated. Please use
    // [ConversationalFilteringSpec.conversational_filtering_mode][google.cloud.retail.v2alpha.ConversationalSearchRequest.ConversationalFilteringSpec.conversational_filtering_mode]
    // instead.
    bool enable_conversational_filtering = 1
        [deprecated = true, (google.api.field_behavior) = OPTIONAL];

    // Optional. This field specifies the current user answer during the
    // conversational filtering search. It can be either user selected from
    // suggested answers or user input plain text.
    UserAnswer user_answer = 2 [(google.api.field_behavior) = OPTIONAL];

    // Optional. Mode to control Conversational Filtering.
    // Defaults to
    // [Mode.DISABLED][google.cloud.retail.v2alpha.ConversationalSearchRequest.ConversationalFilteringSpec.Mode.DISABLED]
    // if it's unset.
    Mode conversational_filtering_mode = 4
        [(google.api.field_behavior) = OPTIONAL];
  }

  // Required. The resource name of the search engine placement, such as
  // `projects/*/locations/global/catalogs/default_catalog/placements/default_search`
  // or
  // `projects/*/locations/global/catalogs/default_catalog/servingConfigs/default_serving_config`
  // This field is used to identify the serving config name and the set
  // of models that will be used to make the search.
  string placement = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. The branch resource name, such as
  // `projects/*/locations/global/catalogs/default_catalog/branches/0`.
  //
  // Use "default_branch" as the branch ID or leave this field empty, to search
  // products under the default branch.
  string branch = 2 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "retail.googleapis.com/Branch" }
  ];

  // Optional. Raw search query to be searched for.
  //
  // If this field is empty, the request is considered a category browsing
  // request.
  string query = 3 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The categories associated with a category page. Must be set for
  // category navigation queries to achieve good search quality. The format
  // should be the same as
  // [UserEvent.page_categories][google.cloud.retail.v2alpha.UserEvent.page_categories];
  //
  // To represent full path of category, use '>' sign to separate different
  // hierarchies. If '>' is part of the category name, replace it with
  // other character(s).
  //
  // Category pages include special pages such as sales or promotions. For
  // instance, a special sale page may have the category hierarchy:
  // "pageCategories" : ["Sales > 2017 Black Friday Deals"].
  repeated string page_categories = 4 [(google.api.field_behavior) = OPTIONAL];

  // Optional. This field specifies the conversation id, which maintains the
  // state of the conversation between client side and server side. Use the
  // value from the previous
  // [ConversationalSearchResponse.conversation_id][google.cloud.retail.v2alpha.ConversationalSearchResponse.conversation_id].
  // For the initial request, this should be empty.
  string conversation_id = 5 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Search parameters.
  SearchParams search_params = 6 [(google.api.field_behavior) = OPTIONAL];

  // Required. A unique identifier for tracking visitors. For example, this
  // could be implemented with an HTTP cookie, which should be able to uniquely
  // identify a visitor on a single device. This unique identifier should not
  // change if the visitor logs in or out of the website.
  //
  // This should be the same identifier as
  // [UserEvent.visitor_id][google.cloud.retail.v2alpha.UserEvent.visitor_id].
  //
  // The field must be a UTF-8 encoded string with a length limit of 128
  // characters. Otherwise, an INVALID_ARGUMENT error is returned.
  string visitor_id = 9 [(google.api.field_behavior) = REQUIRED];

  // Optional. User information.
  UserInfo user_info = 7 [(google.api.field_behavior) = OPTIONAL];

  // Optional. This field specifies all conversational filtering related
  // parameters.
  ConversationalFilteringSpec conversational_filtering_spec = 8
      [(google.api.field_behavior) = OPTIONAL];
}

// Response message for
// [ConversationalSearchService.ConversationalSearch][google.cloud.retail.v2alpha.ConversationalSearchService.ConversationalSearch]
// method.
message ConversationalSearchResponse {
  // The conversational followup question generated for Intent refinement.
  message FollowupQuestion {
    // Suggested answers to the follow-up question.
    // If it's numerical attribute, only ProductAttributeInterval will be set.
    // If it's textual attribute, only productAttributeValue will be set.
    message SuggestedAnswer {
      // Product attribute value, including an attribute key and an
      // attribute value. Other types can be added here in the future.
      ProductAttributeValue product_attribute_value = 1;
    }

    // The conversational followup question generated for Intent refinement.
    string followup_question = 1;

    // The answer options provided to client for the follow-up question.
    repeated SuggestedAnswer suggested_answers = 2;
  }

  // The proposed refined search for intent-refinement/bundled shopping
  // conversation. When using CONVERSATIONAL_FILTER_ONLY mode, the
  // refined_query from search response will be populated here.
  message RefinedSearch {
    // The query to be used for search.
    string query = 1;
  }

  // This field specifies all related information that is needed on client
  // side for UI rendering of conversational filtering search.
  message ConversationalFilteringResult {
    // Additional filter that client side need to apply.
    message AdditionalFilter {
      // Product attribute value, including an attribute key and an
      // attribute value. Other types can be added here in the future.
      ProductAttributeValue product_attribute_value = 1;
    }

    // The conversational filtering question.
    FollowupQuestion followup_question = 1;

    // This is the incremental additional filters implied from the current
    // user answer. User should add the suggested addition filters to the
    // previous [ConversationalSearchRequest.search_params.filter][] and
    // [SearchRequest.filter][google.cloud.retail.v2alpha.SearchRequest.filter],
    // and use the merged filter in the follow up requests.
    AdditionalFilter additional_filter = 2;
  }

  // Conversation UUID. This field will be stored in client side storage to
  // maintain the conversation session with server and will be used for next
  // search request's
  // [ConversationalSearchRequest.conversation_id][google.cloud.retail.v2alpha.ConversationalSearchRequest.conversation_id]
  // to restore conversation state in server.
  string conversation_id = 4;

  // The proposed refined search queries. They can be used to fetch the relevant
  // search results. When using CONVERSATIONAL_FILTER_ONLY mode, the
  // refined_query from search response will be populated here.
  repeated RefinedSearch refined_search = 6;

  // This field specifies all related information that is needed on client
  // side for UI rendering of conversational filtering search.
  ConversationalFilteringResult conversational_filtering_result = 7;
}
