// 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.visionai.v1;

import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/cloud/visionai/v1/common.proto";
import "google/longrunning/operations.proto";
import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
import "google/rpc/status.proto";
import "google/type/datetime.proto";

option csharp_namespace = "Google.Cloud.VisionAI.V1";
option go_package = "cloud.google.com/go/visionai/apiv1/visionaipb;visionaipb";
option java_multiple_files = true;
option java_outer_classname = "WarehouseProto";
option java_package = "com.google.cloud.visionai.v1";
option php_namespace = "Google\\Cloud\\VisionAI\\V1";
option ruby_package = "Google::Cloud::VisionAI::V1";

// Service that manages media content + metadata for streaming.
service Warehouse {
  option (google.api.default_host) = "visionai.googleapis.com";
  option (google.api.oauth_scopes) =
      "https://www.googleapis.com/auth/cloud-platform";

  // Creates an asset inside corpus.
  rpc CreateAsset(CreateAssetRequest) returns (Asset) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*/corpora/*}/assets"
      body: "asset"
    };
    option (google.api.method_signature) = "parent,asset,asset_id";
  }

  // Updates an asset inside corpus.
  rpc UpdateAsset(UpdateAssetRequest) returns (Asset) {
    option (google.api.http) = {
      patch: "/v1/{asset.name=projects/*/locations/*/corpora/*/assets/*}"
      body: "asset"
    };
    option (google.api.method_signature) = "asset,update_mask";
  }

  // Reads an asset inside corpus.
  rpc GetAsset(GetAssetRequest) returns (Asset) {
    option (google.api.http) = {
      get: "/v1/{name=projects/*/locations/*/corpora/*/assets/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists an list of assets inside corpus.
  rpc ListAssets(ListAssetsRequest) returns (ListAssetsResponse) {
    option (google.api.http) = {
      get: "/v1/{parent=projects/*/locations/*/corpora/*}/assets"
    };
    option (google.api.method_signature) = "parent";
  }

  // Deletes asset inside corpus.
  rpc DeleteAsset(DeleteAssetRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      delete: "/v1/{name=projects/*/locations/*/corpora/*/assets/*}"
    };
    option (google.api.method_signature) = "name";
    option (google.longrunning.operation_info) = {
      response_type: "google.protobuf.Empty"
      metadata_type: "DeleteAssetMetadata"
    };
  }

  // Upload asset by specifing the asset Cloud Storage uri.
  // For video warehouse, it requires users who call this API have read access
  // to the cloud storage file. Once it is uploaded, it can be retrieved by
  // GenerateRetrievalUrl API which by default, only can retrieve cloud storage
  // files from the same project of the warehouse. To allow retrieval cloud
  // storage files that are in a separate project, it requires to find the
  // vision ai service account (Go to IAM, check checkbox to show "Include
  // Google-provided role grants", search for "Cloud Vision AI Service Agent")
  // and grant the read access of the cloud storage files to that service
  // account.
  rpc UploadAsset(UploadAssetRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{name=projects/*/locations/*/corpora/*/assets/*}:upload"
      body: "*"
    };
    option (google.longrunning.operation_info) = {
      response_type: "UploadAssetResponse"
      metadata_type: "UploadAssetMetadata"
    };
  }

  // Generates a signed url for downloading the asset.
  // For video warehouse, please see comment of UploadAsset about how to allow
  // retrieval of cloud storage files in a different project.
  rpc GenerateRetrievalUrl(GenerateRetrievalUrlRequest)
      returns (GenerateRetrievalUrlResponse) {
    option (google.api.http) = {
      post: "/v1/{name=projects/*/locations/*/corpora/*/assets/*}:generateRetrievalUrl"
      body: "*"
    };
  }

  // Analyze asset to power search capability.
  rpc AnalyzeAsset(AnalyzeAssetRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{name=projects/*/locations/*/corpora/*/assets/*}:analyze"
      body: "*"
    };
    option (google.longrunning.operation_info) = {
      response_type: "AnalyzeAssetResponse"
      metadata_type: "AnalyzeAssetMetadata"
    };
  }

  // Index one asset for search.
  // Supported corpus type: Corpus.Type.VIDEO_ON_DEMAND
  rpc IndexAsset(IndexAssetRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{name=projects/*/locations/*/corpora/*/assets/*}:index"
      body: "*"
    };
    option (google.longrunning.operation_info) = {
      response_type: "IndexAssetResponse"
      metadata_type: "IndexAssetMetadata"
    };
  }

  // Remove one asset's index data for search.
  // Supported corpus type: Corpus.Type.VIDEO_ON_DEMAND
  rpc RemoveIndexAsset(RemoveIndexAssetRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{name=projects/*/locations/*/corpora/*/assets/*}:removeIndex"
      body: "*"
    };
    option (google.longrunning.operation_info) = {
      response_type: "RemoveIndexAssetResponse"
      metadata_type: "RemoveIndexAssetMetadata"
    };
  }

  // Lists assets inside an index.
  rpc ViewIndexedAssets(ViewIndexedAssetsRequest)
      returns (ViewIndexedAssetsResponse) {
    option (google.api.http) = {
      get: "/v1/{index=projects/*/locations/*/corpora/*/indexes/*}:viewAssets"
    };
    option (google.api.method_signature) = "index";
  }

  // Creates an Index under the corpus.
  rpc CreateIndex(CreateIndexRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*/corpora/*}/indexes"
      body: "index"
    };
    option (google.api.method_signature) = "parent,index,index_id";
    option (google.longrunning.operation_info) = {
      response_type: "Index"
      metadata_type: "CreateIndexMetadata"
    };
  }

  // Updates an Index under the corpus.
  // Users can perform a metadata-only update or trigger a full index rebuild
  // with different update_mask values.
  rpc UpdateIndex(UpdateIndexRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      patch: "/v1/{index.name=projects/*/locations/*/corpora/*/indexes/*}"
      body: "index"
    };
    option (google.api.method_signature) = "index,update_mask";
    option (google.longrunning.operation_info) = {
      response_type: "Index"
      metadata_type: "UpdateIndexMetadata"
    };
  }

  // Gets the details of a single Index under a Corpus.
  rpc GetIndex(GetIndexRequest) returns (Index) {
    option (google.api.http) = {
      get: "/v1/{name=projects/*/locations/*/corpora/*/indexes/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // List all Indexes in a given Corpus.
  rpc ListIndexes(ListIndexesRequest) returns (ListIndexesResponse) {
    option (google.api.http) = {
      get: "/v1/{parent=projects/*/locations/*/corpora/*}/indexes"
    };
    option (google.api.method_signature) = "parent";
  }

  // Delete a single Index. In order to delete an index, the caller must
  // make sure that it is not deployed to any index endpoint.
  rpc DeleteIndex(DeleteIndexRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      delete: "/v1/{name=projects/*/locations/*/corpora/*/indexes/*}"
    };
    option (google.api.method_signature) = "name";
    option (google.longrunning.operation_info) = {
      response_type: "google.protobuf.Empty"
      metadata_type: "DeleteIndexMetadata"
    };
  }

  // Creates a corpus inside a project.
  rpc CreateCorpus(CreateCorpusRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*}/corpora"
      body: "corpus"
    };
    option (google.api.method_signature) = "parent,corpus";
    option (google.longrunning.operation_info) = {
      response_type: "Corpus"
      metadata_type: "CreateCorpusMetadata"
    };
  }

  // Gets corpus details inside a project.
  rpc GetCorpus(GetCorpusRequest) returns (Corpus) {
    option (google.api.http) = {
      get: "/v1/{name=projects/*/locations/*/corpora/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Updates a corpus in a project.
  rpc UpdateCorpus(UpdateCorpusRequest) returns (Corpus) {
    option (google.api.http) = {
      patch: "/v1/{corpus.name=projects/*/locations/*/corpora/*}"
      body: "corpus"
    };
    option (google.api.method_signature) = "corpus,update_mask";
  }

  // Lists all corpora in a project.
  rpc ListCorpora(ListCorporaRequest) returns (ListCorporaResponse) {
    option (google.api.http) = {
      get: "/v1/{parent=projects/*/locations/*}/corpora"
    };
    option (google.api.method_signature) = "parent";
  }

  // Deletes a corpus only if its empty.
  // Returns empty response.
  rpc DeleteCorpus(DeleteCorpusRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1/{name=projects/*/locations/*/corpora/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Analyzes a corpus.
  rpc AnalyzeCorpus(AnalyzeCorpusRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{name=projects/*/locations/*/corpora/*}:analyze"
      body: "*"
    };
    option (google.longrunning.operation_info) = {
      response_type: "AnalyzeCorpusResponse"
      metadata_type: "AnalyzeCorpusMetadata"
    };
  }

  // Creates data schema inside corpus.
  rpc CreateDataSchema(CreateDataSchemaRequest) returns (DataSchema) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*/corpora/*}/dataSchemas"
      body: "data_schema"
    };
    option (google.api.method_signature) = "parent,data_schema";
  }

  // Updates data schema inside corpus.
  rpc UpdateDataSchema(UpdateDataSchemaRequest) returns (DataSchema) {
    option (google.api.http) = {
      patch: "/v1/{data_schema.name=projects/*/locations/*/corpora/*/dataSchemas/*}"
      body: "data_schema"
    };
    option (google.api.method_signature) = "data_schema,update_mask";
  }

  // Gets data schema inside corpus.
  rpc GetDataSchema(GetDataSchemaRequest) returns (DataSchema) {
    option (google.api.http) = {
      get: "/v1/{name=projects/*/locations/*/corpora/*/dataSchemas/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Deletes data schema inside corpus.
  rpc DeleteDataSchema(DeleteDataSchemaRequest)
      returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1/{name=projects/*/locations/*/corpora/*/dataSchemas/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists a list of data schemas inside corpus.
  rpc ListDataSchemas(ListDataSchemasRequest)
      returns (ListDataSchemasResponse) {
    option (google.api.http) = {
      get: "/v1/{parent=projects/*/locations/*/corpora/*}/dataSchemas"
    };
    option (google.api.method_signature) = "parent";
  }

  // Creates annotation inside asset.
  rpc CreateAnnotation(CreateAnnotationRequest) returns (Annotation) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*/corpora/*/assets/*}/annotations"
      body: "annotation"
    };
    option (google.api.method_signature) = "parent,annotation,annotation_id";
  }

  // Reads annotation inside asset.
  rpc GetAnnotation(GetAnnotationRequest) returns (Annotation) {
    option (google.api.http) = {
      get: "/v1/{name=projects/*/locations/*/corpora/*/assets/*/annotations/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists a list of annotations inside asset.
  rpc ListAnnotations(ListAnnotationsRequest)
      returns (ListAnnotationsResponse) {
    option (google.api.http) = {
      get: "/v1/{parent=projects/*/locations/*/corpora/*/assets/*}/annotations"
    };
    option (google.api.method_signature) = "parent";
  }

  // Updates annotation inside asset.
  rpc UpdateAnnotation(UpdateAnnotationRequest) returns (Annotation) {
    option (google.api.http) = {
      patch: "/v1/{annotation.name=projects/*/locations/*/corpora/*/assets/*/annotations/*}"
      body: "annotation"
    };
    option (google.api.method_signature) = "annotation,update_mask";
  }

  // Deletes annotation inside asset.
  rpc DeleteAnnotation(DeleteAnnotationRequest)
      returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1/{name=projects/*/locations/*/corpora/*/assets/*/annotations/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Ingests data for the asset. It is not allowed to ingest a data chunk which
  // is already expired according to TTL.
  // This method is only available via the gRPC API (not HTTP since
  // bi-directional streaming is not supported via HTTP).
  rpc IngestAsset(stream IngestAssetRequest)
      returns (stream IngestAssetResponse) {}

  // Supported by STREAM_VIDEO corpus type.
  // Generates clips for downloading. The api takes in a time range, and
  // generates a clip of the first content available after start_time and
  // before end_time, which may overflow beyond these bounds.
  // Returned clips are truncated if the total size of the clips are larger
  // than 100MB.
  rpc ClipAsset(ClipAssetRequest) returns (ClipAssetResponse) {
    option (google.api.http) = {
      post: "/v1/{name=projects/*/locations/*/corpora/*/assets/*}:clip"
      body: "*"
    };
  }

  // Generates a uri for an HLS manifest. The api takes in a collection of time
  // ranges, and generates a URI for an HLS manifest that covers all the
  // requested time ranges.
  rpc GenerateHlsUri(GenerateHlsUriRequest) returns (GenerateHlsUriResponse) {
    option (google.api.http) = {
      post: "/v1/{name=projects/*/locations/*/corpora/*/assets/*}:generateHlsUri"
      body: "*"
    };
  }

  // Imports assets (images plus annotations) from a meta file on cloud storage.
  // Each row in the meta file is corresponding to an image (specified by a
  // cloud storage uri) and its annotations.
  rpc ImportAssets(ImportAssetsRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*/corpora/*}/assets:import"
      body: "*"
    };
    option (google.longrunning.operation_info) = {
      response_type: "ImportAssetsResponse"
      metadata_type: "ImportAssetsMetadata"
    };
  }

  // Creates a search configuration inside a corpus.
  //
  // Please follow the rules below to create a valid CreateSearchConfigRequest.
  // --- General Rules ---
  // 1. Request.search_config_id must not be associated with an existing
  //    SearchConfig.
  // 2. Request must contain at least one non-empty search_criteria_property or
  //    facet_property.
  // 3. mapped_fields must not be empty, and must map to existing UGA keys.
  // 4. All mapped_fields must be of the same type.
  // 5. All mapped_fields must share the same granularity.
  // 6. All mapped_fields must share the same semantic SearchConfig match
  //    options.
  // For property-specific rules, please reference the comments for
  // FacetProperty and SearchCriteriaProperty.
  rpc CreateSearchConfig(CreateSearchConfigRequest) returns (SearchConfig) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*/corpora/*}/searchConfigs"
      body: "search_config"
    };
    option (google.api.method_signature) =
        "parent,search_config,search_config_id";
  }

  // Updates a search configuration inside a corpus.
  //
  // Please follow the rules below to create a valid UpdateSearchConfigRequest.
  // --- General Rules ---
  // 1. Request.search_configuration.name must already exist.
  // 2. Request must contain at least one non-empty search_criteria_property or
  // facet_property.
  // 3. mapped_fields must not be empty, and must map to existing UGA keys.
  // 4. All mapped_fields must be of the same type.
  // 5. All mapped_fields must share the same granularity.
  // 6. All mapped_fields must share the same semantic SearchConfig match
  //    options.
  // For property-specific rules, please reference the comments for
  // FacetProperty and SearchCriteriaProperty.
  rpc UpdateSearchConfig(UpdateSearchConfigRequest) returns (SearchConfig) {
    option (google.api.http) = {
      patch: "/v1/{search_config.name=projects/*/locations/*/corpora/*/searchConfigs/*}"
      body: "search_config"
    };
    option (google.api.method_signature) = "search_config,update_mask";
  }

  // Gets a search configuration inside a corpus.
  rpc GetSearchConfig(GetSearchConfigRequest) returns (SearchConfig) {
    option (google.api.http) = {
      get: "/v1/{name=projects/*/locations/*/corpora/*/searchConfigs/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Deletes a search configuration inside a corpus.
  //
  // For a DeleteSearchConfigRequest to be valid,
  // Request.search_configuration.name must already exist.
  rpc DeleteSearchConfig(DeleteSearchConfigRequest)
      returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1/{name=projects/*/locations/*/corpora/*/searchConfigs/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists all search configurations inside a corpus.
  rpc ListSearchConfigs(ListSearchConfigsRequest)
      returns (ListSearchConfigsResponse) {
    option (google.api.http) = {
      get: "/v1/{parent=projects/*/locations/*/corpora/*}/searchConfigs"
    };
    option (google.api.method_signature) = "parent";
  }

  // Creates a SearchHypernym inside a corpus.
  rpc CreateSearchHypernym(CreateSearchHypernymRequest)
      returns (SearchHypernym) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*/corpora/*}/searchHypernyms"
      body: "search_hypernym"
    };
    option (google.api.method_signature) =
        "parent,search_hypernym,search_hypernym_id";
  }

  // Updates a SearchHypernym inside a corpus.
  rpc UpdateSearchHypernym(UpdateSearchHypernymRequest)
      returns (SearchHypernym) {
    option (google.api.http) = {
      patch: "/v1/{search_hypernym.name=projects/*/locations/*/corpora/*/searchHypernyms/*}"
      body: "search_hypernym"
    };
    option (google.api.method_signature) = "search_hypernym,update_mask";
  }

  // Gets a SearchHypernym inside a corpus.
  rpc GetSearchHypernym(GetSearchHypernymRequest) returns (SearchHypernym) {
    option (google.api.http) = {
      get: "/v1/{name=projects/*/locations/*/corpora/*/searchHypernyms/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Deletes a SearchHypernym inside a corpus.
  rpc DeleteSearchHypernym(DeleteSearchHypernymRequest)
      returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1/{name=projects/*/locations/*/corpora/*/searchHypernyms/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists SearchHypernyms inside a corpus.
  rpc ListSearchHypernyms(ListSearchHypernymsRequest)
      returns (ListSearchHypernymsResponse) {
    option (google.api.http) = {
      get: "/v1/{parent=projects/*/locations/*/corpora/*}/searchHypernyms"
    };
    option (google.api.method_signature) = "parent";
  }

  // Search media asset.
  rpc SearchAssets(SearchAssetsRequest) returns (SearchAssetsResponse) {
    option (google.api.http) = {
      post: "/v1/{corpus=projects/*/locations/*/corpora/*}:searchAssets"
      body: "*"
    };
  }

  // Search a deployed index endpoint (IMAGE corpus type only).
  rpc SearchIndexEndpoint(SearchIndexEndpointRequest)
      returns (SearchIndexEndpointResponse) {
    option (google.api.http) = {
      post: "/v1/{index_endpoint=projects/*/locations/*/indexEndpoints/*}:searchIndexEndpoint"
      body: "*"
    };
  }

  // Creates an IndexEndpoint.
  rpc CreateIndexEndpoint(CreateIndexEndpointRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*}/indexEndpoints"
      body: "index_endpoint"
    };
    option (google.api.method_signature) =
        "parent,index_endpoint,index_endpoint_id";
    option (google.longrunning.operation_info) = {
      response_type: "IndexEndpoint"
      metadata_type: "CreateIndexEndpointMetadata"
    };
  }

  // Gets an IndexEndpoint.
  rpc GetIndexEndpoint(GetIndexEndpointRequest) returns (IndexEndpoint) {
    option (google.api.http) = {
      get: "/v1/{name=projects/*/locations/*/indexEndpoints/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists all IndexEndpoints in a project.
  rpc ListIndexEndpoints(ListIndexEndpointsRequest)
      returns (ListIndexEndpointsResponse) {
    option (google.api.http) = {
      get: "/v1/{parent=projects/*/locations/*}/indexEndpoints"
    };
    option (google.api.method_signature) = "parent";
  }

  // Updates an IndexEndpoint.
  rpc UpdateIndexEndpoint(UpdateIndexEndpointRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      patch: "/v1/{index_endpoint.name=projects/*/locations/*/indexEndpoints/*}"
      body: "index_endpoint"
    };
    option (google.api.method_signature) = "index_endpoint,update_mask";
    option (google.longrunning.operation_info) = {
      response_type: "IndexEndpoint"
      metadata_type: "UpdateIndexEndpointMetadata"
    };
  }

  // Deletes an IndexEndpoint.
  rpc DeleteIndexEndpoint(DeleteIndexEndpointRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      delete: "/v1/{name=projects/*/locations/*/indexEndpoints/*}"
    };
    option (google.api.method_signature) = "name";
    option (google.longrunning.operation_info) = {
      response_type: "google.protobuf.Empty"
      metadata_type: "DeleteIndexEndpointMetadata"
    };
  }

  // Deploys an Index to IndexEndpoint.
  rpc DeployIndex(DeployIndexRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{index_endpoint=projects/*/locations/*/indexEndpoints/*}:deployIndex"
      body: "*"
    };
    option (google.longrunning.operation_info) = {
      response_type: "DeployIndexResponse"
      metadata_type: "DeployIndexMetadata"
    };
  }

  // Undeploys an Index from IndexEndpoint.
  rpc UndeployIndex(UndeployIndexRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{index_endpoint=projects/*/locations/*/indexEndpoints/*}:undeployIndex"
      body: "*"
    };
    option (google.longrunning.operation_info) = {
      response_type: "UndeployIndexResponse"
      metadata_type: "UndeployIndexMetadata"
    };
  }

  // Creates a collection.
  rpc CreateCollection(CreateCollectionRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1/{parent=projects/*/locations/*/corpora/*}/collections"
      body: "collection"
    };
    option (google.api.method_signature) = "parent,collection,collection_id";
    option (google.longrunning.operation_info) = {
      response_type: "Collection"
      metadata_type: "CreateCollectionMetadata"
    };
  }

  // Deletes a collection.
  rpc DeleteCollection(DeleteCollectionRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      delete: "/v1/{name=projects/*/locations/*/corpora/*/collections/*}"
    };
    option (google.api.method_signature) = "name";
    option (google.longrunning.operation_info) = {
      response_type: "google.protobuf.Empty"
      metadata_type: "DeleteCollectionMetadata"
    };
  }

  // Gets a collection.
  rpc GetCollection(GetCollectionRequest) returns (Collection) {
    option (google.api.http) = {
      get: "/v1/{name=projects/*/locations/*/corpora/*/collections/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Updates a collection.
  rpc UpdateCollection(UpdateCollectionRequest) returns (Collection) {
    option (google.api.http) = {
      patch: "/v1/{collection.name=projects/*/locations/*/corpora/*/collections/*}"
      body: "collection"
    };
    option (google.api.method_signature) = "collection,update_mask";
  }

  // Lists collections inside a corpus.
  rpc ListCollections(ListCollectionsRequest)
      returns (ListCollectionsResponse) {
    option (google.api.http) = {
      get: "/v1/{parent=projects/*/locations/*/corpora/*}/collections"
    };
    option (google.api.method_signature) = "parent";
  }

  // Adds an item into a Collection.
  rpc AddCollectionItem(AddCollectionItemRequest)
      returns (AddCollectionItemResponse) {
    option (google.api.http) = {
      post: "/v1/{item.collection=projects/*/locations/*/corpora/*/collections/*}:addCollectionItem"
      body: "*"
    };
    option (google.api.method_signature) = "item";
  }

  // Removes an item from a collection.
  rpc RemoveCollectionItem(RemoveCollectionItemRequest)
      returns (RemoveCollectionItemResponse) {
    option (google.api.http) = {
      post: "/v1/{item.collection=projects/*/locations/*/corpora/*/collections/*}:removeCollectionItem"
      body: "*"
    };
    option (google.api.method_signature) = "item";
  }

  // View items inside a collection.
  rpc ViewCollectionItems(ViewCollectionItemsRequest)
      returns (ViewCollectionItemsResponse) {
    option (google.api.http) = {
      get: "/v1/{collection=projects/*/locations/*/corpora/*/collections/*}:viewCollectionItems"
    };
    option (google.api.method_signature) = "collection";
  }
}

// Different types for a facet bucket.
enum FacetBucketType {
  // Unspecified type.
  FACET_BUCKET_TYPE_UNSPECIFIED = 0;

  // Value type.
  FACET_BUCKET_TYPE_VALUE = 1;

  // Datetime type.
  FACET_BUCKET_TYPE_DATETIME = 2;

  // Fixed Range type.
  FACET_BUCKET_TYPE_FIXED_RANGE = 3;

  // Custom Range type.
  FACET_BUCKET_TYPE_CUSTOM_RANGE = 4;
}

// Request message for CreateAssetRequest.
message CreateAssetRequest {
  // Required. The parent resource where this asset will be created.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Corpus" }
  ];

  // Required. The asset to create.
  Asset asset = 2 [(google.api.field_behavior) = REQUIRED];

  // Optional. The ID to use for the asset, which will become the final
  // component of the asset's resource name if user choose to specify.
  // Otherwise, asset id will be generated by system.
  //
  // This value should be up to 63 characters, and valid characters
  // are /[a-z][0-9]-/. The first character must be a letter, the last could be
  // a letter or a number.
  optional string asset_id = 3 [(google.api.field_behavior) = OPTIONAL];
}

// Request message for GetAsset.
message GetAssetRequest {
  // Required. The name of the asset to retrieve.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];
}

// Request message for ListAssets.
message ListAssetsRequest {
  // Required. The parent, which owns this collection of assets.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/Asset"
    }
  ];

  // The maximum number of assets to return. The service may return fewer than
  // this value.
  // If unspecified, at most 50 assets will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListAssets` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListAssets` must match
  // the call that provided the page token.
  string page_token = 3;

  // The filter applied to the returned list.
  // Only the following filterings are supported:
  // "assets_with_contents = true", which returns assets with contents uploaded;
  // "assets_with_contents = false", which returns assets without contents.
  string filter = 5;
}

// Response message for ListAssets.
message ListAssetsResponse {
  // The assets from the specified corpus.
  repeated Asset assets = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Request message for UpdateAsset.
message UpdateAssetRequest {
  // Required. The asset to update.
  //
  // The asset's `name` field is used to identify the asset to be updated.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}`
  Asset asset = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for DeleteAsset.
message DeleteAssetRequest {
  // Required. The name of the asset to delete.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];
}

// The source of the asset.
message AssetSource {
  // The asset source is from Cloud Storage.
  message AssetGcsSource {
    // Cloud storage uri.
    string gcs_uri = 1;
  }

  // The content of the asset.
  message AssetContentData {
    bytes asset_content_data = 1;
  }

  oneof source_form {
    // The source of the asset is from Cloud Storage.
    AssetGcsSource asset_gcs_source = 1;

    // The source of the asset is from content bytes.
    AssetContentData asset_content_data = 2;
  }
}

// Request message for UploadAsset.
message UploadAssetRequest {
  // Required. The resource name of the asset to upload.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];

  // The source of the asset.
  AssetSource asset_source = 2;
}

// Response message for UploadAsset.
message UploadAssetResponse {}

// Metadata for UploadAsset.
message UploadAssetMetadata {
  // The start time of the operation.
  google.protobuf.Timestamp start_time = 1;

  // The update time of the operation.
  google.protobuf.Timestamp update_time = 2;
}

// Request message for GenerateRetrievalUrl API.
message GenerateRetrievalUrlRequest {
  // Required. The resource name of the asset to request signed url for.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];
}

// Response message for GenerateRetrievalUrl API.
message GenerateRetrievalUrlResponse {
  // A signed url to download the content of the asset.
  string signed_uri = 1;
}

// An asset is a resource in corpus. It represents a media object inside corpus,
// contains metadata and another resource annotation. Different feature could be
// applied to the asset to generate annotations. User could specified annotation
// related to the target asset.
message Asset {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/Asset"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}"
  };

  // Resource name of the asset.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string name = 1;

  // The duration for which all media assets, associated metadata, and search
  // documents can exist. If not set, then it will using the default ttl in the
  // parent corpus resource.
  google.protobuf.Duration ttl = 2;

  // Output only. The original cloud storage source uri that is associated with
  // this asset.
  AssetSource.AssetGcsSource asset_gcs_source = 4
      [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Request message for AnalyzeAsset.
message AnalyzeAssetRequest {
  // Required. The resource name of the asset to analyze.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];
}

// Metadata for AnalyzeAsset.
message AnalyzeAssetMetadata {
  // The status of analysis on each search capability.
  message AnalysisStatus {
    // The state of the search capability.
    enum State {
      // The default process state should never happen.
      STATE_UNSPECIFIED = 0;

      // The ml model analysis is in progress.
      IN_PROGRESS = 1;

      // The ml model analysis is successfully done.
      SUCCEEDED = 2;

      // The ml model analysis failed.
      FAILED = 3;
    }

    State state = 2;

    string status_message = 3;

    // The search capability requested.
    SearchCapability search_capability = 4;
  }

  // The status of analysis on all search capabilities.
  repeated AnalysisStatus analysis_status = 1;

  // The start time of the operation.
  google.protobuf.Timestamp start_time = 2;

  // The update time of the operation.
  google.protobuf.Timestamp update_time = 3;
}

// Response message for AnalyzeAsset.
message AnalyzeAssetResponse {}

// The status of indexing for the asset.
message IndexingStatus {
  // State enum for this asset's indexing.
  enum State {
    // The default process state should never happen.
    STATE_UNSPECIFIED = 0;

    // The indexing is in progress.
    IN_PROGRESS = 1;

    // The process is successfully done.
    SUCCEEDED = 2;

    // The process failed.
    FAILED = 3;
  }

  // Output only. State of this asset's indexing.
  State state = 2 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Detailed message describing the state.
  string status_message = 3;
}

// Request message for IndexAsset.
message IndexAssetRequest {
  // Required. The resource name of the asset to index.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];

  // Optional. The name of the index.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/indexes/{index}`
  string index = 2 [
    (google.api.field_behavior) = OPTIONAL,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Index" }
  ];
}

// Metadata for IndexAsset.
message IndexAssetMetadata {
  // The status of indexing this asset.
  IndexingStatus status = 4;

  // The start time of the operation.
  google.protobuf.Timestamp start_time = 2;

  // The update time of the operation.
  google.protobuf.Timestamp update_time = 3;
}

// Response message for IndexAsset.
message IndexAssetResponse {}

// Request message for RemoveIndexAsset.
message RemoveIndexAssetRequest {
  // Required. The resource name of the asset to index.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];

  // Optional. The name of the index.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/indexes/{index}`
  string index = 2 [
    (google.api.field_behavior) = OPTIONAL,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Index" }
  ];
}

// Metadata for RemoveIndexAsset.
message RemoveIndexAssetMetadata {
  // The status of indexing this asset.
  IndexingStatus indexing_status = 1;

  // The start time of the operation.
  google.protobuf.Timestamp start_time = 2;

  // The update time of the operation.
  google.protobuf.Timestamp update_time = 3;
}

// Response message for RemoveIndexAsset.
message RemoveIndexAssetResponse {}

// An IndexedAsset is an asset that the index is built upon.
message IndexedAsset {
  // Required. The index that this indexed asset belongs to.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/indexes/{index}`
  string index = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Index" }
  ];

  // Required. The resource name of the asset.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string asset = 2 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];

  // Output only. The create timestamp.
  google.protobuf.Timestamp create_time = 3
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The update timestamp.
  google.protobuf.Timestamp update_time = 4
      [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Request message for ViewIndexedAssets.
message ViewIndexedAssetsRequest {
  // Required. The index that owns this collection of assets.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/indexes/{index}`
  string index = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Index" }
  ];

  // The maximum number of assets to return. The service may return fewer than
  // this value.
  // If unspecified, at most 50 assets will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ViewIndexedAssets` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ViewIndexedAssets` must
  // match the call that provided the page token.
  string page_token = 3;

  // The filter applied to the returned list.
  // Only the following filterings are supported:
  // "asset_id = xxxx", which returns asset with specified id.
  // "asset_id = xxxx, yyyy, zzzz", which returns assets with specified ids.
  string filter = 4;
}

// Response message for ViewIndexedAssets.
message ViewIndexedAssetsResponse {
  // The assets from the specified index.
  repeated IndexedAsset indexed_assets = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Request message of CreateCorpus API.
message CreateCorpusRequest {
  // Required. Form: `projects/{project_number}/locations/{location_id}`
  string parent = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. The corpus to be created.
  Corpus corpus = 2 [(google.api.field_behavior) = REQUIRED];
}

// Metadata for CreateCorpus API.
message CreateCorpusMetadata {
  // The create time of the create corpus operation.
  google.protobuf.Timestamp create_time = 2;

  // The update time of the create corpus operation.
  google.protobuf.Timestamp update_time = 3;
}

// The capability and metadata of search capability.
message SearchCapability {
  // Capability to perform different search on assets.
  enum Type {
    // Unspecified search capability, should never be used.
    TYPE_UNSPECIFIED = 0;

    // Embedding search.
    EMBEDDING_SEARCH = 1;
  }

  // The search capability to enable.
  Type type = 1;
}

// Setting for search capability to enable.
message SearchCapabilitySetting {
  // The metadata of search capability to enable.
  repeated SearchCapability search_capabilities = 1;
}

// Metadata message for CreateCollectionRequest
message CreateCollectionMetadata {
  // Common metadata of the long-running operation.
  OperationMetadata operation_metadata = 1;
}

// Request message for CreateCollection.
message CreateCollectionRequest {
  // Required. The parent resource where this collection will be created.
  // Format: `projects/{project_number}/locations/{location}/corpora/{corpus}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Corpus" }
  ];

  // Required. The collection resource to be created.
  Collection collection = 2 [(google.api.field_behavior) = REQUIRED];

  // Optional. The ID to use for the collection, which will become the final
  // component of the resource name if user choose to specify. Otherwise,
  // collection id will be generated by system.
  //
  // This value should be up to 55 characters, and valid characters
  // are /[a-z][0-9]-/. The first character must be a letter, the last could be
  // a letter or a number.
  optional string collection_id = 3 [(google.api.field_behavior) = OPTIONAL];
}

// Metadata message for DeleteCollectionRequest
message DeleteCollectionMetadata {
  // Common metadata of the long-running operation.
  OperationMetadata operation_metadata = 1;
}

// Request message for DeleteCollectionRequest.
message DeleteCollectionRequest {
  // Required. The name of the collection to delete. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/collections/{collection}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Collection"
    }
  ];
}

// Request message for GetCollectionRequest.
message GetCollectionRequest {
  // Required. The name of the collection to retrieve. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/collections/{collection}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Collection"
    }
  ];
}

// Request message for UpdateCollectionRequest.
message UpdateCollectionRequest {
  // Required. The collection to update.
  //
  // The collection's `name` field is used to identify the collection to be
  // updated. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/collections/{collection}`
  Collection collection = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated.
  // - Unset `update_mask` or set `update_mask` to be a single "*" only will
  //   update all updatable fields with the value provided in `collection`.
  // - To update `display_name` value to empty string, set it in the
  // `collection`
  //   to empty string, and set `update_mask` with "display_name". Same applies
  //   to other updatable string fields in the `collection`.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for ListCollections.
message ListCollectionsRequest {
  // Required. The parent corpus. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/Collection"
    }
  ];

  // The maximum number of collections to return. The service may return fewer
  // than this value. If unspecified, at most 50 collections will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListCollectionsRequest` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListCollectionsRequest`
  // must match the call that provided the page token.
  string page_token = 3;
}

// Response message for ListCollections.
message ListCollectionsResponse {
  // The collections from the specified corpus.
  repeated Collection collections = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Request message for AddCollectionItem.
message AddCollectionItemRequest {
  // Required. The item to be added.
  CollectionItem item = 1 [(google.api.field_behavior) = REQUIRED];
}

// Response message for AddCollectionItem.
message AddCollectionItemResponse {
  // The item that has already been added.
  CollectionItem item = 1;
}

// Request message for RemoveCollectionItem.
message RemoveCollectionItemRequest {
  // Required. The item to be removed.
  CollectionItem item = 1 [(google.api.field_behavior) = REQUIRED];
}

// Request message for RemoveCollectionItem.
message RemoveCollectionItemResponse {
  // The item that has already been removed.
  CollectionItem item = 1;
}

// Request message for ViewCollectionItems.
message ViewCollectionItemsRequest {
  // Required. The collection to view. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/collections/{collection}`
  string collection = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Collection"
    }
  ];

  // The maximum number of collections to return. The service may return fewer
  // than this value. If unspecified, at most 50 collections will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ViewCollectionItemsRequest` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to
  // `ViewCollectionItemsRequest` must match the call that provided the page
  // token.
  string page_token = 3;
}

// Response message for ViewCollectionItems.
message ViewCollectionItemsResponse {
  // The items from the specified collection.
  repeated CollectionItem items = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// A collection is a resource in a corpus. It serves as a container of
// references to original resources.
message Collection {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/Collection"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/collections/{collection}"
  };

  // Output only. Resource name of the collection. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/collections/{collection}`
  string name = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Optional. The collection name for displaying.
  // The name can be up to 256 characters long.
  string display_name = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Description of the collection. Can be up to 25000 characters
  // long.
  string description = 3 [(google.api.field_behavior) = OPTIONAL];
}

// A CollectionItem is an item in a collection.
// Each item is a reference to the original resource in a collection.
message CollectionItem {
  // CollectionItem types.
  enum Type {
    // The default type of item should never happen.
    TYPE_UNSPECIFIED = 0;

    // Asset type item.
    ASSET = 1;
  }

  // Required. The collection name that this item belongs to. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/collections/{collection}`
  string collection = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Collection"
    }
  ];

  // Required. The type of item.
  Type type = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. The name of the CollectionItem. Its format depends on the `type`
  // above. For ASSET:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}`
  string item_resource = 3 [(google.api.field_behavior) = REQUIRED];
}

