From 0bc608a9c045007f12325231ed7f0069a40f469b Mon Sep 17 00:00:00 2001 From: A Vertex SDK engineer Date: Wed, 11 Sep 2024 13:05:33 -0700 Subject: [PATCH 01/17] docs: manually add summary overview page. PiperOrigin-RevId: 673504890 --- docs/README.rst | 2 +- docs/summary_overview.md | 14 ++++++++++++++ owlbot.py | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 docs/summary_overview.md diff --git a/docs/README.rst b/docs/README.rst index b1508b48c5..c393e30903 100644 --- a/docs/README.rst +++ b/docs/README.rst @@ -15,7 +15,7 @@ Vertex AI SDK for Python .. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-aiplatform.svg :target: https://pypi.org/project/google-cloud-aiplatform/ .. _Vertex AI: https://cloud.google.com/vertex-ai/docs -.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/aiplatform/latest +.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/aiplatform/latest/summary_overview .. _Product Documentation: https://cloud.google.com/vertex-ai/docs diff --git a/docs/summary_overview.md b/docs/summary_overview.md new file mode 100644 index 0000000000..7d0ca2569b --- /dev/null +++ b/docs/summary_overview.md @@ -0,0 +1,14 @@ +# Vertex AI API + +Overview of the APIs available for Vertex AI API. + +## All entries + +Classes, methods and properties & attributes for Vertex AI API. + +[classes](https://cloud.google.com/python/docs/reference/aiplatform/latest/summary_class.html) + +[methods](https://cloud.google.com/python/docs/reference/aiplatform/latest/summary_method.html) + +[properties and +attributes](https://cloud.google.com/python/docs/reference/aiplatform/latest/summary_property.html) \ No newline at end of file diff --git a/owlbot.py b/owlbot.py index c8a1afe6ec..0caac7dcb8 100644 --- a/owlbot.py +++ b/owlbot.py @@ -66,6 +66,7 @@ "setup.py", "README.rst", "docs/index.rst", + "docs/summary_overview.md", f"docs/definition_{library.name}/services.rst", f"docs/instance_{library.name}/services.rst", f"docs/params_{library.name}/services.rst", From ef80003f8011a0c9965b9609b18ac7cb7a0c9162 Mon Sep 17 00:00:00 2001 From: Yeesian Ng Date: Wed, 11 Sep 2024 13:51:02 -0700 Subject: [PATCH 02/17] chore: Do not specify PackageSpec.dependency_files_gcs_uri if extra_packages is empty PiperOrigin-RevId: 673523227 --- .../test_reasoning_engines.py | 29 +++-------- .../reasoning_engines/_reasoning_engines.py | 48 ++++--------------- 2 files changed, 15 insertions(+), 62 deletions(-) diff --git a/tests/unit/vertex_langchain/test_reasoning_engines.py b/tests/unit/vertex_langchain/test_reasoning_engines.py index 74fe565c85..0bc8164fff 100644 --- a/tests/unit/vertex_langchain/test_reasoning_engines.py +++ b/tests/unit/vertex_langchain/test_reasoning_engines.py @@ -352,25 +352,7 @@ def setup_method(self): def teardown_method(self): initializer.global_pool.shutdown(wait=True) - def test_prepare_create( - self, - cloud_storage_create_bucket_mock, - tarfile_open_mock, - cloudpickle_dump_mock, - ): - _reasoning_engines._prepare_create( - reasoning_engine=self.test_app, - requirements=_TEST_REASONING_ENGINE_REQUIREMENTS, - extra_packages=[], - project=_TEST_PROJECT, - location=_TEST_LOCATION, - staging_bucket=_TEST_STAGING_BUCKET, - gcs_dir_name=_TEST_GCS_DIR_NAME, - ) - cloudpickle_dump_mock.assert_called() # when preparing object.pkl - tarfile_open_mock.assert_called() # when preparing extra_packages - - def test_prepare_update_with_unspecified_extra_packages( + def test_prepare_with_unspecified_extra_packages( self, cloud_storage_create_bucket_mock, cloudpickle_dump_mock, @@ -379,7 +361,7 @@ def test_prepare_update_with_unspecified_extra_packages( _reasoning_engines, "_upload_extra_packages", ) as upload_extra_packages_mock: - _reasoning_engines._prepare_update( + _reasoning_engines._prepare( reasoning_engine=self.test_app, requirements=_TEST_REASONING_ENGINE_REQUIREMENTS, extra_packages=None, @@ -390,7 +372,7 @@ def test_prepare_update_with_unspecified_extra_packages( ) upload_extra_packages_mock.assert_not_called() - def test_prepare_update_with_empty_extra_packages( + def test_prepare_with_empty_extra_packages( self, cloud_storage_create_bucket_mock, cloudpickle_dump_mock, @@ -399,7 +381,7 @@ def test_prepare_update_with_empty_extra_packages( _reasoning_engines, "_upload_extra_packages", ) as upload_extra_packages_mock: - _reasoning_engines._prepare_update( + _reasoning_engines._prepare( reasoning_engine=self.test_app, requirements=_TEST_REASONING_ENGINE_REQUIREMENTS, extra_packages=[], @@ -429,6 +411,7 @@ def test_create_reasoning_engine( self.test_app, display_name=_TEST_REASONING_ENGINE_DISPLAY_NAME, requirements=_TEST_REASONING_ENGINE_REQUIREMENTS, + extra_packages=[_TEST_REASONING_ENGINE_EXTRA_PACKAGE_PATH], ) # Manually set _gca_resource here to prevent the mocks from propagating. test_reasoning_engine._gca_resource = _TEST_REASONING_ENGINE_OBJ @@ -494,6 +477,7 @@ def test_create_reasoning_engine_requirements_from_file( self.test_app, display_name=_TEST_REASONING_ENGINE_DISPLAY_NAME, requirements="requirements.txt", + extra_packages=[_TEST_REASONING_ENGINE_EXTRA_PACKAGE_PATH], ) mock_file.assert_called_with("requirements.txt") # Manually set _gca_resource here to prevent the mocks from propagating. @@ -668,6 +652,7 @@ def test_delete_after_create_reasoning_engine( self.test_app, display_name=_TEST_REASONING_ENGINE_DISPLAY_NAME, requirements=_TEST_REASONING_ENGINE_REQUIREMENTS, + extra_packages=[_TEST_REASONING_ENGINE_EXTRA_PACKAGE_PATH], ) # Manually set _gca_resource here to prevent the mocks from propagating. test_reasoning_engine._gca_resource = _TEST_REASONING_ENGINE_OBJ diff --git a/vertexai/reasoning_engines/_reasoning_engines.py b/vertexai/reasoning_engines/_reasoning_engines.py index a42fc82753..d9a46fe563 100644 --- a/vertexai/reasoning_engines/_reasoning_engines.py +++ b/vertexai/reasoning_engines/_reasoning_engines.py @@ -209,7 +209,7 @@ def create( # This involves packaging and uploading the artifacts for # reasoning_engine, requirements and extra_packages to # `staging_bucket/gcs_dir_name`. - _prepare_create( + _prepare( reasoning_engine=reasoning_engine, requirements=requirements, project=sdk_resource.project, @@ -226,12 +226,13 @@ def create( gcs_dir_name, _BLOB_FILENAME, ), - dependency_files_gcs_uri="{}/{}/{}".format( + ) + if extra_packages: + package_spec.dependency_files_gcs_uri = "{}/{}/{}".format( staging_bucket, gcs_dir_name, _EXTRA_PACKAGES_FILE, - ), - ) + ) if requirements: package_spec.requirements_gcs_uri = "{}/{}/{}".format( staging_bucket, @@ -377,7 +378,7 @@ def update( # This involves packaging and uploading the artifacts for # reasoning_engine, requirements and extra_packages to # `staging_bucket/gcs_dir_name`. - _prepare_update( + _prepare( reasoning_engine=reasoning_engine, requirements=requirements, project=self.project, @@ -564,40 +565,7 @@ def _upload_extra_packages( _LOGGER.info(f"Writing to {dir_name}/{_EXTRA_PACKAGES_FILE}") -def _prepare_create( - reasoning_engine: Queryable, - requirements: Sequence[str], - extra_packages: Sequence[str], - project: str, - location: str, - staging_bucket: str, - gcs_dir_name: str, -) -> None: - """Prepares the reasoning engine for creation in Vertex AI. - - This involves packaging and uploading artifacts to Cloud Storage. Note that - 1. This does not actually create the Reasoning Engine in Vertex AI. - 2. This will always generate and upload a pickled object. - 3. This will always generate and upload the dependencies.tar.gz file. - - Args: - reasoning_engine: The reasoning engine to be prepared. - requirements (Sequence[str]): The set of PyPI dependencies needed. - extra_packages (Sequence[str]): The set of extra user-provided packages. - project (str): The project for the staging bucket. - location (str): The location for the staging bucket. - staging_bucket (str): The staging bucket name in the form "gs://...". - gcs_dir_name (str): The GCS bucket directory under `staging_bucket` to - use for staging the artifacts needed. - """ - gcs_bucket = _get_gcs_bucket(project, location, staging_bucket) - _upload_reasoning_engine(reasoning_engine, gcs_bucket, gcs_dir_name) - if requirements: - _upload_requirements(requirements, gcs_bucket, gcs_dir_name) - _upload_extra_packages(extra_packages, gcs_bucket, gcs_dir_name) - - -def _prepare_update( +def _prepare( reasoning_engine: Optional[Queryable], requirements: Optional[Sequence[str]], extra_packages: Optional[Sequence[str]], @@ -606,7 +574,7 @@ def _prepare_update( staging_bucket: str, gcs_dir_name: str, ) -> None: - """Prepares the reasoning engine for updates in Vertex AI. + """Prepares the reasoning engine for creation or updates in Vertex AI. This involves packaging and uploading artifacts to Cloud Storage. Note that 1. This does not actually update the Reasoning Engine in Vertex AI. From 73490b22a239cb1a3c31349f8db6cfbc5232e231 Mon Sep 17 00:00:00 2001 From: Jason Dai Date: Wed, 11 Sep 2024 14:35:57 -0700 Subject: [PATCH 03/17] feat: Allow customizing pipeline caching options for model evaluation jobs. PiperOrigin-RevId: 673540795 --- .../pipeline_based_service.py | 12 ++ .../model_evaluation/model_evaluation_job.py | 12 ++ google/cloud/aiplatform/models.py | 12 ++ .../unit/aiplatform/test_model_evaluation.py | 127 ++++++++++++++++++ 4 files changed, 163 insertions(+) diff --git a/google/cloud/aiplatform/_pipeline_based_service/pipeline_based_service.py b/google/cloud/aiplatform/_pipeline_based_service/pipeline_based_service.py index 2ef1db5ad5..fe393926ab 100644 --- a/google/cloud/aiplatform/_pipeline_based_service/pipeline_based_service.py +++ b/google/cloud/aiplatform/_pipeline_based_service/pipeline_based_service.py @@ -269,6 +269,7 @@ def _create_and_submit_pipeline_job( location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, experiment: Optional[Union[str, "aiplatform.Experiment"]] = None, + enable_caching: Optional[bool] = None, ) -> "_VertexAiPipelineBasedService": """Create a new PipelineJob using the provided template and parameters. @@ -310,6 +311,16 @@ def _create_and_submit_pipeline_job( experiment (Union[str, experiments_resource.Experiment]): Optional. The Vertex AI experiment name or instance to associate to the PipelineJob executing this model evaluation job. + enable_caching (bool): + Optional. Whether to turn on caching for the run. + + If this is not set, defaults to the compile time settings, which + are True for all tasks by default, while users may specify + different caching options for individual tasks. + + If this is set, the setting applies to all tasks in the pipeline. + + Overrides the compile time settings. Returns: (VertexAiPipelineBasedService): Instantiated representation of a Vertex AI Pipeline based service. @@ -334,6 +345,7 @@ def _create_and_submit_pipeline_job( project=project, location=location, credentials=credentials, + enable_caching=enable_caching, ) # Suppresses logs from PipelineJob diff --git a/google/cloud/aiplatform/model_evaluation/model_evaluation_job.py b/google/cloud/aiplatform/model_evaluation/model_evaluation_job.py index bde7e6da39..87f4e44b1f 100644 --- a/google/cloud/aiplatform/model_evaluation/model_evaluation_job.py +++ b/google/cloud/aiplatform/model_evaluation/model_evaluation_job.py @@ -174,6 +174,7 @@ def submit( location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, experiment: Optional[Union[str, "aiplatform.Experiment"]] = None, + enable_caching: Optional[bool] = None, ) -> "_ModelEvaluationJob": """Submits a Model Evaluation Job using aiplatform.PipelineJob and returns the ModelEvaluationJob resource. @@ -277,6 +278,16 @@ def submit( experiment (Union[str, experiments_resource.Experiment]): Optional. The Vertex AI experiment name or instance to associate to the PipelineJob executing this model evaluation job. + enable_caching (bool): + Optional. Whether to turn on caching for the run. + + If this is not set, defaults to the compile time settings, which + are True for all tasks by default, while users may specify + different caching options for individual tasks. + + If this is set, the setting applies to all tasks in the pipeline. + + Overrides the compile time settings. Returns: (ModelEvaluationJob): Instantiated represnetation of the model evaluation job. """ @@ -351,6 +362,7 @@ def submit( location=location, credentials=credentials, experiment=experiment, + enable_caching=enable_caching, ) _LOGGER.info( diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index dc8e105ef6..6abb482061 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -6883,6 +6883,7 @@ def evaluate( network: Optional[str] = None, encryption_spec_key_name: Optional[str] = None, experiment: Optional[Union[str, "aiplatform.Experiment"]] = None, + enable_caching: Optional[bool] = None, ) -> "model_evaluation._ModelEvaluationJob": """Creates a model evaluation job running on Vertex Pipelines and returns the resulting ModelEvaluationJob resource. @@ -6968,6 +6969,16 @@ def evaluate( this model evaluation job. Metrics produced by the PipelineJob as system.Metric Artifacts will be associated as metrics to the provided experiment, and parameters from this PipelineJob will be associated as parameters to the provided experiment. + enable_caching (bool): + Optional. Whether to turn on caching for the run. + + If this is not set, defaults to the compile time settings, which + are True for all tasks by default, while users may specify + different caching options for individual tasks. + + If this is set, the setting applies to all tasks in the pipeline. + + Overrides the compile time settings. Returns: model_evaluation.ModelEvaluationJob: Instantiated representation of the _ModelEvaluationJob. @@ -7088,6 +7099,7 @@ def evaluate( encryption_spec_key_name=encryption_spec_key_name, credentials=self.credentials, experiment=experiment, + enable_caching=enable_caching, ) diff --git a/tests/unit/aiplatform/test_model_evaluation.py b/tests/unit/aiplatform/test_model_evaluation.py index dcbb8aed93..0117b83458 100644 --- a/tests/unit/aiplatform/test_model_evaluation.py +++ b/tests/unit/aiplatform/test_model_evaluation.py @@ -198,6 +198,39 @@ } ) +_TEST_MODEL_EVAL_PIPELINE_SPEC_WITH_CACHING_OPTIONS_JSON = json.dumps( + { + "pipelineInfo": {"name": "evaluation-default-pipeline"}, + "root": { + "dag": { + "tasks": { + "model-evaluation-text-generation": { + "taskInfo": {"name": "model-evaluation-text-generation"}, + "cachingOptions": {"enableCache": False}, + } + } + }, + "inputDefinitions": { + "parameters": { + "batch_predict_gcs_source_uris": {"type": "STRING"}, + "dataflow_service_account": {"type": "STRING"}, + "batch_predict_instances_format": {"type": "STRING"}, + "batch_predict_machine_type": {"type": "STRING"}, + "evaluation_class_labels": {"type": "STRING"}, + "location": {"type": "STRING"}, + "model_name": {"type": "STRING"}, + "project": {"type": "STRING"}, + "batch_predict_gcs_destination_output_uri": {"type": "STRING"}, + "target_field_name": {"type": "STRING"}, + } + }, + }, + "schemaVersion": "2.0.0", + "sdkVersion": "kfp-1.8.12", + "components": {}, + } +) + _TEST_INVALID_MODEL_EVAL_PIPELINE_SPEC = json.dumps( { "pipelineInfo": {"name": "my-pipeline"}, @@ -1083,6 +1116,100 @@ def test_model_evaluation_job_submit( assert mock_model_eval_job_get.called_once + @pytest.mark.parametrize( + "job_spec", + [_TEST_MODEL_EVAL_PIPELINE_SPEC_WITH_CACHING_OPTIONS_JSON], + ) + @pytest.mark.usefixtures("mock_pipeline_service_create") + def test_model_evaluation_job_submit_with_caching_disabled( + self, + job_spec, + mock_load_yaml_and_json, + mock_model, + get_model_mock, + mock_model_eval_get, + mock_model_eval_job_get, + mock_pipeline_service_get, + mock_model_eval_job_create, + mock_pipeline_bucket_exists, + mock_request_urlopen, + ): + test_model_eval_job = model_evaluation_job._ModelEvaluationJob.submit( + model_name=_TEST_MODEL_RESOURCE_NAME, + prediction_type=_TEST_MODEL_EVAL_PREDICTION_TYPE, + instances_format=_TEST_MODEL_EVAL_PIPELINE_PARAMETER_VALUES[ + "batch_predict_instances_format" + ], + model_type="automl_tabular", + pipeline_root=_TEST_GCS_BUCKET_NAME, + target_field_name=_TEST_MODEL_EVAL_PIPELINE_PARAMETER_VALUES[ + "target_field_name" + ], + evaluation_pipeline_display_name=_TEST_MODEL_EVAL_PIPELINE_JOB_DISPLAY_NAME, + gcs_source_uris=_TEST_MODEL_EVAL_PIPELINE_PARAMETER_VALUES[ + "batch_predict_gcs_source_uris" + ], + job_id=_TEST_PIPELINE_JOB_ID, + service_account=_TEST_SERVICE_ACCOUNT, + network=_TEST_NETWORK, + enable_caching=False, + ) + + test_model_eval_job.wait() + + expected_runtime_config_dict = { + "gcsOutputDirectory": _TEST_GCS_BUCKET_NAME, + "parameters": { + "batch_predict_gcs_source_uris": { + "stringValue": '["gs://my-bucket/my-prediction-data.csv"]' + }, + "dataflow_service_account": {"stringValue": _TEST_SERVICE_ACCOUNT}, + "batch_predict_instances_format": {"stringValue": "csv"}, + "model_name": {"stringValue": _TEST_MODEL_RESOURCE_NAME}, + "project": {"stringValue": _TEST_PROJECT}, + "location": {"stringValue": _TEST_LOCATION}, + "batch_predict_gcs_destination_output_uri": { + "stringValue": _TEST_GCS_BUCKET_NAME + }, + "target_field_name": {"stringValue": "predict_class"}, + }, + } + + runtime_config = gca_pipeline_job.PipelineJob.RuntimeConfig()._pb + json_format.ParseDict(expected_runtime_config_dict, runtime_config) + + job_spec = yaml.safe_load(job_spec) + pipeline_spec = job_spec.get("pipelineSpec") or job_spec + + # Construct expected request + expected_gapic_pipeline_job = gca_pipeline_job.PipelineJob( + display_name=_TEST_MODEL_EVAL_PIPELINE_JOB_DISPLAY_NAME, + pipeline_spec={ + "components": {}, + "pipelineInfo": pipeline_spec["pipelineInfo"], + "root": pipeline_spec["root"], + "schemaVersion": "2.0.0", + "sdkVersion": "kfp-1.8.12", + }, + runtime_config=runtime_config, + service_account=_TEST_SERVICE_ACCOUNT, + network=_TEST_NETWORK, + template_uri=_TEST_KFP_TEMPLATE_URI, + ) + + mock_model_eval_job_create.assert_called_with( + parent=_TEST_PARENT, + pipeline_job=expected_gapic_pipeline_job, + pipeline_job_id=_TEST_PIPELINE_JOB_ID, + timeout=None, + ) + + assert mock_model_eval_job_get.called_once + + assert mock_pipeline_service_get.called_once + + assert mock_model_eval_job_get.called_once + @pytest.mark.parametrize( "job_spec", [_TEST_MODEL_EVAL_PIPELINE_SPEC_JSON], From cfc3421fe8a883d459b66ed8c9f39697ded23f20 Mon Sep 17 00:00:00 2001 From: Ayush Agrawal Date: Wed, 11 Sep 2024 16:00:57 -0700 Subject: [PATCH 04/17] feat: Adding Feature Store Vector DB option for RAG corpuses to SDK PiperOrigin-RevId: 673571692 --- tests/unit/vertex_rag/test_rag_constants.py | 21 +++++++++++++++++ tests/unit/vertex_rag/test_rag_data.py | 26 +++++++++++++++++++++ vertexai/preview/rag/__init__.py | 2 ++ vertexai/preview/rag/rag_data.py | 3 ++- vertexai/preview/rag/utils/_gapic_utils.py | 21 +++++++++++++---- vertexai/preview/rag/utils/resources.py | 17 ++++++++++++-- 6 files changed, 83 insertions(+), 7 deletions(-) diff --git a/tests/unit/vertex_rag/test_rag_constants.py b/tests/unit/vertex_rag/test_rag_constants.py index cd2a74e30c..356dbf4ba7 100644 --- a/tests/unit/vertex_rag/test_rag_constants.py +++ b/tests/unit/vertex_rag/test_rag_constants.py @@ -28,6 +28,7 @@ JiraSource, JiraQuery, Weaviate, + VertexFeatureStore, ) from google.cloud.aiplatform_v1beta1 import ( GoogleDriveSource, @@ -68,6 +69,7 @@ collection_name=TEST_WEAVIATE_COLLECTION_NAME, api_key=TEST_WEAVIATE_API_KEY_SECRET_VERSION, ) +TEST_VERTEX_FEATURE_STORE_RESOURCE_NAME = "test-feature-view-resource-name" TEST_GAPIC_RAG_CORPUS = GapicRagCorpus( name=TEST_RAG_CORPUS_RESOURCE_NAME, display_name=TEST_CORPUS_DISPLAY_NAME, @@ -94,9 +96,22 @@ ), ), ) +TEST_GAPIC_RAG_CORPUS_VERTEX_FEATURE_STORE = GapicRagCorpus( + name=TEST_RAG_CORPUS_RESOURCE_NAME, + display_name=TEST_CORPUS_DISPLAY_NAME, + description=TEST_CORPUS_DISCRIPTION, + rag_vector_db_config=RagVectorDbConfig( + vertex_feature_store=RagVectorDbConfig.VertexFeatureStore( + feature_view_resource_name=TEST_VERTEX_FEATURE_STORE_RESOURCE_NAME + ), + ), +) TEST_EMBEDDING_MODEL_CONFIG = EmbeddingModelConfig( publisher_model="publishers/google/models/textembedding-gecko", ) +TEST_VERTEX_FEATURE_STORE_CONFIG = VertexFeatureStore( + resource_name=TEST_VERTEX_FEATURE_STORE_RESOURCE_NAME, +) TEST_RAG_CORPUS = RagCorpus( name=TEST_RAG_CORPUS_RESOURCE_NAME, display_name=TEST_CORPUS_DISPLAY_NAME, @@ -109,6 +124,12 @@ description=TEST_CORPUS_DISCRIPTION, vector_db=TEST_WEAVIATE_CONFIG, ) +TEST_RAG_CORPUS_VERTEX_FEATURE_STORE = RagCorpus( + name=TEST_RAG_CORPUS_RESOURCE_NAME, + display_name=TEST_CORPUS_DISPLAY_NAME, + description=TEST_CORPUS_DISCRIPTION, + vector_db=TEST_VERTEX_FEATURE_STORE_CONFIG, +) TEST_PAGE_TOKEN = "test-page-token" # RagFiles diff --git a/tests/unit/vertex_rag/test_rag_data.py b/tests/unit/vertex_rag/test_rag_data.py index fd243920b1..5f7866c389 100644 --- a/tests/unit/vertex_rag/test_rag_data.py +++ b/tests/unit/vertex_rag/test_rag_data.py @@ -62,6 +62,23 @@ def create_rag_corpus_mock_weaviate(): yield create_rag_corpus_mock_weaviate +@pytest.fixture +def create_rag_corpus_mock_vertex_feature_store(): + with mock.patch.object( + VertexRagDataServiceClient, + "create_rag_corpus", + ) as create_rag_corpus_mock_vertex_feature_store: + create_rag_corpus_lro_mock = mock.Mock(ga_operation.Operation) + create_rag_corpus_lro_mock.done.return_value = True + create_rag_corpus_lro_mock.result.return_value = ( + tc.TEST_GAPIC_RAG_CORPUS_VERTEX_FEATURE_STORE + ) + create_rag_corpus_mock_vertex_feature_store.return_value = ( + create_rag_corpus_lro_mock + ) + yield create_rag_corpus_mock_vertex_feature_store + + @pytest.fixture def list_rag_corpora_pager_mock(): with mock.patch.object( @@ -216,6 +233,15 @@ def test_create_corpus_weaviate_success(self): rag_corpus_eq(rag_corpus, tc.TEST_RAG_CORPUS_WEAVIATE) + @pytest.mark.usefixtures("create_rag_corpus_mock_vertex_feature_store") + def test_create_corpus_vertex_feature_store_success(self): + rag_corpus = rag.create_corpus( + display_name=tc.TEST_CORPUS_DISPLAY_NAME, + vector_db=tc.TEST_VERTEX_FEATURE_STORE_CONFIG, + ) + + rag_corpus_eq(rag_corpus, tc.TEST_RAG_CORPUS_VERTEX_FEATURE_STORE) + @pytest.mark.usefixtures("rag_data_client_mock_exception") def test_create_corpus_failure(self): with pytest.raises(RuntimeError) as e: diff --git a/vertexai/preview/rag/__init__.py b/vertexai/preview/rag/__init__.py index 56590fed0e..bbd7cf1f83 100644 --- a/vertexai/preview/rag/__init__.py +++ b/vertexai/preview/rag/__init__.py @@ -45,6 +45,7 @@ RagResource, SlackChannel, SlackChannelsSource, + VertexFeatureStore, Weaviate, ) @@ -59,6 +60,7 @@ "Retrieval", "SlackChannel", "SlackChannelsSource", + "VertexFeatureStore", "VertexRagStore", "Weaviate", "create_corpus", diff --git a/vertexai/preview/rag/rag_data.py b/vertexai/preview/rag/rag_data.py index 0983037173..ea7b730d6a 100644 --- a/vertexai/preview/rag/rag_data.py +++ b/vertexai/preview/rag/rag_data.py @@ -48,6 +48,7 @@ RagCorpus, RagFile, SlackChannelsSource, + VertexFeatureStore, Weaviate, ) @@ -56,7 +57,7 @@ def create_corpus( display_name: Optional[str] = None, description: Optional[str] = None, embedding_model_config: Optional[EmbeddingModelConfig] = None, - vector_db: Optional[Weaviate] = None, + vector_db: Optional[Union[Weaviate, VertexFeatureStore]] = None, ) -> RagCorpus: """Creates a new RagCorpus resource. diff --git a/vertexai/preview/rag/utils/_gapic_utils.py b/vertexai/preview/rag/utils/_gapic_utils.py index 640fd8c5f0..95a4e24612 100644 --- a/vertexai/preview/rag/utils/_gapic_utils.py +++ b/vertexai/preview/rag/utils/_gapic_utils.py @@ -42,6 +42,7 @@ RagFile, SlackChannelsSource, JiraSource, + VertexFeatureStore, Weaviate, ) @@ -97,14 +98,18 @@ def convert_gapic_to_embedding_model_config( def convert_gapic_to_vector_db( gapic_vector_db: RagVectorDbConfig, -) -> Weaviate: - """Convert Gapic RagVectorDbConfig to Weaviate.""" +) -> Union[Weaviate, VertexFeatureStore]: + """Convert Gapic RagVectorDbConfig to Weaviate or VertexFeatureStore.""" if gapic_vector_db.__contains__("weaviate"): return Weaviate( weaviate_http_endpoint=gapic_vector_db.weaviate.http_endpoint, collection_name=gapic_vector_db.weaviate.collection_name, api_key=gapic_vector_db.api_auth.api_key_config.api_key_secret_version, ) + elif gapic_vector_db.__contains__("vertex_feature_store"): + return VertexFeatureStore( + resource_name=gapic_vector_db.vertex_feature_store.feature_view_resource_name, + ) else: return None @@ -390,7 +395,7 @@ def set_embedding_model_config( def set_vector_db( - vector_db: Weaviate, + vector_db: Union[Weaviate, VertexFeatureStore], rag_corpus: GapicRagCorpus, ) -> None: """Sets the vector db configuration for the rag corpus.""" @@ -410,5 +415,13 @@ def set_vector_db( ), ), ) + elif isinstance(vector_db, VertexFeatureStore): + resource_name = vector_db.resource_name + + rag_corpus.rag_vector_db_config = RagVectorDbConfig( + vertex_feature_store=RagVectorDbConfig.VertexFeatureStore( + feature_view_resource_name=resource_name, + ), + ) else: - raise TypeError("vector_db must be a Weaviate.") + raise TypeError("vector_db must be a Weaviate or VertexFeatureStore.") diff --git a/vertexai/preview/rag/utils/resources.py b/vertexai/preview/rag/utils/resources.py index aad7bad35d..ef46f4e6a2 100644 --- a/vertexai/preview/rag/utils/resources.py +++ b/vertexai/preview/rag/utils/resources.py @@ -16,7 +16,7 @@ # import dataclasses -from typing import List, Optional, Sequence +from typing import List, Optional, Sequence, Union from google.protobuf import timestamp_pb2 @@ -85,6 +85,19 @@ class Weaviate: api_key: str +@dataclasses.dataclass +class VertexFeatureStore: + """VertexFeatureStore. + + Attributes: + resource_name: The resource name of the FeatureView. Format: + ``projects/{project}/locations/{location}/featureOnlineStores/ + {feature_online_store}/featureViews/{feature_view}`` + """ + + resource_name: str + + @dataclasses.dataclass class RagCorpus: """RAG corpus(output only). @@ -102,7 +115,7 @@ class RagCorpus: display_name: Optional[str] = None description: Optional[str] = None embedding_model_config: Optional[EmbeddingModelConfig] = None - vector_db: Optional[Weaviate] = None + vector_db: Optional[Union[Weaviate, VertexFeatureStore]] = None @dataclasses.dataclass From 1540963b30d41ffdd6d24a9a388b3e6970ec6a28 Mon Sep 17 00:00:00 2001 From: Amy Wu Date: Thu, 12 Sep 2024 11:05:03 -0700 Subject: [PATCH 05/17] chore: Update kokoro configs to run docfx and gemini_docfx in continuous and only docs in presubmit PiperOrigin-RevId: 673920251 --- .kokoro/continuous/docfx.cfg | 2 +- .kokoro/docs/docs-presubmit.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.kokoro/continuous/docfx.cfg b/.kokoro/continuous/docfx.cfg index 2e14176135..ceac410de4 100644 --- a/.kokoro/continuous/docfx.cfg +++ b/.kokoro/continuous/docfx.cfg @@ -24,5 +24,5 @@ env_vars: { # Only run this nox session. env_vars: { key: "NOX_SESSION" - value: "docfx" + value: "docfx gemini_docs gemini_docfx" } diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg index 503b5c0fae..6da1a95458 100644 --- a/.kokoro/docs/docs-presubmit.cfg +++ b/.kokoro/docs/docs-presubmit.cfg @@ -24,5 +24,5 @@ env_vars: { # Only run this nox session. env_vars: { key: "NOX_SESSION" - value: "docs docfx gemini_docs gemini_docfx" + value: "docs" } From 94ebf9f5daa6137f8263f0dce8713372b0021527 Mon Sep 17 00:00:00 2001 From: Amy Wu Date: Thu, 12 Sep 2024 14:52:05 -0700 Subject: [PATCH 06/17] chore: Update Kokoro configs to run gemini_docs and gemini_docfx in the docs presubmit PiperOrigin-RevId: 674020793 --- .kokoro/continuous/docfx.cfg | 2 +- .kokoro/docs/docs-presubmit.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.kokoro/continuous/docfx.cfg b/.kokoro/continuous/docfx.cfg index ceac410de4..85c4e08775 100644 --- a/.kokoro/continuous/docfx.cfg +++ b/.kokoro/continuous/docfx.cfg @@ -24,5 +24,5 @@ env_vars: { # Only run this nox session. env_vars: { key: "NOX_SESSION" - value: "docfx gemini_docs gemini_docfx" + value: "docs docfx" } diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg index 6da1a95458..e16e19c722 100644 --- a/.kokoro/docs/docs-presubmit.cfg +++ b/.kokoro/docs/docs-presubmit.cfg @@ -24,5 +24,5 @@ env_vars: { # Only run this nox session. env_vars: { key: "NOX_SESSION" - value: "docs" + value: "gemini_docs gemini_docfx" } From 1e6210ad5aa9f214c99f411b87a71f4da79319ac Mon Sep 17 00:00:00 2001 From: Ayush Agrawal Date: Fri, 13 Sep 2024 09:56:04 -0700 Subject: [PATCH 07/17] chore: make final updates to include Ray v2.33.0 support on SDK, make 2.33.0 the default Ray version over 2.9.3 PiperOrigin-RevId: 674336228 --- .kokoro/presubmit/unit_ray_2-33.cfg | 13 ++ .../aiplatform/vertex_ray/cluster_init.py | 6 +- .../aiplatform/vertex_ray/util/resources.py | 11 +- noxfile.py | 2 +- testing/constraints-ray-2.33.0.txt | 13 ++ tests/unit/vertex_ray/test_cluster_init.py | 132 +++++++++++++----- tests/unit/vertex_ray/test_constants.py | 34 +++-- tests/unit/vertex_ray/test_ray_prediction.py | 3 + 8 files changed, 159 insertions(+), 55 deletions(-) create mode 100644 .kokoro/presubmit/unit_ray_2-33.cfg create mode 100644 testing/constraints-ray-2.33.0.txt diff --git a/.kokoro/presubmit/unit_ray_2-33.cfg b/.kokoro/presubmit/unit_ray_2-33.cfg new file mode 100644 index 0000000000..9a556e7c29 --- /dev/null +++ b/.kokoro/presubmit/unit_ray_2-33.cfg @@ -0,0 +1,13 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Run unit tests for Ray 2.33.0 on Python 3.10 +env_vars: { + key: "NOX_SESSION" + value: "unit_ray(ray='2.33.0')" +} + +# Run unit tests in parallel, splitting up by file +env_vars: { + key: "PYTEST_ADDOPTS" + value: "-n=auto --dist=loadscope" +} diff --git a/google/cloud/aiplatform/vertex_ray/cluster_init.py b/google/cloud/aiplatform/vertex_ray/cluster_init.py index bff297bd8a..e9f31f3cf4 100644 --- a/google/cloud/aiplatform/vertex_ray/cluster_init.py +++ b/google/cloud/aiplatform/vertex_ray/cluster_init.py @@ -52,7 +52,7 @@ def create_ray_cluster( head_node_type: Optional[resources.Resources] = resources.Resources(), python_version: Optional[str] = "3.10", - ray_version: Optional[str] = "2.9", + ray_version: Optional[str] = "2.33", network: Optional[str] = None, service_account: Optional[str] = None, cluster_name: Optional[str] = None, @@ -106,7 +106,7 @@ def create_ray_cluster( head_node_type: The head node resource. Resources.node_count must be 1. If not set, default value of Resources() class will be used. python_version: Python version for the ray cluster. - ray_version: Ray version for the ray cluster. + ray_version: Ray version for the ray cluster. Default is 2.33.0. network: Virtual private cloud (VPC) network. For Ray Client, VPC peering is required to connect to the Ray Cluster managed in the Vertex API service. For Ray Job API, VPC network is not required @@ -157,7 +157,7 @@ def create_ray_cluster( local_ray_verion = _validation_utils.get_local_ray_version() if ray_version != local_ray_verion: if custom_images is None and head_node_type.custom_image is None: - install_ray_version = "2.9.3" + install_ray_version = "2.33.0" logging.info( "[Ray on Vertex]: Local runtime has Ray version %s" ", but the requested cluster runtime has %s. Please " diff --git a/google/cloud/aiplatform/vertex_ray/util/resources.py b/google/cloud/aiplatform/vertex_ray/util/resources.py index 117cd69e1c..a1f7dfb4b6 100644 --- a/google/cloud/aiplatform/vertex_ray/util/resources.py +++ b/google/cloud/aiplatform/vertex_ray/util/resources.py @@ -67,11 +67,14 @@ class Resources: @dataclasses.dataclass class NodeImages: - """ - Custom images for a ray cluster. We currently support Ray v2.9 and python v3.10. + """Custom images for a ray cluster. + + We currently support Ray v2.9 and v2.33 and python v3.10. The custom images must be extended from the following base images: - "{region}-docker.pkg.dev/vertex-ai/training/ray-cpu.2-9.py310:latest" or - "{region}-docker.pkg.dev/vertex-ai/training/ray-gpu.2-9.py310:latest". In + "{region}-docker.pkg.dev/vertex-ai/training/ray-cpu.2-9.py310:latest", + "{region}-docker.pkg.dev/vertex-ai/training/ray-gpu.2-9.py310:latest", + "{region}-docker.pkg.dev/vertex-ai/training/ray-cpu.2-33.py310:latest", or + "{region}-docker.pkg.dev/vertex-ai/training/ray-gpu.2-33.py310:latest". In order to use custom images, need to specify both head and worker images. Attributes: diff --git a/noxfile.py b/noxfile.py index 71e4ecb782..48ca9b0e30 100644 --- a/noxfile.py +++ b/noxfile.py @@ -249,7 +249,7 @@ def unit_genai_minimal_dependencies(session): @nox.session(python="3.10") -@nox.parametrize("ray", ["2.9.3"]) +@nox.parametrize("ray", ["2.9.3", "2.33.0"]) def unit_ray(session, ray): # Install all test dependencies, then install this package in-place. diff --git a/testing/constraints-ray-2.33.0.txt b/testing/constraints-ray-2.33.0.txt new file mode 100644 index 0000000000..3eefca954f --- /dev/null +++ b/testing/constraints-ray-2.33.0.txt @@ -0,0 +1,13 @@ +ray==2.33.0 +# Below constraints are inherited from constraints-3.10.txt +google-api-core +proto-plus==1.22.3 +protobuf +mock==4.0.2 +google-cloud-storage==2.2.1 # Increased for kfp 2.0 compatibility +packaging==20.0 # Increased for compatibility with MLFlow +grpcio-testing==1.34.0 +mlflow==1.30.1 # Pinned to speed up installation +pytest-xdist==3.3.1 # Pinned to unbreak unit tests +IPython # Added to test supernova rich html buttons + diff --git a/tests/unit/vertex_ray/test_cluster_init.py b/tests/unit/vertex_ray/test_cluster_init.py index 486679580f..f5777f237c 100644 --- a/tests/unit/vertex_ray/test_cluster_init.py +++ b/tests/unit/vertex_ray/test_cluster_init.py @@ -290,19 +290,20 @@ def cluster_eq(returned_cluster, expected_cluster): assert returned_cluster.state == expected_cluster.state +@pytest.mark.parametrize("ray_version", ["2.9", "2.33"]) @pytest.mark.usefixtures("google_auth_mock", "get_project_number_mock") class TestClusterManagement: - def setup_method(self): + def setup_method(self, ray_version): importlib.reload(aiplatform.initializer) importlib.reload(aiplatform) aiplatform.init() - def teardown_method(self): + def teardown_method(self, ray_version): aiplatform.initializer.global_pool.shutdown(wait=True) @pytest.mark.usefixtures("get_persistent_resource_1_pool_mock") def test_create_ray_cluster_1_pool_gpu_success( - self, create_persistent_resource_1_pool_mock + self, create_persistent_resource_1_pool_mock, ray_version ): """If head and worker nodes are duplicate, merge to head pool.""" cluster_name = vertex_ray.create_ray_cluster( @@ -310,13 +311,24 @@ def test_create_ray_cluster_1_pool_gpu_success( worker_node_types=tc.ClusterConstants.TEST_WORKER_NODE_TYPES_1_POOL, network=tc.ProjectConstants.TEST_VPC_NETWORK, cluster_name=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ID, + ray_version=ray_version, ) assert tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS == cluster_name + test_persistent_resource = tc.ClusterConstants.TEST_REQUEST_RUNNING_1_POOL + if ray_version == "2.9": + test_persistent_resource.resource_runtime_spec.ray_spec.resource_pool_images[ + "head-node" + ] = tc.ClusterConstants.TEST_GPU_IMAGE_2_9 + else: + test_persistent_resource.resource_runtime_spec.ray_spec.resource_pool_images[ + "head-node" + ] = tc.ClusterConstants.TEST_GPU_IMAGE_2_33 + request = persistent_resource_service.CreatePersistentResourceRequest( parent=tc.ProjectConstants.TEST_PARENT, - persistent_resource=tc.ClusterConstants.TEST_REQUEST_RUNNING_1_POOL, + persistent_resource=test_persistent_resource, persistent_resource_id=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ID, ) @@ -326,7 +338,7 @@ def test_create_ray_cluster_1_pool_gpu_success( @pytest.mark.usefixtures("get_persistent_resource_1_pool_custom_image_mock") def test_create_ray_cluster_1_pool_custom_image_success( - self, create_persistent_resource_1_pool_mock + self, create_persistent_resource_1_pool_mock, ray_version ): """If head and worker nodes are duplicate, merge to head pool.""" custom_images = NodeImages( @@ -355,7 +367,7 @@ def test_create_ray_cluster_1_pool_custom_image_success( @pytest.mark.usefixtures("get_persistent_resource_1_pool_mock") def test_create_ray_cluster_1_pool_gpu_with_labels_success( - self, create_persistent_resource_1_pool_mock + self, create_persistent_resource_1_pool_mock, ray_version ): """If head and worker nodes are duplicate, merge to head pool.""" # Also test disable logging and metrics collection. @@ -367,13 +379,26 @@ def test_create_ray_cluster_1_pool_gpu_with_labels_success( labels=tc.ClusterConstants.TEST_LABELS, enable_metrics_collection=False, enable_logging=False, + ray_version=ray_version, ) assert tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS == cluster_name + test_persistent_resource = ( + tc.ClusterConstants.TEST_REQUEST_RUNNING_1_POOL_WITH_LABELS + ) + if ray_version == "2.9": + test_persistent_resource.resource_runtime_spec.ray_spec.resource_pool_images[ + "head-node" + ] = tc.ClusterConstants.TEST_GPU_IMAGE_2_9 + else: + test_persistent_resource.resource_runtime_spec.ray_spec.resource_pool_images[ + "head-node" + ] = tc.ClusterConstants.TEST_GPU_IMAGE_2_33 + request = persistent_resource_service.CreatePersistentResourceRequest( parent=tc.ProjectConstants.TEST_PARENT, - persistent_resource=tc.ClusterConstants.TEST_REQUEST_RUNNING_1_POOL_WITH_LABELS, + persistent_resource=test_persistent_resource, persistent_resource_id=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ID, ) @@ -383,7 +408,7 @@ def test_create_ray_cluster_1_pool_gpu_with_labels_success( @pytest.mark.usefixtures("get_persistent_resource_2_pools_custom_image_mock") def test_create_ray_cluster_2_pools_custom_images_success( - self, create_persistent_resource_2_pools_custom_image_mock + self, create_persistent_resource_2_pools_custom_image_mock, ray_version ): """If head and worker nodes are not duplicate, create separate resource_pools.""" cluster_name = vertex_ray.create_ray_cluster( @@ -407,7 +432,7 @@ def test_create_ray_cluster_2_pools_custom_images_success( @pytest.mark.usefixtures("get_persistent_resource_2_pools_mock") def test_create_ray_cluster_2_pools_success( - self, create_persistent_resource_2_pools_mock + self, create_persistent_resource_2_pools_mock, ray_version ): """If head and worker nodes are not duplicate, create separate resource_pools.""" # Also test PSC-I. @@ -419,12 +444,29 @@ def test_create_ray_cluster_2_pools_success( worker_node_types=tc.ClusterConstants.TEST_WORKER_NODE_TYPES_2_POOLS, cluster_name=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ID, psc_interface_config=psc_interface_config, + ray_version=ray_version, ) + test_persistent_resource = tc.ClusterConstants.TEST_REQUEST_RUNNING_2_POOLS + if ray_version == "2.9": + test_persistent_resource.resource_runtime_spec.ray_spec.resource_pool_images[ + "head-node" + ] = tc.ClusterConstants.TEST_CPU_IMAGE_2_9 + test_persistent_resource.resource_runtime_spec.ray_spec.resource_pool_images[ + "worker-pool1" + ] = tc.ClusterConstants.TEST_GPU_IMAGE_2_9 + else: + test_persistent_resource.resource_runtime_spec.ray_spec.resource_pool_images[ + "head-node" + ] = tc.ClusterConstants.TEST_CPU_IMAGE_2_33 + test_persistent_resource.resource_runtime_spec.ray_spec.resource_pool_images[ + "worker-pool1" + ] = tc.ClusterConstants.TEST_GPU_IMAGE_2_33 + assert tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS == cluster_name request = persistent_resource_service.CreatePersistentResourceRequest( parent=tc.ProjectConstants.TEST_PARENT, - persistent_resource=tc.ClusterConstants.TEST_REQUEST_RUNNING_2_POOLS, + persistent_resource=test_persistent_resource, persistent_resource_id=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ID, ) @@ -434,7 +476,7 @@ def test_create_ray_cluster_2_pools_success( @pytest.mark.usefixtures("persistent_client_mock") def test_create_ray_cluster_initialized_success( - self, get_project_number_mock, api_client_mock + self, get_project_number_mock, api_client_mock, ray_version ): """If initialized, create_ray_cluster doesn't need many call args.""" aiplatform.init( @@ -469,7 +511,7 @@ def test_create_ray_cluster_initialized_success( @pytest.mark.usefixtures("get_persistent_resource_1_pool_byosa_mock") def test_create_ray_cluster_byosa_success( - self, create_persistent_resource_1_pool_byosa_mock + self, create_persistent_resource_1_pool_byosa_mock, ray_version ): """If head and worker nodes are duplicate, merge to head pool.""" cluster_name = vertex_ray.create_ray_cluster( @@ -477,13 +519,24 @@ def test_create_ray_cluster_byosa_success( worker_node_types=tc.ClusterConstants.TEST_WORKER_NODE_TYPES_1_POOL, service_account=tc.ProjectConstants.TEST_SERVICE_ACCOUNT, cluster_name=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ID, + ray_version=ray_version, ) assert tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS == cluster_name + test_persistent_resource = tc.ClusterConstants.TEST_REQUEST_RUNNING_1_POOL_BYOSA + if ray_version == "2.9": + test_persistent_resource.resource_runtime_spec.ray_spec.resource_pool_images[ + "head-node" + ] = tc.ClusterConstants.TEST_GPU_IMAGE_2_9 + else: + test_persistent_resource.resource_runtime_spec.ray_spec.resource_pool_images[ + "head-node" + ] = tc.ClusterConstants.TEST_GPU_IMAGE_2_33 + request = persistent_resource_service.CreatePersistentResourceRequest( parent=tc.ProjectConstants.TEST_PARENT, - persistent_resource=tc.ClusterConstants.TEST_REQUEST_RUNNING_1_POOL_BYOSA, + persistent_resource=test_persistent_resource, persistent_resource_id=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ID, ) @@ -491,7 +544,7 @@ def test_create_ray_cluster_byosa_success( request, ) - def test_create_ray_cluster_2_4_deprecated_error(self): + def test_create_ray_cluster_2_4_deprecated_error(self, ray_version): with pytest.raises(RuntimeError) as e: vertex_ray.create_ray_cluster( head_node_type=Resources(node_count=3), @@ -500,7 +553,7 @@ def test_create_ray_cluster_2_4_deprecated_error(self): ) e.match(regexp=re.escape(_TEST_V2_4_WARNING_MESSAGE)) - def test_create_ray_cluster_head_multinode_error(self): + def test_create_ray_cluster_head_multinode_error(self, ray_version): with pytest.raises(ValueError) as e: vertex_ray.create_ray_cluster( head_node_type=Resources(node_count=3), @@ -508,7 +561,7 @@ def test_create_ray_cluster_head_multinode_error(self): ) e.match(regexp=r"Resources.node_count must be 1.") - def test_create_ray_cluster_python_version_error(self): + def test_create_ray_cluster_python_version_error(self, ray_version): with pytest.raises(ValueError) as e: vertex_ray.create_ray_cluster( network=tc.ProjectConstants.TEST_VPC_NETWORK, @@ -516,7 +569,7 @@ def test_create_ray_cluster_python_version_error(self): ) e.match(regexp=r"The supported Python version is 3") - def test_create_ray_cluster_ray_version_error(self): + def test_create_ray_cluster_ray_version_error(self, ray_version): with pytest.raises(ValueError) as e: vertex_ray.create_ray_cluster( network=tc.ProjectConstants.TEST_VPC_NETWORK, @@ -524,7 +577,7 @@ def test_create_ray_cluster_ray_version_error(self): ) e.match(regexp=r"The supported Ray versions are ") - def test_create_ray_cluster_same_pool_different_disk_error(self): + def test_create_ray_cluster_same_pool_different_disk_error(self, ray_version): with pytest.raises(ValueError) as e: vertex_ray.create_ray_cluster( head_node_type=Resources(machine_type="n1-highmem-32", node_count=1), @@ -540,7 +593,7 @@ def test_create_ray_cluster_same_pool_different_disk_error(self): e.match(regexp=r"Worker disk size must match the head node's disk size if") @pytest.mark.usefixtures("create_persistent_resource_exception_mock") - def test_create_ray_cluster_state_error(self): + def test_create_ray_cluster_state_error(self, ray_version): with pytest.raises(ValueError) as e: vertex_ray.create_ray_cluster( network=tc.ProjectConstants.TEST_VPC_NETWORK, @@ -548,7 +601,7 @@ def test_create_ray_cluster_state_error(self): e.match(regexp=r"Failed in cluster creation due to: ") - def test_delete_ray_cluster_success(self, persistent_client_mock): + def test_delete_ray_cluster_success(self, persistent_client_mock, ray_version): vertex_ray.delete_ray_cluster( cluster_resource_name=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS ) @@ -556,7 +609,7 @@ def test_delete_ray_cluster_success(self, persistent_client_mock): persistent_client_mock.assert_called_once() @pytest.mark.usefixtures("persistent_client_error_mock") - def test_delete_ray_cluster_error(self): + def test_delete_ray_cluster_error(self, ray_version): with pytest.raises(ValueError) as e: vertex_ray.delete_ray_cluster( cluster_resource_name=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS @@ -564,7 +617,9 @@ def test_delete_ray_cluster_error(self): e.match(regexp=r"Failed in cluster deletion due to: ") - def test_get_ray_cluster_success(self, get_persistent_resource_1_pool_mock): + def test_get_ray_cluster_success( + self, get_persistent_resource_1_pool_mock, ray_version + ): cluster = vertex_ray.get_ray_cluster( cluster_resource_name=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS ) @@ -573,7 +628,7 @@ def test_get_ray_cluster_success(self, get_persistent_resource_1_pool_mock): cluster_eq(cluster, tc.ClusterConstants.TEST_CLUSTER) def test_get_ray_cluster_with_custom_image_success( - self, get_persistent_resource_2_pools_custom_image_mock + self, get_persistent_resource_2_pools_custom_image_mock, ray_version ): cluster = vertex_ray.get_ray_cluster( cluster_resource_name=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS @@ -583,7 +638,7 @@ def test_get_ray_cluster_with_custom_image_success( cluster_eq(cluster, tc.ClusterConstants.TEST_CLUSTER_CUSTOM_IMAGE) def test_get_ray_cluster_byosa_success( - self, get_persistent_resource_1_pool_byosa_mock + self, get_persistent_resource_1_pool_byosa_mock, ray_version ): cluster = vertex_ray.get_ray_cluster( cluster_resource_name=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS @@ -593,7 +648,7 @@ def test_get_ray_cluster_byosa_success( cluster_eq(cluster, tc.ClusterConstants.TEST_CLUSTER_BYOSA) @pytest.mark.usefixtures("get_persistent_resource_exception_mock") - def test_get_ray_cluster_error(self): + def test_get_ray_cluster_error(self, ray_version): with pytest.raises(ValueError) as e: vertex_ray.get_ray_cluster( cluster_resource_name=tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS @@ -601,7 +656,9 @@ def test_get_ray_cluster_error(self): e.match(regexp=r"Failed in getting the cluster due to: ") - def test_list_ray_clusters_success(self, list_persistent_resources_mock): + def test_list_ray_clusters_success( + self, list_persistent_resources_mock, ray_version + ): clusters = vertex_ray.list_ray_clusters() list_persistent_resources_mock.assert_called_once() @@ -612,7 +669,7 @@ def test_list_ray_clusters_success(self, list_persistent_resources_mock): cluster_eq(clusters[1], tc.ClusterConstants.TEST_CLUSTER_2) def test_list_ray_clusters_initialized_success( - self, get_project_number_mock, list_persistent_resources_mock + self, get_project_number_mock, list_persistent_resources_mock, ray_version ): aiplatform.init( project=tc.ProjectConstants.TEST_GCP_PROJECT_ID_OVERRIDE, @@ -632,15 +689,16 @@ def test_list_ray_clusters_initialized_success( ) @pytest.mark.usefixtures("list_persistent_resources_exception_mock") - def test_list_ray_clusters_error(self): + def test_list_ray_clusters_error(self, ray_version): with pytest.raises(ValueError) as e: vertex_ray.list_ray_clusters() e.match(regexp=r"Failed in listing the clusters due to: ") @pytest.mark.usefixtures("get_persistent_resource_1_pool_mock") - def test_update_ray_cluster_1_pool(self, update_persistent_resource_1_pool_mock): - + def test_update_ray_cluster_1_pool( + self, update_persistent_resource_1_pool_mock, ray_version + ): new_worker_node_types = [] for worker_node_type in tc.ClusterConstants.TEST_CLUSTER.worker_node_types: # resize worker node to node_count = 1 @@ -662,7 +720,7 @@ def test_update_ray_cluster_1_pool(self, update_persistent_resource_1_pool_mock) @pytest.mark.usefixtures("get_persistent_resource_1_pool_mock") def test_update_ray_cluster_1_pool_to_0_worker( - self, update_persistent_resource_1_pool_mock + self, update_persistent_resource_1_pool_mock, ray_version ): new_worker_node_types = [] @@ -685,7 +743,9 @@ def test_update_ray_cluster_1_pool_to_0_worker( assert returned_name == tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS @pytest.mark.usefixtures("get_persistent_resource_2_pools_mock") - def test_update_ray_cluster_2_pools(self, update_persistent_resource_2_pools_mock): + def test_update_ray_cluster_2_pools( + self, update_persistent_resource_2_pools_mock, ray_version + ): new_worker_node_types = [] for worker_node_type in tc.ClusterConstants.TEST_CLUSTER_2.worker_node_types: @@ -707,7 +767,7 @@ def test_update_ray_cluster_2_pools(self, update_persistent_resource_2_pools_moc assert returned_name == tc.ClusterConstants.TEST_VERTEX_RAY_PR_ADDRESS @pytest.mark.usefixtures("get_persistent_resource_2_pools_mock") - def test_update_ray_cluster_2_pools_0_worker_fail(self): + def test_update_ray_cluster_2_pools_0_worker_fail(self, ray_version): new_worker_node_types = [] for worker_node_type in tc.ClusterConstants.TEST_CLUSTER_2.worker_node_types: @@ -724,7 +784,7 @@ def test_update_ray_cluster_2_pools_0_worker_fail(self): e.match(regexp=r"must update to >= 1 nodes.") @pytest.mark.usefixtures("get_persistent_resource_1_pool_mock") - def test_update_ray_cluster_duplicate_worker_node_types_error(self): + def test_update_ray_cluster_duplicate_worker_node_types_error(self, ray_version): new_worker_node_types = ( tc.ClusterConstants.TEST_CLUSTER_2.worker_node_types + tc.ClusterConstants.TEST_CLUSTER_2.worker_node_types @@ -738,7 +798,9 @@ def test_update_ray_cluster_duplicate_worker_node_types_error(self): e.match(regexp=r"Worker_node_types have duplicate machine specs") @pytest.mark.usefixtures("get_persistent_resource_1_pool_mock") - def test_update_ray_cluster_mismatch_worker_node_types_count_error(self): + def test_update_ray_cluster_mismatch_worker_node_types_count_error( + self, ray_version + ): with pytest.raises(ValueError) as e: new_worker_node_types = tc.ClusterConstants.TEST_CLUSTER_2.worker_node_types vertex_ray.update_ray_cluster( diff --git a/tests/unit/vertex_ray/test_constants.py b/tests/unit/vertex_ray/test_constants.py index 26537391bf..e385237069 100644 --- a/tests/unit/vertex_ray/test_constants.py +++ b/tests/unit/vertex_ray/test_constants.py @@ -63,6 +63,10 @@ ray.__version__ != "2.9.3", reason="Requires xgboost 1.7 or higher" ) +predictionrayversion = pytest.mark.skipif( + ray.__version__ != "2.9.3", reason="Not currently supported on Ray 2.33" +) + @dataclasses.dataclass(frozen=True) class ProjectConstants: @@ -101,8 +105,14 @@ class ClusterConstants: TEST_VERTEX_RAY_PR_ADDRESS = ( f"{ProjectConstants.TEST_PARENT}/persistentResources/" + TEST_VERTEX_RAY_PR_ID ) - TEST_CPU_IMAGE = "us-docker.pkg.dev/vertex-ai/training/ray-cpu.2-9.py310:latest" - TEST_GPU_IMAGE = "us-docker.pkg.dev/vertex-ai/training/ray-gpu.2-9.py310:latest" + TEST_CPU_IMAGE_2_9 = "us-docker.pkg.dev/vertex-ai/training/ray-cpu.2-9.py310:latest" + TEST_GPU_IMAGE_2_9 = "us-docker.pkg.dev/vertex-ai/training/ray-gpu.2-9.py310:latest" + TEST_CPU_IMAGE_2_33 = ( + "us-docker.pkg.dev/vertex-ai/training/ray-cpu.2-33.py310:latest" + ) + TEST_GPU_IMAGE_2_33 = ( + "us-docker.pkg.dev/vertex-ai/training/ray-gpu.2-33.py310:latest" + ) TEST_CUSTOM_IMAGE = "us-docker.pkg.dev/my-project/ray-custom-image.2.9:latest" TEST_PSC_NETWORK_ATTACHMENT = "my-network-attachment" # RUNNING Persistent Cluster w/o Ray @@ -139,7 +149,7 @@ class ClusterConstants: resource_pools=[TEST_RESOURCE_POOL_0], resource_runtime_spec=ResourceRuntimeSpec( ray_spec=RaySpec( - resource_pool_images={"head-node": TEST_GPU_IMAGE}, + resource_pool_images={"head-node": TEST_GPU_IMAGE_2_9}, ray_metric_spec=RayMetricSpec(disabled=False), ray_logs_spec=RayLogsSpec(disabled=False), ), @@ -151,7 +161,7 @@ class ClusterConstants: resource_pools=[TEST_RESOURCE_POOL_0], resource_runtime_spec=ResourceRuntimeSpec( ray_spec=RaySpec( - resource_pool_images={"head-node": TEST_GPU_IMAGE}, + resource_pool_images={"head-node": TEST_GPU_IMAGE_2_9}, ray_metric_spec=RayMetricSpec(disabled=True), ray_logs_spec=RayLogsSpec(disabled=True), ), @@ -176,7 +186,7 @@ class ClusterConstants: resource_pools=[TEST_RESOURCE_POOL_0], resource_runtime_spec=ResourceRuntimeSpec( ray_spec=RaySpec( - resource_pool_images={"head-node": TEST_GPU_IMAGE}, + resource_pool_images={"head-node": TEST_GPU_IMAGE_2_9}, ray_metric_spec=RayMetricSpec(disabled=False), ray_logs_spec=RayLogsSpec(disabled=False), ), @@ -194,7 +204,7 @@ class ClusterConstants: resource_pools=[TEST_RESOURCE_POOL_0], resource_runtime_spec=ResourceRuntimeSpec( ray_spec=RaySpec( - resource_pool_images={"head-node": TEST_GPU_IMAGE}, + resource_pool_images={"head-node": TEST_GPU_IMAGE_2_9}, ray_metric_spec=RayMetricSpec(disabled=False), ray_logs_spec=RayLogsSpec(disabled=False), ), @@ -234,7 +244,7 @@ class ClusterConstants: resource_pools=[TEST_RESOURCE_POOL_0], resource_runtime_spec=ResourceRuntimeSpec( ray_spec=RaySpec( - resource_pool_images={"head-node": TEST_GPU_IMAGE}, + resource_pool_images={"head-node": TEST_GPU_IMAGE_2_9}, ray_metric_spec=RayMetricSpec(disabled=False), ), service_account_spec=ServiceAccountSpec( @@ -257,7 +267,7 @@ class ClusterConstants: resource_pools=[TEST_RESOURCE_POOL_0], resource_runtime_spec=ResourceRuntimeSpec( ray_spec=RaySpec( - resource_pool_images={"head-node": TEST_GPU_IMAGE}, + resource_pool_images={"head-node": TEST_GPU_IMAGE_2_9}, ray_metric_spec=RayMetricSpec(disabled=False), ), service_account_spec=ServiceAccountSpec( @@ -327,8 +337,8 @@ class ClusterConstants: resource_runtime_spec=ResourceRuntimeSpec( ray_spec=RaySpec( resource_pool_images={ - "head-node": TEST_CPU_IMAGE, - "worker-pool1": TEST_GPU_IMAGE, + "head-node": TEST_CPU_IMAGE_2_9, + "worker-pool1": TEST_GPU_IMAGE_2_9, }, ray_metric_spec=RayMetricSpec(disabled=False), ray_logs_spec=RayLogsSpec(disabled=False), @@ -362,8 +372,8 @@ class ClusterConstants: resource_runtime_spec=ResourceRuntimeSpec( ray_spec=RaySpec( resource_pool_images={ - "head-node": TEST_CPU_IMAGE, - "worker-pool1": TEST_GPU_IMAGE, + "head-node": TEST_CPU_IMAGE_2_9, + "worker-pool1": TEST_GPU_IMAGE_2_9, }, ray_metric_spec=RayMetricSpec(disabled=False), ), diff --git a/tests/unit/vertex_ray/test_ray_prediction.py b/tests/unit/vertex_ray/test_ray_prediction.py index c5d41c3725..f6be5c7228 100644 --- a/tests/unit/vertex_ray/test_ray_prediction.py +++ b/tests/unit/vertex_ray/test_ray_prediction.py @@ -353,6 +353,7 @@ def test_register_sklearn_initialized_succeed( pickle_dump.assert_called_once() gcs_utils_upload_to_gcs.assert_called_once() + @tc.predictionrayversion def test_register_sklearnartifact_uri_is_none_raise_error( self, ray_sklearn_checkpoint ) -> None: @@ -365,6 +366,7 @@ def test_register_sklearnartifact_uri_is_none_raise_error( ) assert ve.match(regexp=r".*'artifact_uri' should " "start with 'gs://'.*") + @tc.predictionrayversion def test_register_sklearnartifact_uri_not_gcs_uri_raise_error( self, ray_sklearn_checkpoint ) -> None: @@ -499,6 +501,7 @@ def test_convert_checkpoint_to_torch_model_raises_exception( with pytest.raises(ValueError): prediction_torch.register.get_pytorch_model_from(ray_checkpoint_from_dict) + @tc.predictionrayversion def test_convert_checkpoint_to_pytorch_model_succeed( self, ray_torch_checkpoint ) -> None: From e310e81d97344c26c3ef38f1071e97a6a553fe1d Mon Sep 17 00:00:00 2001 From: Xingyou Song Date: Fri, 13 Sep 2024 14:10:14 -0700 Subject: [PATCH 08/17] chore: Remove emukit PiperOrigin-RevId: 674428570 --- google/cloud/aiplatform/vizier/pyvizier/study_config.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/google/cloud/aiplatform/vizier/pyvizier/study_config.py b/google/cloud/aiplatform/vizier/pyvizier/study_config.py index 75b3015186..0940e56767 100644 --- a/google/cloud/aiplatform/vizier/pyvizier/study_config.py +++ b/google/cloud/aiplatform/vizier/pyvizier/study_config.py @@ -55,7 +55,6 @@ class Algorithm(enum.Enum): GRID_SEARCH = study_pb2.StudySpec.Algorithm.GRID_SEARCH RANDOM_SEARCH = study_pb2.StudySpec.Algorithm.RANDOM_SEARCH # NSGA2 = study_pb2.StudySpec.Algorithm.NSGA2 - # EMUKIT_GP_EI = study_pb2.StudySpec.Algorithm.EMUKIT_GP_EI class ObservationNoise(enum.Enum): @@ -316,7 +315,7 @@ def single_objective_metric_name(self) -> Optional[str]: def _trial_to_external_values( self, pytrial: Trial ) -> Dict[str, Union[float, int, str, bool]]: - """Returns the trial paremeter values cast to external types.""" + """Returns the trial parameter values cast to external types.""" parameter_values: Dict[str, Union[float, int, str]] = {} external_values: Dict[str, Union[float, int, str, bool]] = {} # parameter_configs is a list of Tuple[parent_name, ParameterConfig]. From 32f5b253894a1a3c233208a821bf9b0f925202fb Mon Sep 17 00:00:00 2001 From: Jaycee Li Date: Mon, 16 Sep 2024 12:38:40 -0700 Subject: [PATCH 09/17] chore: Bumped up mlflow version in extra requirements to enable autologging for lightning 2.0 PiperOrigin-RevId: 675252443 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e26b8b7d7f..d36b383884 100644 --- a/setup.py +++ b/setup.py @@ -96,7 +96,7 @@ "requests >= 2.28.1", ] -autologging_extra_require = ["mlflow>=1.27.0,<=2.1.1"] +autologging_extra_require = ["mlflow>=1.27.0,<=2.16.0"] preview_extra_require = [] From a6cbb74318242d2bc5394eafbb13a65ef717d0b4 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:09:05 -0700 Subject: [PATCH 10/17] Copybara import of the project: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- 0f91e738281226e356fcb25217ce5cf5aed6ee51 by Owl Bot : feat: add OFF to HarmBlockThreshold PiperOrigin-RevId: 671827417 Source-Link: https://github.com/googleapis/googleapis/commit/9aad398cf46352b38b6d325bd194fd5932ca054e Source-Link: https://github.com/googleapis/googleapis-gen/commit/428081af4981c7a8dce0bde045c86bc9578e50f2 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNDI4MDgxYWY0OTgxYzdhOGRjZTBiZGUwNDVjODZiYzk1NzhlNTBmMiJ9 -- 9538166dfe8a6f579c45d21370698d9e28b8f7d6 by Owl Bot : 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md -- 1cc3a53ba8f9471431d8870494583bc99f80561e by Owl Bot : feat: add OFF to HarmBlockThreshold PiperOrigin-RevId: 671988102 Source-Link: https://github.com/googleapis/googleapis/commit/cb39bdd75da491466f6c92bc73cd46b0fbd6ba9a Source-Link: https://github.com/googleapis/googleapis-gen/commit/4f92fc4acd2a691ad8c0d4be9546127d000d2c38 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNGY5MmZjNGFjZDJhNjkxYWQ4YzBkNGJlOTU0NjEyN2QwMDBkMmMzOCJ9 -- 8c95f17f5ab116e467e90cfc63ce02b94a0057b0 by Owl Bot : 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md -- f716c80d998d128d7ef8659853389746cbfd3a83 by Owl Bot : feat: add share_point_sources to SharePointSources feat: add sharepoint_folder_path to SharePointSource feat: add sharepoint_folder_id to SharePointSource feat: add drive_name to SharePointSource feat: add drive_id to SharePointSource feat: add client_id to SharePointSource feat: add client_secret to SharePointSource feat: add tenant_id to SharePointSource feat: add sharepoint_site_name to SharePointSource feat: add share_point_sources to ImportRagFilesConfig feat: add partial_failure_gcs_sink tp ImportRagFilesConfig feat: add partial_failure_bigquery_sink to ImportRagFilesConfig PiperOrigin-RevId: 672615314 Source-Link: https://github.com/googleapis/googleapis/commit/ca82297144979c3a0998de71d9b197a2959eac72 Source-Link: https://github.com/googleapis/googleapis-gen/commit/bf7ddca65a071bdea7bc8cd89db13570456020c0 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiYmY3ZGRjYTY1YTA3MWJkZWE3YmM4Y2Q4OWRiMTM1NzA0NTYwMjBjMCJ9 -- 6721e4bb0397bd1972886eca46626ce3c8f2bf1d by Owl Bot : 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md -- bd4ddeb7abfc24c6c079dcb68154cdff5c77f964 by Owl Bot : feat: add Pinecone and Vector Search integration for Vertex RAG PiperOrigin-RevId: 673087899 Source-Link: https://github.com/googleapis/googleapis/commit/afb6b3599d50103e022e9c22c5057bf94be9dcf8 Source-Link: https://github.com/googleapis/googleapis-gen/commit/00a4515ab465e98d56627075675209631ee51f39 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMDBhNDUxNWFiNDY1ZTk4ZDU2NjI3MDc1Njc1MjA5NjMxZWU1MWYzOSJ9 -- 0c3f17614e2d6a8a5d44dfd8c48b4201155ed2c6 by Owl Bot : 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md -- 3c30eb9b166f698e69263fe98cf81b4cacf30d0c by Owl Bot : feat: add file_id to SharePointSource feat: add share_point_sources to RagFile PiperOrigin-RevId: 673921028 Source-Link: https://github.com/googleapis/googleapis/commit/9382d7406b9ccc54a5b68beeaa6bded7d03a35f6 Source-Link: https://github.com/googleapis/googleapis-gen/commit/7cb8810d2eae5a61d4972e1e70b7c3eefdb1c96b Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiN2NiODgxMGQyZWFlNWE2MWQ0OTcyZTFlNzBiN2MzZWVmZGIxYzk2YiJ9 -- a83cb88c97ca1bfa8fedd30a933f7108d7e858be by Owl Bot : 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md -- 2ca5eb6ee28388fdc2af5332a1f711d9c313ca9c by Owl Bot : feat: A new field `property_ordering` is added to message `.google.cloud.aiplatform.v1beta1.Schema` PiperOrigin-RevId: 674918528 Source-Link: https://github.com/googleapis/googleapis/commit/65306b92f03408d6de03589bdf970b78f2e4055c Source-Link: https://github.com/googleapis/googleapis-gen/commit/442fa35d1641de4ff4fefd8d6e672aa896a51354 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNDQyZmEzNWQxNjQxZGU0ZmY0ZmVmZDhkNmU2NzJhYTg5NmE1MTM1NCJ9 -- 5c8b0e9fd78836a5e3f66a939182715fbfd5657f by Owl Bot : 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md -- 453ec68a0d971a5cfe75ec5134bebd4621700f3e by Amy Wu : Update snippet_metadata_google.cloud.aiplatform.v1.json -- 3aeb86486817bbf92b72dc061c0f063ceeeb0598 by Amy Wu : Update snippet_metadata_google.cloud.aiplatform.v1beta1.json -- 1b3a61d8e31d4907427fde974b7c20179fd0290a by Owl Bot : feat: A new field `property_ordering` is added to message `.google.cloud.aiplatform.v1.Schema` PiperOrigin-RevId: 675187467 Source-Link: https://github.com/googleapis/googleapis/commit/38d33ad1da5ece4874e4b7b7921df7299835b253 Source-Link: https://github.com/googleapis/googleapis-gen/commit/95e20a9547e1cbcb0a2ce443f62d05bc001a79bb Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiOTVlMjBhOTU0N2UxY2JjYjBhMmNlNDQzZjYyZDA1YmMwMDFhNzliYiJ9 -- b94f5a02846722ab8288beb9ad26fc2b7332ab68 by Owl Bot : 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md -- 775b14e57817d6a5f1a1359e92a35fbc9b28a69a by Amy Wu : Update snippet_metadata_google.cloud.aiplatform.v1.json -- 5cfd992ae72cf2cb0f8a628b482339028eddafab by Amy Wu : Update snippet_metadata_google.cloud.aiplatform.v1beta1.json COPYBARA_INTEGRATE_REVIEW=https://github.com/googleapis/python-aiplatform/pull/4360 from googleapis:owl-bot-copy 5cfd992ae72cf2cb0f8a628b482339028eddafab PiperOrigin-RevId: 675263166 --- .../services/migration_service/client.py | 18 +-- google/cloud/aiplatform_v1/types/content.py | 3 + google/cloud/aiplatform_v1/types/openapi.py | 12 +- google/cloud/aiplatform_v1beta1/__init__.py | 2 + .../vertex_rag_data_service/async_client.py | 1 + .../vertex_rag_data_service/client.py | 1 + .../aiplatform_v1beta1/types/__init__.py | 2 + .../cloud/aiplatform_v1beta1/types/content.py | 3 + google/cloud/aiplatform_v1beta1/types/io.py | 107 ++++++++++++++++++ .../cloud/aiplatform_v1beta1/types/openapi.py | 12 +- .../types/vertex_rag_data.py | 105 +++++++++++++++++ .../types/vertex_rag_data_service.py | 27 +++++ ...rag_data_service_import_rag_files_async.py | 1 + ..._rag_data_service_import_rag_files_sync.py | 1 + ...adata_google.cloud.aiplatform.v1beta1.json | 28 ++--- .../aiplatform_v1/test_migration_service.py | 26 ++--- .../test_extension_registry_service.py | 8 ++ .../test_gen_ai_cache_service.py | 8 ++ .../test_vertex_rag_data_service.py | 10 ++ 19 files changed, 335 insertions(+), 40 deletions(-) diff --git a/google/cloud/aiplatform_v1/services/migration_service/client.py b/google/cloud/aiplatform_v1/services/migration_service/client.py index cdbf0fb332..3f87659bc6 100644 --- a/google/cloud/aiplatform_v1/services/migration_service/client.py +++ b/google/cloud/aiplatform_v1/services/migration_service/client.py @@ -216,40 +216,40 @@ def parse_annotated_dataset_path(path: str) -> Dict[str, str]: @staticmethod def dataset_path( project: str, - location: str, dataset: str, ) -> str: """Returns a fully-qualified dataset string.""" - return "projects/{project}/locations/{location}/datasets/{dataset}".format( + return "projects/{project}/datasets/{dataset}".format( project=project, - location=location, dataset=dataset, ) @staticmethod def parse_dataset_path(path: str) -> Dict[str, str]: """Parses a dataset path into its component segments.""" - m = re.match( - r"^projects/(?P.+?)/locations/(?P.+?)/datasets/(?P.+?)$", - path, - ) + m = re.match(r"^projects/(?P.+?)/datasets/(?P.+?)$", path) return m.groupdict() if m else {} @staticmethod def dataset_path( project: str, + location: str, dataset: str, ) -> str: """Returns a fully-qualified dataset string.""" - return "projects/{project}/datasets/{dataset}".format( + return "projects/{project}/locations/{location}/datasets/{dataset}".format( project=project, + location=location, dataset=dataset, ) @staticmethod def parse_dataset_path(path: str) -> Dict[str, str]: """Parses a dataset path into its component segments.""" - m = re.match(r"^projects/(?P.+?)/datasets/(?P.+?)$", path) + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/datasets/(?P.+?)$", + path, + ) return m.groupdict() if m else {} @staticmethod diff --git a/google/cloud/aiplatform_v1/types/content.py b/google/cloud/aiplatform_v1/types/content.py index a9122efe41..a4f481577e 100644 --- a/google/cloud/aiplatform_v1/types/content.py +++ b/google/cloud/aiplatform_v1/types/content.py @@ -520,12 +520,15 @@ class HarmBlockThreshold(proto.Enum): Block only high threshold (i.e. block less). BLOCK_NONE (4): Block none. + OFF (5): + Turn off the safety filter. """ HARM_BLOCK_THRESHOLD_UNSPECIFIED = 0 BLOCK_LOW_AND_ABOVE = 1 BLOCK_MEDIUM_AND_ABOVE = 2 BLOCK_ONLY_HIGH = 3 BLOCK_NONE = 4 + OFF = 5 class HarmBlockMethod(proto.Enum): r"""Probability vs severity. diff --git a/google/cloud/aiplatform_v1/types/openapi.py b/google/cloud/aiplatform_v1/types/openapi.py index 3213783cb6..a8a018c982 100644 --- a/google/cloud/aiplatform_v1/types/openapi.py +++ b/google/cloud/aiplatform_v1/types/openapi.py @@ -63,8 +63,8 @@ class Type(proto.Enum): class Schema(proto.Message): r"""Schema is used to define the format of input/output data. Represents a select subset of an `OpenAPI 3.0 schema - object `__. More fields - may be added in the future as needed. + object `__. More + fields may be added in the future as needed. Attributes: type_ (google.cloud.aiplatform_v1.types.Type): @@ -101,6 +101,10 @@ class Schema(proto.Message): properties (MutableMapping[str, google.cloud.aiplatform_v1.types.Schema]): Optional. SCHEMA FIELDS FOR TYPE OBJECT Properties of Type.OBJECT. + property_ordering (MutableSequence[str]): + Optional. The order of the properties. + Not a standard field in open api spec. Only used + to support the order of the properties. required (MutableSequence[str]): Optional. Required properties of Type.OBJECT. min_properties (int): @@ -178,6 +182,10 @@ class Schema(proto.Message): number=3, message="Schema", ) + property_ordering: MutableSequence[str] = proto.RepeatedField( + proto.STRING, + number=25, + ) required: MutableSequence[str] = proto.RepeatedField( proto.STRING, number=5, diff --git a/google/cloud/aiplatform_v1beta1/__init__.py b/google/cloud/aiplatform_v1beta1/__init__.py index 3bd01743e9..1ee81a1e9b 100644 --- a/google/cloud/aiplatform_v1beta1/__init__.py +++ b/google/cloud/aiplatform_v1beta1/__init__.py @@ -531,6 +531,7 @@ from .types.io import GcsSource from .types.io import GoogleDriveSource from .types.io import JiraSource +from .types.io import SharePointSources from .types.io import SlackSource from .types.io import TFRecordDestination from .types.job_service import CancelBatchPredictionJobRequest @@ -1884,6 +1885,7 @@ "SearchNearestEntitiesResponse", "Segment", "ServiceAccountSpec", + "SharePointSources", "ShieldedVmConfig", "SlackSource", "SmoothGradConfig", diff --git a/google/cloud/aiplatform_v1beta1/services/vertex_rag_data_service/async_client.py b/google/cloud/aiplatform_v1beta1/services/vertex_rag_data_service/async_client.py index 3c6d7b8f85..5db47394ea 100644 --- a/google/cloud/aiplatform_v1beta1/services/vertex_rag_data_service/async_client.py +++ b/google/cloud/aiplatform_v1beta1/services/vertex_rag_data_service/async_client.py @@ -1074,6 +1074,7 @@ async def sample_import_rag_files(): # Initialize request argument(s) import_rag_files_config = aiplatform_v1beta1.ImportRagFilesConfig() import_rag_files_config.gcs_source.uris = ['uris_value1', 'uris_value2'] + import_rag_files_config.partial_failure_gcs_sink.output_uri_prefix = "output_uri_prefix_value" request = aiplatform_v1beta1.ImportRagFilesRequest( parent="parent_value", diff --git a/google/cloud/aiplatform_v1beta1/services/vertex_rag_data_service/client.py b/google/cloud/aiplatform_v1beta1/services/vertex_rag_data_service/client.py index 23618833d5..36e8853579 100644 --- a/google/cloud/aiplatform_v1beta1/services/vertex_rag_data_service/client.py +++ b/google/cloud/aiplatform_v1beta1/services/vertex_rag_data_service/client.py @@ -1557,6 +1557,7 @@ def sample_import_rag_files(): # Initialize request argument(s) import_rag_files_config = aiplatform_v1beta1.ImportRagFilesConfig() import_rag_files_config.gcs_source.uris = ['uris_value1', 'uris_value2'] + import_rag_files_config.partial_failure_gcs_sink.output_uri_prefix = "output_uri_prefix_value" request = aiplatform_v1beta1.ImportRagFilesRequest( parent="parent_value", diff --git a/google/cloud/aiplatform_v1beta1/types/__init__.py b/google/cloud/aiplatform_v1beta1/types/__init__.py index 7ed18b6fd2..92b8a80bb4 100644 --- a/google/cloud/aiplatform_v1beta1/types/__init__.py +++ b/google/cloud/aiplatform_v1beta1/types/__init__.py @@ -542,6 +542,7 @@ GcsSource, GoogleDriveSource, JiraSource, + SharePointSources, SlackSource, TFRecordDestination, ) @@ -1621,6 +1622,7 @@ "GcsSource", "GoogleDriveSource", "JiraSource", + "SharePointSources", "SlackSource", "TFRecordDestination", "CancelBatchPredictionJobRequest", diff --git a/google/cloud/aiplatform_v1beta1/types/content.py b/google/cloud/aiplatform_v1beta1/types/content.py index 1103d90833..b6cb876fd0 100644 --- a/google/cloud/aiplatform_v1beta1/types/content.py +++ b/google/cloud/aiplatform_v1beta1/types/content.py @@ -520,12 +520,15 @@ class HarmBlockThreshold(proto.Enum): Block only high threshold (i.e. block less). BLOCK_NONE (4): Block none. + OFF (5): + Turn off the safety filter. """ HARM_BLOCK_THRESHOLD_UNSPECIFIED = 0 BLOCK_LOW_AND_ABOVE = 1 BLOCK_MEDIUM_AND_ABOVE = 2 BLOCK_ONLY_HIGH = 3 BLOCK_NONE = 4 + OFF = 5 class HarmBlockMethod(proto.Enum): r"""Probability vs severity. diff --git a/google/cloud/aiplatform_v1beta1/types/io.py b/google/cloud/aiplatform_v1beta1/types/io.py index ebd4bb09c6..1eb9935ce5 100644 --- a/google/cloud/aiplatform_v1beta1/types/io.py +++ b/google/cloud/aiplatform_v1beta1/types/io.py @@ -39,6 +39,7 @@ "DirectUploadSource", "SlackSource", "JiraSource", + "SharePointSources", }, ) @@ -400,4 +401,110 @@ class JiraQueries(proto.Message): ) +class SharePointSources(proto.Message): + r"""The SharePointSources to pass to ImportRagFiles. + + Attributes: + share_point_sources (MutableSequence[google.cloud.aiplatform_v1beta1.types.SharePointSources.SharePointSource]): + The SharePoint sources. + """ + + class SharePointSource(proto.Message): + r"""An individual SharePointSource. + + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + sharepoint_folder_path (str): + The path of the SharePoint folder to download + from. + + This field is a member of `oneof`_ ``folder_source``. + sharepoint_folder_id (str): + The ID of the SharePoint folder to download + from. + + This field is a member of `oneof`_ ``folder_source``. + drive_name (str): + The name of the drive to download from. + + This field is a member of `oneof`_ ``drive_source``. + drive_id (str): + The ID of the drive to download from. + + This field is a member of `oneof`_ ``drive_source``. + client_id (str): + The Application ID for the app registered in + Microsoft Azure Portal. The application must + also be configured with MS Graph permissions + "Files.ReadAll", "Sites.ReadAll" and + BrowserSiteLists.Read.All. + client_secret (google.cloud.aiplatform_v1beta1.types.ApiAuth.ApiKeyConfig): + The application secret for the app registered + in Azure. + tenant_id (str): + Unique identifier of the Azure Active + Directory Instance. + sharepoint_site_name (str): + The name of the SharePoint site to download + from. This can be the site name or the site id. + file_id (str): + Output only. The SharePoint file id. Output + only. + """ + + sharepoint_folder_path: str = proto.Field( + proto.STRING, + number=5, + oneof="folder_source", + ) + sharepoint_folder_id: str = proto.Field( + proto.STRING, + number=6, + oneof="folder_source", + ) + drive_name: str = proto.Field( + proto.STRING, + number=7, + oneof="drive_source", + ) + drive_id: str = proto.Field( + proto.STRING, + number=8, + oneof="drive_source", + ) + client_id: str = proto.Field( + proto.STRING, + number=1, + ) + client_secret: api_auth.ApiAuth.ApiKeyConfig = proto.Field( + proto.MESSAGE, + number=2, + message=api_auth.ApiAuth.ApiKeyConfig, + ) + tenant_id: str = proto.Field( + proto.STRING, + number=3, + ) + sharepoint_site_name: str = proto.Field( + proto.STRING, + number=4, + ) + file_id: str = proto.Field( + proto.STRING, + number=9, + ) + + share_point_sources: MutableSequence[SharePointSource] = proto.RepeatedField( + proto.MESSAGE, + number=1, + message=SharePointSource, + ) + + __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/aiplatform_v1beta1/types/openapi.py b/google/cloud/aiplatform_v1beta1/types/openapi.py index eac939d725..51b6ef9787 100644 --- a/google/cloud/aiplatform_v1beta1/types/openapi.py +++ b/google/cloud/aiplatform_v1beta1/types/openapi.py @@ -63,8 +63,8 @@ class Type(proto.Enum): class Schema(proto.Message): r"""Schema is used to define the format of input/output data. Represents a select subset of an `OpenAPI 3.0 schema - object `__. More fields - may be added in the future as needed. + object `__. More + fields may be added in the future as needed. Attributes: type_ (google.cloud.aiplatform_v1beta1.types.Type): @@ -101,6 +101,10 @@ class Schema(proto.Message): properties (MutableMapping[str, google.cloud.aiplatform_v1beta1.types.Schema]): Optional. SCHEMA FIELDS FOR TYPE OBJECT Properties of Type.OBJECT. + property_ordering (MutableSequence[str]): + Optional. The order of the properties. + Not a standard field in open api spec. Only used + to support the order of the properties. required (MutableSequence[str]): Optional. Required properties of Type.OBJECT. min_properties (int): @@ -178,6 +182,10 @@ class Schema(proto.Message): number=3, message="Schema", ) + property_ordering: MutableSequence[str] = proto.RepeatedField( + proto.STRING, + number=25, + ) required: MutableSequence[str] = proto.RepeatedField( proto.STRING, number=5, diff --git a/google/cloud/aiplatform_v1beta1/types/vertex_rag_data.py b/google/cloud/aiplatform_v1beta1/types/vertex_rag_data.py index 787dd96a6d..75427fca5b 100644 --- a/google/cloud/aiplatform_v1beta1/types/vertex_rag_data.py +++ b/google/cloud/aiplatform_v1beta1/types/vertex_rag_data.py @@ -220,10 +220,18 @@ class RagVectorDbConfig(proto.Message): weaviate (google.cloud.aiplatform_v1beta1.types.RagVectorDbConfig.Weaviate): The config for the Weaviate. + This field is a member of `oneof`_ ``vector_db``. + pinecone (google.cloud.aiplatform_v1beta1.types.RagVectorDbConfig.Pinecone): + The config for the Pinecone. + This field is a member of `oneof`_ ``vector_db``. vertex_feature_store (google.cloud.aiplatform_v1beta1.types.RagVectorDbConfig.VertexFeatureStore): The config for the Vertex Feature Store. + This field is a member of `oneof`_ ``vector_db``. + vertex_vector_search (google.cloud.aiplatform_v1beta1.types.RagVectorDbConfig.VertexVectorSearch): + The config for the Vertex Vector Search. + This field is a member of `oneof`_ ``vector_db``. api_auth (google.cloud.aiplatform_v1beta1.types.ApiAuth): Authentication config for the chosen Vector @@ -256,6 +264,20 @@ class Weaviate(proto.Message): number=2, ) + class Pinecone(proto.Message): + r"""The config for the Pinecone. + + Attributes: + index_name (str): + Pinecone index name. + This value cannot be changed after it's set. + """ + + index_name: str = proto.Field( + proto.STRING, + number=1, + ) + class VertexFeatureStore(proto.Message): r"""The config for the Vertex Feature Store. @@ -270,6 +292,27 @@ class VertexFeatureStore(proto.Message): number=1, ) + class VertexVectorSearch(proto.Message): + r"""The config for the Vertex Vector Search. + + Attributes: + index_endpoint (str): + The resource name of the Index Endpoint. Format: + ``projects/{project}/locations/{location}/indexEndpoints/{index_endpoint}`` + index (str): + The resource name of the Index. Format: + ``projects/{project}/locations/{location}/indexes/{index}`` + """ + + index_endpoint: str = proto.Field( + proto.STRING, + number=1, + ) + index: str = proto.Field( + proto.STRING, + number=2, + ) + rag_managed_db: RagManagedDb = proto.Field( proto.MESSAGE, number=1, @@ -282,12 +325,24 @@ class VertexFeatureStore(proto.Message): oneof="vector_db", message=Weaviate, ) + pinecone: Pinecone = proto.Field( + proto.MESSAGE, + number=3, + oneof="vector_db", + message=Pinecone, + ) vertex_feature_store: VertexFeatureStore = proto.Field( proto.MESSAGE, number=4, oneof="vector_db", message=VertexFeatureStore, ) + vertex_vector_search: VertexVectorSearch = proto.Field( + proto.MESSAGE, + number=6, + oneof="vector_db", + message=VertexVectorSearch, + ) api_auth: gca_api_auth.ApiAuth = proto.Field( proto.MESSAGE, number=5, @@ -480,6 +535,11 @@ class RagFile(proto.Message): jira_source (google.cloud.aiplatform_v1beta1.types.JiraSource): The RagFile is imported from a Jira query. + This field is a member of `oneof`_ ``rag_file_source``. + share_point_sources (google.cloud.aiplatform_v1beta1.types.SharePointSources): + The RagFile is imported from a SharePoint + source. + This field is a member of `oneof`_ ``rag_file_source``. name (str): Output only. The resource name of the @@ -550,6 +610,12 @@ class RagFileType(proto.Enum): oneof="rag_file_source", message=io.JiraSource, ) + share_point_sources: io.SharePointSources = proto.Field( + proto.MESSAGE, + number=14, + oneof="rag_file_source", + message=io.SharePointSources, + ) name: str = proto.Field( proto.STRING, number=1, @@ -674,6 +740,27 @@ class ImportRagFilesConfig(proto.Message): authentication. This field is a member of `oneof`_ ``import_source``. + share_point_sources (google.cloud.aiplatform_v1beta1.types.SharePointSources): + SharePoint sources. + + This field is a member of `oneof`_ ``import_source``. + partial_failure_gcs_sink (google.cloud.aiplatform_v1beta1.types.GcsDestination): + The Cloud Storage path to write partial + failures to. + + This field is a member of `oneof`_ ``partial_failure_sink``. + partial_failure_bigquery_sink (google.cloud.aiplatform_v1beta1.types.BigQueryDestination): + The BigQuery destination to write partial + failures to. It should be a bigquery table + resource name (e.g. + "bq://projectId.bqDatasetId.bqTableId"). If the + dataset id does not exist, it will be created. + If the table does not exist, it will be created + with the expected schema. If the table exists, + the schema will be validated and data will be + added to this existing table. + + This field is a member of `oneof`_ ``partial_failure_sink``. rag_file_chunking_config (google.cloud.aiplatform_v1beta1.types.RagFileChunkingConfig): Specifies the size and overlap of chunks after importing RagFiles. @@ -714,6 +801,24 @@ class ImportRagFilesConfig(proto.Message): oneof="import_source", message=io.JiraSource, ) + share_point_sources: io.SharePointSources = proto.Field( + proto.MESSAGE, + number=13, + oneof="import_source", + message=io.SharePointSources, + ) + partial_failure_gcs_sink: io.GcsDestination = proto.Field( + proto.MESSAGE, + number=11, + oneof="partial_failure_sink", + message=io.GcsDestination, + ) + partial_failure_bigquery_sink: io.BigQueryDestination = proto.Field( + proto.MESSAGE, + number=12, + oneof="partial_failure_sink", + message=io.BigQueryDestination, + ) rag_file_chunking_config: "RagFileChunkingConfig" = proto.Field( proto.MESSAGE, number=4, diff --git a/google/cloud/aiplatform_v1beta1/types/vertex_rag_data_service.py b/google/cloud/aiplatform_v1beta1/types/vertex_rag_data_service.py index 94f0621ba4..a6d45b147f 100644 --- a/google/cloud/aiplatform_v1beta1/types/vertex_rag_data_service.py +++ b/google/cloud/aiplatform_v1beta1/types/vertex_rag_data_service.py @@ -277,7 +277,24 @@ class ImportRagFilesResponse(proto.Message): r"""Response message for [VertexRagDataService.ImportRagFiles][google.cloud.aiplatform.v1beta1.VertexRagDataService.ImportRagFiles]. + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + Attributes: + partial_failures_gcs_path (str): + The Google Cloud Storage path into which the + partial failures were written. + + This field is a member of `oneof`_ ``partial_failure_sink``. + partial_failures_bigquery_table (str): + The BigQuery table into which the partial + failures were written. + + This field is a member of `oneof`_ ``partial_failure_sink``. imported_rag_files_count (int): The number of RagFiles that had been imported into the RagCorpus. @@ -289,6 +306,16 @@ class ImportRagFilesResponse(proto.Message): importing into the RagCorpus. """ + partial_failures_gcs_path: str = proto.Field( + proto.STRING, + number=4, + oneof="partial_failure_sink", + ) + partial_failures_bigquery_table: str = proto.Field( + proto.STRING, + number=5, + oneof="partial_failure_sink", + ) imported_rag_files_count: int = proto.Field( proto.INT64, number=1, diff --git a/samples/generated_samples/aiplatform_v1beta1_generated_vertex_rag_data_service_import_rag_files_async.py b/samples/generated_samples/aiplatform_v1beta1_generated_vertex_rag_data_service_import_rag_files_async.py index 8ae50a697c..de9b426123 100644 --- a/samples/generated_samples/aiplatform_v1beta1_generated_vertex_rag_data_service_import_rag_files_async.py +++ b/samples/generated_samples/aiplatform_v1beta1_generated_vertex_rag_data_service_import_rag_files_async.py @@ -41,6 +41,7 @@ async def sample_import_rag_files(): # Initialize request argument(s) import_rag_files_config = aiplatform_v1beta1.ImportRagFilesConfig() import_rag_files_config.gcs_source.uris = ['uris_value1', 'uris_value2'] + import_rag_files_config.partial_failure_gcs_sink.output_uri_prefix = "output_uri_prefix_value" request = aiplatform_v1beta1.ImportRagFilesRequest( parent="parent_value", diff --git a/samples/generated_samples/aiplatform_v1beta1_generated_vertex_rag_data_service_import_rag_files_sync.py b/samples/generated_samples/aiplatform_v1beta1_generated_vertex_rag_data_service_import_rag_files_sync.py index 0733c28d9f..f97dcb910f 100644 --- a/samples/generated_samples/aiplatform_v1beta1_generated_vertex_rag_data_service_import_rag_files_sync.py +++ b/samples/generated_samples/aiplatform_v1beta1_generated_vertex_rag_data_service_import_rag_files_sync.py @@ -41,6 +41,7 @@ def sample_import_rag_files(): # Initialize request argument(s) import_rag_files_config = aiplatform_v1beta1.ImportRagFilesConfig() import_rag_files_config.gcs_source.uris = ['uris_value1', 'uris_value2'] + import_rag_files_config.partial_failure_gcs_sink.output_uri_prefix = "output_uri_prefix_value" request = aiplatform_v1beta1.ImportRagFilesRequest( parent="parent_value", diff --git a/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1beta1.json b/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1beta1.json index 9431d69b09..b8c672fa99 100644 --- a/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1beta1.json +++ b/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1beta1.json @@ -52409,12 +52409,12 @@ "regionTag": "aiplatform_v1beta1_generated_VertexRagDataService_ImportRagFiles_async", "segments": [ { - "end": 59, + "end": 60, "start": 27, "type": "FULL" }, { - "end": 59, + "end": 60, "start": 27, "type": "SHORT" }, @@ -52424,18 +52424,18 @@ "type": "CLIENT_INITIALIZATION" }, { - "end": 49, + "end": 50, "start": 41, "type": "REQUEST_INITIALIZATION" }, { - "end": 56, - "start": 50, + "end": 57, + "start": 51, "type": "REQUEST_EXECUTION" }, { - "end": 60, - "start": 57, + "end": 61, + "start": 58, "type": "RESPONSE_HANDLING" } ], @@ -52493,12 +52493,12 @@ "regionTag": "aiplatform_v1beta1_generated_VertexRagDataService_ImportRagFiles_sync", "segments": [ { - "end": 59, + "end": 60, "start": 27, "type": "FULL" }, { - "end": 59, + "end": 60, "start": 27, "type": "SHORT" }, @@ -52508,18 +52508,18 @@ "type": "CLIENT_INITIALIZATION" }, { - "end": 49, + "end": 50, "start": 41, "type": "REQUEST_INITIALIZATION" }, { - "end": 56, - "start": 50, + "end": 57, + "start": 51, "type": "REQUEST_EXECUTION" }, { - "end": 60, - "start": 57, + "end": 61, + "start": 58, "type": "RESPONSE_HANDLING" } ], diff --git a/tests/unit/gapic/aiplatform_v1/test_migration_service.py b/tests/unit/gapic/aiplatform_v1/test_migration_service.py index dfe666fa67..1ba4c9d52e 100644 --- a/tests/unit/gapic/aiplatform_v1/test_migration_service.py +++ b/tests/unit/gapic/aiplatform_v1/test_migration_service.py @@ -3534,22 +3534,19 @@ def test_parse_annotated_dataset_path(): def test_dataset_path(): project = "cuttlefish" - location = "mussel" - dataset = "winkle" - expected = "projects/{project}/locations/{location}/datasets/{dataset}".format( + dataset = "mussel" + expected = "projects/{project}/datasets/{dataset}".format( project=project, - location=location, dataset=dataset, ) - actual = MigrationServiceClient.dataset_path(project, location, dataset) + actual = MigrationServiceClient.dataset_path(project, dataset) assert expected == actual def test_parse_dataset_path(): expected = { - "project": "nautilus", - "location": "scallop", - "dataset": "abalone", + "project": "winkle", + "dataset": "nautilus", } path = MigrationServiceClient.dataset_path(**expected) @@ -3559,19 +3556,22 @@ def test_parse_dataset_path(): def test_dataset_path(): - project = "squid" - dataset = "clam" - expected = "projects/{project}/datasets/{dataset}".format( + project = "scallop" + location = "abalone" + dataset = "squid" + expected = "projects/{project}/locations/{location}/datasets/{dataset}".format( project=project, + location=location, dataset=dataset, ) - actual = MigrationServiceClient.dataset_path(project, dataset) + actual = MigrationServiceClient.dataset_path(project, location, dataset) assert expected == actual def test_parse_dataset_path(): expected = { - "project": "whelk", + "project": "clam", + "location": "whelk", "dataset": "octopus", } path = MigrationServiceClient.dataset_path(**expected) diff --git a/tests/unit/gapic/aiplatform_v1beta1/test_extension_registry_service.py b/tests/unit/gapic/aiplatform_v1beta1/test_extension_registry_service.py index 405e7a01f2..c42560846a 100644 --- a/tests/unit/gapic/aiplatform_v1beta1/test_extension_registry_service.py +++ b/tests/unit/gapic/aiplatform_v1beta1/test_extension_registry_service.py @@ -3381,6 +3381,10 @@ def test_import_extension_rest(request_type): "max_items": 967, "enum": ["enum_value1", "enum_value2"], "properties": {}, + "property_ordering": [ + "property_ordering_value1", + "property_ordering_value2", + ], "required": ["required_value1", "required_value2"], "min_properties": 1520, "max_properties": 1522, @@ -4568,6 +4572,10 @@ def test_update_extension_rest(request_type): "max_items": 967, "enum": ["enum_value1", "enum_value2"], "properties": {}, + "property_ordering": [ + "property_ordering_value1", + "property_ordering_value2", + ], "required": ["required_value1", "required_value2"], "min_properties": 1520, "max_properties": 1522, diff --git a/tests/unit/gapic/aiplatform_v1beta1/test_gen_ai_cache_service.py b/tests/unit/gapic/aiplatform_v1beta1/test_gen_ai_cache_service.py index b86d41e91e..e648c041b4 100644 --- a/tests/unit/gapic/aiplatform_v1beta1/test_gen_ai_cache_service.py +++ b/tests/unit/gapic/aiplatform_v1beta1/test_gen_ai_cache_service.py @@ -3443,6 +3443,10 @@ def test_create_cached_content_rest(request_type): "max_items": 967, "enum": ["enum_value1", "enum_value2"], "properties": {}, + "property_ordering": [ + "property_ordering_value1", + "property_ordering_value2", + ], "required": ["required_value1", "required_value2"], "min_properties": 1520, "max_properties": 1522, @@ -4261,6 +4265,10 @@ def test_update_cached_content_rest(request_type): "max_items": 967, "enum": ["enum_value1", "enum_value2"], "properties": {}, + "property_ordering": [ + "property_ordering_value1", + "property_ordering_value2", + ], "required": ["required_value1", "required_value2"], "min_properties": 1520, "max_properties": 1522, diff --git a/tests/unit/gapic/aiplatform_v1beta1/test_vertex_rag_data_service.py b/tests/unit/gapic/aiplatform_v1beta1/test_vertex_rag_data_service.py index a891b9f491..060ccaf422 100644 --- a/tests/unit/gapic/aiplatform_v1beta1/test_vertex_rag_data_service.py +++ b/tests/unit/gapic/aiplatform_v1beta1/test_vertex_rag_data_service.py @@ -5478,9 +5478,14 @@ def test_create_rag_corpus_rest(request_type): "http_endpoint": "http_endpoint_value", "collection_name": "collection_name_value", }, + "pinecone": {"index_name": "index_name_value"}, "vertex_feature_store": { "feature_view_resource_name": "feature_view_resource_name_value" }, + "vertex_vector_search": { + "index_endpoint": "index_endpoint_value", + "index": "index_value", + }, "api_auth": { "api_key_config": { "api_key_secret_version": "api_key_secret_version_value" @@ -5897,9 +5902,14 @@ def test_update_rag_corpus_rest(request_type): "http_endpoint": "http_endpoint_value", "collection_name": "collection_name_value", }, + "pinecone": {"index_name": "index_name_value"}, "vertex_feature_store": { "feature_view_resource_name": "feature_view_resource_name_value" }, + "vertex_vector_search": { + "index_endpoint": "index_endpoint_value", + "index": "index_value", + }, "api_auth": { "api_key_config": { "api_key_secret_version": "api_key_secret_version_value" From f78b953f561b8697d07a530e89c7e727db1161ed Mon Sep 17 00:00:00 2001 From: Ayush Agrawal Date: Mon, 16 Sep 2024 16:13:41 -0700 Subject: [PATCH 11/17] feat: Adding Pinecone Vector DB option for RAG corpuses to SDK PiperOrigin-RevId: 675328511 --- tests/unit/vertex_rag/test_rag_constants.py | 28 +++++++++++++++++++ tests/unit/vertex_rag/test_rag_data.py | 24 +++++++++++++++++ vertexai/preview/rag/__init__.py | 2 ++ vertexai/preview/rag/rag_data.py | 3 ++- vertexai/preview/rag/utils/_gapic_utils.py | 30 ++++++++++++++++++--- vertexai/preview/rag/utils/resources.py | 16 ++++++++++- 6 files changed, 97 insertions(+), 6 deletions(-) diff --git a/tests/unit/vertex_rag/test_rag_constants.py b/tests/unit/vertex_rag/test_rag_constants.py index 356dbf4ba7..79a7dc5e5f 100644 --- a/tests/unit/vertex_rag/test_rag_constants.py +++ b/tests/unit/vertex_rag/test_rag_constants.py @@ -20,6 +20,7 @@ from vertexai.preview.rag import ( EmbeddingModelConfig, + Pinecone, RagCorpus, RagFile, RagResource, @@ -69,6 +70,14 @@ collection_name=TEST_WEAVIATE_COLLECTION_NAME, api_key=TEST_WEAVIATE_API_KEY_SECRET_VERSION, ) +TEST_PINECONE_INDEX_NAME = "test-pinecone-index" +TEST_PINECONE_API_KEY_SECRET_VERSION = ( + "projects/test-project/secrets/test-secret/versions/1" +) +TEST_PINECONE_CONFIG = Pinecone( + index_name=TEST_PINECONE_INDEX_NAME, + api_key=TEST_PINECONE_API_KEY_SECRET_VERSION, +) TEST_VERTEX_FEATURE_STORE_RESOURCE_NAME = "test-feature-view-resource-name" TEST_GAPIC_RAG_CORPUS = GapicRagCorpus( name=TEST_RAG_CORPUS_RESOURCE_NAME, @@ -106,6 +115,19 @@ ), ), ) +TEST_GAPIC_RAG_CORPUS_PINECONE = GapicRagCorpus( + name=TEST_RAG_CORPUS_RESOURCE_NAME, + display_name=TEST_CORPUS_DISPLAY_NAME, + description=TEST_CORPUS_DISCRIPTION, + rag_vector_db_config=RagVectorDbConfig( + pinecone=RagVectorDbConfig.Pinecone(index_name=TEST_PINECONE_INDEX_NAME), + api_auth=api_auth.ApiAuth( + api_key_config=api_auth.ApiAuth.ApiKeyConfig( + api_key_secret_version=TEST_PINECONE_API_KEY_SECRET_VERSION + ), + ), + ), +) TEST_EMBEDDING_MODEL_CONFIG = EmbeddingModelConfig( publisher_model="publishers/google/models/textembedding-gecko", ) @@ -130,6 +152,12 @@ description=TEST_CORPUS_DISCRIPTION, vector_db=TEST_VERTEX_FEATURE_STORE_CONFIG, ) +TEST_RAG_CORPUS_PINECONE = RagCorpus( + name=TEST_RAG_CORPUS_RESOURCE_NAME, + display_name=TEST_CORPUS_DISPLAY_NAME, + description=TEST_CORPUS_DISCRIPTION, + vector_db=TEST_PINECONE_CONFIG, +) TEST_PAGE_TOKEN = "test-page-token" # RagFiles diff --git a/tests/unit/vertex_rag/test_rag_data.py b/tests/unit/vertex_rag/test_rag_data.py index 5f7866c389..89c509e249 100644 --- a/tests/unit/vertex_rag/test_rag_data.py +++ b/tests/unit/vertex_rag/test_rag_data.py @@ -79,6 +79,21 @@ def create_rag_corpus_mock_vertex_feature_store(): yield create_rag_corpus_mock_vertex_feature_store +@pytest.fixture +def create_rag_corpus_mock_pinecone(): + with mock.patch.object( + VertexRagDataServiceClient, + "create_rag_corpus", + ) as create_rag_corpus_mock_pinecone: + create_rag_corpus_lro_mock = mock.Mock(ga_operation.Operation) + create_rag_corpus_lro_mock.done.return_value = True + create_rag_corpus_lro_mock.result.return_value = ( + tc.TEST_GAPIC_RAG_CORPUS_PINECONE + ) + create_rag_corpus_mock_pinecone.return_value = create_rag_corpus_lro_mock + yield create_rag_corpus_mock_pinecone + + @pytest.fixture def list_rag_corpora_pager_mock(): with mock.patch.object( @@ -242,6 +257,15 @@ def test_create_corpus_vertex_feature_store_success(self): rag_corpus_eq(rag_corpus, tc.TEST_RAG_CORPUS_VERTEX_FEATURE_STORE) + @pytest.mark.usefixtures("create_rag_corpus_mock_pinecone") + def test_create_corpus_pinecone_success(self): + rag_corpus = rag.create_corpus( + display_name=tc.TEST_CORPUS_DISPLAY_NAME, + vector_db=tc.TEST_PINECONE_CONFIG, + ) + + rag_corpus_eq(rag_corpus, tc.TEST_RAG_CORPUS_PINECONE) + @pytest.mark.usefixtures("rag_data_client_mock_exception") def test_create_corpus_failure(self): with pytest.raises(RuntimeError) as e: diff --git a/vertexai/preview/rag/__init__.py b/vertexai/preview/rag/__init__.py index bbd7cf1f83..3ad4a782e0 100644 --- a/vertexai/preview/rag/__init__.py +++ b/vertexai/preview/rag/__init__.py @@ -40,6 +40,7 @@ EmbeddingModelConfig, JiraQuery, JiraSource, + Pinecone, RagCorpus, RagFile, RagResource, @@ -54,6 +55,7 @@ "EmbeddingModelConfig", "JiraQuery", "JiraSource", + "Pinecone", "RagCorpus", "RagFile", "RagResource", diff --git a/vertexai/preview/rag/rag_data.py b/vertexai/preview/rag/rag_data.py index ea7b730d6a..43edd2ce63 100644 --- a/vertexai/preview/rag/rag_data.py +++ b/vertexai/preview/rag/rag_data.py @@ -45,6 +45,7 @@ from vertexai.preview.rag.utils.resources import ( EmbeddingModelConfig, JiraSource, + Pinecone, RagCorpus, RagFile, SlackChannelsSource, @@ -57,7 +58,7 @@ def create_corpus( display_name: Optional[str] = None, description: Optional[str] = None, embedding_model_config: Optional[EmbeddingModelConfig] = None, - vector_db: Optional[Union[Weaviate, VertexFeatureStore]] = None, + vector_db: Optional[Union[Weaviate, VertexFeatureStore, Pinecone]] = None, ) -> RagCorpus: """Creates a new RagCorpus resource. diff --git a/vertexai/preview/rag/utils/_gapic_utils.py b/vertexai/preview/rag/utils/_gapic_utils.py index 95a4e24612..61a0f68d0b 100644 --- a/vertexai/preview/rag/utils/_gapic_utils.py +++ b/vertexai/preview/rag/utils/_gapic_utils.py @@ -38,6 +38,7 @@ ) from vertexai.preview.rag.utils.resources import ( EmbeddingModelConfig, + Pinecone, RagCorpus, RagFile, SlackChannelsSource, @@ -98,8 +99,8 @@ def convert_gapic_to_embedding_model_config( def convert_gapic_to_vector_db( gapic_vector_db: RagVectorDbConfig, -) -> Union[Weaviate, VertexFeatureStore]: - """Convert Gapic RagVectorDbConfig to Weaviate or VertexFeatureStore.""" +) -> Union[Weaviate, VertexFeatureStore, Pinecone]: + """Convert Gapic RagVectorDbConfig to Weaviate, VertexFeatureStore, or Pinecone.""" if gapic_vector_db.__contains__("weaviate"): return Weaviate( weaviate_http_endpoint=gapic_vector_db.weaviate.http_endpoint, @@ -110,6 +111,11 @@ def convert_gapic_to_vector_db( return VertexFeatureStore( resource_name=gapic_vector_db.vertex_feature_store.feature_view_resource_name, ) + elif gapic_vector_db.__contains__("pinecone"): + return Pinecone( + index_name=gapic_vector_db.pinecone.index_name, + api_key=gapic_vector_db.api_auth.api_key_config.api_key_secret_version, + ) else: return None @@ -395,7 +401,7 @@ def set_embedding_model_config( def set_vector_db( - vector_db: Union[Weaviate, VertexFeatureStore], + vector_db: Union[Weaviate, VertexFeatureStore, Pinecone], rag_corpus: GapicRagCorpus, ) -> None: """Sets the vector db configuration for the rag corpus.""" @@ -423,5 +429,21 @@ def set_vector_db( feature_view_resource_name=resource_name, ), ) + elif isinstance(vector_db, Pinecone): + index_name = vector_db.index_name + api_key = vector_db.api_key + + rag_corpus.rag_vector_db_config = RagVectorDbConfig( + pinecone=RagVectorDbConfig.Pinecone( + index_name=index_name, + ), + api_auth=api_auth.ApiAuth( + api_key_config=api_auth.ApiAuth.ApiKeyConfig( + api_key_secret_version=api_key + ), + ), + ) else: - raise TypeError("vector_db must be a Weaviate or VertexFeatureStore.") + raise TypeError( + "vector_db must be a Weaviate, VertexFeatureStore, or Pinecone." + ) diff --git a/vertexai/preview/rag/utils/resources.py b/vertexai/preview/rag/utils/resources.py index ef46f4e6a2..8ae2a184ab 100644 --- a/vertexai/preview/rag/utils/resources.py +++ b/vertexai/preview/rag/utils/resources.py @@ -98,6 +98,20 @@ class VertexFeatureStore: resource_name: str +@dataclasses.dataclass +class Pinecone: + """Pinecone. + + Attributes: + index_name: The Pinecone index name. + api_key: The SecretManager resource name for the Pinecone DB API token. Format: + ``projects/{project}/secrets/{secret}/versions/{version}`` + """ + + index_name: str + api_key: str + + @dataclasses.dataclass class RagCorpus: """RAG corpus(output only). @@ -115,7 +129,7 @@ class RagCorpus: display_name: Optional[str] = None description: Optional[str] = None embedding_model_config: Optional[EmbeddingModelConfig] = None - vector_db: Optional[Union[Weaviate, VertexFeatureStore]] = None + vector_db: Optional[Union[Weaviate, VertexFeatureStore, Pinecone]] = None @dataclasses.dataclass From 66d84afdd5b20f70b3ff62f25cc32ac0b324d5d5 Mon Sep 17 00:00:00 2001 From: Alexey Volkov Date: Tue, 17 Sep 2024 00:50:33 -0700 Subject: [PATCH 12/17] feat: GenAI - Switched the GA version of the `generative_models` classes to use the v1 service APIs instead of v1beta1 PiperOrigin-RevId: 675454152 --- tests/unit/vertexai/test_generative_models.py | 190 ++++++------- tests/unit/vertexai/test_prompts.py | 8 +- .../generative_models/_generative_models.py | 263 ++++++++++++++++-- 3 files changed, 329 insertions(+), 132 deletions(-) diff --git a/tests/unit/vertexai/test_generative_models.py b/tests/unit/vertexai/test_generative_models.py index e6e75fb34a..1e49a6262f 100644 --- a/tests/unit/vertexai/test_generative_models.py +++ b/tests/unit/vertexai/test_generative_models.py @@ -23,6 +23,11 @@ import vertexai from google.cloud.aiplatform import initializer +from google.cloud.aiplatform_v1 import types as types_v1 +from google.cloud.aiplatform_v1.services import ( + prediction_service as prediction_service_v1, +) +from google.cloud.aiplatform_v1beta1 import types as types_v1beta1 from vertexai import generative_models from vertexai.preview import ( generative_models as preview_generative_models, @@ -326,6 +331,72 @@ def mock_stream_generate_content( yield blocked_chunk +def mock_generate_content_v1( + self, + request: types_v1.GenerateContentRequest, + *, + model: Optional[str] = None, + contents: Optional[MutableSequence[types_v1.Content]] = None, +) -> types_v1.GenerateContentResponse: + request_v1beta1 = types_v1beta1.GenerateContentRequest.deserialize( + type(request).serialize(request) + ) + response_v1beta1 = mock_generate_content( + self=self, + request=request_v1beta1, + ) + response_v1 = types_v1.GenerateContentResponse.deserialize( + type(response_v1beta1).serialize(response_v1beta1) + ) + return response_v1 + + +def mock_stream_generate_content_v1( + self, + request: types_v1.GenerateContentRequest, + *, + model: Optional[str] = None, + contents: Optional[MutableSequence[types_v1.Content]] = None, +) -> Iterable[types_v1.GenerateContentResponse]: + request_v1beta1 = types_v1beta1.GenerateContentRequest.deserialize( + type(request).serialize(request) + ) + for response_v1beta1 in mock_stream_generate_content( + self=self, + request=request_v1beta1, + ): + response_v1 = types_v1.GenerateContentResponse.deserialize( + type(response_v1beta1).serialize(response_v1beta1) + ) + yield response_v1 + + +def patch_genai_services(func: callable): + """Patches GenAI services (v1 and v1beta1, streaming and non-streaming).""" + + func = mock.patch.object( + target=prediction_service.PredictionServiceClient, + attribute="generate_content", + new=mock_generate_content, + )(func) + func = mock.patch.object( + target=prediction_service_v1.PredictionServiceClient, + attribute="generate_content", + new=mock_generate_content_v1, + )(func) + func = mock.patch.object( + target=prediction_service.PredictionServiceClient, + attribute="stream_generate_content", + new=mock_stream_generate_content, + )(func) + func = mock.patch.object( + target=prediction_service_v1.PredictionServiceClient, + attribute="stream_generate_content", + new=mock_stream_generate_content_v1, + )(func) + return func + + @pytest.fixture def mock_get_cached_content_fixture(): """Mocks GenAiCacheServiceClient.get_cached_content().""" @@ -376,11 +447,6 @@ def setup_method(self): def teardown_method(self): initializer.global_pool.shutdown(wait=True) - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -489,11 +555,7 @@ def test_generative_model_from_cached_content_with_resource_name( == "cached-content-id-in-from-cached-content-test" ) - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -601,11 +663,7 @@ def test_generate_content_with_cached_content( assert response.text == "response to " + cached_content.resource_name - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="stream_generate_content", - new=mock_stream_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -616,11 +674,7 @@ def test_generate_content_streaming(self, generative_models: generative_models): for chunk in stream: assert chunk.text - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -668,11 +722,7 @@ def test_generate_content_response_accessor_errors( assert e.match("no text") assert e.match("function_call") - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -685,11 +735,7 @@ def test_chat_send_message(self, generative_models: generative_models): response2 = chat.send_message("Is sky blue on other planets?") assert response2.text - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="stream_generate_content", - new=mock_stream_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -704,11 +750,7 @@ def test_chat_send_message_streaming(self, generative_models: generative_models) for chunk in stream2: assert chunk.candidates - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -727,11 +769,7 @@ def test_chat_send_message_response_validation_errors( # Checking that history did not get updated assert len(chat.history) == 2 - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -754,11 +792,7 @@ def test_chat_send_message_response_blocked_errors( # Checking that history did not get updated assert len(chat.history) == 2 - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -775,11 +809,7 @@ def test_chat_send_message_response_candidate_blocked_error( # Checking that history did not get updated assert not chat.history - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -808,11 +838,7 @@ def test_finish_reason_max_tokens_in_generate_content_and_send_message( # Verify that history did not get updated assert not chat.history - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -861,11 +887,7 @@ def test_chat_function_calling(self, generative_models: generative_models): assert "nice" in response2.text assert not response2.candidates[0].function_calls - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -922,11 +944,7 @@ def test_chat_forced_function_calling(self, generative_models: generative_models assert "nice" in response2.text assert not response2.candidates[0].function_calls - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services @pytest.mark.parametrize( "generative_models", [generative_models, preview_generative_models], @@ -982,11 +1000,7 @@ def test_conversion_methods(self, generative_models: generative_models): # Checking that the enums are serialized as strings, not integers. assert response.to_dict()["candidates"][0]["finish_reason"] == "STOP" - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services def test_generate_content_grounding_google_search_retriever_preview(self): model = preview_generative_models.GenerativeModel("gemini-pro") google_search_retriever_tool = ( @@ -999,11 +1013,7 @@ def test_generate_content_grounding_google_search_retriever_preview(self): ) assert response.text - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services def test_generate_content_grounding_google_search_retriever(self): model = generative_models.GenerativeModel("gemini-pro") google_search_retriever_tool = ( @@ -1016,11 +1026,7 @@ def test_generate_content_grounding_google_search_retriever(self): ) assert response.text - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services def test_generate_content_grounding_vertex_ai_search_retriever(self): model = preview_generative_models.GenerativeModel("gemini-pro") vertex_ai_search_retriever_tool = preview_generative_models.Tool.from_retrieval( @@ -1035,11 +1041,7 @@ def test_generate_content_grounding_vertex_ai_search_retriever(self): ) assert response.text - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services def test_generate_content_grounding_vertex_ai_search_retriever_with_project_and_location( self, ): @@ -1058,11 +1060,7 @@ def test_generate_content_grounding_vertex_ai_search_retriever_with_project_and_ ) assert response.text - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services def test_generate_content_vertex_rag_retriever(self): model = preview_generative_models.GenerativeModel("gemini-pro") rag_resources = [ @@ -1085,11 +1083,7 @@ def test_generate_content_vertex_rag_retriever(self): ) assert response.text - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services def test_chat_automatic_function_calling_with_function_returning_dict(self): generative_models = preview_generative_models get_current_weather_func = generative_models.FunctionDeclaration.from_func( @@ -1124,11 +1118,7 @@ def test_chat_automatic_function_calling_with_function_returning_dict(self): chat2.send_message("What is the weather like in Boston?") assert err.match("Exceeded the maximum") - @mock.patch.object( - target=prediction_service.PredictionServiceClient, - attribute="generate_content", - new=mock_generate_content, - ) + @patch_genai_services def test_chat_automatic_function_calling_with_function_returning_value(self): # Define a new function that returns a value instead of a dict. def get_current_weather(location: str): diff --git a/tests/unit/vertexai/test_prompts.py b/tests/unit/vertexai/test_prompts.py index 39bb666dea..83d1b5dbe9 100644 --- a/tests/unit/vertexai/test_prompts.py +++ b/tests/unit/vertexai/test_prompts.py @@ -36,10 +36,10 @@ # TODO(b/360932655): Use mock_generate_content from test_generative_models from vertexai.preview import rag from vertexai.generative_models._generative_models import ( - prediction_service, - gapic_prediction_service_types, - gapic_content_types, - gapic_tool_types, + prediction_service_v1 as prediction_service, + types_v1 as gapic_prediction_service_types, + types_v1 as gapic_content_types, + types_v1 as gapic_tool_types, ) diff --git a/vertexai/generative_models/_generative_models.py b/vertexai/generative_models/_generative_models.py index 80b21b9591..30073e2ae4 100644 --- a/vertexai/generative_models/_generative_models.py +++ b/vertexai/generative_models/_generative_models.py @@ -39,6 +39,11 @@ from google.cloud.aiplatform import initializer as aiplatform_initializer from google.cloud.aiplatform import utils as aiplatform_utils +from google.cloud.aiplatform_v1 import types as types_v1 +from google.cloud.aiplatform_v1.services import ( + prediction_service as prediction_service_v1, + llm_utility_service as llm_utility_service_v1, +) from google.cloud.aiplatform_v1beta1 import types as aiplatform_types from google.cloud.aiplatform_v1beta1.services import prediction_service from google.cloud.aiplatform_v1beta1.services import llm_utility_service @@ -847,14 +852,11 @@ def count_tokens( contents=contents, tools=tools, ) - return self._prediction_client.count_tokens( - request=gapic_prediction_service_types.CountTokensRequest( - endpoint=self._prediction_resource_name, - model=self._prediction_resource_name, - contents=request.contents, - system_instruction=request.system_instruction, - tools=request.tools, - ) + return self._gapic_count_tokens( + prediction_resource_name=self._prediction_resource_name, + contents=request.contents, + system_instruction=request.system_instruction, + tools=request.tools, ) async def count_tokens_async( @@ -884,15 +886,44 @@ async def count_tokens_async( contents=contents, tools=tools, ) - return await self._prediction_async_client.count_tokens( - request=gapic_prediction_service_types.CountTokensRequest( - endpoint=self._prediction_resource_name, - model=self._prediction_resource_name, - contents=request.contents, - system_instruction=request.system_instruction, - tools=request.tools, - ) + return await self._gapic_count_tokens_async( + prediction_resource_name=self._prediction_resource_name, + contents=request.contents, + system_instruction=request.system_instruction, + tools=request.tools, + ) + + def _gapic_count_tokens( + self, + prediction_resource_name: str, + contents: List[gapic_content_types.Content], + system_instruction: Optional[gapic_content_types.Content] = None, + tools: Optional[List[gapic_tool_types.Tool]] = None, + ) -> gapic_prediction_service_types.CountTokensResponse: + request = gapic_prediction_service_types.CountTokensRequest( + endpoint=prediction_resource_name, + model=prediction_resource_name, + contents=contents, + system_instruction=system_instruction, + tools=tools, + ) + return self._prediction_client.count_tokens(request=request) + + async def _gapic_count_tokens_async( + self, + prediction_resource_name: str, + contents: List[gapic_content_types.Content], + system_instruction: Optional[gapic_content_types.Content] = None, + tools: Optional[List[gapic_tool_types.Tool]] = None, + ) -> gapic_prediction_service_types.CountTokensResponse: + request = gapic_prediction_service_types.CountTokensRequest( + endpoint=prediction_resource_name, + model=prediction_resource_name, + contents=contents, + system_instruction=system_instruction, + tools=tools, ) + return await self._prediction_async_client.count_tokens(request=request) def compute_tokens( self, contents: ContentsType @@ -917,12 +948,9 @@ def compute_tokens( info consists tokens list, token_ids list and a role. """ - return self._llm_utility_client.compute_tokens( - request=gapic_llm_utility_service_types.ComputeTokensRequest( - endpoint=self._prediction_resource_name, - model=self._prediction_resource_name, - contents=self._prepare_request(contents=contents).contents, - ) + return self._gapic_compute_tokens( + prediction_resource_name=self._prediction_resource_name, + contents=self._prepare_request(contents=contents).contents, ) async def compute_tokens_async( @@ -948,13 +976,34 @@ async def compute_tokens_async( info consists tokens list, token_ids list and a role. """ - return await self._llm_utility_async_client.compute_tokens( - request=gapic_llm_utility_service_types.ComputeTokensRequest( - endpoint=self._prediction_resource_name, - model=self._prediction_resource_name, - contents=self._prepare_request(contents=contents).contents, - ) + return await self._gapic_compute_tokens_async( + prediction_resource_name=self._prediction_resource_name, + contents=self._prepare_request(contents=contents).contents, + ) + + def _gapic_compute_tokens( + self, + prediction_resource_name: str, + contents: List[gapic_content_types.Content], + ) -> gapic_prediction_service_types.CountTokensResponse: + request = gapic_llm_utility_service_types.ComputeTokensRequest( + endpoint=prediction_resource_name, + model=prediction_resource_name, + contents=contents, ) + return self._llm_utility_client.compute_tokens(request=request) + + async def _gapic_compute_tokens_async( + self, + prediction_resource_name: str, + contents: List[gapic_content_types.Content], + ) -> gapic_prediction_service_types.CountTokensResponse: + request = gapic_llm_utility_service_types.ComputeTokensRequest( + endpoint=prediction_resource_name, + model=prediction_resource_name, + contents=contents, + ) + return await self._llm_utility_async_client.compute_tokens(request=request) def start_chat( self, @@ -2772,6 +2821,164 @@ def respond_to_model_response( class GenerativeModel(_GenerativeModel): __module__ = "vertexai.generative_models" + @property + def _prediction_client(self) -> prediction_service_v1.PredictionServiceClient: + # Switch to @functools.cached_property once its available. + if not getattr(self, "_prediction_client_value", None): + self._prediction_client_value = ( + aiplatform_initializer.global_config.create_client( + client_class=prediction_service_v1.PredictionServiceClient, + location_override=self._location, + prediction_client=True, + ) + ) + return self._prediction_client_value + + @property + def _prediction_async_client( + self, + ) -> prediction_service_v1.PredictionServiceAsyncClient: + # Switch to @functools.cached_property once its available. + if not getattr(self, "_prediction_async_client_value", None): + self._prediction_async_client_value = ( + aiplatform_initializer.global_config.create_client( + client_class=prediction_service_v1.PredictionServiceAsyncClient, + location_override=self._location, + prediction_client=True, + ) + ) + return self._prediction_async_client_value + + @property + def _llm_utility_client(self) -> llm_utility_service_v1.LlmUtilityServiceClient: + # Switch to @functools.cached_property once its available. + if not getattr(self, "_llm_utility_client_value", None): + self._llm_utility_client_value = ( + aiplatform_initializer.global_config.create_client( + client_class=llm_utility_service_v1.LlmUtilityServiceClient, + location_override=self._location, + prediction_client=True, + ) + ) + return self._llm_utility_client_value + + @property + def _llm_utility_async_client( + self, + ) -> llm_utility_service_v1.LlmUtilityServiceAsyncClient: + # Switch to @functools.cached_property once its available. + if not getattr(self, "_llm_utility_async_client_value", None): + self._llm_utility_async_client_value = ( + aiplatform_initializer.global_config.create_client( + client_class=llm_utility_service_v1.LlmUtilityServiceAsyncClient, + location_override=self._location, + prediction_client=True, + ) + ) + return self._llm_utility_async_client_value + + def _prepare_request( + self, + contents: ContentsType, + *, + generation_config: Optional[GenerationConfigType] = None, + safety_settings: Optional[SafetySettingsType] = None, + tools: Optional[List["Tool"]] = None, + tool_config: Optional["ToolConfig"] = None, + system_instruction: Optional[PartsType] = None, + ) -> types_v1.GenerateContentRequest: + """Prepares a GAPIC GenerateContentRequest.""" + request_v1beta1 = super()._prepare_request( + contents=contents, + generation_config=generation_config, + safety_settings=safety_settings, + tools=tools, + tool_config=tool_config, + system_instruction=system_instruction, + ) + serialized_message_v1beta1 = type(request_v1beta1).serialize(request_v1beta1) + try: + response_v1 = types_v1.GenerateContentRequest.deserialize( + serialized_message_v1beta1 + ) + except Exception as ex: + raise ValueError( + "Failed to convert GenerateContentRequest from v1beta1 to v1:\n" + f"{serialized_message_v1beta1}" + ) from ex + return response_v1 + + def _parse_response( + self, + response: types_v1.GenerateContentResponse, + ) -> "GenerationResponse": + response_v1beta1 = aiplatform_types.GenerateContentResponse.deserialize( + type(response).serialize(response) + ) + return super()._parse_response( + response=response_v1beta1, + ) + + # The count_tokens methods need to be overridden since in v1, the + # `count_tokens` method is implemented by the `LLMUtilityService` API + # not the `PredictionService` API. + def _gapic_count_tokens( + self, + prediction_resource_name: str, + contents: List[types_v1.Content], + system_instruction: Optional[types_v1.Content] = None, + tools: Optional[List[types_v1.Tool]] = None, + ) -> types_v1.CountTokensResponse: + request = types_v1.CountTokensRequest( + endpoint=prediction_resource_name, + model=prediction_resource_name, + contents=contents, + system_instruction=system_instruction, + tools=tools, + ) + return self._llm_utility_client.count_tokens(request=request) + + async def _gapic_count_tokens_async( + self, + prediction_resource_name: str, + contents: List[types_v1.Content], + system_instruction: Optional[types_v1.Content] = None, + tools: Optional[List[types_v1.Tool]] = None, + ) -> types_v1.CountTokensResponse: + request = types_v1.CountTokensRequest( + endpoint=prediction_resource_name, + model=prediction_resource_name, + contents=contents, + system_instruction=system_instruction, + tools=tools, + ) + return await self._llm_utility_async_client.count_tokens(request=request) + + # The compute_tokens methods need to be overridden since the request types differ. + def _gapic_compute_tokens( + self, + prediction_resource_name: str, + contents: List[gapic_content_types.Content], + ) -> gapic_prediction_service_types.CountTokensResponse: + request = gapic_llm_utility_service_types.ComputeTokensRequest( + endpoint=prediction_resource_name, + model=prediction_resource_name, + contents=contents, + ) + return self._llm_utility_client.compute_tokens(request=request) + + async def _gapic_compute_tokens_async( + self, + prediction_resource_name: str, + contents: List[gapic_content_types.Content], + ) -> gapic_prediction_service_types.CountTokensResponse: + request = gapic_llm_utility_service_types.ComputeTokensRequest( + endpoint=prediction_resource_name, + model=prediction_resource_name, + contents=contents, + ) + return await self._llm_utility_async_client.compute_tokens(request=request) + class _PreviewGenerativeModel(_GenerativeModel): __name__ = "GenerativeModel" From 07e471e0a069551f2c855e167e549fa92ac6af95 Mon Sep 17 00:00:00 2001 From: A Vertex SDK engineer Date: Tue, 17 Sep 2024 12:45:46 -0700 Subject: [PATCH 13/17] feat: add support for partial failures sink in import rag files. PiperOrigin-RevId: 675672254 --- vertexai/preview/rag/rag_data.py | 20 ++++++++++++++++++++ vertexai/preview/rag/utils/_gapic_utils.py | 17 +++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/vertexai/preview/rag/rag_data.py b/vertexai/preview/rag/rag_data.py index 43edd2ce63..fe2c697f2a 100644 --- a/vertexai/preview/rag/rag_data.py +++ b/vertexai/preview/rag/rag_data.py @@ -293,6 +293,7 @@ def import_files( timeout: int = 600, max_embedding_requests_per_min: int = 1000, use_advanced_pdf_parsing: Optional[bool] = False, + partial_failures_sink: Optional[str] = None, ) -> ImportRagFilesResponse: """ Import files to an existing RagCorpus, wait until completion. @@ -378,6 +379,14 @@ def import_files( timeout: Default is 600 seconds. use_advanced_pdf_parsing: Whether to use advanced PDF parsing on uploaded files. + partial_failures_sink: Either a GCS path to store partial failures or a + BigQuery table to store partial failures. The format is + "gs://my-bucket/my/object.ndjson" for GCS or + "bq://my-project.my-dataset.my-table" for BigQuery. An existing GCS + object cannot be used. However, the BigQuery table may or may not + exist - if it does not exist, it will be created. If it does exist, + the schema will be checked and the partial failures will be appended + to the table. Returns: ImportRagFilesResponse. """ @@ -394,6 +403,7 @@ def import_files( chunk_overlap=chunk_overlap, max_embedding_requests_per_min=max_embedding_requests_per_min, use_advanced_pdf_parsing=use_advanced_pdf_parsing, + partial_failures_sink=partial_failures_sink, ) client = _gapic_utils.create_rag_data_service_client() try: @@ -412,6 +422,7 @@ async def import_files_async( chunk_overlap: int = 200, max_embedding_requests_per_min: int = 1000, use_advanced_pdf_parsing: Optional[bool] = False, + partial_failures_sink: Optional[str] = None, ) -> operation_async.AsyncOperation: """ Import files to an existing RagCorpus asynchronously. @@ -497,6 +508,14 @@ async def import_files_async( QPM would be used. use_advanced_pdf_parsing: Whether to use advanced PDF parsing on uploaded files. + partial_failures_sink: Either a GCS path to store partial failures or a + BigQuery table to store partial failures. The format is + "gs://my-bucket/my/object.ndjson" for GCS or + "bq://my-project.my-dataset.my-table" for BigQuery. An existing GCS + object cannot be used. However, the BigQuery table may or may not + exist - if it does not exist, it will be created. If it does exist, + the schema will be checked and the partial failures will be appended + to the table. Returns: operation_async.AsyncOperation. """ @@ -513,6 +532,7 @@ async def import_files_async( chunk_overlap=chunk_overlap, max_embedding_requests_per_min=max_embedding_requests_per_min, use_advanced_pdf_parsing=use_advanced_pdf_parsing, + partial_failures_sink=partial_failures_sink, ) async_client = _gapic_utils.create_rag_data_service_async_client() try: diff --git a/vertexai/preview/rag/utils/_gapic_utils.py b/vertexai/preview/rag/utils/_gapic_utils.py index 61a0f68d0b..8e729d6cc7 100644 --- a/vertexai/preview/rag/utils/_gapic_utils.py +++ b/vertexai/preview/rag/utils/_gapic_utils.py @@ -247,6 +247,7 @@ def prepare_import_files_request( chunk_overlap: int = 200, max_embedding_requests_per_min: int = 1000, use_advanced_pdf_parsing: bool = False, + partial_failures_sink: Optional[str] = None, ) -> ImportRagFilesRequest: if len(corpus_name.split("/")) != 6: raise ValueError( @@ -289,6 +290,22 @@ def prepare_import_files_request( ) import_rag_files_config.google_drive_source = google_drive_source + if partial_failures_sink is not None: + if partial_failures_sink.startswith("gs://"): + import_rag_files_config.partial_failure_gcs_sink.output_uri_prefix = ( + partial_failures_sink + ) + elif partial_failures_sink.startswith( + "bq://" + ) or partial_failures_sink.startswith("bigquery://"): + import_rag_files_config.partial_failure_bigquery_sink.output_uri = ( + partial_failures_sink + ) + else: + raise ValueError( + "if provided, partial_failures_sink must be a GCS path or a BigQuery table." + ) + request = ImportRagFilesRequest( parent=corpus_name, import_rag_files_config=import_rag_files_config ) From f882657183e34c8e07baa4b8dc9f45ed8bca9db7 Mon Sep 17 00:00:00 2001 From: Ayush Agrawal Date: Tue, 17 Sep 2024 14:29:26 -0700 Subject: [PATCH 14/17] feat: Adding Vertex Vector Search Vector DB option for RAG corpuses to SDK PiperOrigin-RevId: 675710933 --- tests/unit/vertex_rag/test_rag_constants.py | 24 +++++++++++++++++++ tests/unit/vertex_rag/test_rag_data.py | 26 +++++++++++++++++++++ vertexai/preview/rag/__init__.py | 2 ++ vertexai/preview/rag/rag_data.py | 5 +++- vertexai/preview/rag/utils/_gapic_utils.py | 24 +++++++++++++++---- vertexai/preview/rag/utils/resources.py | 21 ++++++++++++++++- 6 files changed, 96 insertions(+), 6 deletions(-) diff --git a/tests/unit/vertex_rag/test_rag_constants.py b/tests/unit/vertex_rag/test_rag_constants.py index 79a7dc5e5f..e85e176006 100644 --- a/tests/unit/vertex_rag/test_rag_constants.py +++ b/tests/unit/vertex_rag/test_rag_constants.py @@ -29,6 +29,7 @@ JiraSource, JiraQuery, Weaviate, + VertexVectorSearch, VertexFeatureStore, ) from google.cloud.aiplatform_v1beta1 import ( @@ -78,6 +79,12 @@ index_name=TEST_PINECONE_INDEX_NAME, api_key=TEST_PINECONE_API_KEY_SECRET_VERSION, ) +TEST_VERTEX_VECTOR_SEARCH_INDEX_ENDPOINT = "test-vector-search-index-endpoint" +TEST_VERTEX_VECTOR_SEARCH_INDEX = "test-vector-search-index" +TEST_VERTEX_VECTOR_SEARCH_CONFIG = VertexVectorSearch( + index_endpoint=TEST_VERTEX_VECTOR_SEARCH_INDEX_ENDPOINT, + index=TEST_VERTEX_VECTOR_SEARCH_INDEX, +) TEST_VERTEX_FEATURE_STORE_RESOURCE_NAME = "test-feature-view-resource-name" TEST_GAPIC_RAG_CORPUS = GapicRagCorpus( name=TEST_RAG_CORPUS_RESOURCE_NAME, @@ -115,6 +122,17 @@ ), ), ) +TEST_GAPIC_RAG_CORPUS_VERTEX_VECTOR_SEARCH = GapicRagCorpus( + name=TEST_RAG_CORPUS_RESOURCE_NAME, + display_name=TEST_CORPUS_DISPLAY_NAME, + description=TEST_CORPUS_DISCRIPTION, + rag_vector_db_config=RagVectorDbConfig( + vertex_vector_search=RagVectorDbConfig.VertexVectorSearch( + index_endpoint=TEST_VERTEX_VECTOR_SEARCH_INDEX_ENDPOINT, + index=TEST_VERTEX_VECTOR_SEARCH_INDEX, + ), + ), +) TEST_GAPIC_RAG_CORPUS_PINECONE = GapicRagCorpus( name=TEST_RAG_CORPUS_RESOURCE_NAME, display_name=TEST_CORPUS_DISPLAY_NAME, @@ -158,6 +176,12 @@ description=TEST_CORPUS_DISCRIPTION, vector_db=TEST_PINECONE_CONFIG, ) +TEST_RAG_CORPUS_VERTEX_VECTOR_SEARCH = RagCorpus( + name=TEST_RAG_CORPUS_RESOURCE_NAME, + display_name=TEST_CORPUS_DISPLAY_NAME, + description=TEST_CORPUS_DISCRIPTION, + vector_db=TEST_VERTEX_VECTOR_SEARCH_CONFIG, +) TEST_PAGE_TOKEN = "test-page-token" # RagFiles diff --git a/tests/unit/vertex_rag/test_rag_data.py b/tests/unit/vertex_rag/test_rag_data.py index 89c509e249..c20011c361 100644 --- a/tests/unit/vertex_rag/test_rag_data.py +++ b/tests/unit/vertex_rag/test_rag_data.py @@ -79,6 +79,23 @@ def create_rag_corpus_mock_vertex_feature_store(): yield create_rag_corpus_mock_vertex_feature_store +@pytest.fixture +def create_rag_corpus_mock_vertex_vector_search(): + with mock.patch.object( + VertexRagDataServiceClient, + "create_rag_corpus", + ) as create_rag_corpus_mock_vertex_vector_search: + create_rag_corpus_lro_mock = mock.Mock(ga_operation.Operation) + create_rag_corpus_lro_mock.done.return_value = True + create_rag_corpus_lro_mock.result.return_value = ( + tc.TEST_GAPIC_RAG_CORPUS_VERTEX_VECTOR_SEARCH + ) + create_rag_corpus_mock_vertex_vector_search.return_value = ( + create_rag_corpus_lro_mock + ) + yield create_rag_corpus_mock_vertex_vector_search + + @pytest.fixture def create_rag_corpus_mock_pinecone(): with mock.patch.object( @@ -257,6 +274,15 @@ def test_create_corpus_vertex_feature_store_success(self): rag_corpus_eq(rag_corpus, tc.TEST_RAG_CORPUS_VERTEX_FEATURE_STORE) + @pytest.mark.usefixtures("create_rag_corpus_mock_vertex_vector_search") + def test_create_corpus_vertex_vector_search_success(self): + rag_corpus = rag.create_corpus( + display_name=tc.TEST_CORPUS_DISPLAY_NAME, + vector_db=tc.TEST_VERTEX_VECTOR_SEARCH_CONFIG, + ) + + rag_corpus_eq(rag_corpus, tc.TEST_RAG_CORPUS_VERTEX_VECTOR_SEARCH) + @pytest.mark.usefixtures("create_rag_corpus_mock_pinecone") def test_create_corpus_pinecone_success(self): rag_corpus = rag.create_corpus( diff --git a/vertexai/preview/rag/__init__.py b/vertexai/preview/rag/__init__.py index 3ad4a782e0..7c7737276b 100644 --- a/vertexai/preview/rag/__init__.py +++ b/vertexai/preview/rag/__init__.py @@ -47,6 +47,7 @@ SlackChannel, SlackChannelsSource, VertexFeatureStore, + VertexVectorSearch, Weaviate, ) @@ -64,6 +65,7 @@ "SlackChannelsSource", "VertexFeatureStore", "VertexRagStore", + "VertexVectorSearch", "Weaviate", "create_corpus", "delete_corpus", diff --git a/vertexai/preview/rag/rag_data.py b/vertexai/preview/rag/rag_data.py index fe2c697f2a..e332b0337b 100644 --- a/vertexai/preview/rag/rag_data.py +++ b/vertexai/preview/rag/rag_data.py @@ -50,6 +50,7 @@ RagFile, SlackChannelsSource, VertexFeatureStore, + VertexVectorSearch, Weaviate, ) @@ -58,7 +59,9 @@ def create_corpus( display_name: Optional[str] = None, description: Optional[str] = None, embedding_model_config: Optional[EmbeddingModelConfig] = None, - vector_db: Optional[Union[Weaviate, VertexFeatureStore, Pinecone]] = None, + vector_db: Optional[ + Union[Weaviate, VertexFeatureStore, VertexVectorSearch, Pinecone] + ] = None, ) -> RagCorpus: """Creates a new RagCorpus resource. diff --git a/vertexai/preview/rag/utils/_gapic_utils.py b/vertexai/preview/rag/utils/_gapic_utils.py index 8e729d6cc7..3c526723c1 100644 --- a/vertexai/preview/rag/utils/_gapic_utils.py +++ b/vertexai/preview/rag/utils/_gapic_utils.py @@ -44,6 +44,7 @@ SlackChannelsSource, JiraSource, VertexFeatureStore, + VertexVectorSearch, Weaviate, ) @@ -99,8 +100,8 @@ def convert_gapic_to_embedding_model_config( def convert_gapic_to_vector_db( gapic_vector_db: RagVectorDbConfig, -) -> Union[Weaviate, VertexFeatureStore, Pinecone]: - """Convert Gapic RagVectorDbConfig to Weaviate, VertexFeatureStore, or Pinecone.""" +) -> Union[Weaviate, VertexFeatureStore, VertexVectorSearch, Pinecone]: + """Convert Gapic RagVectorDbConfig to Weaviate, VertexFeatureStore, VertexVectorSearch, or Pinecone.""" if gapic_vector_db.__contains__("weaviate"): return Weaviate( weaviate_http_endpoint=gapic_vector_db.weaviate.http_endpoint, @@ -116,6 +117,11 @@ def convert_gapic_to_vector_db( index_name=gapic_vector_db.pinecone.index_name, api_key=gapic_vector_db.api_auth.api_key_config.api_key_secret_version, ) + elif gapic_vector_db.__contains__("vertex_vector_search"): + return VertexVectorSearch( + index_endpoint=gapic_vector_db.vertex_vector_search.index_endpoint, + index=gapic_vector_db.vertex_vector_search.index, + ) else: return None @@ -418,7 +424,7 @@ def set_embedding_model_config( def set_vector_db( - vector_db: Union[Weaviate, VertexFeatureStore, Pinecone], + vector_db: Union[Weaviate, VertexFeatureStore, VertexVectorSearch, Pinecone], rag_corpus: GapicRagCorpus, ) -> None: """Sets the vector db configuration for the rag corpus.""" @@ -446,6 +452,16 @@ def set_vector_db( feature_view_resource_name=resource_name, ), ) + elif isinstance(vector_db, VertexVectorSearch): + index_endpoint = vector_db.index_endpoint + index = vector_db.index + + rag_corpus.rag_vector_db_config = RagVectorDbConfig( + vertex_vector_search=RagVectorDbConfig.VertexVectorSearch( + index_endpoint=index_endpoint, + index=index, + ), + ) elif isinstance(vector_db, Pinecone): index_name = vector_db.index_name api_key = vector_db.api_key @@ -462,5 +478,5 @@ def set_vector_db( ) else: raise TypeError( - "vector_db must be a Weaviate, VertexFeatureStore, or Pinecone." + "vector_db must be a Weaviate, VertexFeatureStore, VertexVectorSearch, or Pinecone." ) diff --git a/vertexai/preview/rag/utils/resources.py b/vertexai/preview/rag/utils/resources.py index 8ae2a184ab..371ccc3e9e 100644 --- a/vertexai/preview/rag/utils/resources.py +++ b/vertexai/preview/rag/utils/resources.py @@ -98,6 +98,23 @@ class VertexFeatureStore: resource_name: str +@dataclasses.dataclass +class VertexVectorSearch: + """VertexVectorSearch. + + Attributes: + index_endpoint (str): + The resource name of the Index Endpoint. Format: + ``projects/{project}/locations/{location}/indexEndpoints/{index_endpoint}`` + index (str): + The resource name of the Index. Format: + ``projects/{project}/locations/{location}/indexes/{index}`` + """ + + index_endpoint: str + index: str + + @dataclasses.dataclass class Pinecone: """Pinecone. @@ -129,7 +146,9 @@ class RagCorpus: display_name: Optional[str] = None description: Optional[str] = None embedding_model_config: Optional[EmbeddingModelConfig] = None - vector_db: Optional[Union[Weaviate, VertexFeatureStore, Pinecone]] = None + vector_db: Optional[ + Union[Weaviate, VertexFeatureStore, VertexVectorSearch, Pinecone] + ] = None @dataclasses.dataclass From 0de298786c43427cb1a20b91cbabd1ce921c16da Mon Sep 17 00:00:00 2001 From: Alexey Volkov Date: Tue, 17 Sep 2024 14:31:02 -0700 Subject: [PATCH 15/17] fix: GenAI - Fixed `GenerativeModel.compute_tokens` for v1 API PiperOrigin-RevId: 675711678 --- vertexai/generative_models/_generative_models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vertexai/generative_models/_generative_models.py b/vertexai/generative_models/_generative_models.py index 30073e2ae4..74b03d51fc 100644 --- a/vertexai/generative_models/_generative_models.py +++ b/vertexai/generative_models/_generative_models.py @@ -2960,7 +2960,7 @@ def _gapic_compute_tokens( prediction_resource_name: str, contents: List[gapic_content_types.Content], ) -> gapic_prediction_service_types.CountTokensResponse: - request = gapic_llm_utility_service_types.ComputeTokensRequest( + request = types_v1.ComputeTokensRequest( endpoint=prediction_resource_name, model=prediction_resource_name, contents=contents, @@ -2972,7 +2972,7 @@ async def _gapic_compute_tokens_async( prediction_resource_name: str, contents: List[gapic_content_types.Content], ) -> gapic_prediction_service_types.CountTokensResponse: - request = gapic_llm_utility_service_types.ComputeTokensRequest( + request = types_v1.ComputeTokensRequest( endpoint=prediction_resource_name, model=prediction_resource_name, contents=contents, From ab157c8ead718b2a1a1d13306c1256c1cb2561f1 Mon Sep 17 00:00:00 2001 From: A Vertex SDK engineer Date: Tue, 17 Sep 2024 14:44:27 -0700 Subject: [PATCH 16/17] fix: Tensorboard - Fix error in tensorboard batch upload of nested dirs PiperOrigin-RevId: 675716748 --- .../cloud/aiplatform/tensorboard/uploader.py | 1 + tests/unit/aiplatform/test_uploader.py | 89 ++++++++++++++++--- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/google/cloud/aiplatform/tensorboard/uploader.py b/google/cloud/aiplatform/tensorboard/uploader.py index c24455ae5e..b77a176004 100644 --- a/google/cloud/aiplatform/tensorboard/uploader.py +++ b/google/cloud/aiplatform/tensorboard/uploader.py @@ -386,6 +386,7 @@ def _pre_create_runs_and_time_series(self): if (run_name and run_name != ".") else uploader_utils.DEFAULT_RUN_NAME ) + run_name = uploader_utils.reformat_run_name(run_name) run_names.append(run_name) for event in events: _filter_graph_defs(event) diff --git a/tests/unit/aiplatform/test_uploader.py b/tests/unit/aiplatform/test_uploader.py index 46e3141ac6..1b145eab0c 100644 --- a/tests/unit/aiplatform/test_uploader.py +++ b/tests/unit/aiplatform/test_uploader.py @@ -591,6 +591,11 @@ def test_start_uploading_without_create_experiment_fails(self): with self.assertRaisesRegex(RuntimeError, "call create_experiment()"): uploader.start_uploading() + @parameterized.parameters( + {"nested_run_dir": ""}, + {"nested_run_dir": "nested-dir/"}, + {"nested_run_dir": "double/nested-dir/"}, + ) @patch.object( uploader_utils.OnePlatformResourceManager, "get_run_resource_name", @@ -599,7 +604,11 @@ def test_start_uploading_without_create_experiment_fails(self): @patch.object(metadata, "_experiment_tracker", autospec=True) @patch.object(experiment_resources, "Experiment", autospec=True) def test_start_uploading_scalars( - self, experiment_resources_mock, experiment_tracker_mock, run_resource_mock + self, + experiment_resources_mock, + experiment_tracker_mock, + run_resource_mock, + nested_run_dir, ): experiment_resources_mock.get.return_value = _TEST_EXPERIMENT_NAME experiment_tracker_mock.set_experiment.return_value = _TEST_EXPERIMENT_NAME @@ -628,21 +637,21 @@ def test_start_uploading_scalars( mock_logdir_loader = mock.create_autospec(logdir_loader.LogdirLoader) mock_logdir_loader.get_run_events.side_effect = [ { - "run 1": _apply_compat( + f"{nested_run_dir}run 1": _apply_compat( [_scalar_event("1.1", 5.0), _scalar_event("1.2", 5.0)] ), - "run 2": _apply_compat( + f"{nested_run_dir}run 2": _apply_compat( [_scalar_event("2.1", 5.0), _scalar_event("2.2", 5.0)] ), }, { - "run 3": _apply_compat( + f"{nested_run_dir}run 3": _apply_compat( [_scalar_event("3.1", 5.0), _scalar_event("3.2", 5.0)] ), - "run 4": _apply_compat( + f"{nested_run_dir}run 4": _apply_compat( [_scalar_event("4.1", 5.0), _scalar_event("4.2", 5.0)] ), - "run 5": _apply_compat( + f"{nested_run_dir}run 5": _apply_compat( [_scalar_event("5.1", 5.0), _scalar_event("5.2", 5.0)] ), }, @@ -666,11 +675,20 @@ def test_start_uploading_scalars( self.assertEqual(mock_tracker.blob_tracker.call_count, 0) @parameterized.parameters( - {"existing_experiment": None, "one_platform_run_name": None}, - {"existing_experiment": None, "one_platform_run_name": "."}, + { + "existing_experiment": None, + "one_platform_run_name": None, + "nested_run_dir": "", + }, + { + "existing_experiment": None, + "one_platform_run_name": ".", + "nested_run_dir": "nested-dir/", + }, { "existing_experiment": _TEST_EXPERIMENT_NAME, "one_platform_run_name": _TEST_ONE_PLATFORM_RUN_NAME, + "nested_run_dir": "double/nested-dir/", }, ) @patch.object( @@ -693,6 +711,7 @@ def test_start_uploading_scalars_one_shot( run_resource_mock, existing_experiment, one_platform_run_name, + nested_run_dir, ): """Check that one-shot uploading stops without AbortUploadError.""" @@ -760,10 +779,10 @@ def batch_create_time_series(parent, requests): mock_logdir_loader = mock.create_autospec(logdir_loader.LogdirLoader) mock_logdir_loader.get_run_events.side_effect = [ { - "run 1": _apply_compat( + f"{nested_run_dir}run 1": _apply_compat( [_scalar_event("tag_1.1", 5.0), _scalar_event("tag_1.2", 5.0)] ), - "run 2": _apply_compat( + f"{nested_run_dir}run 2": _apply_compat( [_scalar_event("tag_2.1", 5.0), _scalar_event("tag_2.2", 5.0)] ), }, @@ -772,10 +791,10 @@ def batch_create_time_series(parent, requests): mock_logdir_loader_pre_create = mock.create_autospec(logdir_loader.LogdirLoader) mock_logdir_loader_pre_create.get_run_events.side_effect = [ { - "run 1": _apply_compat( + f"{nested_run_dir}run 1": _apply_compat( [_scalar_event("tag_1.1", 5.0), _scalar_event("tag_1.2", 5.0)] ), - "run 2": _apply_compat( + f"{nested_run_dir}run 2": _apply_compat( [_scalar_event("tag_2.1", 5.0), _scalar_event("tag_2.2", 5.0)] ), }, @@ -804,6 +823,52 @@ def batch_create_time_series(parent, requests): self.assertEqual(mock_tracker.blob_tracker.call_count, 0) experiment_tracker_mock.set_experiment.assert_called_once() + @parameterized.parameters( + {"nested_run_dir": ""}, + {"nested_run_dir": "nested-dir/"}, + {"nested_run_dir": "double/nested-dir/"}, + ) + @patch.object(metadata, "_experiment_tracker", autospec=True) + @patch.object(experiment_resources, "Experiment", autospec=True) + def test_upload_nested_scalars_one_shot( + self, + experiment_resources_mock, + experiment_tracker_mock, + nested_run_dir, + ): + """Check that one-shot uploading stops without AbortUploadError.""" + + logdir = self.get_temp_dir() + uploader = _create_uploader( + logdir=logdir, + ) + uploader.create_experiment() + + run_1 = f"{nested_run_dir}run 1" + run_2 = f"{nested_run_dir}run 2" + + mock_dispatcher = mock.create_autospec(uploader_lib._Dispatcher) + uploader._dispatcher = mock_dispatcher + mock_logdir_loader = mock.create_autospec(logdir_loader.LogdirLoader) + mock_logdir_loader.get_run_events.side_effect = [ + { + run_1: _apply_compat( + [_scalar_event("tag_1.1", 5.0), _scalar_event("tag_1.2", 5.0)] + ), + run_2: _apply_compat( + [_scalar_event("tag_2.1", 5.0), _scalar_event("tag_2.2", 5.0)] + ), + }, + ] + with mock.patch.object(uploader, "_logdir_loader", mock_logdir_loader): + uploader._upload_once() + + self.assertEqual(1, mock_logdir_loader.get_run_events.call_count) + self.assertEqual(1, mock_dispatcher.dispatch_requests.call_count) + run_to_events = mock_dispatcher.dispatch_requests.call_args[0][0] + self.assertIn(run_1, run_to_events) + self.assertIn(run_2, run_to_events) + @patch.object(metadata, "_experiment_tracker", autospec=True) @patch.object(experiment_resources, "Experiment", autospec=True) def test_upload_empty_logdir( From 10d9105ffdc95f625facb359c4b854b43aa3b999 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:33:32 -0700 Subject: [PATCH 17/17] chore(main): release 1.67.0 (#4384) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 23 +++++++++++++++++++ google/cloud/aiplatform/gapic_version.py | 2 +- .../schema/predict/instance/gapic_version.py | 2 +- .../predict/instance_v1/gapic_version.py | 2 +- .../v1/schema/predict/params/gapic_version.py | 2 +- .../schema/predict/params_v1/gapic_version.py | 2 +- .../predict/prediction/gapic_version.py | 2 +- .../predict/prediction_v1/gapic_version.py | 2 +- .../trainingjob/definition/gapic_version.py | 2 +- .../definition_v1/gapic_version.py | 2 +- .../schema/predict/instance/gapic_version.py | 2 +- .../predict/instance_v1beta1/gapic_version.py | 2 +- .../schema/predict/params/gapic_version.py | 2 +- .../predict/params_v1beta1/gapic_version.py | 2 +- .../predict/prediction/gapic_version.py | 2 +- .../prediction_v1beta1/gapic_version.py | 2 +- .../trainingjob/definition/gapic_version.py | 2 +- .../definition_v1beta1/gapic_version.py | 2 +- google/cloud/aiplatform/version.py | 2 +- google/cloud/aiplatform_v1/gapic_version.py | 2 +- .../cloud/aiplatform_v1beta1/gapic_version.py | 2 +- pypi/_vertex_ai_placeholder/version.py | 2 +- ...t_metadata_google.cloud.aiplatform.v1.json | 2 +- ...adata_google.cloud.aiplatform.v1beta1.json | 2 +- 25 files changed, 47 insertions(+), 24 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c800b7cc66..a65093e31a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.66.0" + ".": "1.67.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 2df0bf8a32..99b727ff20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## [1.67.0](https://github.com/googleapis/python-aiplatform/compare/v1.66.0...v1.67.0) (2024-09-17) + + +### Features + +* Add support for partial failures sink in import rag files. ([07e471e](https://github.com/googleapis/python-aiplatform/commit/07e471e0a069551f2c855e167e549fa92ac6af95)) +* Adding Feature Store Vector DB option for RAG corpuses to SDK ([cfc3421](https://github.com/googleapis/python-aiplatform/commit/cfc3421fe8a883d459b66ed8c9f39697ded23f20)) +* Adding Pinecone Vector DB option for RAG corpuses to SDK ([f78b953](https://github.com/googleapis/python-aiplatform/commit/f78b953f561b8697d07a530e89c7e727db1161ed)) +* Adding Vertex Vector Search Vector DB option for RAG corpuses to SDK ([f882657](https://github.com/googleapis/python-aiplatform/commit/f882657183e34c8e07baa4b8dc9f45ed8bca9db7)) +* Allow customizing pipeline caching options for model evaluation jobs. ([73490b2](https://github.com/googleapis/python-aiplatform/commit/73490b22a239cb1a3c31349f8db6cfbc5232e231)) +* GenAI - Switched the GA version of the `generative_models` classes to use the v1 service APIs instead of v1beta1 ([66d84af](https://github.com/googleapis/python-aiplatform/commit/66d84afdd5b20f70b3ff62f25cc32ac0b324d5d5)) + + +### Bug Fixes + +* GenAI - Fixed `GenerativeModel.compute_tokens` for v1 API ([0de2987](https://github.com/googleapis/python-aiplatform/commit/0de298786c43427cb1a20b91cbabd1ce921c16da)) +* Tensorboard - Fix error in tensorboard batch upload of nested dirs ([ab157c8](https://github.com/googleapis/python-aiplatform/commit/ab157c8ead718b2a1a1d13306c1256c1cb2561f1)) + + +### Documentation + +* Manually add summary overview page. ([0bc608a](https://github.com/googleapis/python-aiplatform/commit/0bc608a9c045007f12325231ed7f0069a40f469b)) + ## [1.66.0](https://github.com/googleapis/python-aiplatform/compare/v1.65.0...v1.66.0) (2024-09-11) diff --git a/google/cloud/aiplatform/gapic_version.py b/google/cloud/aiplatform/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/gapic_version.py +++ b/google/cloud/aiplatform/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1/schema/predict/instance/gapic_version.py b/google/cloud/aiplatform/v1/schema/predict/instance/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1/schema/predict/instance/gapic_version.py +++ b/google/cloud/aiplatform/v1/schema/predict/instance/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1/schema/predict/instance_v1/gapic_version.py b/google/cloud/aiplatform/v1/schema/predict/instance_v1/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1/schema/predict/instance_v1/gapic_version.py +++ b/google/cloud/aiplatform/v1/schema/predict/instance_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1/schema/predict/params/gapic_version.py b/google/cloud/aiplatform/v1/schema/predict/params/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1/schema/predict/params/gapic_version.py +++ b/google/cloud/aiplatform/v1/schema/predict/params/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1/schema/predict/params_v1/gapic_version.py b/google/cloud/aiplatform/v1/schema/predict/params_v1/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1/schema/predict/params_v1/gapic_version.py +++ b/google/cloud/aiplatform/v1/schema/predict/params_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1/schema/predict/prediction/gapic_version.py b/google/cloud/aiplatform/v1/schema/predict/prediction/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1/schema/predict/prediction/gapic_version.py +++ b/google/cloud/aiplatform/v1/schema/predict/prediction/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1/schema/predict/prediction_v1/gapic_version.py b/google/cloud/aiplatform/v1/schema/predict/prediction_v1/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1/schema/predict/prediction_v1/gapic_version.py +++ b/google/cloud/aiplatform/v1/schema/predict/prediction_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1/schema/trainingjob/definition/gapic_version.py b/google/cloud/aiplatform/v1/schema/trainingjob/definition/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1/schema/trainingjob/definition/gapic_version.py +++ b/google/cloud/aiplatform/v1/schema/trainingjob/definition/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1/schema/trainingjob/definition_v1/gapic_version.py b/google/cloud/aiplatform/v1/schema/trainingjob/definition_v1/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1/schema/trainingjob/definition_v1/gapic_version.py +++ b/google/cloud/aiplatform/v1/schema/trainingjob/definition_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1beta1/schema/predict/instance/gapic_version.py b/google/cloud/aiplatform/v1beta1/schema/predict/instance/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1beta1/schema/predict/instance/gapic_version.py +++ b/google/cloud/aiplatform/v1beta1/schema/predict/instance/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1beta1/schema/predict/instance_v1beta1/gapic_version.py b/google/cloud/aiplatform/v1beta1/schema/predict/instance_v1beta1/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1beta1/schema/predict/instance_v1beta1/gapic_version.py +++ b/google/cloud/aiplatform/v1beta1/schema/predict/instance_v1beta1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1beta1/schema/predict/params/gapic_version.py b/google/cloud/aiplatform/v1beta1/schema/predict/params/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1beta1/schema/predict/params/gapic_version.py +++ b/google/cloud/aiplatform/v1beta1/schema/predict/params/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1beta1/schema/predict/params_v1beta1/gapic_version.py b/google/cloud/aiplatform/v1beta1/schema/predict/params_v1beta1/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1beta1/schema/predict/params_v1beta1/gapic_version.py +++ b/google/cloud/aiplatform/v1beta1/schema/predict/params_v1beta1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1beta1/schema/predict/prediction/gapic_version.py b/google/cloud/aiplatform/v1beta1/schema/predict/prediction/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1beta1/schema/predict/prediction/gapic_version.py +++ b/google/cloud/aiplatform/v1beta1/schema/predict/prediction/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1beta1/schema/predict/prediction_v1beta1/gapic_version.py b/google/cloud/aiplatform/v1beta1/schema/predict/prediction_v1beta1/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1beta1/schema/predict/prediction_v1beta1/gapic_version.py +++ b/google/cloud/aiplatform/v1beta1/schema/predict/prediction_v1beta1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1beta1/schema/trainingjob/definition/gapic_version.py b/google/cloud/aiplatform/v1beta1/schema/trainingjob/definition/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1beta1/schema/trainingjob/definition/gapic_version.py +++ b/google/cloud/aiplatform/v1beta1/schema/trainingjob/definition/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/v1beta1/schema/trainingjob/definition_v1beta1/gapic_version.py b/google/cloud/aiplatform/v1beta1/schema/trainingjob/definition_v1beta1/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform/v1beta1/schema/trainingjob/definition_v1beta1/gapic_version.py +++ b/google/cloud/aiplatform/v1beta1/schema/trainingjob/definition_v1beta1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform/version.py b/google/cloud/aiplatform/version.py index 696fd29b9c..dc942bd1b6 100644 --- a/google/cloud/aiplatform/version.py +++ b/google/cloud/aiplatform/version.py @@ -15,4 +15,4 @@ # limitations under the License. # -__version__ = "1.66.0" +__version__ = "1.67.0" diff --git a/google/cloud/aiplatform_v1/gapic_version.py b/google/cloud/aiplatform_v1/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform_v1/gapic_version.py +++ b/google/cloud/aiplatform_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/google/cloud/aiplatform_v1beta1/gapic_version.py b/google/cloud/aiplatform_v1beta1/gapic_version.py index 6f0a1f23ad..92d1ea91e2 100644 --- a/google/cloud/aiplatform_v1beta1/gapic_version.py +++ b/google/cloud/aiplatform_v1beta1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.66.0" # {x-release-please-version} +__version__ = "1.67.0" # {x-release-please-version} diff --git a/pypi/_vertex_ai_placeholder/version.py b/pypi/_vertex_ai_placeholder/version.py index e251e811ab..ba663751ef 100644 --- a/pypi/_vertex_ai_placeholder/version.py +++ b/pypi/_vertex_ai_placeholder/version.py @@ -15,4 +15,4 @@ # limitations under the License. # -__version__ = "1.66.0" +__version__ = "1.67.0" diff --git a/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1.json b/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1.json index f5f6625974..63939852f0 100644 --- a/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1.json +++ b/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-aiplatform", - "version": "1.66.0" + "version": "1.67.0" }, "snippets": [ { diff --git a/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1beta1.json b/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1beta1.json index b8c672fa99..5247a838d9 100644 --- a/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1beta1.json +++ b/samples/generated_samples/snippet_metadata_google.cloud.aiplatform.v1beta1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-aiplatform", - "version": "1.66.0" + "version": "1.67.0" }, "snippets": [ {