diff --git a/.kokoro/continuous/docfx.cfg b/.kokoro/continuous/docfx.cfg index 2e14176135..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" + value: "docs docfx" } diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg index 503b5c0fae..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 docfx gemini_docs gemini_docfx" + value: "gemini_docs gemini_docfx" } 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/.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/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/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/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/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/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/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/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/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]. 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_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/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/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/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/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", 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/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.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 9431d69b09..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": [ { @@ -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/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 = [] 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/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], 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( 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" 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/tests/unit/vertex_rag/test_rag_constants.py b/tests/unit/vertex_rag/test_rag_constants.py index cd2a74e30c..e85e176006 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, @@ -28,6 +29,8 @@ JiraSource, JiraQuery, Weaviate, + VertexVectorSearch, + VertexFeatureStore, ) from google.cloud.aiplatform_v1beta1 import ( GoogleDriveSource, @@ -68,6 +71,21 @@ 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_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, display_name=TEST_CORPUS_DISPLAY_NAME, @@ -94,9 +112,46 @@ ), ), ) +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_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, + 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", ) +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 +164,24 @@ 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_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_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 fd243920b1..c20011c361 100644 --- a/tests/unit/vertex_rag/test_rag_data.py +++ b/tests/unit/vertex_rag/test_rag_data.py @@ -62,6 +62,55 @@ 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 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( + 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( @@ -216,6 +265,33 @@ 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("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( + 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/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: 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..74b03d51fc 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 = types_v1.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 = types_v1.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" diff --git a/vertexai/preview/rag/__init__.py b/vertexai/preview/rag/__init__.py index 56590fed0e..7c7737276b 100644 --- a/vertexai/preview/rag/__init__.py +++ b/vertexai/preview/rag/__init__.py @@ -40,11 +40,14 @@ EmbeddingModelConfig, JiraQuery, JiraSource, + Pinecone, RagCorpus, RagFile, RagResource, SlackChannel, SlackChannelsSource, + VertexFeatureStore, + VertexVectorSearch, Weaviate, ) @@ -53,13 +56,16 @@ "EmbeddingModelConfig", "JiraQuery", "JiraSource", + "Pinecone", "RagCorpus", "RagFile", "RagResource", "Retrieval", "SlackChannel", "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 0983037173..e332b0337b 100644 --- a/vertexai/preview/rag/rag_data.py +++ b/vertexai/preview/rag/rag_data.py @@ -45,9 +45,12 @@ from vertexai.preview.rag.utils.resources import ( EmbeddingModelConfig, JiraSource, + Pinecone, RagCorpus, RagFile, SlackChannelsSource, + VertexFeatureStore, + VertexVectorSearch, Weaviate, ) @@ -56,7 +59,9 @@ 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, VertexVectorSearch, Pinecone] + ] = None, ) -> RagCorpus: """Creates a new RagCorpus resource. @@ -291,6 +296,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. @@ -376,6 +382,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. """ @@ -392,6 +406,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: @@ -410,6 +425,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. @@ -495,6 +511,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. """ @@ -511,6 +535,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 640fd8c5f0..3c526723c1 100644 --- a/vertexai/preview/rag/utils/_gapic_utils.py +++ b/vertexai/preview/rag/utils/_gapic_utils.py @@ -38,10 +38,13 @@ ) from vertexai.preview.rag.utils.resources import ( EmbeddingModelConfig, + Pinecone, RagCorpus, RagFile, SlackChannelsSource, JiraSource, + VertexFeatureStore, + VertexVectorSearch, Weaviate, ) @@ -97,14 +100,28 @@ 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, 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, 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, + ) + 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, + ) + 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 @@ -236,6 +253,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( @@ -278,6 +296,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 ) @@ -390,7 +424,7 @@ def set_embedding_model_config( def set_vector_db( - vector_db: Weaviate, + vector_db: Union[Weaviate, VertexFeatureStore, VertexVectorSearch, Pinecone], rag_corpus: GapicRagCorpus, ) -> None: """Sets the vector db configuration for the rag corpus.""" @@ -410,5 +444,39 @@ 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, + ), + ) + 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 + + 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.") + raise TypeError( + "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 aad7bad35d..371ccc3e9e 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,50 @@ 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 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. + + 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). @@ -102,7 +146,9 @@ 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, VertexVectorSearch, Pinecone] + ] = None @dataclasses.dataclass 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.