// Message for creating an Index.
message CreateIndexRequest {
  // Required. Value for the parent. The resource name of the Corpus under which
  // this index is created. Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Corpus" }
  ];

  // Optional. The ID for the index. This will become the final resource name
  // for the index. If the user does not specify this value, it will be
  // generated by system.
  //
  // This value should be up to 63 characters, and valid characters
  // are /[a-z][0-9]-/. The first character must be a letter, the last could be
  // a letter or a number.
  string index_id = 2 [(google.api.field_behavior) = OPTIONAL];

  // Required. The index being created.
  Index index = 3 [(google.api.field_behavior) = REQUIRED];
}

// Metadata message for CreateIndexRequest
message CreateIndexMetadata {
  // Common metadata of the long-running operation.
  OperationMetadata operation_metadata = 1;
}

// Request message for UpdateIndex.
message UpdateIndexRequest {
  // Required. The resource being updated.
  Index index = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Field mask is used to specify the fields to be overwritten in the
  // Index resource by the update. The fields specified in the update_mask are
  // relative to the resource, not the full request. A field of the resource
  // will be overwritten if it is in the mask. Empty field mask is not allowed.
  // If the mask is "*", it triggers a full update of the index, and also a
  // whole rebuild of index data.
  google.protobuf.FieldMask update_mask = 2
      [(google.api.field_behavior) = REQUIRED];
}

// Metadata message for UpdateIndexRequest
message UpdateIndexMetadata {
  // Common metadata of the long-running operation.
  OperationMetadata operation_metadata = 1;
}

// Request message for getting an Index.
message GetIndexRequest {
  // Required. Name of the Index resource.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/indexes/{index}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Index" }
  ];
}

// Request message for listing Indexes.
message ListIndexesRequest {
  // Required. The parent corpus that owns this collection of indexes.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/Index"
    }
  ];

  // The maximum number of indexes to return. The service may return fewer than
  // this value.
  // If unspecified, at most 50 indexes will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListIndexes` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListIndexes` must match
  // the call that provided the page token.
  string page_token = 3;
}

// Response message for ListIndexes.
message ListIndexesResponse {
  // The indexes under the specified corpus.
  repeated Index indexes = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Request message for DeleteIndex.
message DeleteIndexRequest {
  // Required. The name of the index to delete.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/indexes/{index}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Index" }
  ];
}

// Metadata message for DeleteIndexRequest
message DeleteIndexMetadata {}

// An Index is a resource in Corpus. It contains an indexed version of the
// assets and annotations. When deployed to an endpoint, it will allow users to
// search the Index.
message Index {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/Index"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/indexes/{index}"
  };

  // Enum representing the different states through which an Index might cycle
  // during its lifetime.
  enum State {
    // The default value. Should not be used.
    STATE_UNSPECIFIED = 0;

    // State CREATING.
    CREATING = 1;

    // State CREATED.
    CREATED = 2;

    // State UPDATING.
    UPDATING = 3;
  }

  // Specifies how assets are selected for this index. Default to
  // entire_corpus if unspecified. Behavior in UpdateIndex: if update_mask
  // includes one of the asset_filter field paths, the index will be rebuilt
  // with latest assets, including their analyzed data and annotations.
  oneof asset_filter {
    // Include all assets under the corpus.
    bool entire_corpus = 9;
  }

  // Output only. Resource name of the Index resource.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/indexes/{index_id}`
  string name = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Optional. Optional user-specified display name of the index.
  string display_name = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Optional description of the index.
  string description = 3 [(google.api.field_behavior) = OPTIONAL];

  // Output only. State of the index.
  State state = 4 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The create timestamp.
  google.protobuf.Timestamp create_time = 5
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The update timestamp.
  google.protobuf.Timestamp update_time = 6
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. References to the deployed index instance.
  // Index of VIDEO_ON_DEMAND corpus can have at most one deployed index.
  // Index of IMAGE corpus can have multiple deployed indexes.
  repeated DeployedIndexReference deployed_indexes = 8
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. This boolean field is only set for projects that have Physical
  // Zone Separation enabled via an Org Policy constraint. It is set to true
  // when the index is a valid zone separated index and false if it isn't.
  optional bool satisfies_pzs = 11 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. This boolean field is only set for projects that have Physical
  // Zone Isolation enabled via an Org Policy constraint. It is set to true when
  // the index is a valid zone isolated index and false if it isn't.
  optional bool satisfies_pzi = 12 [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Points to a DeployedIndex.
message DeployedIndexReference {
  // Immutable. A resource name of the IndexEndpoint.
  string index_endpoint = 1 [
    (google.api.field_behavior) = IMMUTABLE,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/IndexEndpoint"
    }
  ];
}

// Corpus is a set of media contents for management.
// Within a corpus, media shares the same data schema. Search is also restricted
// within a single corpus.
message Corpus {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/Corpus"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}"
  };

  // Type of the asset inside the corpus.
  enum Type {
    // The default type, not supposed to be used. If this default type is used,
    // the corpus will be created as STREAM_VIDEO corpus.
    TYPE_UNSPECIFIED = 0;

    // Asset is a live streaming video.
    STREAM_VIDEO = 1;

    // Asset is an image.
    IMAGE = 2;

    // Asset is a batch video.
    VIDEO_ON_DEMAND = 3;
  }

  // Resource name of the corpus.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}`
  string name = 1;

  // Required. The corpus name to shown in the UI. The name can be up to 32
  // characters long.
  string display_name = 2 [(google.api.field_behavior) = REQUIRED];

  // Optional. Description of the corpus. Can be up to 25000 characters long.
  string description = 3 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The default TTL value for all assets under the corpus without a
  // asset level user-defined TTL. For STREAM_VIDEO type corpora, this is
  // required and the maximum allowed
  //   default_ttl is 10 years.
  google.protobuf.Duration default_ttl = 5
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. Type of the asset inside corpus.
  Type type = 7 [(google.api.field_behavior) = OPTIONAL];

  // Default search capability setting on corpus level.
  SearchCapabilitySetting search_capability_setting = 8;

  // Output only. This boolean field is only set for projects that have Physical
  // Zone Separation enabled via an Org Policy constraint. It is set to true
  // when the corpus is a valid zone separated corpus and false if it isn't.
  optional bool satisfies_pzs = 11 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. This boolean field is only set for projects that have Physical
  // Zone Isolation enabled via an Org Policy constraint. It is set to true when
  // the corpus is a valid zone isolated corpus and false if it isn't.
  optional bool satisfies_pzi = 12 [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Request message for GetCorpus.
message GetCorpusRequest {
  // Required. The resource name of the corpus to retrieve.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Corpus" }
  ];
}

// Request message for UpdateCorpus.
message UpdateCorpusRequest {
  // Required. The corpus which replaces the resource on the server.
  Corpus corpus = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for ListCorpora.
message ListCorporaRequest {
  // Required. The resource name of the project from which to list corpora.
  string parent = 1 [(google.api.field_behavior) = REQUIRED];

  // Requested page size. API may return fewer results than requested.
  // If negative, INVALID_ARGUMENT error will be returned.
  // If unspecified or 0, API will pick a default size, which is 10.
  // If the requested page size is larger than the maximum size, API will pick
  // use the maximum size, which is 20.
  int32 page_size = 2;

  // A token identifying a page of results for the server to return.
  // Typically obtained via
  // [ListCorporaResponse.next_page_token][google.cloud.visionai.v1.ListCorporaResponse.next_page_token]
  // of the previous
  // [Warehouse.ListCorpora][google.cloud.visionai.v1.Warehouse.ListCorpora]
  // call.
  string page_token = 3;

  // The filter applied to the returned corpora list.
  // Only the following restrictions are supported:
  // `type=<Corpus.Type>`,
  // `type!=<Corpus.Type>`.
  string filter = 5;
}

// Response message for ListCorpora.
message ListCorporaResponse {
  // The corpora in the project.
  repeated Corpus corpora = 1;

  // A token to retrieve next page of results.
  // Pass to
  // [ListCorporaRequest.page_token][google.cloud.visionai.v1.ListCorporaRequest.page_token]
  // to obtain that page.
  string next_page_token = 2;
}

// Request message for DeleteCorpus.
message DeleteCorpusRequest {
  // Required. The resource name of the corpus to delete.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Corpus" }
  ];
}

// Request message for AnalyzeCorpus.
message AnalyzeCorpusRequest {
  // Required. The parent corpus resource where the assets will be analyzed.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Corpus" }
  ];
}

// The metadata message for AnalyzeCorpus LRO.
message AnalyzeCorpusMetadata {
  // The metadata of the operation.
  OperationMetadata metadata = 1;
}

// The response message for AnalyzeCorpus LRO.
message AnalyzeCorpusResponse {}

// Request message for CreateDataSchema.
message CreateDataSchemaRequest {
  // Required. The parent resource where this data schema will be created.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Corpus" }
  ];

  // Required. The data schema to create.
  DataSchema data_schema = 2 [(google.api.field_behavior) = REQUIRED];
}

// Data schema indicates how the user specified annotation is interpreted in the
// system.
message DataSchema {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/DataSchema"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/dataSchemas/{data_schema}"
  };

  // Resource name of the data schema in the form of:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/dataSchemas/{data_schema}`
  // where {data_schema} part should be the same as the `key` field below.
  string name = 1;

  // Required. The key of this data schema. This key should be matching the key
  // of user specified annotation and unique inside corpus. This value can be up
  // to 63 characters, and valid characters are /[a-z][0-9]-/. The first
  // character must be a letter, the last could be a letter or a number.
  string key = 2 [(google.api.field_behavior) = REQUIRED];

  // The schema details mapping to the key.
  DataSchemaDetails schema_details = 3;
}

// Data schema details indicates the data type and the data struct corresponding
// to the key of user specified annotation.
message DataSchemaDetails {
  // The configuration for `PROTO_ANY` data type.
  message ProtoAnyConfig {
    // The type URI of the proto message.
    string type_uri = 1;
  }

  // The configuration for `LIST` data type.
  message ListConfig {
    // The value's data schema in the list.
    DataSchemaDetails value_schema = 1;
  }

  // The configuration for `CUSTOMIZED_STRUCT` data type.
  message CustomizedStructConfig {
    // Direct child elements data schemas.
    map<string, DataSchemaDetails> field_schemas = 1;
  }

  // The search strategy for annotations value of the `key`.
  message SearchStrategy {
    // Filter on the confidence score. Only adds to index if the confidence
    // score is higher than the threshold.
    // Example data schema:
    // key: "name-confidence-pair"
    // type: CUSTOMIZED_STRUCT
    // granularity: GRANULARITY_PARTITION_LEVEL
    // customized_struct_config {
    //   field_schemas {
    //     key: "name"
    //     type: STRING
    //     granularity: GRANULARITY_PARTITION_LEVEL
    //     search_strategy {
    //       search_strategy_type: SMART_SEARCH
    //       confidence_score_index_config {
    //         field_path: "name-confidence-pair.score"
    //         threshold: 0.6
    //       }
    //     }
    //   }
    //   field_schemas {
    //     key: "score"
    //     type: FLOAT
    //     granularity: GRANULARITY_PARTITION_LEVEL
    //   }
    // }
    // This means only "name" with score > 0.6 will be indexed.
    message ConfidenceScoreIndexConfig {
      // Required. The path to the confidence score field. It is a string that
      // concatenates all the data schema keys along the path. See the example
      // above. If the data schema contains LIST, use '_ENTRIES' to concatenate.
      // Example data schema contains a list:
      // "key": "list-name-score",
      // "schemaDetails": {
      //   "type": "LIST",
      //   "granularity": "GRANULARITY_PARTITION_LEVEL",
      //   "listConfig": {
      //     "valueSchema": {
      //       "type": "CUSTOMIZED_STRUCT",
      //       "granularity": "GRANULARITY_PARTITION_LEVEL",
      //       "customizedStructConfig": {
      //         "fieldSchemas": {
      //           "name": {
      //             "type": "STRING",
      //             "granularity": "GRANULARITY_PARTITION_LEVEL",
      //             "searchStrategy": {
      //               "searchStrategyType": "SMART_SEARCH"
      //               "confidence_score_index_config": {
      //                 "field_path": "list-name-score._ENTRIES.score",
      //                 "threshold": "0.9",
      //               }
      //             }
      //           },
      //           "score": {
      //             "type": "FLOAT",
      //             "granularity": "GRANULARITY_PARTITION_LEVEL",
      //           }
      //         }
      //       }
      //     }
      //   }
      // }
      string field_path = 1 [(google.api.field_behavior) = REQUIRED];

      // Required. The threshold.
      float threshold = 2 [(google.api.field_behavior) = REQUIRED];
    }

    // The types of search strategies to be applied on the annotation key.
    enum SearchStrategyType {
      // Annotatation values of the `key` above will not be searchable.
      NO_SEARCH = 0;

      // When searching with `key`, the value must be exactly as the annotation
      // value that has been ingested.
      EXACT_SEARCH = 1;

      // When searching with `key`, Warehouse will perform broad search based on
      // semantic of the annotation value.
      SMART_SEARCH = 2;
    }

    // The type of search strategy to be applied on the `key` above.
    // The allowed `search_strategy_type` is different for different data types,
    // which is documented in the DataSchemaDetails.DataType. Specifying
    // unsupported `search_strategy_type` for data types will result in
    // INVALID_ARGUMENT error.
    optional SearchStrategyType search_strategy_type = 1;

    // Optional. Configs the path to the confidence score, and the threshold.
    // Only if the score is greater than the threshold, current field will be
    // built into the index. Only applies to leaf nodes using EXACT_SEARCH or
    // SMART_SEARCH.
    ConfidenceScoreIndexConfig confidence_score_index_config = 2
        [(google.api.field_behavior) = OPTIONAL];
  }

  // Data type of the annotation.
  enum DataType {
    // Unspecified type.
    DATA_TYPE_UNSPECIFIED = 0;

    // Integer type.
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH.
    //   Supports query by IntRangeArray.
    INTEGER = 1;

    // Float type.
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH.
    //   Supports query by FloatRangeArray.
    FLOAT = 2;

    // String type.
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH,
    // - DataSchema.SearchStrategy.SMART_SEARCH.
    STRING = 3;

    // Supported formats:
    // %Y-%m-%dT%H:%M:%E*S%E*z (absl::RFC3339_full)
    // %Y-%m-%dT%H:%M:%E*S
    // %Y-%m-%dT%H:%M%E*z
    // %Y-%m-%dT%H:%M
    // %Y-%m-%dT%H%E*z
    // %Y-%m-%dT%H
    // %Y-%m-%d%E*z
    // %Y-%m-%d
    // %Y-%m
    // %Y
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH.
    //   Supports query by DateTimeRangeArray.
    DATETIME = 5;

    // Geo coordinate type.
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH.
    //   Supports query by GeoLocationArray.
    GEO_COORDINATE = 7;

    // Type to pass any proto as available in annotations.proto. Only use
    // internally.
    // Available proto types and its corresponding search behavior:
    // - ImageObjectDetectionPredictionResult, allows SMART_SEARCH on
    //   display_names and NO_SEARCH.
    // - ClassificationPredictionResult, allows SMART_SEARCH on display_names
    //   and NO_SEARCH.
    // - ImageSegmentationPredictionResult, allows NO_SEARCH.
    // - VideoActionRecognitionPredictionResult, allows SMART_SEARCH on
    //   display_name and NO_SEARCH.
    // - VideoObjectTrackingPredictionResult, allows SMART_SEARCH on
    //   display_name and NO_SEARCH.
    // - VideoClassificationPredictionResult, allows SMART_SEARCH on
    //   display_name and NO_SEARCH.
    // - OccupancyCountingPredictionResult, allows EXACT_SEARCH on
    //   stats.full_frame_count.count and NO_SEARCH.
    // - ObjectDetectionPredictionResult, allows SMART_SEARCH on
    //   identified_boxes.entity.label_string and NO_SEARCH.
    PROTO_ANY = 8;

    // Boolean type.
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH.
    BOOLEAN = 9;

    // List type.
    //  - Each element in the list must be of the exact same data schema;
    //    otherwise, they are invalid arguments.
    //  - List level cannot set search strategy. Leaf node level can do.
    //  - Elements cannot be another list (no list of list).
    //  - Elements can be CUSTOMIZED_STRUCT, and max number of layers is 10.
    LIST = 10;

    // Struct type.
    // - SearchStrategy:
    //   * Data Schema that's CUSTOMIZED_STRUCT cannot set search strategy.
    //   * Leaf-node elements allow setting search strategy based on element's
    //     SearchStrategy restriction.
    // - Nested layer restrictions:
    //   * Data Schema that's CUSTOMIZED_STRUCT allows its fields to be of
    //     CUSTOMIZED_STRUCT as well, but the overall layers restriction is 10.
    CUSTOMIZED_STRUCT = 6;
  }

  // The granularity of annotations under this DataSchema.
  enum Granularity {
    // Unspecified granularity.
    GRANULARITY_UNSPECIFIED = 0;

    // Asset-level granularity (annotations must not contain partition info).
    GRANULARITY_ASSET_LEVEL = 1;

    // Partition-level granularity (annotations must contain partition info).
    GRANULARITY_PARTITION_LEVEL = 2;
  }

  // Type of the annotation.
  optional DataType type = 1;

  // Config for protobuf any type.
  ProtoAnyConfig proto_any_config = 6;

  // Config for List data type.
  ListConfig list_config = 8;

  // Config for CustomizedStruct data type.
  CustomizedStructConfig customized_struct_config = 9;

  // The granularity associated with this DataSchema.
  optional Granularity granularity = 5;

  // The search strategy to be applied on the `key` above.
  SearchStrategy search_strategy = 7;
}

// Request message for UpdateDataSchema.
message UpdateDataSchemaRequest {
  // Required. The data schema's `name` field is used to identify the data
  // schema to be updated. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/dataSchemas/{data_schema}`
  DataSchema data_schema = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for GetDataSchema.
message GetDataSchemaRequest {
  // Required. The name of the data schema to retrieve.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/dataSchemas/{data_schema_id}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/DataSchema"
    }
  ];
}

// Request message for DeleteDataSchema.
message DeleteDataSchemaRequest {
  // Required. The name of the data schema to delete.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/dataSchemas/{data_schema_id}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/DataSchema"
    }
  ];
}

// Request message for ListDataSchemas.
message ListDataSchemasRequest {
  // Required. The parent, which owns this collection of data schemas.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/DataSchema"
    }
  ];

  // The maximum number of data schemas to return. The service may return fewer
  // than this value. If unspecified, at most 50 data schemas will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListDataSchemas` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListDataSchemas` must
  // match the call that provided the page token.
  string page_token = 3;
}

// Response message for ListDataSchemas.
message ListDataSchemasResponse {
  // The data schemas from the specified corpus.
  repeated DataSchema data_schemas = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Request message for CreateAnnotation.
message CreateAnnotationRequest {
  // Required. The parent resource where this annotation will be created.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];

  // Required. The annotation to create.
  Annotation annotation = 2 [(google.api.field_behavior) = REQUIRED];

  // Optional. The ID to use for the annotation, which will become the final
  // component of the annotation's resource name if user choose to specify.
  // Otherwise, annotation id will be generated by system.
  //
  // This value should be up to 63 characters, and valid characters
  // are /[a-z][0-9]-/. The first character must be a letter, the last could be
  // a letter or a number.
  optional string annotation_id = 3 [(google.api.field_behavior) = OPTIONAL];
}

// An annotation is a resource in asset. It represents a key-value mapping of
// content in asset.
message Annotation {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/Annotation"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}/annotations/{annotation}"
  };

  // Resource name of the annotation.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}/annotations/{annotation}`
  string name = 1;

  // User provided annotation.
  UserSpecifiedAnnotation user_specified_annotation = 2;
}

// Annotation provided by users.
message UserSpecifiedAnnotation {
  // Required. Key of the annotation. The key must be set with type by
  // CreateDataSchema.
  string key = 1 [(google.api.field_behavior) = REQUIRED];

  // Value of the annotation. The value must be able to convert
  // to the type according to the data schema.
  AnnotationValue value = 2;

  // Partition information in time and space for the sub-asset level annotation.
  Partition partition = 3;
}

// Location Coordinate Representation
message GeoCoordinate {
  // Latitude Coordinate. Degrees [-90 .. 90]
  double latitude = 1;

  // Longitude Coordinate. Degrees [-180 .. 180]
  double longitude = 2;
}

// Value of annotation, including all types available in data schema.
message AnnotationValue {
  oneof value {
    // Value of int type annotation.
    int64 int_value = 1;

    // Value of float type annotation.
    float float_value = 2;

    // Value of string type annotation.
    string str_value = 3;

    // Value of date time type annotation.
    string datetime_value = 5;

    // Value of geo coordinate type annotation.
    GeoCoordinate geo_coordinate = 7;

    // Value of any proto value.
    google.protobuf.Any proto_any_value = 8;

    // Value of boolean type annotation.
    bool bool_value = 9;

    // Value of customized struct annotation. This field does not have effects.
    // Use customized_struct_value instead for customized struct annotation.
    google.protobuf.Struct customized_struct_data_value = 10;

    // Value of list type annotation.
    AnnotationList list_value = 11;

    // Value of custom struct type annotation.
    AnnotationCustomizedStruct customized_struct_value = 6;
  }
}

// List representation in annotation.
message AnnotationList {
  // The values of `LIST` data type annotation.
  repeated AnnotationValue values = 1;
}

// Customized struct represnation in annotation.
message AnnotationCustomizedStruct {
  // A map from elements' keys to element's annotation value.
  map<string, AnnotationValue> elements = 2;
}

// Request message for GetAnnotation API.
message ListAnnotationsRequest {
  // The parent, which owns this collection of annotations.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}`
  string parent = 1 [
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];

  // The maximum number of annotations to return. The service may return fewer
  // than this value. If unspecified, at most 50 annotations will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListAnnotations` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListAnnotations` must
  // match the call that provided the page token.
  string page_token = 3;

  // The filter applied to the returned list.
  // We only support filtering for the following fields:
  // For corpus of STREAM_VIDEO type:
  // `partition.temporal_partition.start_time`,
  // `partition.temporal_partition.end_time`, and `key`.
  // For corpus of VIDEO_ON_DEMAND type,
  // `partition.relative_temporal_partition.start_offset`,
  // `partition.relative_temporal_partition.end_offset`, and `key`.
  // For corpus of IMAGE type, only `key` is supported.
  // Timestamps are specified in the RFC-3339 format, and only one restriction
  // may be applied per field, joined by conjunctions.
  // Format:
  // "partition.temporal_partition.start_time > "2012-04-21T11:30:00-04:00" AND
  // partition.temporal_partition.end_time < "2012-04-22T11:30:00-04:00" AND
  // key = "example_key""
  string filter = 4;
}

// Request message for ListAnnotations API.
message ListAnnotationsResponse {
  // The annotations from the specified asset.
  repeated Annotation annotations = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Request message for GetAnnotation API.
message GetAnnotationRequest {
  // Required. The name of the annotation to retrieve.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}/annotations/{annotation}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Annotation"
    }
  ];
}

// Request message for UpdateAnnotation API.
message UpdateAnnotationRequest {
  // Required. The annotation to update.
  // The annotation's `name` field is used to identify the annotation to be
  // updated. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}/annotations/{annotation}`
  Annotation annotation = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for DeleteAnnotation API.
message DeleteAnnotationRequest {
  // Required. The name of the annotation to delete.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}/annotations/{annotation}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Annotation"
    }
  ];
}

// The request message for ImportAssets.
message ImportAssetsRequest {
  // The source.
  oneof source {
    // The file contains all assets information to be imported.
    // * The file is in JSONL format.
    // * Each line corresponding to one asset.
    // * Each line will be converted into InputImageAsset proto.
    string assets_gcs_uri = 2;
  }

  // Required. The parent corpus resource where the assets will be imported.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Corpus" }
  ];
}

// The metadata message for ImportAssets LRO.
message ImportAssetsMetadata {
  // The metadata of the operation.
  OperationMetadata metadata = 1;

  // The importing status including partial failures, if the implementation can
  // provide such information during the progress of the ImportAssets.
  BatchOperationStatus status = 2;
}

// The batch operation status.
message BatchOperationStatus {
  // The count of assets (together with their annotations if any) successfully
  // ingested.
  int32 success_count = 1;

  // The count of assets failed to ingested; it might be due to the annotation
  // ingestion error.
  int32 failure_count = 2;
}

// The response message for ImportAssets LRO.
message ImportAssetsResponse {}

// Request message for CreateSearchConfig.
message CreateSearchConfigRequest {
  // Required. The parent resource where this search configuration will be
  // created. Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/SearchConfig"
    }
  ];

  // Required. The search config to create.
  SearchConfig search_config = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. ID to use for the new search config. Will become the final
  // component of the SearchConfig's resource name. This value should be up to
  // 63 characters, and valid characters are /[a-z][0-9]-_/. The first character
  // must be a letter, the last could be a letter or a number.
  string search_config_id = 3 [(google.api.field_behavior) = REQUIRED];
}

// Request message for UpdateSearchConfig.
message UpdateSearchConfigRequest {
  // Required. The search configuration to update.
  //
  // The search configuration's `name` field is used to identify the resource to
  // be updated. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/searchConfigs/{search_config}`
  SearchConfig search_config = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated. If left unset, all field paths will be
  // updated/overwritten.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for GetSearchConfig.
message GetSearchConfigRequest {
  // Required. The name of the search configuration to retrieve.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/searchConfigs/{search_config}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/SearchConfig"
    }
  ];
}

// Request message for DeleteSearchConfig.
message DeleteSearchConfigRequest {
  // Required. The name of the search configuration to delete.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/searchConfigs/{search_config}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/SearchConfig"
    }
  ];
}

// Request message for ListSearchConfigs.
message ListSearchConfigsRequest {
  // Required. The parent, which owns this collection of search configurations.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/SearchConfig"
    }
  ];

  // The maximum number of search configurations to return. The service may
  // return fewer than this value. If unspecified, a page size of 50 will be
  // used. The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListSearchConfigs` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to
  // `ListSearchConfigs` must match the call that provided the page
  // token.
  string page_token = 3;
}

// Response message for ListSearchConfigs.
message ListSearchConfigsResponse {
  // The search configurations from the specified corpus.
  repeated SearchConfig search_configs = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// SearchConfig stores different properties that will affect search
// behaviors and search results.
message SearchConfig {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/SearchConfig"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/searchConfigs/{search_config}"
  };

  // Resource name of the search configuration.
  // For CustomSearchCriteria, search_config would be the search
  // operator name. For Facets, search_config would be the facet
  // dimension name.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/searchConfigs/{search_config}`
  string name = 1;

  // Establishes a FacetDimension and associated specifications.
  FacetProperty facet_property = 2;

  // Creates a mapping between a custom SearchCriteria and one or more UGA keys.
  SearchCriteriaProperty search_criteria_property = 3;
}

// Message representing IndexEndpoint resource. Indexes are deployed into it.
message IndexEndpoint {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/IndexEndpoint"
    pattern: "projects/{project}/locations/{location}/indexEndpoints/{index_endpoint}"
  };

  // IndexEndpoint stage.
  enum State {
    // The default value. Should not be used.
    STATE_UNSPECIFIED = 0;

    // State CREATING.
    CREATING = 1;

    // State CREATED.
    CREATED = 2;

    // State UPDATING.
    UPDATING = 3;

    // State FAILED.
    FAILED = 4;
  }

  // Output only. Resource name of the IndexEndpoint.
  // Format:
  // `projects/{project}/locations/{location}/indexEndpoints/{index_endpoint_id}`
  string name = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Optional. Display name of the IndexEndpoint. Can be up to 32 characters
  // long.
  string display_name = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Description of the IndexEndpoint. Can be up to 25000 characters
  // long.
  string description = 3 [(google.api.field_behavior) = OPTIONAL];

  // Output only. The Index deployed in this IndexEndpoint.
  DeployedIndex deployed_index = 9 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. IndexEndpoint state.
  State state = 5 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Optional. The labels applied to a resource must meet the following
  // requirements:
  //
  // * Each resource can have multiple labels, up to a maximum of 64.
  // * Each label must be a key-value pair.
  // * Keys have a minimum length of 1 character and a maximum length of 63
  //   characters and cannot be empty. Values can be empty and have a maximum
  //   length of 63 characters.
  // * Keys and values can contain only lowercase letters, numeric characters,
  //   underscores, and dashes. All characters must use UTF-8 encoding, and
  //   international characters are allowed.
  // * The key portion of a label must be unique. However, you can use the same
  //   key with multiple resources.
  // * Keys must start with a lowercase letter or international character.
  //
  // See [Google Cloud
  // Document](https://cloud.google.com/resource-manager/docs/creating-managing-labels#requirements)
  // for more details.
  map<string, string> labels = 6 [(google.api.field_behavior) = OPTIONAL];

  // Output only. Create timestamp.
  google.protobuf.Timestamp create_time = 7
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. Update timestamp.
  google.protobuf.Timestamp update_time = 8
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. This boolean field is only set for projects that have Physical
  // Zone Separation enabled via an Org Policy constraint. It is set to true
  // when the index endpoint is a valid zone separated index endpoint and false
  // if it isn't.
  optional bool satisfies_pzs = 10 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. This boolean field is only set for projects that have Physical
  // Zone Isolation enabled via an Org Policy constraint. It is set to true when
  // the index endpoint is a valid zone isolated index endpoint and false if it
  // isn't.
  optional bool satisfies_pzi = 11 [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Request message for CreateIndexEndpoint.
message CreateIndexEndpointRequest {
  // Required. Format: `projects/{project}/locations/{location}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "locations.googleapis.com/Location"
    }
  ];

  // Optional. The ID to use for the IndexEndpoint, which will become the final
  // component of the IndexEndpoint's resource name if the user specifies it.
  // Otherwise, IndexEndpoint id will be autogenerated.
  //
  // This value should be up to 63 characters, and valid characters
  // are a-z, 0-9 and dash (-). The first character must be a letter, the last
  // must be a letter or a number.
  string index_endpoint_id = 2 [(google.api.field_behavior) = OPTIONAL];

  // Required. The resource being created.
  IndexEndpoint index_endpoint = 3 [(google.api.field_behavior) = REQUIRED];
}

// Metadata message for CreateIndexEndpoint.
message CreateIndexEndpointMetadata {
  // Common metadata of the long-running operation.
  OperationMetadata operation_metadata = 1;
}

// Request message for GetIndexEndpoint.
message GetIndexEndpointRequest {
  // Required. Name of the IndexEndpoint resource.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/IndexEndpoint"
    }
  ];
}

// Request message for ListIndexEndpoints.
message ListIndexEndpointsRequest {
  // Required. Format: `projects/{project}/locations/{location}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "locations.googleapis.com/Location"
    }
  ];

  // Optional. Requested page size. Server may return fewer items than
  // requested. The service may return fewer than this value. If unspecified, a
  // page size of 50 will be used. The maximum value is 1000; values above 1000
  // will be coerced to 1000.
  int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. A token identifying a page of results the server should return.
  string page_token = 3 [(google.api.field_behavior) = OPTIONAL];

  // Optional. The filter applied to the returned list.
  // We only support filtering for the `deployed_image_index.image_index` field.
  // However, to filter by a corpus instead of an image index, simply use
  // `deployed_image_index.corpus`, which will return all endpoints with
  // `deployed_image_index.image_index` inside of the given corpus.
  // A basic filter on image index would look like:
  //   deployed_image_index.image_index =
  //     "projects/123/locations/us-central1/corpora/my_corpus/imageIndexes/my_image_index"
  // A basic filter on corpus would look like:
  //   deployed_image_index.corpus =
  //     "projects/123/locations/us-central1/corpora/my_corpus"
  string filter = 4 [(google.api.field_behavior) = OPTIONAL];
}

// Response message for ListIndexEndpoints.
message ListIndexEndpointsResponse {
  // The list of IndexEndpoints.
  repeated IndexEndpoint index_endpoints = 1;

  // A token identifying a page of results the server should return.
  string next_page_token = 2;
}

// Request message for UpdateIndexEndpoint.
message UpdateIndexEndpointRequest {
  // Required. The resource being updated.
  IndexEndpoint index_endpoint = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Field mask is used to specify the fields to be overwritten in the
  // IndexEndpoint resource by the update.
  // The fields specified in the update_mask are relative to the resource, not
  // the full request.
  // A field of the resource will be overwritten if it is in the mask.
  // Empty field mask is not allowed.
  // If the mask is "*", then this is a full replacement of the resource.
  google.protobuf.FieldMask update_mask = 2
      [(google.api.field_behavior) = REQUIRED];
}

// Metadata message for UpdateIndexEndpoint.
message UpdateIndexEndpointMetadata {
  // Common metadata of the long-running operation.
  OperationMetadata operation_metadata = 1;
}

// Request message for DeleteIndexEndpoint.
message DeleteIndexEndpointRequest {
  // Required. Name of the resource.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/IndexEndpoint"
    }
  ];
}

// Metadata message for DeleteIndexEndpoint.
message DeleteIndexEndpointMetadata {
  // Common metadata of the long-running operation.
  OperationMetadata operation_metadata = 1;
}

// Request message for DeployIndex.
message DeployIndexRequest {
  // Required. IndexEndpoint the index is deployed to.
  // Format:
  // `projects/{project}/locations/{location}/indexEndpoints/{index_endpoint}`
  string index_endpoint = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/IndexEndpoint"
    }
  ];

  // Required. Index to deploy.
  DeployedIndex deployed_index = 3 [(google.api.field_behavior) = REQUIRED];
}

// DeployIndex response once the operation is done.
message DeployIndexResponse {}

// Metadata message for DeployIndex.
message DeployIndexMetadata {
  // Common metadata of the long-running operation.
  OperationMetadata operation_metadata = 1;

  // Output only. The index being deployed.
  string deployed_index = 2 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Index" }
  ];
}

// Metadata message for UndeployIndex.
message UndeployIndexMetadata {
  // Common metadata of the long-running operation.
  OperationMetadata operation_metadata = 1;

  // Output only. The index being undeployed.
  string deployed_index = 2 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Index" }
  ];
}

// Request message for UndeployIndexEndpoint.
message UndeployIndexRequest {
  // Required. Resource name of the IndexEndpoint resource on which the
  // undeployment will act. Format:
  // `projects/{project}/locations/{location}/indexEndpoints/{index_endpoint}`
  string index_endpoint = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/IndexEndpoint"
    }
  ];
}

// UndeployIndex response once the operation is done.
message UndeployIndexResponse {}

// A deployment of an Index.
message DeployedIndex {
  // Required. Name of the deployed Index.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/indexes/{index_id}`
  string index = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Index" }
  ];
}

// Central configuration for a facet.
message FacetProperty {
  // If bucket type is FIXED_RANGE, specify how values are bucketized. Use
  // FixedRangeBucketSpec when you want to create multiple buckets with equal
  // granularities. Using integer bucket value as an example, when
  // bucket_start = 0, bucket_granularity = 10, bucket_count = 5, this facet
  // will be aggregated via the following buckets:
  // [-inf, 0), [0, 10), [10, 20), [20, 30), [30, inf).
  // Notably, bucket_count <= 1 is an invalid spec.
  message FixedRangeBucketSpec {
    // Lower bound of the bucket. NOTE: Only integer type is currently supported
    // for this field.
    FacetValue bucket_start = 1;

    // Bucket granularity. NOTE: Only integer type is currently supported for
    // this field.
    FacetValue bucket_granularity = 2;

    // Total number of buckets.
    int32 bucket_count = 3;
  }

  // If bucket type is CUSTOM_RANGE, specify how values are bucketized. Use
  // integer bucket value as an example, when the endpoints are 0, 10, 100, and
  // 1000, we will generate the following facets:
  // [-inf, 0), [0, 10), [10, 100), [100, 1000), [1000, inf).
  // Notably:
  // - endpoints must be listed in ascending order. Otherwise, the SearchConfig
  //   API will reject the facet config.
  // - < 1 endpoints is an invalid spec.
  message CustomRangeBucketSpec {
    // Currently, only integer type is supported for this field.
    repeated FacetValue endpoints = 1;
  }

  // If bucket type is DATE, specify how date values are bucketized.
  message DateTimeBucketSpec {
    // Granularity enum for the datetime bucket.
    enum Granularity {
      // Unspecified granularity.
      GRANULARITY_UNSPECIFIED = 0;

      // Granularity is year.
      YEAR = 1;

      // Granularity is month.
      MONTH = 2;

      // Granularity is day.
      DAY = 3;
    }

    // Granularity of date type facet.
    Granularity granularity = 1;
  }

  oneof range_facet_config {
    // Fixed range facet bucket config.
    FixedRangeBucketSpec fixed_range_bucket_spec = 5;

    // Custom range facet bucket config.
    CustomRangeBucketSpec custom_range_bucket_spec = 6;

    // Datetime range facet bucket config.
    DateTimeBucketSpec datetime_bucket_spec = 7;
  }

  // Name of the facets, which are the dimensions users want to use to refine
  // search results. `mapped_fields` will match UserSpecifiedDataSchema keys.
  //
  // For example, user can add a bunch of UGAs with the same key, such as
  // player:adam, player:bob, player:charles. When multiple mapped_fields are
  // specified, will merge their value together as final facet value. E.g.
  // home_team: a, home_team:b, away_team:a, away_team:c, when facet_field =
  // [home_team, away_team], facet_value will be [a, b, c].
  //
  // UNLESS this is a 1:1 facet dimension (mapped_fields.size() == 1) AND the
  // mapped_field equals the parent SearchConfig.name, the parent must
  // also contain a SearchCriteriaProperty that maps to the same fields.
  // mapped_fields must not be empty.
  repeated string mapped_fields = 1;

  // Display name of the facet. To be used by UI for facet rendering.
  string display_name = 2;

  // Maximum number of unique bucket to return for one facet. Bucket number can
  // be large for high-cardinality facet such as "player". We only return top-n
  // most related ones to user. If it's <= 0, the server will decide the
  // appropriate result_size.
  int64 result_size = 3;

  // Facet bucket type e.g. value, range.
  FacetBucketType bucket_type = 4;
}

// Search resource: SearchHypernym.
// For example,
// { hypernym: "vehicle"
//   hyponyms: ["sedan", "truck"] }
// This means in SMART_SEARCH mode, searching for "vehicle" will also return
// results with "sedan" or "truck" as annotations.
message SearchHypernym {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/SearchHypernym"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/searchHypernyms/{search_hypernym}"
  };

  // Resource name of the SearchHypernym.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/searchHypernyms/{search_hypernym}`
  string name = 1;

  // Optional. The hypernym.
  string hypernym = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Hyponyms that the hypernym is mapped to.
  repeated string hyponyms = 3 [(google.api.field_behavior) = OPTIONAL];
}

// Request message for creating SearchHypernym.
message CreateSearchHypernymRequest {
  // Required. The parent resource where this SearchHypernym will be created.
  // Format: `projects/{project_number}/locations/{location}/corpora/{corpus}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Corpus" }
  ];

  // Required. The SearchHypernym to create.
  SearchHypernym search_hypernym = 2 [(google.api.field_behavior) = REQUIRED];

  // Optional. The search hypernym id.
  // If omitted, a random UUID will be generated.
  optional string search_hypernym_id = 3
      [(google.api.field_behavior) = OPTIONAL];
}

// Request message for updating SearchHypernym.
message UpdateSearchHypernymRequest {
  // Required. The SearchHypernym to update.
  // The search hypernym's `name` field is used to identify the search hypernym
  // to be updated. Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/searchHypernyms/{search_hypernym}`
  SearchHypernym search_hypernym = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated. If left unset, all field paths will be
  // updated/overwritten.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for getting SearchHypernym.
message GetSearchHypernymRequest {
  // Required. The name of the SearchHypernym to retrieve.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/searchHypernyms/{search_hypernym}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/SearchHypernym"
    }
  ];
}

// Request message for deleting SearchHypernym.
message DeleteSearchHypernymRequest {
  // Required. The name of the SearchHypernym to delete.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/searchHypernyms/{search_hypernym}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/SearchHypernym"
    }
  ];
}

// Request message for listing SearchHypernyms.
message ListSearchHypernymsRequest {
  // Required. The parent, which owns this collection of SearchHypernyms.
  // Format:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}`
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/SearchHypernym"
    }
  ];

  // The maximum number of SearchHypernyms returned. The service may
  // return fewer than this value. If unspecified, a page size of 50 will be
  // used. The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `SearchHypernym` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to
  // `SearchHypernym` must match the call that provided the page
  // token.
  string page_token = 3;
}

// Response message for listing SearchHypernyms.
message ListSearchHypernymsResponse {
  // The SearchHypernyms from the specified corpus.
  repeated SearchHypernym search_hypernyms = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Central configuration for custom search criteria.
message SearchCriteriaProperty {
  // Each mapped_field corresponds to a UGA key. To understand how this property
  // works, take the following example. In the SearchConfig table, the
  // user adds this entry:
  //   search_config {
  //     name: "person"
  //     search_criteria_property {
  //       mapped_fields: "player"
  //       mapped_fields: "coach"
  //     }
  //   }
  //
  // Now, when a user issues a query like:
  //   criteria {
  //     field: "person"
  //     text_array {
  //       txt_values: "Tom Brady"
  //       txt_values: "Bill Belichick"
  //     }
  //   }
  //
  // MWH search will return search documents where (player=Tom Brady ||
  // coach=Tom Brady || player=Bill Belichick || coach=Bill Belichick).
  repeated string mapped_fields = 1;
}

// Definition of a single value with generic type.
message FacetValue {
  oneof value {
    // String type value.
    string string_value = 1;

    // Integer type value.
    int64 integer_value = 2;

    // Datetime type value.
    google.type.DateTime datetime_value = 3;
  }
}

// Holds the facet value, selections state, and metadata.
message FacetBucket {
  // The range of values [start, end) for which faceting is applied.
  message Range {
    // Start of the range. Non-existence indicates some bound (e.g. -inf).
    FacetValue start = 1;

    // End of the range. Non-existence indicates some bound (e.g. inf).
    FacetValue end = 2;
  }

  // Bucket associated with a facet. For example, bucket of facet “team”
  // can be "49ers", "patriots", etc; bucket of facet "player" can be "tom
  // brady", "drew brees", etc.
  oneof bucket_value {
    // Singular value.
    FacetValue value = 2;

    // Range value.
    Range range = 4;
  }

  // Whether one facet bucket is selected. This field represents user's facet
  // selection. It is set by frontend in SearchVideosRequest.
  bool selected = 3;
}

// A group of facet buckets to be passed back and forth between backend &
// frontend.
message FacetGroup {
  // Unique id of the facet group.
  string facet_id = 1;

  // Display name of the facet. To be used by UI for facet rendering.
  string display_name = 2;

  // Buckets associated with the facet. E.g. for "Team" facet, the bucket
  // can be 49ers, patriots, etc.
  repeated FacetBucket buckets = 3;

  // Facet bucket type.
  FacetBucketType bucket_type = 4;

  // If true, return query matched annotations for this facet group's selection.
  // This option is only applicable for facets based on partition level
  // annotations. It supports the following facet values:
  //  - INTEGER
  //  - STRING (DataSchema.SearchStrategy.EXACT_SEARCH only)
  bool fetch_matched_annotations = 5;
}

// Request message for IngestAsset API.
message IngestAssetRequest {
  // Configuration for the data.
  message Config {
    // Type information for video data.
    message VideoType {
      // Container format of the video.
      enum ContainerFormat {
        // The default type, not supposed to be used.
        CONTAINER_FORMAT_UNSPECIFIED = 0;

        // Mp4 container format.
        CONTAINER_FORMAT_MP4 = 1;
      }

      // Container format of the video data.
      ContainerFormat container_format = 1;
    }

    oneof data_type {
      // Type information for video data.
      VideoType video_type = 2;
    }

    // Required. The resource name of the asset that the ingested data belongs
    // to.
    string asset = 1 [
      (google.api.field_behavior) = REQUIRED,
      (google.api.resource_reference) = {
        type: "visionai.googleapis.com/Asset"
      }
    ];
  }

  // Contains the data and the corresponding time range this data is for.
  message TimeIndexedData {
    // Data to be ingested.
    bytes data = 1;

    // Time range of the data.
    Partition.TemporalPartition temporal_partition = 2;
  }

  oneof streaming_request {
    // Provides information for the data and the asset resource name that the
    // data belongs to. The first `IngestAssetRequest` message must only contain
    // a `Config` message.
    Config config = 1;

    // Data to be ingested.
    TimeIndexedData time_indexed_data = 2;
  }
}

// Response message for IngestAsset API.
message IngestAssetResponse {
  // Time range of the data that has been successfully ingested.
  Partition.TemporalPartition successfully_ingested_partition = 1;
}

// Request message for ClipAsset API.
message ClipAssetRequest {
  // Required. The resource name of the asset to request clips for.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];

  // Required. The time range to request clips for.
  Partition.TemporalPartition temporal_partition = 2
      [(google.api.field_behavior) = REQUIRED];
}

// Response message for ClipAsset API.
message ClipAssetResponse {
  // Signed uri with corresponding time range.
  message TimeIndexedUri {
    // Time range of the video that the uri is for.
    Partition.TemporalPartition temporal_partition = 1;

    // Signed uri to download the video clip.
    string uri = 2;
  }

  // A list of signed uris to download the video clips that cover the requested
  // time range ordered by time.
  repeated TimeIndexedUri time_indexed_uris = 1;
}

// Request message for GenerateHlsUri API.
message GenerateHlsUriRequest {
  // Required. The resource name of the asset to request clips for.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Asset" }
  ];

  // The time range to request clips for. Will be ignored if `get_live_view` is
  // set to True. The total time range requested should be smaller than 24h.
  repeated Partition.TemporalPartition temporal_partitions = 2;

  // Option to exclusively show a livestream of the asset with up to 3 minutes
  // of backlog data.
  bool live_view_enabled = 3;
}

// Response message for GenerateHlsUri API.
message GenerateHlsUriResponse {
  // A signed uri to download the HLS manifest corresponding to the requested
  // times.
  string uri = 1;

  // A list of temporal partitions of the content returned in the order they
  // appear in the stream.
  repeated Partition.TemporalPartition temporal_partitions = 2;
}

// Request message for SearchAssets.
message SearchAssetsRequest {
  // Sorting specification. If this field is not specified, by default:
  // - STREAM_VIDEO: search results are sorted by the start time.
  oneof sort_spec {
    // Sort by the value under the data schema key.
    SchemaKeySortingStrategy schema_key_sorting_strategy = 9;
  }

  // Required. The parent corpus to search.
  // Format: `projects/{project_id}/locations/{location_id}/corpora/{corpus_id}'
  string corpus = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "visionai.googleapis.com/Corpus" }
  ];

  // The number of results to be returned in this page. If it's 0, the server
  // will decide the appropriate page_size.
  int32 page_size = 2;

  // The continuation token to fetch the next page. If empty, it means it is
  // fetching the first page.
  string page_token = 3;

  // Time ranges that matching video content must fall within. If no ranges are
  // provided, there will be no time restriction. This field is treated just
  // like the criteria below, but defined separately for convenience as it is
  // used frequently. Note that if the end_time is in the future, it will be
  // clamped to the time the request was received.
  DateTimeRangeArray content_time_ranges = 5;

  // Criteria applied to search results.
  repeated Criteria criteria = 4;

  // Stores most recent facet selection state. Only facet groups with user's
  // selection will be presented here. Selection state is either selected or
  // unselected. Only selected facet buckets will be used as search criteria.
  repeated FacetGroup facet_selections = 6;

  // A list of annotation keys to specify the annotations to be retrieved and
  // returned with each search result.
  // Annotation granularity must be GRANULARITY_ASSET_LEVEL and its search
  // strategy must not be NO_SEARCH.
  repeated string result_annotation_keys = 8;

  // Global search query. Allows user to search assets without needing to
  // specify which field the value belongs to.
  string search_query = 10;
}

// Request message for SearchIndexEndpoint.
message SearchIndexEndpointRequest {
  // The oneof global search query.
  oneof query {
    // An image-only query.
    ImageQuery image_query = 2;

    // A text-only query.
    string text_query = 3;
  }

  // Required. The index endpoint to search.
  // Format:
  // `projects/{project_id}/locations/{location_id}/indexEndpoints/{index_endpoint_id}'
  string index_endpoint = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/IndexEndpoint"
    }
  ];

  // Criteria applied to search results.
  repeated Criteria criteria = 4;

  // Criteria to exclude from search results.
  // Note that `fetch_matched_annotations` will be ignored.
  repeated Criteria exclusion_criteria = 7;

  // Requested page size. API may return fewer results than requested.
  // If negative, INVALID_ARGUMENT error will be returned.
  // If unspecified or 0, API will pick a default size, which is 10.
  // If the requested page size is larger than the maximum size, API will pick
  // the maximum size, which is 100.
  int32 page_size = 5;

  // The continuation token to fetch the next page. If empty, it means it is
  // fetching the first page.
  string page_token = 6;
}

// Image query for search endpoint request.
message ImageQuery {
  oneof image {
    // Input image in raw bytes.
    bytes input_image = 1;

    // Resource name of the asset. Only supported in IMAGE corpus type.
    // Format:
    // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
    string asset = 2 [(google.api.resource_reference) = {
      type: "visionai.googleapis.com/Asset"
    }];
  }
}

// A strategy to specify how to sort by data schema key.
message SchemaKeySortingStrategy {
  // Option for one data schema key.
  message Option {
    // When one result has multiple values with the same key, specify
    // which value is used to sort. By default, AGGREGATE_METHOD_LARGEST
    // is used when results are sorted in decreasing order,
    // AGGREGATE_METHOD_SMALLEST is used when results are sorted in
    // incresing order.
    enum AggregateMethod {
      // The unspecified aggregate method will be overwritten as mentioned
      // above.
      AGGREGATE_METHOD_UNSPECIFIED = 0;

      // Take the (lexicographical or numerical) largest value to sort.
      AGGREGATE_METHOD_LARGEST = 1;

      // Take the (lexicographical or numerical) smallest value to sort.
      AGGREGATE_METHOD_SMALLEST = 2;
    }

    // The data used to sort.
    string data_schema_key = 1;

    // Whether to sort in decreasing order or increasing order.
    // By default, results are sorted in incresing order.
    bool sort_decreasing = 2;

    // Aggregate method for the current data schema key.
    optional AggregateMethod aggregate_method = 3;
  }

  // Options in the front have high priority than those in the back.
  repeated Option options = 1;
}

// The metadata for DeleteAsset API that embeds in
// [metadata][google.longrunning.Operation.metadata] field.
message DeleteAssetMetadata {}

// Stores the criteria-annotation matching results for each search result item.
message AnnotationMatchingResult {
  // The criteria used for matching. It can be an input search criteria or a
  // criteria converted from a facet selection.
  Criteria criteria = 1;

  // Matched annotations for the criteria.
  repeated Annotation matched_annotations = 2;

  // Status of the match result. Possible values:
  // FAILED_PRECONDITION - the criteria is not eligible for match.
  // OK - matching is performed.
  google.rpc.Status status = 3;
}

// Search result contains asset name and corresponding time ranges.
message SearchResultItem {
  // The resource name of the asset.
  // Format:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string asset = 1;

  // The matched asset segments.
  // Deprecated: please use singular `segment` field.
  repeated Partition.TemporalPartition segments = 2 [deprecated = true];

  // The matched asset segment.
  Partition.TemporalPartition segment = 5;

  // Available to IMAGE corpus types.
  // Relevance of this `SearchResultItem` to user search query (text query or
  // image query).
  // By default this represents cosine similarity between the query and the
  // retrieved media content. The value is in the range of [-1, 1].
  // Note that search ranking is not only decided by this relevance score,
  // but also other factors such as the match of annotations.
  double relevance = 6;

  // Search result annotations specified by result_annotation_keys in search
  // request.
  repeated Annotation requested_annotations = 3;

  // Criteria or facet-selection based annotation matching results associated to
  // this search result item. Only contains results for criteria or
  // facet_selections with fetch_matched_annotations=true.
  repeated AnnotationMatchingResult annotation_matching_results = 4;
}

// Response message for SearchAssets.
message SearchAssetsResponse {
  // Returned search results.
  repeated SearchResultItem search_result_items = 1;

  // The next-page continuation token.
  string next_page_token = 2;

  // Facet search results of a given query, which contains user's
  // already-selected facet values and updated facet search results.
  repeated FacetGroup facet_results = 3;
}

// Response message for SearchIndexEndpoint.
message SearchIndexEndpointResponse {
  // Returned search results.
  repeated SearchResultItem search_result_items = 1;

  // The next-page continuation token.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Integer range type.
message IntRange {
  // Start of the int range.
  optional int64 start = 1;

  // End of the int range.
  optional int64 end = 2;
}

// Float range type.
message FloatRange {
  // Start of the float range.
  optional float start = 1;

  // End of the float range.
  optional float end = 2;
}

// A list of string-type values.
message StringArray {
  // String type values.
  repeated string txt_values = 1;
}

// A list of integer range values.
message IntRangeArray {
  // Int range values.
  repeated IntRange int_ranges = 1;
}

// A list of float range values.
message FloatRangeArray {
  // Float range values.
  repeated FloatRange float_ranges = 1;
}

// Datetime range type.
message DateTimeRange {
  // Start date time.
  google.type.DateTime start = 1;

  // End data time.
  google.type.DateTime end = 2;
}

// A list of datetime range values.
message DateTimeRangeArray {
  // Date time ranges.
  repeated DateTimeRange date_time_ranges = 1;
}

// Representation of a circle area.
message CircleArea {
  // Latitude of circle area's center. Degrees [-90 .. 90]
  double latitude = 1;

  // Longitude of circle area's center. Degrees [-180 .. 180]
  double longitude = 2;

  // Radius of the circle area in meters.
  double radius_meter = 3;
}

// A list of locations.
message GeoLocationArray {
  // A list of circle areas.
  repeated CircleArea circle_areas = 1;
}

message BoolValue {
  bool value = 1;
}

// Filter criteria applied to current search results.
message Criteria {
  oneof value {
    // The text values associated with the field.
    StringArray text_array = 2;

    // The integer ranges associated with the field.
    IntRangeArray int_range_array = 3;

    // The float ranges associated with the field.
    FloatRangeArray float_range_array = 4;

    // The datetime ranges associated with the field.
    DateTimeRangeArray date_time_range_array = 5;

    // Geo Location array.
    GeoLocationArray geo_location_array = 6;

    // A Boolean value.
    BoolValue bool_value = 7;
  }

  // The UGA field or ML field to apply filtering criteria.
  string field = 1;

  // If true, return query matched annotations for this criteria.
  // This option is only applicable for inclusion criteria, i.e., not exclusion
  // criteria, with partition level annotations.  It supports the following data
  // types:
  //  - INTEGER
  //  - FLOAT
  //  - STRING (DataSchema.SearchStrategy.EXACT_SEARCH only)
  //  - BOOLEAN
  bool fetch_matched_annotations = 8;
}

// Partition to specify the partition in time and space for sub-asset level
// annotation.
message Partition {
  // Partition of asset in UTC Epoch time. Supported by STREAM_VIDEO corpus
  // type.
  message TemporalPartition {
    // Start time of the partition.
    google.protobuf.Timestamp start_time = 1;

    // End time of the partition.
    google.protobuf.Timestamp end_time = 2;
  }

  // Partition of asset in space.
  message SpatialPartition {
    // The minimum x coordinate value.
    optional int64 x_min = 1;

    // The minimum y coordinate value.
    optional int64 y_min = 2;

    // The maximum x coordinate value.
    optional int64 x_max = 3;

    // The maximum y coordinate value.
    optional int64 y_max = 4;
  }

  // Partition of asset in relative time. Supported by VIDEO_ON_DEMAND corpus
  // type.
  message RelativeTemporalPartition {
    // Start time offset of the partition.
    google.protobuf.Duration start_offset = 1;

    // End time offset of the partition.
    google.protobuf.Duration end_offset = 2;
  }

  // Partition of asset in time.
  TemporalPartition temporal_partition = 1;

  // Partition of asset in space.
  SpatialPartition spatial_partition = 2;

  // Partition of asset in time.
  RelativeTemporalPartition relative_temporal_partition = 3;
}
