anyscale 0.24.86__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anyscale/__init__.py +181 -0
- anyscale/_private/anyscale_client/README.md +16 -0
- anyscale/_private/anyscale_client/__init__.py +8 -0
- anyscale/_private/anyscale_client/anyscale_client.py +1847 -0
- anyscale/_private/anyscale_client/common.py +593 -0
- anyscale/_private/anyscale_client/fake_anyscale_client.py +1080 -0
- anyscale/_private/docgen/README.md +15 -0
- anyscale/_private/docgen/__main__.py +700 -0
- anyscale/_private/docgen/api.md +1106 -0
- anyscale/_private/docgen/generator.py +559 -0
- anyscale/_private/docgen/generator_legacy.py +104 -0
- anyscale/_private/docgen/models.md +2261 -0
- anyscale/_private/models/__init__.py +2 -0
- anyscale/_private/models/image_uri.py +116 -0
- anyscale/_private/models/model_base.py +251 -0
- anyscale/_private/sdk/__init__.py +102 -0
- anyscale/_private/sdk/base_sdk.py +35 -0
- anyscale/_private/sdk/timer.py +46 -0
- anyscale/_private/utils/__init__.py +0 -0
- anyscale/_private/utils/progress_util.py +85 -0
- anyscale/_private/workload/__init__.py +2 -0
- anyscale/_private/workload/workload_config.py +195 -0
- anyscale/_private/workload/workload_sdk.py +324 -0
- anyscale/aggregated_instance_usage/__init__.py +36 -0
- anyscale/aggregated_instance_usage/_private/aggregated_instance_usage_sdk.py +30 -0
- anyscale/aggregated_instance_usage/commands.py +42 -0
- anyscale/aggregated_instance_usage/models.py +85 -0
- anyscale/anyscale-cloud-setup-gcp-oa.yaml +88 -0
- anyscale/anyscale-cloud-setup-gcp.yaml +113 -0
- anyscale/anyscale-cloud-setup-oa.yaml +121 -0
- anyscale/anyscale-cloud-setup.yaml +327 -0
- anyscale/anyscale_pydantic/HISTORY.md +1254 -0
- anyscale/anyscale_pydantic/LICENSE +21 -0
- anyscale/anyscale_pydantic/PKG-INFO +1351 -0
- anyscale/anyscale_pydantic/README.md +7 -0
- anyscale/anyscale_pydantic/__init__.py +131 -0
- anyscale/anyscale_pydantic/_hypothesis_plugin.py +391 -0
- anyscale/anyscale_pydantic/annotated_types.py +72 -0
- anyscale/anyscale_pydantic/class_validators.py +361 -0
- anyscale/anyscale_pydantic/color.py +494 -0
- anyscale/anyscale_pydantic/config.py +191 -0
- anyscale/anyscale_pydantic/dataclasses.py +478 -0
- anyscale/anyscale_pydantic/datetime_parse.py +248 -0
- anyscale/anyscale_pydantic/decorator.py +264 -0
- anyscale/anyscale_pydantic/env_settings.py +350 -0
- anyscale/anyscale_pydantic/error_wrappers.py +162 -0
- anyscale/anyscale_pydantic/errors.py +646 -0
- anyscale/anyscale_pydantic/fields.py +1256 -0
- anyscale/anyscale_pydantic/generics.py +400 -0
- anyscale/anyscale_pydantic/json.py +112 -0
- anyscale/anyscale_pydantic/main.py +1109 -0
- anyscale/anyscale_pydantic/mypy.py +943 -0
- anyscale/anyscale_pydantic/networks.py +739 -0
- anyscale/anyscale_pydantic/parse.py +66 -0
- anyscale/anyscale_pydantic/py.typed +0 -0
- anyscale/anyscale_pydantic/schema.py +1164 -0
- anyscale/anyscale_pydantic/tools.py +92 -0
- anyscale/anyscale_pydantic/types.py +1206 -0
- anyscale/anyscale_pydantic/typing.py +603 -0
- anyscale/anyscale_pydantic/utils.py +803 -0
- anyscale/anyscale_pydantic/validators.py +765 -0
- anyscale/anyscale_pydantic/version.py +38 -0
- anyscale/anyscale_schema.json +9 -0
- anyscale/api.py +215 -0
- anyscale/api_utils/README.md +2 -0
- anyscale/api_utils/__init__.py +0 -0
- anyscale/api_utils/common_utils.py +81 -0
- anyscale/api_utils/exceptions/__init__.py +0 -0
- anyscale/api_utils/exceptions/job_errors.py +2 -0
- anyscale/api_utils/job_logs_util.py +116 -0
- anyscale/api_utils/job_util.py +22 -0
- anyscale/api_utils/logs_util.py +61 -0
- anyscale/authenticate.py +298 -0
- anyscale/aws_iam_policies.py +465 -0
- anyscale/background/__init__.py +0 -0
- anyscale/background/job_runner.py +64 -0
- anyscale/cli_logger.py +378 -0
- anyscale/client/.gitignore +66 -0
- anyscale/client/.openapi-generator/VERSION +1 -0
- anyscale/client/.openapi-generator-ignore +23 -0
- anyscale/client/README.md +1070 -0
- anyscale/client/git_push.sh +58 -0
- anyscale/client/openapi_client/__init__.py +667 -0
- anyscale/client/openapi_client/api/__init__.py +6 -0
- anyscale/client/openapi_client/api/default_api.py +40922 -0
- anyscale/client/openapi_client/api_client.py +647 -0
- anyscale/client/openapi_client/configuration.py +373 -0
- anyscale/client/openapi_client/exceptions.py +120 -0
- anyscale/client/openapi_client/models/__init__.py +652 -0
- anyscale/client/openapi_client/models/access_config.py +122 -0
- anyscale/client/openapi_client/models/aggregated_instance_usage_with_cost_model.py +733 -0
- anyscale/client/openapi_client/models/aggregatedinstanceusagewithcostmodel_list_response.py +147 -0
- anyscale/client/openapi_client/models/aica_endpoint.py +527 -0
- anyscale/client/openapi_client/models/aica_endpoint_event.py +433 -0
- anyscale/client/openapi_client/models/aica_endpoint_event_level.py +103 -0
- anyscale/client/openapi_client/models/aica_endpoint_event_type.py +120 -0
- anyscale/client/openapi_client/models/aica_endpoint_scope.py +102 -0
- anyscale/client/openapi_client/models/aica_model.py +398 -0
- anyscale/client/openapi_client/models/aica_model_accelerator_map.py +123 -0
- anyscale/client/openapi_client/models/aica_model_configuration.py +209 -0
- anyscale/client/openapi_client/models/aica_observability_urls.py +178 -0
- anyscale/client/openapi_client/models/aicaendpoint_list_response.py +147 -0
- anyscale/client/openapi_client/models/aicaendpoint_response.py +121 -0
- anyscale/client/openapi_client/models/aicaendpointevent_list_response.py +147 -0
- anyscale/client/openapi_client/models/aicamodel_list_response.py +147 -0
- anyscale/client/openapi_client/models/aicamodel_response.py +121 -0
- anyscale/client/openapi_client/models/aioa_cloud_waitlist_record.py +254 -0
- anyscale/client/openapi_client/models/aioacloudwaitlistrecord_response.py +121 -0
- anyscale/client/openapi_client/models/alert_type.py +103 -0
- anyscale/client/openapi_client/models/anyscale_aws_account.py +121 -0
- anyscale/client/openapi_client/models/anyscale_service_account.py +256 -0
- anyscale/client/openapi_client/models/anyscale_version_response.py +121 -0
- anyscale/client/openapi_client/models/anyscaleawsaccount_response.py +121 -0
- anyscale/client/openapi_client/models/anyscaled_credential_response.py +121 -0
- anyscale/client/openapi_client/models/anyscaledcredentialresponse_response.py +121 -0
- anyscale/client/openapi_client/models/anyscaleserviceaccount_response.py +121 -0
- anyscale/client/openapi_client/models/anyscaleversionresponse_response.py +121 -0
- anyscale/client/openapi_client/models/api_key_parameters.py +147 -0
- anyscale/client/openapi_client/models/app_config.py +436 -0
- anyscale/client/openapi_client/models/app_config_config_schema.py +235 -0
- anyscale/client/openapi_client/models/appconfig_list_response.py +147 -0
- anyscale/client/openapi_client/models/appconfig_response.py +121 -0
- anyscale/client/openapi_client/models/application_type.py +99 -0
- anyscale/client/openapi_client/models/applied_snapshot.py +175 -0
- anyscale/client/openapi_client/models/apply_production_service_v2_model.py +490 -0
- anyscale/client/openapi_client/models/archive_status.py +101 -0
- anyscale/client/openapi_client/models/archived_logs_info.py +164 -0
- anyscale/client/openapi_client/models/archivedlogsinfo_response.py +121 -0
- anyscale/client/openapi_client/models/attach_machine_pool_to_cloud_request.py +152 -0
- anyscale/client/openapi_client/models/attachmachinepooltocloudresponse_response.py +121 -0
- anyscale/client/openapi_client/models/aviary_model_config_v2.py +358 -0
- anyscale/client/openapi_client/models/aws_credentials.py +181 -0
- anyscale/client/openapi_client/models/aws_memory_db_cluster_config.py +148 -0
- anyscale/client/openapi_client/models/aws_region_and_zones.py +123 -0
- anyscale/client/openapi_client/models/aws_region_info.py +152 -0
- anyscale/client/openapi_client/models/awsregionandzones_response.py +121 -0
- anyscale/client/openapi_client/models/bank_account_information.py +239 -0
- anyscale/client/openapi_client/models/base_job_status.py +105 -0
- anyscale/client/openapi_client/models/baseimagesenum.py +2130 -0
- anyscale/client/openapi_client/models/batch_response_batched_result_organization_invitation_base.py +121 -0
- anyscale/client/openapi_client/models/batched_result_organization_invitation_base.py +173 -0
- anyscale/client/openapi_client/models/billing_information.py +181 -0
- anyscale/client/openapi_client/models/billing_version_code.py +100 -0
- anyscale/client/openapi_client/models/body_aws_marketplace_registration_api_v2_organization_billing_aws_marketplace_registration_post.py +121 -0
- anyscale/client/openapi_client/models/buffer_registration.py +285 -0
- anyscale/client/openapi_client/models/build.py +607 -0
- anyscale/client/openapi_client/models/build_log_response.py +123 -0
- anyscale/client/openapi_client/models/build_registration.py +285 -0
- anyscale/client/openapi_client/models/build_response.py +121 -0
- anyscale/client/openapi_client/models/build_status.py +104 -0
- anyscale/client/openapi_client/models/buildlogresponse_response.py +121 -0
- anyscale/client/openapi_client/models/card.py +181 -0
- anyscale/client/openapi_client/models/card_id.py +108 -0
- anyscale/client/openapi_client/models/card_list_response.py +147 -0
- anyscale/client/openapi_client/models/change_password_params.py +148 -0
- anyscale/client/openapi_client/models/cleanup_leaked_grafana_dashboard_response.py +208 -0
- anyscale/client/openapi_client/models/cleanupleakedgrafanadashboardresponse_response.py +121 -0
- anyscale/client/openapi_client/models/clone_experimental_workspace.py +151 -0
- anyscale/client/openapi_client/models/cloud.py +802 -0
- anyscale/client/openapi_client/models/cloud_analytics_event.py +351 -0
- anyscale/client/openapi_client/models/cloud_analytics_event_cloud_provider_error.py +152 -0
- anyscale/client/openapi_client/models/cloud_analytics_event_cloud_resource.py +117 -0
- anyscale/client/openapi_client/models/cloud_analytics_event_command_name.py +103 -0
- anyscale/client/openapi_client/models/cloud_analytics_event_error.py +150 -0
- anyscale/client/openapi_client/models/cloud_analytics_event_name.py +109 -0
- anyscale/client/openapi_client/models/cloud_collaborator.py +175 -0
- anyscale/client/openapi_client/models/cloud_collaborator_value.py +177 -0
- anyscale/client/openapi_client/models/cloud_collaborators_query.py +122 -0
- anyscale/client/openapi_client/models/cloud_config.py +206 -0
- anyscale/client/openapi_client/models/cloud_data_bucket_access_mode.py +100 -0
- anyscale/client/openapi_client/models/cloud_data_bucket_file_type.py +102 -0
- anyscale/client/openapi_client/models/cloud_data_bucket_presigned_upload_info.py +268 -0
- anyscale/client/openapi_client/models/cloud_data_bucket_presigned_upload_request.py +152 -0
- anyscale/client/openapi_client/models/cloud_data_bucket_presigned_upload_scheme.py +100 -0
- anyscale/client/openapi_client/models/cloud_data_bucket_presigned_url_request.py +209 -0
- anyscale/client/openapi_client/models/cloud_data_bucket_presigned_url_response.py +296 -0
- anyscale/client/openapi_client/models/cloud_data_bucket_presigned_url_scheme.py +100 -0
- anyscale/client/openapi_client/models/cloud_data_bucket_request_scope.py +100 -0
- anyscale/client/openapi_client/models/cloud_hosting_type.py +100 -0
- anyscale/client/openapi_client/models/cloud_list_response.py +147 -0
- anyscale/client/openapi_client/models/cloud_name_options.py +121 -0
- anyscale/client/openapi_client/models/cloud_overview_dashboard.py +175 -0
- anyscale/client/openapi_client/models/cloud_project_collaborator.py +175 -0
- anyscale/client/openapi_client/models/cloud_project_collaborator_value.py +121 -0
- anyscale/client/openapi_client/models/cloud_provider.py +102 -0
- anyscale/client/openapi_client/models/cloud_providers.py +103 -0
- anyscale/client/openapi_client/models/cloud_region_and_zones.py +123 -0
- anyscale/client/openapi_client/models/cloud_region_info.py +152 -0
- anyscale/client/openapi_client/models/cloud_resource.py +740 -0
- anyscale/client/openapi_client/models/cloud_resource_gcp.py +691 -0
- anyscale/client/openapi_client/models/cloud_response.py +121 -0
- anyscale/client/openapi_client/models/cloud_state.py +104 -0
- anyscale/client/openapi_client/models/cloud_status.py +100 -0
- anyscale/client/openapi_client/models/cloud_type.py +100 -0
- anyscale/client/openapi_client/models/cloud_types.py +100 -0
- anyscale/client/openapi_client/models/cloud_version.py +100 -0
- anyscale/client/openapi_client/models/cloud_waitlist_status.py +102 -0
- anyscale/client/openapi_client/models/cloud_with_cloud_resource.py +830 -0
- anyscale/client/openapi_client/models/cloud_with_cloud_resource_gcp.py +830 -0
- anyscale/client/openapi_client/models/cloudcollaborator_list_response.py +147 -0
- anyscale/client/openapi_client/models/clouddatabucketpresigneduploadinfo_response.py +121 -0
- anyscale/client/openapi_client/models/clouddatabucketpresignedurlresponse_response.py +121 -0
- anyscale/client/openapi_client/models/cloudoverviewdashboard_response.py +121 -0
- anyscale/client/openapi_client/models/cloudregionandzones_response.py +121 -0
- anyscale/client/openapi_client/models/cloudresource_response.py +121 -0
- anyscale/client/openapi_client/models/cloudresourcegcp_response.py +121 -0
- anyscale/client/openapi_client/models/cloudwithcloudresource_response.py +121 -0
- anyscale/client/openapi_client/models/cloudwithcloudresourcegcp_response.py +121 -0
- anyscale/client/openapi_client/models/cluster_auth_response.py +148 -0
- anyscale/client/openapi_client/models/cluster_config.py +178 -0
- anyscale/client/openapi_client/models/cluster_config_with_session_idle_timeout.py +204 -0
- anyscale/client/openapi_client/models/cluster_environments_query.py +290 -0
- anyscale/client/openapi_client/models/cluster_event.py +174 -0
- anyscale/client/openapi_client/models/cluster_events_output.py +175 -0
- anyscale/client/openapi_client/models/cluster_features.py +152 -0
- anyscale/client/openapi_client/models/cluster_management_stack_versions.py +100 -0
- anyscale/client/openapi_client/models/cluster_startup.py +208 -0
- anyscale/client/openapi_client/models/cluster_status.py +104 -0
- anyscale/client/openapi_client/models/cluster_status_details.py +100 -0
- anyscale/client/openapi_client/models/clusterauthresponse_response.py +121 -0
- anyscale/client/openapi_client/models/clusterconfig_response.py +121 -0
- anyscale/client/openapi_client/models/clusterconfigwithsessionidletimeout_response.py +121 -0
- anyscale/client/openapi_client/models/clustereventsoutput_response.py +121 -0
- anyscale/client/openapi_client/models/clusterfeatures_response.py +121 -0
- anyscale/client/openapi_client/models/company_size.py +103 -0
- anyscale/client/openapi_client/models/compute_node_type.py +292 -0
- anyscale/client/openapi_client/models/compute_stack.py +100 -0
- anyscale/client/openapi_client/models/compute_template.py +415 -0
- anyscale/client/openapi_client/models/compute_template_config.py +461 -0
- anyscale/client/openapi_client/models/compute_template_query.py +316 -0
- anyscale/client/openapi_client/models/computetemplate_response.py +121 -0
- anyscale/client/openapi_client/models/computetemplateconfig_response.py +121 -0
- anyscale/client/openapi_client/models/create_aica_endpoint.py +210 -0
- anyscale/client/openapi_client/models/create_aioa_cloud_waitlist.py +173 -0
- anyscale/client/openapi_client/models/create_analytics_event.py +122 -0
- anyscale/client/openapi_client/models/create_app_config.py +235 -0
- anyscale/client/openapi_client/models/create_app_config_configuration_schema.py +235 -0
- anyscale/client/openapi_client/models/create_billing_version.py +181 -0
- anyscale/client/openapi_client/models/create_bug_report_response.py +152 -0
- anyscale/client/openapi_client/models/create_build.py +263 -0
- anyscale/client/openapi_client/models/create_byod_app_config.py +180 -0
- anyscale/client/openapi_client/models/create_byod_app_config_configuration_schema.py +206 -0
- anyscale/client/openapi_client/models/create_byod_build.py +152 -0
- anyscale/client/openapi_client/models/create_cloud_collaborator.py +148 -0
- anyscale/client/openapi_client/models/create_cloud_resource.py +682 -0
- anyscale/client/openapi_client/models/create_cloud_resource_gcp.py +633 -0
- anyscale/client/openapi_client/models/create_cloud_with_cloud_resource.py +546 -0
- anyscale/client/openapi_client/models/create_cluster_compute_config.py +463 -0
- anyscale/client/openapi_client/models/create_compute_template.py +229 -0
- anyscale/client/openapi_client/models/create_compute_template_config.py +464 -0
- anyscale/client/openapi_client/models/create_dataset.py +200 -0
- anyscale/client/openapi_client/models/create_experimental_workspace.py +435 -0
- anyscale/client/openapi_client/models/create_experimental_workspace_from_job.py +123 -0
- anyscale/client/openapi_client/models/create_fine_tuning_hyperparameters.py +156 -0
- anyscale/client/openapi_client/models/create_fine_tuning_job_product_request.py +353 -0
- anyscale/client/openapi_client/models/create_instance_usage_budget.py +253 -0
- anyscale/client/openapi_client/models/create_internal_production_job.py +262 -0
- anyscale/client/openapi_client/models/create_job_queue_config.py +206 -0
- anyscale/client/openapi_client/models/create_job_queue_requests.py +323 -0
- anyscale/client/openapi_client/models/create_machine_pool_request.py +151 -0
- anyscale/client/openapi_client/models/create_machine_pool_response.py +123 -0
- anyscale/client/openapi_client/models/create_machine_request.py +151 -0
- anyscale/client/openapi_client/models/create_machine_response.py +123 -0
- anyscale/client/openapi_client/models/create_metronome_webhook_notification.py +175 -0
- anyscale/client/openapi_client/models/create_notification_channel_record.py +146 -0
- anyscale/client/openapi_client/models/create_organization_configuration.py +199 -0
- anyscale/client/openapi_client/models/create_organization_invitation.py +121 -0
- anyscale/client/openapi_client/models/create_otp_return_api_model.py +148 -0
- anyscale/client/openapi_client/models/create_production_job_config.py +347 -0
- anyscale/client/openapi_client/models/create_resource_quota.py +293 -0
- anyscale/client/openapi_client/models/create_schedule.py +263 -0
- anyscale/client/openapi_client/models/create_session_from_snapshot_options.py +565 -0
- anyscale/client/openapi_client/models/create_session_in_db.py +434 -0
- anyscale/client/openapi_client/models/create_session_response.py +174 -0
- anyscale/client/openapi_client/models/create_user.py +439 -0
- anyscale/client/openapi_client/models/create_user_project_collaborator.py +148 -0
- anyscale/client/openapi_client/models/create_user_project_collaborator_value.py +121 -0
- anyscale/client/openapi_client/models/create_workspace_from_template.py +263 -0
- anyscale/client/openapi_client/models/createbugreportresponse_response.py +121 -0
- anyscale/client/openapi_client/models/createcomputetemplateconfig_response.py +121 -0
- anyscale/client/openapi_client/models/createmachinepoolresponse_response.py +121 -0
- anyscale/client/openapi_client/models/createmachineresponse_response.py +121 -0
- anyscale/client/openapi_client/models/createotpreturnapimodel_response.py +121 -0
- anyscale/client/openapi_client/models/createsessionresponse_response.py +121 -0
- anyscale/client/openapi_client/models/credit_card_information.py +268 -0
- anyscale/client/openapi_client/models/customer_alert_status.py +101 -0
- anyscale/client/openapi_client/models/customer_billing_type.py +101 -0
- anyscale/client/openapi_client/models/dataplane_services.py +102 -0
- anyscale/client/openapi_client/models/dataset.py +416 -0
- anyscale/client/openapi_client/models/dataset_list_response.py +150 -0
- anyscale/client/openapi_client/models/dataset_response.py +121 -0
- anyscale/client/openapi_client/models/dataset_upload.py +148 -0
- anyscale/client/openapi_client/models/datasetupload_response.py +121 -0
- anyscale/client/openapi_client/models/decorated_application_template.py +493 -0
- anyscale/client/openapi_client/models/decorated_build.py +664 -0
- anyscale/client/openapi_client/models/decorated_compute_template.py +446 -0
- anyscale/client/openapi_client/models/decorated_compute_template_config.py +490 -0
- anyscale/client/openapi_client/models/decorated_interactive_session.py +793 -0
- anyscale/client/openapi_client/models/decorated_job.py +793 -0
- anyscale/client/openapi_client/models/decorated_job_queue.py +639 -0
- anyscale/client/openapi_client/models/decorated_job_submission.py +575 -0
- anyscale/client/openapi_client/models/decorated_list_service_api_model.py +670 -0
- anyscale/client/openapi_client/models/decorated_production_job.py +805 -0
- anyscale/client/openapi_client/models/decorated_production_job_state_transition.py +319 -0
- anyscale/client/openapi_client/models/decorated_production_service_v2_api_model.py +641 -0
- anyscale/client/openapi_client/models/decorated_production_service_v2_version_api_model.py +437 -0
- anyscale/client/openapi_client/models/decorated_runtime_env.py +488 -0
- anyscale/client/openapi_client/models/decorated_schedule.py +552 -0
- anyscale/client/openapi_client/models/decorated_serve_deployment.py +711 -0
- anyscale/client/openapi_client/models/decorated_service_event_api_model.py +513 -0
- anyscale/client/openapi_client/models/decorated_session.py +1789 -0
- anyscale/client/openapi_client/models/decorated_support_request.py +283 -0
- anyscale/client/openapi_client/models/decorated_unified_job.py +466 -0
- anyscale/client/openapi_client/models/decoratedapplicationtemplate_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedapplicationtemplate_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedbuild_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedbuild_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedcomputetemplate_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedcomputetemplate_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedinteractivesession_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedinteractivesession_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedjob_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedjob_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedjobqueue_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedjobqueue_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedjobsubmission_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedjobsubmission_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedlistserviceapimodel_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedproductionjob_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedproductionjob_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedproductionjobstatetransition_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedproductionservicev2_apimodel_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedproductionservicev2_versionapimodel_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedruntimeenv_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedruntimeenv_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedschedule_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedschedule_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedservedeployment_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedservedeployment_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedserviceeventapimodel_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedsession_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedsession_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedsupportrequest_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedsupportrequest_response.py +121 -0
- anyscale/client/openapi_client/models/decoratedunifiedjob_list_response.py +147 -0
- anyscale/client/openapi_client/models/decoratedunifiedjob_response.py +121 -0
- anyscale/client/openapi_client/models/delete_machine_pool_request.py +123 -0
- anyscale/client/openapi_client/models/delete_machine_request.py +206 -0
- anyscale/client/openapi_client/models/deleted_platform_fine_tuned_model.py +148 -0
- anyscale/client/openapi_client/models/deletedplatformfinetunedmodel_response.py +121 -0
- anyscale/client/openapi_client/models/deletemachinepoolresponse_response.py +121 -0
- anyscale/client/openapi_client/models/detach_machine_pool_from_cloud_request.py +152 -0
- anyscale/client/openapi_client/models/detachmachinepoolfromcloudresponse_response.py +121 -0
- anyscale/client/openapi_client/models/dismissal_type.py +100 -0
- anyscale/client/openapi_client/models/editable_cloud_resource.py +206 -0
- anyscale/client/openapi_client/models/editable_cloud_resource_gcp.py +178 -0
- anyscale/client/openapi_client/models/error.py +174 -0
- anyscale/client/openapi_client/models/event_level.py +104 -0
- anyscale/client/openapi_client/models/execute_command_response.py +175 -0
- anyscale/client/openapi_client/models/execute_interactive_command_options.py +147 -0
- anyscale/client/openapi_client/models/execute_shell_command_options.py +121 -0
- anyscale/client/openapi_client/models/executecommandresponse_response.py +121 -0
- anyscale/client/openapi_client/models/experimental_workspace.py +748 -0
- anyscale/client/openapi_client/models/experimental_workspaces_sort_field.py +100 -0
- anyscale/client/openapi_client/models/experimentalworkspace_list_response.py +147 -0
- anyscale/client/openapi_client/models/experimentalworkspace_response.py +121 -0
- anyscale/client/openapi_client/models/external_service_status.py +147 -0
- anyscale/client/openapi_client/models/external_service_status_response.py +250 -0
- anyscale/client/openapi_client/models/external_terminal_command.py +280 -0
- anyscale/client/openapi_client/models/externalservicestatusresponse_response.py +121 -0
- anyscale/client/openapi_client/models/feature_compatibility.py +152 -0
- anyscale/client/openapi_client/models/feature_flag_response.py +121 -0
- anyscale/client/openapi_client/models/featureflagresponse_response.py +121 -0
- anyscale/client/openapi_client/models/fine_tune_type.py +100 -0
- anyscale/client/openapi_client/models/fine_tuned_model.py +412 -0
- anyscale/client/openapi_client/models/fine_tuning_job_status.py +103 -0
- anyscale/client/openapi_client/models/finetunedmodel_list_response.py +147 -0
- anyscale/client/openapi_client/models/finetunedmodel_response.py +121 -0
- anyscale/client/openapi_client/models/finish_ft_job_request.py +204 -0
- anyscale/client/openapi_client/models/finish_ft_job_request_v2.py +183 -0
- anyscale/client/openapi_client/models/gcp_file_store_config.py +175 -0
- anyscale/client/openapi_client/models/gcp_memorystore_instance_config.py +148 -0
- anyscale/client/openapi_client/models/grafana_dashboard.py +201 -0
- anyscale/client/openapi_client/models/grpc_protocol_config.py +178 -0
- anyscale/client/openapi_client/models/ha_job_error_types.py +103 -0
- anyscale/client/openapi_client/models/ha_job_event_level.py +101 -0
- anyscale/client/openapi_client/models/ha_job_event_origin.py +100 -0
- anyscale/client/openapi_client/models/ha_job_goal_states.py +102 -0
- anyscale/client/openapi_client/models/ha_job_states.py +109 -0
- anyscale/client/openapi_client/models/ha_job_type.py +100 -0
- anyscale/client/openapi_client/models/ha_jobs_sort_field.py +105 -0
- anyscale/client/openapi_client/models/head_ip.py +121 -0
- anyscale/client/openapi_client/models/headip_response.py +121 -0
- anyscale/client/openapi_client/models/http_protocol_config.py +150 -0
- anyscale/client/openapi_client/models/http_validation_error.py +120 -0
- anyscale/client/openapi_client/models/idle_termination_status.py +104 -0
- anyscale/client/openapi_client/models/import_aica_model.py +241 -0
- anyscale/client/openapi_client/models/instance_usage_budget.py +572 -0
- anyscale/client/openapi_client/models/instance_usage_budget_evaluation_period.py +100 -0
- anyscale/client/openapi_client/models/instanceusagebudget_list_response.py +147 -0
- anyscale/client/openapi_client/models/instanceusagebudget_response.py +121 -0
- anyscale/client/openapi_client/models/integration_details.py +120 -0
- anyscale/client/openapi_client/models/interactive_session_logs.py +152 -0
- anyscale/client/openapi_client/models/interactivesessionlogs_response.py +121 -0
- anyscale/client/openapi_client/models/internal_production_job.py +663 -0
- anyscale/client/openapi_client/models/internalproductionjob_response.py +121 -0
- anyscale/client/openapi_client/models/invoice.py +413 -0
- anyscale/client/openapi_client/models/invoice_list_response.py +147 -0
- anyscale/client/openapi_client/models/invoice_status.py +102 -0
- anyscale/client/openapi_client/models/invoices_query.py +150 -0
- anyscale/client/openapi_client/models/job_access.py +102 -0
- anyscale/client/openapi_client/models/job_queue.py +467 -0
- anyscale/client/openapi_client/models/job_queue_config.py +122 -0
- anyscale/client/openapi_client/models/job_queue_execution_mode.py +101 -0
- anyscale/client/openapi_client/models/job_queue_spec.py +263 -0
- anyscale/client/openapi_client/models/job_queue_state.py +100 -0
- anyscale/client/openapi_client/models/job_queues_query.py +262 -0
- anyscale/client/openapi_client/models/job_run_type.py +101 -0
- anyscale/client/openapi_client/models/job_state_log_level_types.py +100 -0
- anyscale/client/openapi_client/models/job_status.py +105 -0
- anyscale/client/openapi_client/models/job_submissions_sort_field.py +101 -0
- anyscale/client/openapi_client/models/jobqueue_response.py +121 -0
- anyscale/client/openapi_client/models/jobs_logs.py +152 -0
- anyscale/client/openapi_client/models/jobs_logs_query_info.py +181 -0
- anyscale/client/openapi_client/models/jobs_sort_field.py +104 -0
- anyscale/client/openapi_client/models/jobslogs_response.py +121 -0
- anyscale/client/openapi_client/models/jobslogsqueryinfo_response.py +121 -0
- anyscale/client/openapi_client/models/json_patch_operation.py +200 -0
- anyscale/client/openapi_client/models/kubernetes_manager_registration_request.py +123 -0
- anyscale/client/openapi_client/models/kubernetes_manager_registration_response.py +123 -0
- anyscale/client/openapi_client/models/kubernetesmanagerregistrationresponse_response.py +121 -0
- anyscale/client/openapi_client/models/lb_resource.py +123 -0
- anyscale/client/openapi_client/models/lbresource_response.py +121 -0
- anyscale/client/openapi_client/models/list_machine_pools_response.py +123 -0
- anyscale/client/openapi_client/models/list_machines_response.py +121 -0
- anyscale/client/openapi_client/models/list_resource_quotas_query.py +234 -0
- anyscale/client/openapi_client/models/list_response_metadata.py +146 -0
- anyscale/client/openapi_client/models/listmachinepoolsresponse_response.py +121 -0
- anyscale/client/openapi_client/models/listmachinesresponse_response.py +121 -0
- anyscale/client/openapi_client/models/log_detail.py +187 -0
- anyscale/client/openapi_client/models/log_details.py +151 -0
- anyscale/client/openapi_client/models/log_download_config.py +206 -0
- anyscale/client/openapi_client/models/log_download_request.py +150 -0
- anyscale/client/openapi_client/models/log_download_result.py +207 -0
- anyscale/client/openapi_client/models/log_file_chunk.py +439 -0
- anyscale/client/openapi_client/models/log_filter.py +430 -0
- anyscale/client/openapi_client/models/log_item.py +181 -0
- anyscale/client/openapi_client/models/log_item_batch.py +151 -0
- anyscale/client/openapi_client/models/log_level_types.py +100 -0
- anyscale/client/openapi_client/models/log_stream.py +151 -0
- anyscale/client/openapi_client/models/logdetails_response.py +121 -0
- anyscale/client/openapi_client/models/logdownloadresult_response.py +121 -0
- anyscale/client/openapi_client/models/login_user_params.py +205 -0
- anyscale/client/openapi_client/models/logitembatch_response.py +121 -0
- anyscale/client/openapi_client/models/logs_output.py +202 -0
- anyscale/client/openapi_client/models/logsoutput_response.py +121 -0
- anyscale/client/openapi_client/models/logstream_response.py +121 -0
- anyscale/client/openapi_client/models/long_running_workload.py +256 -0
- anyscale/client/openapi_client/models/longrunningworkload_list_response.py +147 -0
- anyscale/client/openapi_client/models/machine_allocation_state.py +100 -0
- anyscale/client/openapi_client/models/machine_connection_state.py +100 -0
- anyscale/client/openapi_client/models/machine_info.py +466 -0
- anyscale/client/openapi_client/models/machine_pool.py +266 -0
- anyscale/client/openapi_client/models/metronome_customer_info_model.py +148 -0
- anyscale/client/openapi_client/models/metronome_dashboard_type.py +101 -0
- anyscale/client/openapi_client/models/metronomecustomerinfomodel_list_response.py +147 -0
- anyscale/client/openapi_client/models/metronomecustomerinfomodel_response.py +121 -0
- anyscale/client/openapi_client/models/mini_build.py +267 -0
- anyscale/client/openapi_client/models/mini_cloud.py +321 -0
- anyscale/client/openapi_client/models/mini_cluster.py +148 -0
- anyscale/client/openapi_client/models/mini_compute_template.py +228 -0
- anyscale/client/openapi_client/models/mini_compute_template_config.py +121 -0
- anyscale/client/openapi_client/models/mini_job_run.py +599 -0
- anyscale/client/openapi_client/models/mini_namespace.py +148 -0
- anyscale/client/openapi_client/models/mini_organization.py +148 -0
- anyscale/client/openapi_client/models/mini_production_job.py +202 -0
- anyscale/client/openapi_client/models/mini_project.py +205 -0
- anyscale/client/openapi_client/models/mini_runtime_environment.py +147 -0
- anyscale/client/openapi_client/models/mini_schedule.py +180 -0
- anyscale/client/openapi_client/models/mini_user.py +266 -0
- anyscale/client/openapi_client/models/minibuild_list_response.py +147 -0
- anyscale/client/openapi_client/models/minicomputetemplate_list_response.py +147 -0
- anyscale/client/openapi_client/models/miniproject_list_response.py +147 -0
- anyscale/client/openapi_client/models/monitor_logs_extension.py +100 -0
- anyscale/client/openapi_client/models/named_entity.py +148 -0
- anyscale/client/openapi_client/models/nfs_mount_target.py +151 -0
- anyscale/client/openapi_client/models/node_registration_aws.py +152 -0
- anyscale/client/openapi_client/models/node_registration_gcp.py +123 -0
- anyscale/client/openapi_client/models/node_registration_k8_s.py +178 -0
- anyscale/client/openapi_client/models/node_registration_provisioned.py +150 -0
- anyscale/client/openapi_client/models/node_registration_v2.py +279 -0
- anyscale/client/openapi_client/models/node_type.py +100 -0
- anyscale/client/openapi_client/models/notification_channel_email_config.py +121 -0
- anyscale/client/openapi_client/models/notification_channel_webhook_config.py +121 -0
- anyscale/client/openapi_client/models/onboarding_user_cards_query.py +122 -0
- anyscale/client/openapi_client/models/organization.py +490 -0
- anyscale/client/openapi_client/models/organization_availability.py +148 -0
- anyscale/client/openapi_client/models/organization_collaborator.py +259 -0
- anyscale/client/openapi_client/models/organization_configuration.py +280 -0
- anyscale/client/openapi_client/models/organization_configuration_response.py +227 -0
- anyscale/client/openapi_client/models/organization_invitation.py +255 -0
- anyscale/client/openapi_client/models/organization_invitation_base.py +121 -0
- anyscale/client/openapi_client/models/organization_marketing_questions.py +198 -0
- anyscale/client/openapi_client/models/organization_permission_level.py +100 -0
- anyscale/client/openapi_client/models/organization_project_collaborator.py +175 -0
- anyscale/client/openapi_client/models/organization_project_collaborator_value.py +148 -0
- anyscale/client/openapi_client/models/organization_public_identifier.py +121 -0
- anyscale/client/openapi_client/models/organization_response.py +121 -0
- anyscale/client/openapi_client/models/organization_summary.py +229 -0
- anyscale/client/openapi_client/models/organization_usage_alert.py +210 -0
- anyscale/client/openapi_client/models/organization_usage_alert_severity.py +100 -0
- anyscale/client/openapi_client/models/organizationavailability_response.py +121 -0
- anyscale/client/openapi_client/models/organizationcollaborator_list_response.py +147 -0
- anyscale/client/openapi_client/models/organizationconfiguration_list_response.py +147 -0
- anyscale/client/openapi_client/models/organizationconfigurationresponse_response.py +121 -0
- anyscale/client/openapi_client/models/organizationinvitation_list_response.py +147 -0
- anyscale/client/openapi_client/models/organizationinvitation_response.py +121 -0
- anyscale/client/openapi_client/models/organizationinvitationbase_response.py +121 -0
- anyscale/client/openapi_client/models/organizationprojectcollaborator_list_response.py +147 -0
- anyscale/client/openapi_client/models/organizationpublicidentifier_response.py +121 -0
- anyscale/client/openapi_client/models/organizationusagealert_list_response.py +147 -0
- anyscale/client/openapi_client/models/page_query.py +153 -0
- anyscale/client/openapi_client/models/pause_schedule.py +123 -0
- anyscale/client/openapi_client/models/permission_level.py +101 -0
- anyscale/client/openapi_client/models/platform_fine_tuning_job.py +577 -0
- anyscale/client/openapi_client/models/platformfinetuningjob_list_response.py +147 -0
- anyscale/client/openapi_client/models/platformfinetuningjob_response.py +121 -0
- anyscale/client/openapi_client/models/product_autoscaler_flag.py +122 -0
- anyscale/client/openapi_client/models/product_type.py +100 -0
- anyscale/client/openapi_client/models/productautoscalerflag_response.py +121 -0
- anyscale/client/openapi_client/models/production_job.py +437 -0
- anyscale/client/openapi_client/models/production_job_config.py +348 -0
- anyscale/client/openapi_client/models/production_job_event.py +378 -0
- anyscale/client/openapi_client/models/production_job_event_scope_filter.py +101 -0
- anyscale/client/openapi_client/models/production_job_state_transition.py +293 -0
- anyscale/client/openapi_client/models/productionjob_response.py +121 -0
- anyscale/client/openapi_client/models/productionjobevent_list_response.py +147 -0
- anyscale/client/openapi_client/models/project.py +554 -0
- anyscale/client/openapi_client/models/project_base.py +121 -0
- anyscale/client/openapi_client/models/project_collaborator.py +175 -0
- anyscale/client/openapi_client/models/project_collaborator_value.py +175 -0
- anyscale/client/openapi_client/models/project_collaborators_put_message.py +121 -0
- anyscale/client/openapi_client/models/project_create_message.py +148 -0
- anyscale/client/openapi_client/models/project_default_session_name.py +121 -0
- anyscale/client/openapi_client/models/project_delete_message.py +121 -0
- anyscale/client/openapi_client/models/project_list_response.py +147 -0
- anyscale/client/openapi_client/models/project_patch_message.py +121 -0
- anyscale/client/openapi_client/models/project_response.py +121 -0
- anyscale/client/openapi_client/models/projectbase_response.py +121 -0
- anyscale/client/openapi_client/models/projectcollaborator_list_response.py +147 -0
- anyscale/client/openapi_client/models/projectdefaultsessionname_response.py +121 -0
- anyscale/client/openapi_client/models/projects_sort_field.py +101 -0
- anyscale/client/openapi_client/models/projects_violating_tree_hierarchy_response.py +121 -0
- anyscale/client/openapi_client/models/projectsviolatingtreehierarchyresponse_response.py +121 -0
- anyscale/client/openapi_client/models/protocols.py +150 -0
- anyscale/client/openapi_client/models/provider_metadata.py +205 -0
- anyscale/client/openapi_client/models/providermetadata_response.py +121 -0
- anyscale/client/openapi_client/models/python_modules.py +150 -0
- anyscale/client/openapi_client/models/quota.py +198 -0
- anyscale/client/openapi_client/models/ray_gcs_external_storage_config.py +178 -0
- anyscale/client/openapi_client/models/ray_runtime_env_config.py +262 -0
- anyscale/client/openapi_client/models/read_billing_version.py +210 -0
- anyscale/client/openapi_client/models/readbillingversion_list_response.py +147 -0
- anyscale/client/openapi_client/models/replica_details.py +152 -0
- anyscale/client/openapi_client/models/replica_state.py +104 -0
- anyscale/client/openapi_client/models/request_email_magic_link_response.py +147 -0
- anyscale/client/openapi_client/models/request_otp_return_api_model.py +148 -0
- anyscale/client/openapi_client/models/request_password_reset_params.py +147 -0
- anyscale/client/openapi_client/models/requestemailmagiclinkresponse_response.py +121 -0
- anyscale/client/openapi_client/models/requestotpreturnapimodel_response.py +121 -0
- anyscale/client/openapi_client/models/reset_password_params.py +148 -0
- anyscale/client/openapi_client/models/resource_quota.py +465 -0
- anyscale/client/openapi_client/models/resource_quota_status.py +123 -0
- anyscale/client/openapi_client/models/resourcequota_list_response.py +147 -0
- anyscale/client/openapi_client/models/resourcequota_response.py +121 -0
- anyscale/client/openapi_client/models/resources.py +234 -0
- anyscale/client/openapi_client/models/resubmit_ft_job_request.py +121 -0
- anyscale/client/openapi_client/models/rollback_service_model.py +122 -0
- anyscale/client/openapi_client/models/rollout_strategy.py +100 -0
- anyscale/client/openapi_client/models/s3_download_location.py +148 -0
- anyscale/client/openapi_client/models/schedule_config.py +151 -0
- anyscale/client/openapi_client/models/serve_deployment_grafana_dashboard_status.py +101 -0
- anyscale/client/openapi_client/models/serve_deployment_logs.py +152 -0
- anyscale/client/openapi_client/models/serve_deployment_state.py +104 -0
- anyscale/client/openapi_client/models/servedeploymentlogs_response.py +121 -0
- anyscale/client/openapi_client/models/server_session_token.py +121 -0
- anyscale/client/openapi_client/models/serversessiontoken_response.py +121 -0
- anyscale/client/openapi_client/models/service_config.py +178 -0
- anyscale/client/openapi_client/models/service_event_current_state.py +108 -0
- anyscale/client/openapi_client/models/service_event_level.py +102 -0
- anyscale/client/openapi_client/models/service_event_origin.py +103 -0
- anyscale/client/openapi_client/models/service_event_scope.py +103 -0
- anyscale/client/openapi_client/models/service_event_scope_filter.py +104 -0
- anyscale/client/openapi_client/models/service_event_type.py +125 -0
- anyscale/client/openapi_client/models/service_event_verbose_message_model.py +180 -0
- anyscale/client/openapi_client/models/service_goal_states.py +100 -0
- anyscale/client/openapi_client/models/service_observability_urls.py +206 -0
- anyscale/client/openapi_client/models/service_sort_field.py +101 -0
- anyscale/client/openapi_client/models/service_type.py +100 -0
- anyscale/client/openapi_client/models/service_usage.py +353 -0
- anyscale/client/openapi_client/models/service_version_state.py +106 -0
- anyscale/client/openapi_client/models/serviceeventverbosemessagemodel_list_response.py +147 -0
- anyscale/client/openapi_client/models/session.py +834 -0
- anyscale/client/openapi_client/models/session_access.py +102 -0
- anyscale/client/openapi_client/models/session_autosync_sessions_update_message.py +121 -0
- anyscale/client/openapi_client/models/session_command.py +413 -0
- anyscale/client/openapi_client/models/session_command_finish_options.py +226 -0
- anyscale/client/openapi_client/models/session_command_id.py +121 -0
- anyscale/client/openapi_client/models/session_command_types.py +100 -0
- anyscale/client/openapi_client/models/session_create_message.py +148 -0
- anyscale/client/openapi_client/models/session_delete_message.py +121 -0
- anyscale/client/openapi_client/models/session_describe.py +175 -0
- anyscale/client/openapi_client/models/session_details.py +148 -0
- anyscale/client/openapi_client/models/session_event.py +267 -0
- anyscale/client/openapi_client/models/session_event_cause.py +150 -0
- anyscale/client/openapi_client/models/session_event_types.py +111 -0
- anyscale/client/openapi_client/models/session_execute_message.py +121 -0
- anyscale/client/openapi_client/models/session_finish_command_message.py +175 -0
- anyscale/client/openapi_client/models/session_history_item.py +146 -0
- anyscale/client/openapi_client/models/session_kill_command_message.py +121 -0
- anyscale/client/openapi_client/models/session_list_response.py +147 -0
- anyscale/client/openapi_client/models/session_patch_message.py +121 -0
- anyscale/client/openapi_client/models/session_response.py +121 -0
- anyscale/client/openapi_client/models/session_ssh_key.py +148 -0
- anyscale/client/openapi_client/models/session_starting_up_data.py +146 -0
- anyscale/client/openapi_client/models/session_state.py +111 -0
- anyscale/client/openapi_client/models/session_state_change_message.py +121 -0
- anyscale/client/openapi_client/models/session_state_data.py +146 -0
- anyscale/client/openapi_client/models/session_stopping_data.py +146 -0
- anyscale/client/openapi_client/models/sessioncommand_list_response.py +147 -0
- anyscale/client/openapi_client/models/sessioncommandid_response.py +121 -0
- anyscale/client/openapi_client/models/sessiondescribe_response.py +121 -0
- anyscale/client/openapi_client/models/sessiondetails_response.py +121 -0
- anyscale/client/openapi_client/models/sessionevent_list_response.py +147 -0
- anyscale/client/openapi_client/models/sessionhistoryitem_list_response.py +147 -0
- anyscale/client/openapi_client/models/sessions_sort_field.py +104 -0
- anyscale/client/openapi_client/models/sessionsshkey_response.py +121 -0
- anyscale/client/openapi_client/models/setup_initialize_session_options.py +225 -0
- anyscale/client/openapi_client/models/show_otp_source_return_api_model.py +121 -0
- anyscale/client/openapi_client/models/showotpsourcereturnapimodel_response.py +121 -0
- anyscale/client/openapi_client/models/snapshot_create_message.py +148 -0
- anyscale/client/openapi_client/models/snapshot_delete_message.py +148 -0
- anyscale/client/openapi_client/models/snapshot_patch_message.py +148 -0
- anyscale/client/openapi_client/models/socket_message_schemas.py +499 -0
- anyscale/client/openapi_client/models/socket_message_types.py +113 -0
- anyscale/client/openapi_client/models/socketmessageschemas_response.py +121 -0
- anyscale/client/openapi_client/models/socketmessagetypes_response.py +121 -0
- anyscale/client/openapi_client/models/sort_order.py +100 -0
- anyscale/client/openapi_client/models/sso_login_info.py +151 -0
- anyscale/client/openapi_client/models/ssologininfo_response.py +121 -0
- anyscale/client/openapi_client/models/start_session_options.py +146 -0
- anyscale/client/openapi_client/models/stop_session_options.py +227 -0
- anyscale/client/openapi_client/models/stream_publish_request.py +239 -0
- anyscale/client/openapi_client/models/subnet_id_with_availability_zone_aws.py +148 -0
- anyscale/client/openapi_client/models/support_requests_query.py +178 -0
- anyscale/client/openapi_client/models/supportedbaseimagesenum.py +1570 -0
- anyscale/client/openapi_client/models/templatized_compute_configs.py +202 -0
- anyscale/client/openapi_client/models/templatized_decorated_application_templates.py +202 -0
- anyscale/client/openapi_client/models/templatizedcomputeconfigs_response.py +121 -0
- anyscale/client/openapi_client/models/templatizeddecoratedapplicationtemplates_response.py +121 -0
- anyscale/client/openapi_client/models/text_query.py +178 -0
- anyscale/client/openapi_client/models/timestamped_logs_output.py +148 -0
- anyscale/client/openapi_client/models/timestampedlogsoutput_response.py +121 -0
- anyscale/client/openapi_client/models/tool.py +100 -0
- anyscale/client/openapi_client/models/tracing_config.py +178 -0
- anyscale/client/openapi_client/models/try_login_email_response.py +208 -0
- anyscale/client/openapi_client/models/tryloginemailresponse_response.py +121 -0
- anyscale/client/openapi_client/models/unified_job_sort_field.py +103 -0
- anyscale/client/openapi_client/models/unified_job_status.py +114 -0
- anyscale/client/openapi_client/models/unified_job_type.py +100 -0
- anyscale/client/openapi_client/models/update_cloud_with_cloud_resource.py +178 -0
- anyscale/client/openapi_client/models/update_cloud_with_cloud_resource_gcp.py +178 -0
- anyscale/client/openapi_client/models/update_cluster_dns.py +152 -0
- anyscale/client/openapi_client/models/update_compute_template.py +146 -0
- anyscale/client/openapi_client/models/update_compute_template_config.py +464 -0
- anyscale/client/openapi_client/models/update_endpoint.py +152 -0
- anyscale/client/openapi_client/models/update_machine_pool_request.py +151 -0
- anyscale/client/openapi_client/models/update_model_deployment.py +152 -0
- anyscale/client/openapi_client/models/update_organization_collaborator.py +121 -0
- anyscale/client/openapi_client/models/update_project_collaborator.py +121 -0
- anyscale/client/openapi_client/models/update_resource_quota.py +122 -0
- anyscale/client/openapi_client/models/updatemachinepoolresponse_response.py +121 -0
- anyscale/client/openapi_client/models/upload_session_command_logs_locations.py +148 -0
- anyscale/client/openapi_client/models/uploadsessioncommandlogslocations_response.py +121 -0
- anyscale/client/openapi_client/models/user_info.py +569 -0
- anyscale/client/openapi_client/models/user_resend_email_options.py +147 -0
- anyscale/client/openapi_client/models/user_service_access_types.py +100 -0
- anyscale/client/openapi_client/models/userinfo_response.py +121 -0
- anyscale/client/openapi_client/models/utm_fields.py +224 -0
- anyscale/client/openapi_client/models/ux_instance.py +468 -0
- anyscale/client/openapi_client/models/validate_otp_params_api_model.py +121 -0
- anyscale/client/openapi_client/models/validation_error.py +175 -0
- anyscale/client/openapi_client/models/verify_response.py +147 -0
- anyscale/client/openapi_client/models/verifyresponse_response.py +121 -0
- anyscale/client/openapi_client/models/visibility.py +100 -0
- anyscale/client/openapi_client/models/waitlist_status_response.py +121 -0
- anyscale/client/openapi_client/models/waitlist_status_type.py +100 -0
- anyscale/client/openapi_client/models/waitliststatusresponse_response.py +121 -0
- anyscale/client/openapi_client/models/wand_b_run_details.py +147 -0
- anyscale/client/openapi_client/models/web_terminal.py +121 -0
- anyscale/client/openapi_client/models/webterminal_list_response.py +147 -0
- anyscale/client/openapi_client/models/webterminal_response.py +121 -0
- anyscale/client/openapi_client/models/worker_node_type.py +404 -0
- anyscale/client/openapi_client/models/workload_type.py +102 -0
- anyscale/client/openapi_client/models/workspace_dataplane_artifact.py +181 -0
- anyscale/client/openapi_client/models/workspace_dataplane_artifacts.py +123 -0
- anyscale/client/openapi_client/models/workspace_dataplane_proxied_artifacts.py +178 -0
- anyscale/client/openapi_client/models/workspace_event.py +325 -0
- anyscale/client/openapi_client/models/workspace_event_source.py +100 -0
- anyscale/client/openapi_client/models/workspace_event_source_filter.py +101 -0
- anyscale/client/openapi_client/models/workspace_readme.py +123 -0
- anyscale/client/openapi_client/models/workspace_snapshot_states.py +108 -0
- anyscale/client/openapi_client/models/workspace_template.py +353 -0
- anyscale/client/openapi_client/models/workspace_template_cluster_environment_metadata.py +178 -0
- anyscale/client/openapi_client/models/workspacedataplaneartifacts_response.py +121 -0
- anyscale/client/openapi_client/models/workspacedataplaneproxiedartifacts_response.py +121 -0
- anyscale/client/openapi_client/models/workspaceevent_list_response.py +147 -0
- anyscale/client/openapi_client/models/workspacereadme_response.py +121 -0
- anyscale/client/openapi_client/models/workspacetemplate_list_response.py +147 -0
- anyscale/client/openapi_client/models/workspacetemplateclusterenvironmentmetadata_response.py +121 -0
- anyscale/client/openapi_client/models/write_cloud.py +546 -0
- anyscale/client/openapi_client/models/write_cluster_config.py +123 -0
- anyscale/client/openapi_client/models/write_project.py +226 -0
- anyscale/client/openapi_client/models/write_session.py +147 -0
- anyscale/client/openapi_client/models/write_support_request.py +121 -0
- anyscale/client/openapi_client/rest.py +296 -0
- anyscale/client/requirements.txt +6 -0
- anyscale/client/setup.cfg +2 -0
- anyscale/client/setup.py +40 -0
- anyscale/client/test-requirements.txt +3 -0
- anyscale/client/tox.ini +9 -0
- anyscale/cloud.py +216 -0
- anyscale/cloud_resource.py +1032 -0
- anyscale/cluster.py +138 -0
- anyscale/cluster_compute.py +167 -0
- anyscale/cluster_env.py +173 -0
- anyscale/commands/__init__.py +0 -0
- anyscale/commands/aggregated_instance_usage_commands.py +86 -0
- anyscale/commands/anyscale_api/__init__.py +0 -0
- anyscale/commands/anyscale_api/api_commands.py +23 -0
- anyscale/commands/anyscale_api/session_commands_commands.py +80 -0
- anyscale/commands/anyscale_api/session_operations_commands.py +28 -0
- anyscale/commands/anyscale_api/sessions_commands.py +152 -0
- anyscale/commands/auth_commands.py +41 -0
- anyscale/commands/cloud_commands.py +1011 -0
- anyscale/commands/cloud_commands_util.py +10 -0
- anyscale/commands/cluster_commands.py +476 -0
- anyscale/commands/cluster_env_commands.py +139 -0
- anyscale/commands/command_examples.py +495 -0
- anyscale/commands/compute_config_commands.py +252 -0
- anyscale/commands/config_commands.py +213 -0
- anyscale/commands/exec_commands.py +14 -0
- anyscale/commands/experimental_integrations_commands.py +70 -0
- anyscale/commands/image_commands.py +125 -0
- anyscale/commands/job_commands.py +745 -0
- anyscale/commands/list_commands.py +85 -0
- anyscale/commands/llm/dataset_commands.py +269 -0
- anyscale/commands/llm/group.py +15 -0
- anyscale/commands/llm/models_commands.py +123 -0
- anyscale/commands/login_commands.py +79 -0
- anyscale/commands/logs_commands.py +312 -0
- anyscale/commands/machine_commands.py +116 -0
- anyscale/commands/machine_pool_commands.py +163 -0
- anyscale/commands/migrate_commands.py +84 -0
- anyscale/commands/project_commands.py +203 -0
- anyscale/commands/resource_quota_commands.py +214 -0
- anyscale/commands/schedule_commands.py +436 -0
- anyscale/commands/service_account_commands.py +72 -0
- anyscale/commands/service_commands.py +738 -0
- anyscale/commands/session_commands_hidden.py +179 -0
- anyscale/commands/util.py +152 -0
- anyscale/commands/workspace_commands.py +511 -0
- anyscale/commands/workspace_commands_v2.py +874 -0
- anyscale/component_activity_util.py +83 -0
- anyscale/compute_config/__init__.py +84 -0
- anyscale/compute_config/_private/compute_config_sdk.py +433 -0
- anyscale/compute_config/commands.py +122 -0
- anyscale/compute_config/models.py +630 -0
- anyscale/conf.py +23 -0
- anyscale/connect.py +1323 -0
- anyscale/connect_utils/__init__.py +0 -0
- anyscale/connect_utils/prepare_cluster.py +962 -0
- anyscale/connect_utils/project.py +298 -0
- anyscale/connect_utils/start_interactive_session.py +437 -0
- anyscale/controllers/__init__.py +0 -0
- anyscale/controllers/auth_controller.py +134 -0
- anyscale/controllers/base_controller.py +52 -0
- anyscale/controllers/cloud_controller.py +3609 -0
- anyscale/controllers/cloud_functional_verification_controller.py +858 -0
- anyscale/controllers/cluster_controller.py +720 -0
- anyscale/controllers/cluster_env_controller.py +219 -0
- anyscale/controllers/compute_config_controller.py +351 -0
- anyscale/controllers/config_controller.py +422 -0
- anyscale/controllers/experimental_integrations_controller.py +80 -0
- anyscale/controllers/job_controller.py +647 -0
- anyscale/controllers/jobs_bg_controller.py +0 -0
- anyscale/controllers/list_controller.py +290 -0
- anyscale/controllers/llm/__init__.py +0 -0
- anyscale/controllers/llm/models_controller.py +144 -0
- anyscale/controllers/logs_controller.py +449 -0
- anyscale/controllers/machine_controller.py +37 -0
- anyscale/controllers/machine_pool_controller.py +86 -0
- anyscale/controllers/project_controller.py +281 -0
- anyscale/controllers/resource_quota_controller.py +183 -0
- anyscale/controllers/schedule_controller.py +333 -0
- anyscale/controllers/service_account_controller.py +168 -0
- anyscale/controllers/service_controller.py +453 -0
- anyscale/controllers/workspace_controller.py +253 -0
- anyscale/feature_flags.py +4 -0
- anyscale/fingerprint.py +62 -0
- anyscale/formatters/__init__.py +0 -0
- anyscale/formatters/clouds_formatter.py +65 -0
- anyscale/formatters/common_formatter.py +22 -0
- anyscale/gcp_verification.py +792 -0
- anyscale/image/__init__.py +73 -0
- anyscale/image/_private/image_sdk.py +202 -0
- anyscale/image/commands.py +117 -0
- anyscale/image/models.py +57 -0
- anyscale/integrations.py +329 -0
- anyscale/job/__init__.py +166 -0
- anyscale/job/_private/job_sdk.py +497 -0
- anyscale/job/commands.py +267 -0
- anyscale/job/models.py +500 -0
- anyscale/links.py +4 -0
- anyscale/llm/__init__.py +2 -0
- anyscale/llm/dataset/__init__.py +2 -0
- anyscale/llm/dataset/_private/__init__.py +0 -0
- anyscale/llm/dataset/_private/docs.py +63 -0
- anyscale/llm/dataset/_private/models.py +71 -0
- anyscale/llm/dataset/_private/sdk.py +147 -0
- anyscale/llm/model/__init__.py +2 -0
- anyscale/llm/model/_private/models_sdk.py +62 -0
- anyscale/llm/model/commands.py +93 -0
- anyscale/llm/model/models.py +171 -0
- anyscale/llm/model/sdk.py +62 -0
- anyscale/llm/sdk.py +27 -0
- anyscale/memorydb_supported_zones.json +22 -0
- anyscale/models/job_model.py +457 -0
- anyscale/models/service_model.py +125 -0
- anyscale/project.py +501 -0
- anyscale/schedule/__init__.py +91 -0
- anyscale/schedule/_private/schedule_sdk.py +165 -0
- anyscale/schedule/commands.py +149 -0
- anyscale/schedule/models.py +145 -0
- anyscale/scripts.py +164 -0
- anyscale/sdk/anyscale_client/__init__.py +235 -0
- anyscale/sdk/anyscale_client/api/__init__.py +6 -0
- anyscale/sdk/anyscale_client/api/default_api.py +11625 -0
- anyscale/sdk/anyscale_client/api_client.py +647 -0
- anyscale/sdk/anyscale_client/configuration.py +373 -0
- anyscale/sdk/anyscale_client/exceptions.py +120 -0
- anyscale/sdk/anyscale_client/models/__init__.py +220 -0
- anyscale/sdk/anyscale_client/models/access_config.py +122 -0
- anyscale/sdk/anyscale_client/models/app_config.py +436 -0
- anyscale/sdk/anyscale_client/models/app_config_config_schema.py +235 -0
- anyscale/sdk/anyscale_client/models/appconfig_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/appconfig_response.py +121 -0
- anyscale/sdk/anyscale_client/models/apply_production_service_v2_model.py +490 -0
- anyscale/sdk/anyscale_client/models/apply_service_model.py +490 -0
- anyscale/sdk/anyscale_client/models/archive_status.py +101 -0
- anyscale/sdk/anyscale_client/models/base_job_status.py +105 -0
- anyscale/sdk/anyscale_client/models/baseimagesenum.py +2130 -0
- anyscale/sdk/anyscale_client/models/build.py +607 -0
- anyscale/sdk/anyscale_client/models/build_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/build_log_response.py +123 -0
- anyscale/sdk/anyscale_client/models/build_response.py +121 -0
- anyscale/sdk/anyscale_client/models/build_status.py +104 -0
- anyscale/sdk/anyscale_client/models/buildlogresponse_response.py +121 -0
- anyscale/sdk/anyscale_client/models/cloud.py +802 -0
- anyscale/sdk/anyscale_client/models/cloud_config.py +206 -0
- anyscale/sdk/anyscale_client/models/cloud_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/cloud_providers.py +103 -0
- anyscale/sdk/anyscale_client/models/cloud_response.py +121 -0
- anyscale/sdk/anyscale_client/models/cloud_state.py +104 -0
- anyscale/sdk/anyscale_client/models/cloud_status.py +100 -0
- anyscale/sdk/anyscale_client/models/cloud_type.py +100 -0
- anyscale/sdk/anyscale_client/models/cloud_types.py +100 -0
- anyscale/sdk/anyscale_client/models/cloud_version.py +100 -0
- anyscale/sdk/anyscale_client/models/clouds_query.py +150 -0
- anyscale/sdk/anyscale_client/models/cluster.py +721 -0
- anyscale/sdk/anyscale_client/models/cluster_compute.py +415 -0
- anyscale/sdk/anyscale_client/models/cluster_compute_config.py +461 -0
- anyscale/sdk/anyscale_client/models/cluster_computes_query.py +293 -0
- anyscale/sdk/anyscale_client/models/cluster_environment.py +380 -0
- anyscale/sdk/anyscale_client/models/cluster_environment_build.py +578 -0
- anyscale/sdk/anyscale_client/models/cluster_environment_build_log_response.py +123 -0
- anyscale/sdk/anyscale_client/models/cluster_environment_build_operation.py +237 -0
- anyscale/sdk/anyscale_client/models/cluster_environment_build_status.py +104 -0
- anyscale/sdk/anyscale_client/models/cluster_environments_query.py +290 -0
- anyscale/sdk/anyscale_client/models/cluster_head_node_info.py +152 -0
- anyscale/sdk/anyscale_client/models/cluster_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/cluster_management_stack_versions.py +100 -0
- anyscale/sdk/anyscale_client/models/cluster_operation.py +266 -0
- anyscale/sdk/anyscale_client/models/cluster_operation_type.py +101 -0
- anyscale/sdk/anyscale_client/models/cluster_response.py +121 -0
- anyscale/sdk/anyscale_client/models/cluster_services_urls.py +430 -0
- anyscale/sdk/anyscale_client/models/cluster_state.py +108 -0
- anyscale/sdk/anyscale_client/models/cluster_status.py +104 -0
- anyscale/sdk/anyscale_client/models/cluster_status_details.py +100 -0
- anyscale/sdk/anyscale_client/models/clustercompute_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/clustercompute_response.py +121 -0
- anyscale/sdk/anyscale_client/models/clusterenvironment_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/clusterenvironment_response.py +121 -0
- anyscale/sdk/anyscale_client/models/clusterenvironmentbuild_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/clusterenvironmentbuild_response.py +121 -0
- anyscale/sdk/anyscale_client/models/clusterenvironmentbuildlogresponse_response.py +121 -0
- anyscale/sdk/anyscale_client/models/clusterenvironmentbuildoperation_response.py +121 -0
- anyscale/sdk/anyscale_client/models/clusteroperation_response.py +121 -0
- anyscale/sdk/anyscale_client/models/clusters_query.py +234 -0
- anyscale/sdk/anyscale_client/models/compute_node_type.py +292 -0
- anyscale/sdk/anyscale_client/models/compute_stack.py +100 -0
- anyscale/sdk/anyscale_client/models/compute_template.py +415 -0
- anyscale/sdk/anyscale_client/models/compute_template_config.py +461 -0
- anyscale/sdk/anyscale_client/models/compute_template_query.py +316 -0
- anyscale/sdk/anyscale_client/models/computetemplate_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/computetemplate_response.py +121 -0
- anyscale/sdk/anyscale_client/models/computetemplateconfig_response.py +121 -0
- anyscale/sdk/anyscale_client/models/create_app_config.py +235 -0
- anyscale/sdk/anyscale_client/models/create_app_config_configuration_schema.py +235 -0
- anyscale/sdk/anyscale_client/models/create_build.py +263 -0
- anyscale/sdk/anyscale_client/models/create_byod_app_config_configuration_schema.py +206 -0
- anyscale/sdk/anyscale_client/models/create_byod_cluster_environment.py +180 -0
- anyscale/sdk/anyscale_client/models/create_byod_cluster_environment_build.py +152 -0
- anyscale/sdk/anyscale_client/models/create_byod_cluster_environment_configuration_schema.py +208 -0
- anyscale/sdk/anyscale_client/models/create_cloud.py +518 -0
- anyscale/sdk/anyscale_client/models/create_cluster.py +376 -0
- anyscale/sdk/anyscale_client/models/create_cluster_compute.py +229 -0
- anyscale/sdk/anyscale_client/models/create_cluster_compute_config.py +463 -0
- anyscale/sdk/anyscale_client/models/create_cluster_environment.py +235 -0
- anyscale/sdk/anyscale_client/models/create_cluster_environment_build.py +263 -0
- anyscale/sdk/anyscale_client/models/create_cluster_environment_configuration_schema.py +235 -0
- anyscale/sdk/anyscale_client/models/create_compute_template.py +229 -0
- anyscale/sdk/anyscale_client/models/create_compute_template_config.py +464 -0
- anyscale/sdk/anyscale_client/models/create_job_queue_config.py +206 -0
- anyscale/sdk/anyscale_client/models/create_production_job.py +234 -0
- anyscale/sdk/anyscale_client/models/create_production_job_config.py +347 -0
- anyscale/sdk/anyscale_client/models/create_project.py +207 -0
- anyscale/sdk/anyscale_client/models/create_schedule.py +263 -0
- anyscale/sdk/anyscale_client/models/create_session.py +432 -0
- anyscale/sdk/anyscale_client/models/create_session_command.py +152 -0
- anyscale/sdk/anyscale_client/models/create_sso_config.py +150 -0
- anyscale/sdk/anyscale_client/models/grpc_protocol_config.py +178 -0
- anyscale/sdk/anyscale_client/models/ha_job_goal_states.py +102 -0
- anyscale/sdk/anyscale_client/models/ha_job_states.py +109 -0
- anyscale/sdk/anyscale_client/models/http_protocol_config.py +150 -0
- anyscale/sdk/anyscale_client/models/http_validation_error.py +120 -0
- anyscale/sdk/anyscale_client/models/idle_termination_status.py +104 -0
- anyscale/sdk/anyscale_client/models/job.py +466 -0
- anyscale/sdk/anyscale_client/models/job_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/job_queue_config.py +122 -0
- anyscale/sdk/anyscale_client/models/job_queue_execution_mode.py +101 -0
- anyscale/sdk/anyscale_client/models/job_queue_spec.py +263 -0
- anyscale/sdk/anyscale_client/models/job_run_type.py +101 -0
- anyscale/sdk/anyscale_client/models/job_status.py +105 -0
- anyscale/sdk/anyscale_client/models/jobs_query.py +458 -0
- anyscale/sdk/anyscale_client/models/jobs_sort_field.py +104 -0
- anyscale/sdk/anyscale_client/models/list_response_metadata.py +146 -0
- anyscale/sdk/anyscale_client/models/list_service_model.py +347 -0
- anyscale/sdk/anyscale_client/models/listservicemodel_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/log_download_result.py +207 -0
- anyscale/sdk/anyscale_client/models/log_file_chunk.py +439 -0
- anyscale/sdk/anyscale_client/models/log_level_types.py +100 -0
- anyscale/sdk/anyscale_client/models/log_stream.py +151 -0
- anyscale/sdk/anyscale_client/models/logdownloadresult_response.py +121 -0
- anyscale/sdk/anyscale_client/models/logstream_response.py +121 -0
- anyscale/sdk/anyscale_client/models/node_type.py +100 -0
- anyscale/sdk/anyscale_client/models/object_storage_config.py +122 -0
- anyscale/sdk/anyscale_client/models/object_storage_config_s3.py +256 -0
- anyscale/sdk/anyscale_client/models/objectstorageconfig_response.py +121 -0
- anyscale/sdk/anyscale_client/models/operation_error.py +123 -0
- anyscale/sdk/anyscale_client/models/operation_progress.py +123 -0
- anyscale/sdk/anyscale_client/models/operation_result.py +150 -0
- anyscale/sdk/anyscale_client/models/organization.py +209 -0
- anyscale/sdk/anyscale_client/models/organization_response.py +121 -0
- anyscale/sdk/anyscale_client/models/page_query.py +153 -0
- anyscale/sdk/anyscale_client/models/pause_schedule.py +123 -0
- anyscale/sdk/anyscale_client/models/production_job.py +437 -0
- anyscale/sdk/anyscale_client/models/production_job_config.py +348 -0
- anyscale/sdk/anyscale_client/models/production_job_state_transition.py +293 -0
- anyscale/sdk/anyscale_client/models/production_service_v2_model.py +612 -0
- anyscale/sdk/anyscale_client/models/production_service_v2_version_model.py +437 -0
- anyscale/sdk/anyscale_client/models/productionjob_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/productionjob_response.py +121 -0
- anyscale/sdk/anyscale_client/models/productionservicev2_model_response.py +121 -0
- anyscale/sdk/anyscale_client/models/project.py +467 -0
- anyscale/sdk/anyscale_client/models/project_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/project_response.py +121 -0
- anyscale/sdk/anyscale_client/models/projects_query.py +234 -0
- anyscale/sdk/anyscale_client/models/protocols.py +150 -0
- anyscale/sdk/anyscale_client/models/python_modules.py +150 -0
- anyscale/sdk/anyscale_client/models/python_version.py +105 -0
- anyscale/sdk/anyscale_client/models/ray_gcs_external_storage_config.py +178 -0
- anyscale/sdk/anyscale_client/models/ray_runtime_env_config.py +262 -0
- anyscale/sdk/anyscale_client/models/resources.py +234 -0
- anyscale/sdk/anyscale_client/models/rollback_service_model.py +122 -0
- anyscale/sdk/anyscale_client/models/rollout_strategy.py +100 -0
- anyscale/sdk/anyscale_client/models/runtime_environment.py +406 -0
- anyscale/sdk/anyscale_client/models/runtimeenvironment_response.py +121 -0
- anyscale/sdk/anyscale_client/models/schedule_api_model.py +467 -0
- anyscale/sdk/anyscale_client/models/schedule_config.py +151 -0
- anyscale/sdk/anyscale_client/models/scheduleapimodel_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/scheduleapimodel_response.py +121 -0
- anyscale/sdk/anyscale_client/models/service_config.py +178 -0
- anyscale/sdk/anyscale_client/models/service_event_current_state.py +108 -0
- anyscale/sdk/anyscale_client/models/service_goal_states.py +100 -0
- anyscale/sdk/anyscale_client/models/service_model.py +612 -0
- anyscale/sdk/anyscale_client/models/service_observability_urls.py +206 -0
- anyscale/sdk/anyscale_client/models/service_sort_field.py +101 -0
- anyscale/sdk/anyscale_client/models/service_type.py +100 -0
- anyscale/sdk/anyscale_client/models/service_version_state.py +106 -0
- anyscale/sdk/anyscale_client/models/servicemodel_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/servicemodel_response.py +121 -0
- anyscale/sdk/anyscale_client/models/session.py +1535 -0
- anyscale/sdk/anyscale_client/models/session_command.py +350 -0
- anyscale/sdk/anyscale_client/models/session_command_types.py +100 -0
- anyscale/sdk/anyscale_client/models/session_event.py +267 -0
- anyscale/sdk/anyscale_client/models/session_event_cause.py +150 -0
- anyscale/sdk/anyscale_client/models/session_event_types.py +111 -0
- anyscale/sdk/anyscale_client/models/session_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/session_operation.py +266 -0
- anyscale/sdk/anyscale_client/models/session_operation_type.py +101 -0
- anyscale/sdk/anyscale_client/models/session_response.py +121 -0
- anyscale/sdk/anyscale_client/models/session_starting_up_data.py +146 -0
- anyscale/sdk/anyscale_client/models/session_state.py +111 -0
- anyscale/sdk/anyscale_client/models/session_state_data.py +146 -0
- anyscale/sdk/anyscale_client/models/session_stopping_data.py +146 -0
- anyscale/sdk/anyscale_client/models/sessioncommand_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/sessioncommand_response.py +121 -0
- anyscale/sdk/anyscale_client/models/sessionevent_list_response.py +147 -0
- anyscale/sdk/anyscale_client/models/sessionoperation_response.py +121 -0
- anyscale/sdk/anyscale_client/models/sessions_query.py +206 -0
- anyscale/sdk/anyscale_client/models/sort_by_clause_jobs_sort_field.py +148 -0
- anyscale/sdk/anyscale_client/models/sort_order.py +100 -0
- anyscale/sdk/anyscale_client/models/sso_config.py +237 -0
- anyscale/sdk/anyscale_client/models/sso_mode.py +101 -0
- anyscale/sdk/anyscale_client/models/ssoconfig_response.py +121 -0
- anyscale/sdk/anyscale_client/models/start_cluster_options.py +178 -0
- anyscale/sdk/anyscale_client/models/start_session_options.py +206 -0
- anyscale/sdk/anyscale_client/models/static_sso_config.py +210 -0
- anyscale/sdk/anyscale_client/models/supportedbaseimagesenum.py +1570 -0
- anyscale/sdk/anyscale_client/models/terminate_cluster_options.py +122 -0
- anyscale/sdk/anyscale_client/models/terminate_session_options.py +206 -0
- anyscale/sdk/anyscale_client/models/text_query.py +178 -0
- anyscale/sdk/anyscale_client/models/tracing_config.py +178 -0
- anyscale/sdk/anyscale_client/models/update_app_config.py +122 -0
- anyscale/sdk/anyscale_client/models/update_cloud.py +150 -0
- anyscale/sdk/anyscale_client/models/update_cluster.py +206 -0
- anyscale/sdk/anyscale_client/models/update_compute_template.py +146 -0
- anyscale/sdk/anyscale_client/models/update_compute_template_config.py +464 -0
- anyscale/sdk/anyscale_client/models/update_organization.py +123 -0
- anyscale/sdk/anyscale_client/models/update_project.py +150 -0
- anyscale/sdk/anyscale_client/models/update_session.py +150 -0
- anyscale/sdk/anyscale_client/models/user_service_access_types.py +100 -0
- anyscale/sdk/anyscale_client/models/ux_instance.py +468 -0
- anyscale/sdk/anyscale_client/models/validation_error.py +175 -0
- anyscale/sdk/anyscale_client/models/worker_node_type.py +404 -0
- anyscale/sdk/anyscale_client/rest.py +296 -0
- anyscale/sdk/anyscale_client/sdk.py +634 -0
- anyscale/service/__init__.py +168 -0
- anyscale/service/_private/service_sdk.py +702 -0
- anyscale/service/commands.py +261 -0
- anyscale/service/models.py +671 -0
- anyscale/shared_anyscale_utils/__init__.py +1 -0
- anyscale/shared_anyscale_utils/aws.py +153 -0
- anyscale/shared_anyscale_utils/bytes_util.py +10 -0
- anyscale/shared_anyscale_utils/conf.py +47 -0
- anyscale/shared_anyscale_utils/default_anyscale_aws.yaml +74 -0
- anyscale/shared_anyscale_utils/default_anyscale_gcp.yaml +80 -0
- anyscale/shared_anyscale_utils/headers.py +38 -0
- anyscale/shared_anyscale_utils/latest_ray_version.py +2 -0
- anyscale/shared_anyscale_utils/project.py +15 -0
- anyscale/shared_anyscale_utils/test_util.py +22 -0
- anyscale/shared_anyscale_utils/tests/__init__.py +1 -0
- anyscale/shared_anyscale_utils/tests/test_asyncio.py +41 -0
- anyscale/shared_anyscale_utils/tests/test_ray_semver.py +63 -0
- anyscale/shared_anyscale_utils/util.py +50 -0
- anyscale/shared_anyscale_utils/utils/__init__.py +2 -0
- anyscale/shared_anyscale_utils/utils/asyncio.py +120 -0
- anyscale/shared_anyscale_utils/utils/byod.py +40 -0
- anyscale/shared_anyscale_utils/utils/collections.py +33 -0
- anyscale/shared_anyscale_utils/utils/id_gen.py +147 -0
- anyscale/shared_anyscale_utils/utils/protected_string.py +89 -0
- anyscale/shared_anyscale_utils/utils/ray_semver.py +81 -0
- anyscale/snapshot.py +46 -0
- anyscale/tables.py +82 -0
- anyscale/util.py +1155 -0
- anyscale/utils/__init__.py +0 -0
- anyscale/utils/cli_version_check_util.py +63 -0
- anyscale/utils/cloud_update_utils.py +862 -0
- anyscale/utils/cloud_utils.py +317 -0
- anyscale/utils/cluster_debug.py +191 -0
- anyscale/utils/connect_helpers.py +155 -0
- anyscale/utils/deprecation_util.py +32 -0
- anyscale/utils/entity_arg_utils.py +43 -0
- anyscale/utils/env_utils.py +17 -0
- anyscale/utils/gcp_managed_setup_utils.py +888 -0
- anyscale/utils/gcp_utils.py +312 -0
- anyscale/utils/imports/__init__.py +0 -0
- anyscale/utils/imports/all.py +13 -0
- anyscale/utils/imports/azure.py +14 -0
- anyscale/utils/imports/gcp.py +59 -0
- anyscale/utils/logs_utils.py +141 -0
- anyscale/utils/name_utils.py +33 -0
- anyscale/utils/network_verification.py +153 -0
- anyscale/utils/ray_utils.py +128 -0
- anyscale/utils/ray_version_checker.py +48 -0
- anyscale/utils/ray_version_utils.py +53 -0
- anyscale/utils/runtime_env.py +487 -0
- anyscale/utils/s3.py +92 -0
- anyscale/utils/user_utils.py +17 -0
- anyscale/utils/workload_types.py +7 -0
- anyscale/utils/workspace_notification.py +39 -0
- anyscale/utils/workspace_utils.py +65 -0
- anyscale/version.py +1 -0
- anyscale/webterminal/__init__.py +0 -0
- anyscale/webterminal/bash-preexec.sh +370 -0
- anyscale/webterminal/command_persister.py +164 -0
- anyscale/webterminal/utils.py +176 -0
- anyscale/webterminal/webterminal.py +311 -0
- anyscale/workspace/__init__.py +270 -0
- anyscale/workspace/_private/workspace_sdk.py +737 -0
- anyscale/workspace/commands.py +472 -0
- anyscale/workspace/models.py +296 -0
- anyscale/workspace_utils.py +35 -0
- anyscale-0.24.86.dist-info/LICENSE +68 -0
- anyscale-0.24.86.dist-info/METADATA +82 -0
- anyscale-0.24.86.dist-info/NOTICE +6 -0
- anyscale-0.24.86.dist-info/RECORD +1131 -0
- anyscale-0.24.86.dist-info/WHEEL +5 -0
- anyscale-0.24.86.dist-info/entry_points.txt +2 -0
- anyscale-0.24.86.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1847 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
import contextlib
|
|
3
|
+
from functools import wraps
|
|
4
|
+
import io
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import pathlib
|
|
9
|
+
import re
|
|
10
|
+
import time
|
|
11
|
+
from typing import Any, Callable, Dict, Generator, IO, List, Optional, Tuple
|
|
12
|
+
from urllib.parse import urlparse
|
|
13
|
+
|
|
14
|
+
from openapi_client.exceptions import ApiException
|
|
15
|
+
import requests
|
|
16
|
+
from rich.style import Style
|
|
17
|
+
import smart_open
|
|
18
|
+
|
|
19
|
+
from anyscale._private.anyscale_client.common import (
|
|
20
|
+
AnyscaleClientInterface,
|
|
21
|
+
DEFAULT_PYTHON_VERSION,
|
|
22
|
+
DEFAULT_RAY_VERSION,
|
|
23
|
+
RUNTIME_ENV_PACKAGE_FORMAT,
|
|
24
|
+
)
|
|
25
|
+
from anyscale._private.models.image_uri import ImageURI
|
|
26
|
+
from anyscale._private.models.model_base import InternalListResponse, ListResponse
|
|
27
|
+
from anyscale._private.utils.progress_util import (
|
|
28
|
+
FileDownloadProgress,
|
|
29
|
+
ProgressFileReader,
|
|
30
|
+
)
|
|
31
|
+
from anyscale.api_utils.common_utils import source_cloud_id_and_project_id
|
|
32
|
+
from anyscale.api_utils.logs_util import _download_log_from_s3_url_sync
|
|
33
|
+
from anyscale.authenticate import AuthenticationBlock, get_auth_api_client
|
|
34
|
+
from anyscale.cli_logger import BlockLogger
|
|
35
|
+
from anyscale.client.openapi_client.api.default_api import DefaultApi as InternalApi
|
|
36
|
+
from anyscale.client.openapi_client.models import (
|
|
37
|
+
ArchiveStatus,
|
|
38
|
+
Cloud,
|
|
39
|
+
CloudDataBucketAccessMode,
|
|
40
|
+
CloudDataBucketFileType,
|
|
41
|
+
CloudDataBucketPresignedUrlRequest,
|
|
42
|
+
CloudDataBucketPresignedUrlResponse,
|
|
43
|
+
CloudDataBucketPresignedUrlScheme,
|
|
44
|
+
CloudNameOptions,
|
|
45
|
+
ComputeTemplate,
|
|
46
|
+
ComputeTemplateConfig,
|
|
47
|
+
ComputeTemplateQuery,
|
|
48
|
+
CreateComputeTemplate,
|
|
49
|
+
CreateDataset,
|
|
50
|
+
CreateExperimentalWorkspace,
|
|
51
|
+
CreateInternalProductionJob,
|
|
52
|
+
Dataset as InternalDataset,
|
|
53
|
+
DatasetUpload,
|
|
54
|
+
DecoratedComputeTemplate,
|
|
55
|
+
DecoratedSession,
|
|
56
|
+
DeletedPlatformFineTunedModel,
|
|
57
|
+
ExperimentalWorkspace,
|
|
58
|
+
FineTunedModel,
|
|
59
|
+
FinetunedmodelListResponse,
|
|
60
|
+
InternalProductionJob,
|
|
61
|
+
SessionSshKey,
|
|
62
|
+
StartSessionOptions,
|
|
63
|
+
StopSessionOptions,
|
|
64
|
+
WorkspaceDataplaneProxiedArtifacts,
|
|
65
|
+
)
|
|
66
|
+
from anyscale.client.openapi_client.models.create_schedule import CreateSchedule
|
|
67
|
+
from anyscale.client.openapi_client.models.decorated_schedule import DecoratedSchedule
|
|
68
|
+
from anyscale.client.openapi_client.models.production_job import ProductionJob
|
|
69
|
+
from anyscale.client.openapi_client.rest import ApiException as InternalApiException
|
|
70
|
+
from anyscale.cluster_compute import parse_cluster_compute_name_version
|
|
71
|
+
from anyscale.feature_flags import FLAG_DEFAULT_WORKING_DIR_FOR_PROJ
|
|
72
|
+
from anyscale.sdk.anyscale_client.api.default_api import DefaultApi as ExternalApi
|
|
73
|
+
from anyscale.sdk.anyscale_client.models import (
|
|
74
|
+
ApplyServiceModel,
|
|
75
|
+
Cluster,
|
|
76
|
+
ClusterCompute,
|
|
77
|
+
ClusterComputeConfig,
|
|
78
|
+
ClusterEnvironment,
|
|
79
|
+
ClusterEnvironmentBuild,
|
|
80
|
+
ClusterenvironmentbuildListResponse,
|
|
81
|
+
ClusterEnvironmentBuildStatus,
|
|
82
|
+
ClusterEnvironmentsQuery,
|
|
83
|
+
CreateBYODClusterEnvironment,
|
|
84
|
+
CreateBYODClusterEnvironmentConfigurationSchema,
|
|
85
|
+
CreateClusterEnvironment,
|
|
86
|
+
CreateClusterEnvironmentBuild,
|
|
87
|
+
Job as APIJobRun,
|
|
88
|
+
ProductionServiceV2VersionModel,
|
|
89
|
+
Project,
|
|
90
|
+
RollbackServiceModel,
|
|
91
|
+
ServiceModel,
|
|
92
|
+
TextQuery,
|
|
93
|
+
)
|
|
94
|
+
from anyscale.sdk.anyscale_client.models.jobs_query import JobsQuery
|
|
95
|
+
from anyscale.sdk.anyscale_client.models.jobs_sort_field import JobsSortField
|
|
96
|
+
from anyscale.sdk.anyscale_client.models.page_query import PageQuery
|
|
97
|
+
from anyscale.sdk.anyscale_client.models.sort_by_clause_jobs_sort_field import (
|
|
98
|
+
SortByClauseJobsSortField,
|
|
99
|
+
)
|
|
100
|
+
from anyscale.sdk.anyscale_client.models.sort_order import SortOrder
|
|
101
|
+
from anyscale.sdk.anyscale_client.models.update_cluster import UpdateCluster
|
|
102
|
+
from anyscale.sdk.anyscale_client.rest import ApiException as ExternalApiException
|
|
103
|
+
from anyscale.shared_anyscale_utils.bytes_util import Bytes
|
|
104
|
+
from anyscale.shared_anyscale_utils.conf import ANYSCALE_HOST
|
|
105
|
+
from anyscale.shared_anyscale_utils.latest_ray_version import LATEST_RAY_VERSION
|
|
106
|
+
from anyscale.util import (
|
|
107
|
+
get_cluster_model_for_current_workspace,
|
|
108
|
+
get_endpoint,
|
|
109
|
+
is_anyscale_workspace,
|
|
110
|
+
)
|
|
111
|
+
from anyscale.utils.connect_helpers import search_entities
|
|
112
|
+
from anyscale.utils.runtime_env import (
|
|
113
|
+
is_workspace_dependency_tracking_disabled,
|
|
114
|
+
parse_dot_env_file,
|
|
115
|
+
WORKSPACE_REQUIREMENTS_FILE_PATH,
|
|
116
|
+
zip_local_dir,
|
|
117
|
+
)
|
|
118
|
+
from anyscale.utils.workspace_notification import (
|
|
119
|
+
WORKSPACE_NOTIFICATION_ADDRESS,
|
|
120
|
+
WorkspaceNotification,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
WORKSPACE_ID_ENV_VAR = "ANYSCALE_EXPERIMENTAL_WORKSPACE_ID"
|
|
125
|
+
OVERWRITE_EXISTING_CLOUD_STORAGE_FILES = (
|
|
126
|
+
os.environ.get("ANYSCALE_OVERWRITE_EXISTING_CLOUD_STORAGE_FILES", "0") == "1"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# internal_logger is used for logging internal errors or debug messages that we do not expect users to see.
|
|
130
|
+
internal_logger = logging.getLogger(__name__)
|
|
131
|
+
|
|
132
|
+
# A decorator to handle ApiException and raise ValueError with the error message.
|
|
133
|
+
def handle_api_exceptions(func):
|
|
134
|
+
@wraps(func)
|
|
135
|
+
def wrapper(*args, **kwargs):
|
|
136
|
+
try:
|
|
137
|
+
return func(*args, **kwargs)
|
|
138
|
+
except (ApiException, InternalApiException, ExternalApiException) as e:
|
|
139
|
+
if e.status >= 400 and e.status < 500:
|
|
140
|
+
try:
|
|
141
|
+
body_dict = json.loads(e.body)
|
|
142
|
+
msg = body_dict["error"]["detail"]
|
|
143
|
+
raise ValueError(msg) from None
|
|
144
|
+
except KeyError:
|
|
145
|
+
# ApiException doesn't conform to expected format, raise original error
|
|
146
|
+
raise e from None
|
|
147
|
+
raise e from None
|
|
148
|
+
|
|
149
|
+
return wrapper
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@contextlib.contextmanager
|
|
153
|
+
def set_env(**environ):
|
|
154
|
+
"""
|
|
155
|
+
Temporarily set the process environment variables.
|
|
156
|
+
|
|
157
|
+
>>> with set_env(PLUGINS_DIR='test/plugins'):
|
|
158
|
+
... "PLUGINS_DIR" in os.environ
|
|
159
|
+
True
|
|
160
|
+
|
|
161
|
+
>>> "PLUGINS_DIR" in os.environ
|
|
162
|
+
False
|
|
163
|
+
|
|
164
|
+
:type environ: dict[str, unicode]
|
|
165
|
+
:param environ: Environment variables to set
|
|
166
|
+
"""
|
|
167
|
+
old_environ = dict(os.environ)
|
|
168
|
+
os.environ.update(environ)
|
|
169
|
+
try:
|
|
170
|
+
yield
|
|
171
|
+
finally:
|
|
172
|
+
os.environ.clear()
|
|
173
|
+
os.environ.update(old_environ)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class AWSS3ClientInterface(ABC):
|
|
177
|
+
@abstractmethod
|
|
178
|
+
def download_fileobj(self, Bucket: str, Key: str, Fileobj: IO[Any],) -> None:
|
|
179
|
+
"""Download a file from an S3 bucket to a file-like object."""
|
|
180
|
+
raise NotImplementedError
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class GCSBlobInterface(ABC):
|
|
184
|
+
@abstractmethod
|
|
185
|
+
def download_to_file(self, fileobj: IO[Any]) -> None:
|
|
186
|
+
"""Download the blob to a file-like object."""
|
|
187
|
+
raise NotImplementedError
|
|
188
|
+
|
|
189
|
+
@abstractmethod
|
|
190
|
+
def exists(self) -> bool:
|
|
191
|
+
"""Check if the blob exists."""
|
|
192
|
+
raise NotImplementedError
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class GCSBucketInterface(ABC):
|
|
196
|
+
@abstractmethod
|
|
197
|
+
def blob(self, object_name: str) -> GCSBlobInterface:
|
|
198
|
+
"""Get a blob object for the given object name."""
|
|
199
|
+
raise NotImplementedError
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class GCPGCSClientInterface(ABC):
|
|
203
|
+
@abstractmethod
|
|
204
|
+
def bucket(self, bucket: str) -> GCSBucketInterface:
|
|
205
|
+
"""Get a bucket object for the given bucket name."""
|
|
206
|
+
raise NotImplementedError
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class AnyscaleClient(AnyscaleClientInterface):
|
|
210
|
+
# Number of entries to fetch per request for list endpoints.
|
|
211
|
+
LIST_ENDPOINT_COUNT = 50
|
|
212
|
+
|
|
213
|
+
def __init__(
|
|
214
|
+
self,
|
|
215
|
+
*,
|
|
216
|
+
api_clients: Optional[Tuple[ExternalApi, InternalApi]] = None,
|
|
217
|
+
sleep: Optional[Callable[[float], None]] = None,
|
|
218
|
+
workspace_requirements_file_path: str = WORKSPACE_REQUIREMENTS_FILE_PATH,
|
|
219
|
+
logger: Optional[BlockLogger] = None,
|
|
220
|
+
host: Optional[str] = None,
|
|
221
|
+
s3_client: Optional[AWSS3ClientInterface] = None,
|
|
222
|
+
gcs_client: Optional[GCPGCSClientInterface] = None,
|
|
223
|
+
):
|
|
224
|
+
if api_clients is None:
|
|
225
|
+
auth_block: AuthenticationBlock = get_auth_api_client(
|
|
226
|
+
raise_structured_exception=True
|
|
227
|
+
)
|
|
228
|
+
api_clients = (auth_block.anyscale_api_client, auth_block.api_client)
|
|
229
|
+
|
|
230
|
+
self._external_api_client, self._internal_api_client = api_clients
|
|
231
|
+
self._workspace_requirements_file_path = workspace_requirements_file_path
|
|
232
|
+
self._sleep = sleep or time.sleep
|
|
233
|
+
self._s3_client = s3_client
|
|
234
|
+
self._gcs_client = gcs_client
|
|
235
|
+
|
|
236
|
+
# Cached IDs and models to avoid duplicate lookups.
|
|
237
|
+
self._default_project_id_from_cloud_id: Dict[Optional[str], str] = {}
|
|
238
|
+
self._cloud_id_cache: Dict[Optional[str], str] = {}
|
|
239
|
+
self._current_workspace_cluster: Optional[Cluster] = None
|
|
240
|
+
self._logger = logger or BlockLogger()
|
|
241
|
+
self._host = host or ANYSCALE_HOST
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def s3_client(self) -> AWSS3ClientInterface:
|
|
245
|
+
if self._s3_client is None:
|
|
246
|
+
# initialize the s3 client lazily so that we import the boto3 library only when needed.
|
|
247
|
+
try:
|
|
248
|
+
import boto3
|
|
249
|
+
import botocore.config
|
|
250
|
+
except ImportError:
|
|
251
|
+
raise RuntimeError(
|
|
252
|
+
"Could not import the Amazon S3 Python API via `import boto3`. Please check your installation or try running `pip install boto3`."
|
|
253
|
+
)
|
|
254
|
+
self._s3_client = boto3.client( # type: ignore
|
|
255
|
+
"s3", config=botocore.config.Config(signature_version="s3v4")
|
|
256
|
+
)
|
|
257
|
+
return self._s3_client # type: ignore
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def gcs_client(self) -> GCPGCSClientInterface:
|
|
261
|
+
if self._gcs_client is None:
|
|
262
|
+
# initialize the gcs client lazily so that we import the google cloud storage library only when needed.
|
|
263
|
+
try:
|
|
264
|
+
from google.cloud import storage
|
|
265
|
+
except ImportError:
|
|
266
|
+
raise RuntimeError(
|
|
267
|
+
"Could not import the Google Storage Python API via `from google.cloud import storage`. Please check your installation or try running `pip install --upgrade google-cloud-storage`."
|
|
268
|
+
)
|
|
269
|
+
self._gcs_client = storage.Client()
|
|
270
|
+
return self._gcs_client # type: ignore
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def host(self) -> str:
|
|
274
|
+
return self._host
|
|
275
|
+
|
|
276
|
+
@property
|
|
277
|
+
def logger(self) -> BlockLogger:
|
|
278
|
+
return self._logger
|
|
279
|
+
|
|
280
|
+
def get_job_ui_url(self, job_id: str) -> str:
|
|
281
|
+
return get_endpoint(f"/jobs/{job_id}", host=self.host)
|
|
282
|
+
|
|
283
|
+
def get_service_ui_url(self, service_id: str) -> str:
|
|
284
|
+
return get_endpoint(f"/services/{service_id}", host=self.host)
|
|
285
|
+
|
|
286
|
+
def get_compute_config_ui_url(
|
|
287
|
+
self, compute_config_id: str, *, cloud_id: str
|
|
288
|
+
) -> str:
|
|
289
|
+
return get_endpoint(
|
|
290
|
+
f"/v2/{cloud_id}/compute-configs/{compute_config_id}", host=self.host
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def get_build_ui_url(self, cluster_env_id, build_id: str) -> str:
|
|
294
|
+
return get_endpoint(
|
|
295
|
+
f"v2/container-images/{cluster_env_id}/versions/{build_id}", host=self.host
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
def get_current_workspace_id(self) -> Optional[str]:
|
|
299
|
+
return os.environ.get(WORKSPACE_ID_ENV_VAR, None)
|
|
300
|
+
|
|
301
|
+
def inside_workspace(self) -> bool:
|
|
302
|
+
return self.get_current_workspace_id() is not None
|
|
303
|
+
|
|
304
|
+
def get_workspace_requirements_path(self) -> Optional[str]:
|
|
305
|
+
if (
|
|
306
|
+
not self.inside_workspace()
|
|
307
|
+
or is_workspace_dependency_tracking_disabled()
|
|
308
|
+
or not pathlib.Path(self._workspace_requirements_file_path).is_file()
|
|
309
|
+
):
|
|
310
|
+
return None
|
|
311
|
+
|
|
312
|
+
return self._workspace_requirements_file_path
|
|
313
|
+
|
|
314
|
+
def _download_file_from_google_cloud_storage(
|
|
315
|
+
self, bucket: str, object_name: str
|
|
316
|
+
) -> Optional[bytes]:
|
|
317
|
+
try:
|
|
318
|
+
bucket_obj = self.gcs_client.bucket(bucket)
|
|
319
|
+
blob = bucket_obj.blob(object_name)
|
|
320
|
+
fileobj = io.BytesIO()
|
|
321
|
+
if blob.exists():
|
|
322
|
+
blob.download_to_file(fileobj)
|
|
323
|
+
return fileobj.getvalue()
|
|
324
|
+
return None
|
|
325
|
+
except Exception as e: # noqa: BLE001
|
|
326
|
+
raise RuntimeError(
|
|
327
|
+
f"Failed to download the working directory from Google Cloud Storage. Error {e!r}"
|
|
328
|
+
"Please validate you have exported cloud credentials with the correct read permissions and the intended bucket exists in your Cloud Storage account. "
|
|
329
|
+
) from e
|
|
330
|
+
|
|
331
|
+
def _download_file_from_s3(self, bucket: str, object_key: str) -> Optional[bytes]:
|
|
332
|
+
try:
|
|
333
|
+
from botocore.exceptions import ClientError
|
|
334
|
+
except Exception: # noqa: BLE001
|
|
335
|
+
raise RuntimeError(
|
|
336
|
+
"Could not download file from S3: Could not import the Amazon S3 Python API via `import boto3`. Please check your installation or try running `pip install boto3`."
|
|
337
|
+
)
|
|
338
|
+
try:
|
|
339
|
+
fileobj = io.BytesIO()
|
|
340
|
+
self.s3_client.download_fileobj(bucket, object_key, fileobj)
|
|
341
|
+
return fileobj.getvalue()
|
|
342
|
+
except ClientError as e:
|
|
343
|
+
if e.response["Error"]["Code"] == "404":
|
|
344
|
+
return None
|
|
345
|
+
raise
|
|
346
|
+
except Exception as e: # noqa: BLE001
|
|
347
|
+
raise RuntimeError(
|
|
348
|
+
f"Failed to download the working directory from S3. Error {e!r}"
|
|
349
|
+
"Please validate you have exported cloud credentials with the correct read permissions and the intended bucket exists in your S3 account. "
|
|
350
|
+
) from e
|
|
351
|
+
|
|
352
|
+
def _download_file_from_remote_storage(self, remote_uri: str) -> Optional[bytes]:
|
|
353
|
+
parsed_uri = urlparse(remote_uri)
|
|
354
|
+
service = parsed_uri.scheme
|
|
355
|
+
bucket = parsed_uri.netloc
|
|
356
|
+
object_name = parsed_uri.path.lstrip("/")
|
|
357
|
+
if service == "s3":
|
|
358
|
+
return self._download_file_from_s3(bucket, object_name)
|
|
359
|
+
if service == "gs":
|
|
360
|
+
return self._download_file_from_google_cloud_storage(bucket, object_name)
|
|
361
|
+
return None
|
|
362
|
+
|
|
363
|
+
def get_workspace_env_vars(self) -> Optional[Dict[str, str]]:
|
|
364
|
+
system_storage_path = os.environ.get("ANYSCALE_INTERNAL_SYSTEM_STORAGE", "")
|
|
365
|
+
workspace_id = os.environ.get(WORKSPACE_ID_ENV_VAR, "")
|
|
366
|
+
workspace_artifact_path = (
|
|
367
|
+
os.path.join(
|
|
368
|
+
system_storage_path, "workspace_tracking_dependencies", workspace_id,
|
|
369
|
+
)
|
|
370
|
+
if workspace_id and system_storage_path
|
|
371
|
+
else ""
|
|
372
|
+
)
|
|
373
|
+
workspace_dot_env_path = (
|
|
374
|
+
os.path.join(workspace_artifact_path, ".env")
|
|
375
|
+
if workspace_artifact_path
|
|
376
|
+
else ""
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
if not self.inside_workspace() or not workspace_dot_env_path:
|
|
380
|
+
return None
|
|
381
|
+
|
|
382
|
+
dot_env = self._download_file_from_remote_storage(workspace_dot_env_path)
|
|
383
|
+
if dot_env:
|
|
384
|
+
parsed_dot_env = parse_dot_env_file(dot_env)
|
|
385
|
+
if parsed_dot_env:
|
|
386
|
+
self.logger.info(
|
|
387
|
+
f"Using workspace runtime dependencies env vars: {parsed_dot_env}."
|
|
388
|
+
)
|
|
389
|
+
return parsed_dot_env
|
|
390
|
+
return None
|
|
391
|
+
|
|
392
|
+
@handle_api_exceptions
|
|
393
|
+
def get_current_workspace_cluster(self) -> Optional[Cluster]:
|
|
394
|
+
# Checks for the existence of the ANYSCALE_EXPERIMENTAL_WORKSPACE_ID env var.
|
|
395
|
+
if not is_anyscale_workspace():
|
|
396
|
+
return None
|
|
397
|
+
|
|
398
|
+
if self._current_workspace_cluster is None:
|
|
399
|
+
# Picks up the cluster ID from the ANYSCALE_SESSION_ID env var.
|
|
400
|
+
self._current_workspace_cluster = get_cluster_model_for_current_workspace(
|
|
401
|
+
self._external_api_client
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
return self._current_workspace_cluster
|
|
405
|
+
|
|
406
|
+
def _get_project_id_by_name(
|
|
407
|
+
self, *, parent_cloud_id: Optional[str] = None, name: Optional[str] = None
|
|
408
|
+
) -> str:
|
|
409
|
+
"""Resolves `name`, a project name, to a project ID.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
parent_cloud_id: If specified, return the project that has this `parent_cloud_id`. \
|
|
413
|
+
Else (`None`), return the first project with the given `name`. \
|
|
414
|
+
Defaults to `None`.
|
|
415
|
+
name: The name of the project.
|
|
416
|
+
"""
|
|
417
|
+
# Find if project with name already exists
|
|
418
|
+
matching_projects = self._internal_api_client.find_project_by_project_name_api_v2_projects_find_by_name_get(
|
|
419
|
+
name
|
|
420
|
+
).results
|
|
421
|
+
if len(matching_projects) == 0:
|
|
422
|
+
raise ValueError(f"Project '{name}' was not found.")
|
|
423
|
+
else:
|
|
424
|
+
for project in matching_projects:
|
|
425
|
+
if (
|
|
426
|
+
parent_cloud_id is None
|
|
427
|
+
or project.parent_cloud_id == parent_cloud_id
|
|
428
|
+
):
|
|
429
|
+
return project.id
|
|
430
|
+
raise ValueError(
|
|
431
|
+
f"{len(matching_projects)} project(s) found with name '{name}' and none matched cloud_id '{parent_cloud_id}'"
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
def _get_project_id_by_cloud_id(
|
|
435
|
+
self, *, parent_cloud_id: Optional[str] = None,
|
|
436
|
+
) -> str:
|
|
437
|
+
workspace_cluster = self.get_current_workspace_cluster()
|
|
438
|
+
if workspace_cluster is not None:
|
|
439
|
+
if (
|
|
440
|
+
workspace_cluster.cluster_compute_config is not None
|
|
441
|
+
and workspace_cluster.cluster_compute_config.cloud_id == parent_cloud_id
|
|
442
|
+
):
|
|
443
|
+
return workspace_cluster.project_id
|
|
444
|
+
elif workspace_cluster.cluster_compute_id is not None:
|
|
445
|
+
workspace_cluster_compute = self.get_compute_config(
|
|
446
|
+
workspace_cluster.cluster_compute_id
|
|
447
|
+
)
|
|
448
|
+
if (
|
|
449
|
+
workspace_cluster_compute is not None
|
|
450
|
+
and workspace_cluster_compute.config is not None
|
|
451
|
+
and workspace_cluster_compute.config.cloud_id == parent_cloud_id
|
|
452
|
+
):
|
|
453
|
+
return workspace_cluster.project_id
|
|
454
|
+
|
|
455
|
+
if self._default_project_id_from_cloud_id.get(parent_cloud_id) is None:
|
|
456
|
+
# Cloud isolation organizations follow the permissions model in https://docs.anyscale.com/organization-and-user-account/access-controls
|
|
457
|
+
# TODO(nikita): Remove this FF check after completing the cloud isolation migration in Q2 2024
|
|
458
|
+
cloud_isolation_ff_on = self._internal_api_client.check_is_feature_flag_on_api_v2_userinfo_check_is_feature_flag_on_get(
|
|
459
|
+
"cloud-isolation-phase-1"
|
|
460
|
+
).result.is_on
|
|
461
|
+
default_project: Project = self._external_api_client.get_default_project(
|
|
462
|
+
parent_cloud_id=(parent_cloud_id if cloud_isolation_ff_on else None)
|
|
463
|
+
).result
|
|
464
|
+
self._default_project_id_from_cloud_id[parent_cloud_id] = default_project.id
|
|
465
|
+
|
|
466
|
+
return self._default_project_id_from_cloud_id[parent_cloud_id]
|
|
467
|
+
|
|
468
|
+
@handle_api_exceptions
|
|
469
|
+
def get_project_id(
|
|
470
|
+
self, *, parent_cloud_id: Optional[str] = None, name: Optional[str] = None
|
|
471
|
+
) -> str:
|
|
472
|
+
if name is not None:
|
|
473
|
+
return self._get_project_id_by_name(
|
|
474
|
+
parent_cloud_id=parent_cloud_id, name=name
|
|
475
|
+
)
|
|
476
|
+
else:
|
|
477
|
+
return self._get_project_id_by_cloud_id(parent_cloud_id=parent_cloud_id)
|
|
478
|
+
|
|
479
|
+
def _get_cloud_id_for_compute_config_id(self, compute_config_id: str) -> str:
|
|
480
|
+
cluster_compute: ClusterCompute = self._external_api_client.get_cluster_compute(
|
|
481
|
+
compute_config_id
|
|
482
|
+
).result
|
|
483
|
+
cluster_compute_config: ClusterComputeConfig = cluster_compute.config
|
|
484
|
+
return cluster_compute_config.cloud_id
|
|
485
|
+
|
|
486
|
+
def _get_cloud_id_by_name(self, cloud_name: str) -> Optional[str]:
|
|
487
|
+
try:
|
|
488
|
+
return self._internal_api_client.find_cloud_by_name_api_v2_clouds_find_by_name_post(
|
|
489
|
+
CloudNameOptions(name=cloud_name),
|
|
490
|
+
).result.id
|
|
491
|
+
except InternalApiException as e:
|
|
492
|
+
if e.status == 404:
|
|
493
|
+
return None
|
|
494
|
+
|
|
495
|
+
raise e from None
|
|
496
|
+
|
|
497
|
+
@handle_api_exceptions
|
|
498
|
+
def get_cloud_id(
|
|
499
|
+
self, cloud_name: Optional[str] = None, compute_config_id: Optional[str] = None
|
|
500
|
+
) -> str:
|
|
501
|
+
if cloud_name is not None and compute_config_id is not None:
|
|
502
|
+
raise ValueError(
|
|
503
|
+
"Only one of cloud_name or compute_config_id should be provided."
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
if compute_config_id is not None:
|
|
507
|
+
return self._get_cloud_id_for_compute_config_id(compute_config_id)
|
|
508
|
+
|
|
509
|
+
if cloud_name in self._cloud_id_cache:
|
|
510
|
+
return self._cloud_id_cache[cloud_name]
|
|
511
|
+
|
|
512
|
+
if cloud_name is not None:
|
|
513
|
+
cloud_id = self._get_cloud_id_by_name(cloud_name)
|
|
514
|
+
if cloud_id is None:
|
|
515
|
+
raise RuntimeError(f"Cloud '{cloud_name}' not found.")
|
|
516
|
+
elif self.inside_workspace():
|
|
517
|
+
workspace_cluster = self.get_current_workspace_cluster()
|
|
518
|
+
assert workspace_cluster is not None
|
|
519
|
+
# NOTE(edoakes): the Cluster model has a compute_config_config model that includes
|
|
520
|
+
# its cloud ID, but it's not always populated.
|
|
521
|
+
# TODO(edoakes): add cloud_id to the Cluster model to avoid a second RTT.
|
|
522
|
+
cloud_id = self._get_cloud_id_for_compute_config_id(
|
|
523
|
+
workspace_cluster.cluster_compute_id
|
|
524
|
+
)
|
|
525
|
+
else:
|
|
526
|
+
cloud_id = self._external_api_client.get_default_cloud().result.id
|
|
527
|
+
|
|
528
|
+
assert cloud_id is not None
|
|
529
|
+
self._cloud_id_cache[cloud_name] = cloud_id
|
|
530
|
+
return cloud_id
|
|
531
|
+
|
|
532
|
+
@handle_api_exceptions
|
|
533
|
+
def get_cloud(self, *, cloud_id: str) -> Optional[Cloud]:
|
|
534
|
+
try:
|
|
535
|
+
cloud: Cloud = self._internal_api_client.get_cloud_api_v2_clouds_cloud_id_get(
|
|
536
|
+
cloud_id
|
|
537
|
+
).result
|
|
538
|
+
return cloud
|
|
539
|
+
except InternalApiException as e:
|
|
540
|
+
if e.status == 404:
|
|
541
|
+
return None
|
|
542
|
+
|
|
543
|
+
raise e from None
|
|
544
|
+
|
|
545
|
+
@handle_api_exceptions
|
|
546
|
+
def create_compute_config(
|
|
547
|
+
self, config: ComputeTemplateConfig, *, name: Optional[str] = None
|
|
548
|
+
) -> Tuple[str, str]:
|
|
549
|
+
result: ComputeTemplate = self._internal_api_client.create_compute_template_api_v2_compute_templates_post(
|
|
550
|
+
create_compute_template=CreateComputeTemplate(
|
|
551
|
+
config=config, name=name, anonymous=name is None, new_version=True
|
|
552
|
+
)
|
|
553
|
+
).result
|
|
554
|
+
return f"{result.name}:{result.version}", result.id
|
|
555
|
+
|
|
556
|
+
@handle_api_exceptions
|
|
557
|
+
def get_compute_config(
|
|
558
|
+
self, compute_config_id: str
|
|
559
|
+
) -> Optional[DecoratedComputeTemplate]:
|
|
560
|
+
try:
|
|
561
|
+
return self._internal_api_client.get_compute_template_api_v2_compute_templates_template_id_get(
|
|
562
|
+
compute_config_id
|
|
563
|
+
).result
|
|
564
|
+
except InternalApiException as e:
|
|
565
|
+
if e.status == 404:
|
|
566
|
+
return None
|
|
567
|
+
|
|
568
|
+
raise e from None
|
|
569
|
+
|
|
570
|
+
@handle_api_exceptions
|
|
571
|
+
def get_compute_config_id(
|
|
572
|
+
self,
|
|
573
|
+
compute_config_name: Optional[str] = None,
|
|
574
|
+
*,
|
|
575
|
+
include_archived: bool = False,
|
|
576
|
+
) -> Optional[str]:
|
|
577
|
+
if compute_config_name is not None:
|
|
578
|
+
name, version = parse_cluster_compute_name_version(compute_config_name)
|
|
579
|
+
if version is None:
|
|
580
|
+
# Setting `version=-1` will return only the latest version if there are multiple.
|
|
581
|
+
version = -1
|
|
582
|
+
cluster_computes = self._internal_api_client.search_compute_templates_api_v2_compute_templates_search_post(
|
|
583
|
+
ComputeTemplateQuery(
|
|
584
|
+
orgwide=True,
|
|
585
|
+
name={"equals": name},
|
|
586
|
+
include_anonymous=True,
|
|
587
|
+
archive_status=ArchiveStatus.ALL
|
|
588
|
+
if include_archived
|
|
589
|
+
else ArchiveStatus.NOT_ARCHIVED,
|
|
590
|
+
version=version,
|
|
591
|
+
)
|
|
592
|
+
).results
|
|
593
|
+
|
|
594
|
+
if len(cluster_computes) == 0:
|
|
595
|
+
return None
|
|
596
|
+
|
|
597
|
+
compute_template: DecoratedComputeTemplate = cluster_computes[0]
|
|
598
|
+
return compute_template.id
|
|
599
|
+
|
|
600
|
+
# If the compute config name is not provided, we pick an appropriate default.
|
|
601
|
+
#
|
|
602
|
+
# - If running in a workspace:
|
|
603
|
+
# * If auto_select_worker_config enabled: we switch over to a standardized
|
|
604
|
+
# default compute config (copying over any cluster-level attributes, e.g.
|
|
605
|
+
# max-gpus).
|
|
606
|
+
# * Otherwise, we use the workspace's compute config.
|
|
607
|
+
#
|
|
608
|
+
# - Otherwise, we use the default compute config provided by the API.
|
|
609
|
+
|
|
610
|
+
workspace_cluster = self.get_current_workspace_cluster()
|
|
611
|
+
if workspace_cluster is not None:
|
|
612
|
+
workspace_compute_config: DecoratedComputeTemplate = self.get_compute_config(
|
|
613
|
+
workspace_cluster.cluster_compute_id
|
|
614
|
+
)
|
|
615
|
+
workspace_config: ClusterComputeConfig = workspace_compute_config.config
|
|
616
|
+
if workspace_config.auto_select_worker_config:
|
|
617
|
+
standard_template = self._build_standard_compute_template_from_existing_auto_config(
|
|
618
|
+
workspace_config
|
|
619
|
+
)
|
|
620
|
+
_, compute_config_id = self.create_compute_config(standard_template)
|
|
621
|
+
return compute_config_id
|
|
622
|
+
else:
|
|
623
|
+
return workspace_cluster.cluster_compute_id
|
|
624
|
+
|
|
625
|
+
return self.get_default_compute_config(cloud_id=self.get_cloud_id()).id
|
|
626
|
+
|
|
627
|
+
@handle_api_exceptions
|
|
628
|
+
def archive_compute_config(self, *, compute_config_id):
|
|
629
|
+
self._internal_api_client.archive_compute_template_api_v2_compute_templates_compute_template_id_archive_post(
|
|
630
|
+
compute_config_id
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
@handle_api_exceptions
|
|
634
|
+
def get_default_compute_config(self, *, cloud_id: str) -> ClusterCompute:
|
|
635
|
+
return self._external_api_client.get_default_cluster_compute(
|
|
636
|
+
cloud_id=cloud_id,
|
|
637
|
+
).result
|
|
638
|
+
|
|
639
|
+
def _build_standard_compute_template_from_existing_auto_config(
|
|
640
|
+
self, compute_config: ClusterComputeConfig
|
|
641
|
+
) -> ComputeTemplateConfig:
|
|
642
|
+
"""
|
|
643
|
+
Build a standard compute template config from an existing compute config.
|
|
644
|
+
|
|
645
|
+
1. Pull the default compute template config.
|
|
646
|
+
2. Disable scheduling on the head node.
|
|
647
|
+
3. Enable auto_select_worker_config.
|
|
648
|
+
4. Copy any cluster-level flags from the provided compute config to the template.
|
|
649
|
+
"""
|
|
650
|
+
# Retrieve the default cluster compute config for the cloud.
|
|
651
|
+
default_compute_template: DecoratedComputeTemplate = self._external_api_client.get_default_cluster_compute(
|
|
652
|
+
cloud_id=compute_config.cloud_id,
|
|
653
|
+
).result.config
|
|
654
|
+
|
|
655
|
+
# Disable head node scheduling.
|
|
656
|
+
if default_compute_template.head_node_type.resources is None:
|
|
657
|
+
default_compute_template.head_node_type.resources = {}
|
|
658
|
+
default_compute_template.head_node_type.resources["CPU"] = 0
|
|
659
|
+
|
|
660
|
+
# Ensure auto_select_worker_config is enabled.
|
|
661
|
+
default_compute_template.auto_select_worker_config = True
|
|
662
|
+
|
|
663
|
+
# Copy flags set at a cluster level over to the workspace.
|
|
664
|
+
#
|
|
665
|
+
# NOTE (shomilj): If there are more attributes we want to
|
|
666
|
+
# persist from the provided compute config --> the compute
|
|
667
|
+
# config used for deploying the service, we should copy them
|
|
668
|
+
# over here.
|
|
669
|
+
default_compute_template.flags = compute_config.flags
|
|
670
|
+
|
|
671
|
+
return default_compute_template
|
|
672
|
+
|
|
673
|
+
@handle_api_exceptions
|
|
674
|
+
def get_cluster_env_build(self, build_id: str) -> Optional[ClusterEnvironmentBuild]:
|
|
675
|
+
return self._external_api_client.get_cluster_environment_build(build_id).result
|
|
676
|
+
|
|
677
|
+
@handle_api_exceptions
|
|
678
|
+
def get_cluster_env_build_image_uri(
|
|
679
|
+
self, cluster_env_build_id: str, use_image_alias: bool = False
|
|
680
|
+
) -> Optional[ImageURI]:
|
|
681
|
+
try:
|
|
682
|
+
build: ClusterEnvironmentBuild = self._external_api_client.get_cluster_environment_build(
|
|
683
|
+
cluster_env_build_id
|
|
684
|
+
).result
|
|
685
|
+
cluster_env = self._external_api_client.get_cluster_environment(
|
|
686
|
+
build.cluster_environment_id
|
|
687
|
+
).result
|
|
688
|
+
return ImageURI.from_cluster_env_build(cluster_env, build, use_image_alias)
|
|
689
|
+
except ExternalApiException as e:
|
|
690
|
+
if e.status == 404:
|
|
691
|
+
return None
|
|
692
|
+
|
|
693
|
+
raise e from None
|
|
694
|
+
|
|
695
|
+
@handle_api_exceptions
|
|
696
|
+
def get_default_build_id(self) -> str:
|
|
697
|
+
workspace_cluster = self.get_current_workspace_cluster()
|
|
698
|
+
if workspace_cluster is not None:
|
|
699
|
+
return workspace_cluster.cluster_environment_build_id
|
|
700
|
+
result: ClusterEnvironmentBuild = self._external_api_client.get_default_cluster_environment_build(
|
|
701
|
+
DEFAULT_PYTHON_VERSION, DEFAULT_RAY_VERSION,
|
|
702
|
+
).result
|
|
703
|
+
return result.id
|
|
704
|
+
|
|
705
|
+
@handle_api_exceptions
|
|
706
|
+
def get_cluster_env_by_name(self, name: str) -> Optional[ClusterEnvironment]:
|
|
707
|
+
resp = self._external_api_client.search_cluster_environments(
|
|
708
|
+
ClusterEnvironmentsQuery(
|
|
709
|
+
name=TextQuery(equals=name), # pyright: ignore reportGeneralTypeIssues
|
|
710
|
+
paging=PageQuery(count=1),
|
|
711
|
+
include_anonymous=True,
|
|
712
|
+
)
|
|
713
|
+
)
|
|
714
|
+
if resp.results:
|
|
715
|
+
return resp.results[0]
|
|
716
|
+
return None
|
|
717
|
+
|
|
718
|
+
@handle_api_exceptions
|
|
719
|
+
def list_cluster_env_builds(
|
|
720
|
+
self, cluster_env_id: str,
|
|
721
|
+
) -> Generator[ClusterEnvironmentBuild, None, None]:
|
|
722
|
+
paging_token = None
|
|
723
|
+
while True:
|
|
724
|
+
resp: ClusterenvironmentbuildListResponse = self._external_api_client.list_cluster_environment_builds(
|
|
725
|
+
cluster_environment_id=cluster_env_id,
|
|
726
|
+
count=self.LIST_ENDPOINT_COUNT,
|
|
727
|
+
paging_token=paging_token,
|
|
728
|
+
desc=True,
|
|
729
|
+
)
|
|
730
|
+
for build in resp.results:
|
|
731
|
+
yield build
|
|
732
|
+
|
|
733
|
+
if resp.metadata.next_paging_token is None:
|
|
734
|
+
break
|
|
735
|
+
paging_token = resp.metadata.next_paging_token
|
|
736
|
+
|
|
737
|
+
def _wait_for_build_to_succeed(
|
|
738
|
+
self, build_id: str, poll_interval_seconds=3, timeout_secs=3600,
|
|
739
|
+
):
|
|
740
|
+
"""Periodically check the status of the build operation until it completes.
|
|
741
|
+
Raise a RuntimeError if the build fails or cancelled.
|
|
742
|
+
Raise a TimeoutError if the build does not complete within the timeout.
|
|
743
|
+
"""
|
|
744
|
+
elapsed_secs = 0
|
|
745
|
+
while elapsed_secs < timeout_secs:
|
|
746
|
+
build = self._external_api_client.get_cluster_environment_build(
|
|
747
|
+
build_id
|
|
748
|
+
).result
|
|
749
|
+
if build.status == ClusterEnvironmentBuildStatus.SUCCEEDED:
|
|
750
|
+
self.logger.info("")
|
|
751
|
+
return
|
|
752
|
+
elif build.status == ClusterEnvironmentBuildStatus.FAILED:
|
|
753
|
+
raise RuntimeError(f"Image build {build_id} failed.")
|
|
754
|
+
elif build.status == ClusterEnvironmentBuildStatus.CANCELED:
|
|
755
|
+
raise RuntimeError(f"Image build {build_id} unexpectedly cancelled.")
|
|
756
|
+
|
|
757
|
+
elapsed_secs += poll_interval_seconds
|
|
758
|
+
self.logger.info(
|
|
759
|
+
f"Waiting for image build to complete. Elapsed time: {elapsed_secs} seconds.",
|
|
760
|
+
end="\r",
|
|
761
|
+
)
|
|
762
|
+
self._sleep(poll_interval_seconds)
|
|
763
|
+
raise TimeoutError(
|
|
764
|
+
f"Timed out waiting for image build {build_id} to complete after {timeout_secs}s."
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
def _find_or_create_cluster_env(
|
|
768
|
+
self,
|
|
769
|
+
cluster_env_name: str,
|
|
770
|
+
anonymous: bool,
|
|
771
|
+
*,
|
|
772
|
+
image_uri: Optional[str] = None,
|
|
773
|
+
registry_login_secret: Optional[str] = None,
|
|
774
|
+
ray_version: Optional[str] = None,
|
|
775
|
+
) -> ClusterEnvironment:
|
|
776
|
+
"""
|
|
777
|
+
Find or create a cluster environment with the given name.
|
|
778
|
+
|
|
779
|
+
There're two possible race conditions:
|
|
780
|
+
1) A tries to create a cluster env with the same name as B, but B has already created it.
|
|
781
|
+
-> A will get a 409 conflict error and should retry to get the existing cluster env.
|
|
782
|
+
2) A and B creates two identical builds under the same cluster env. This would cause job queue to
|
|
783
|
+
reject the job submission.
|
|
784
|
+
-> Cluster env and BYOD build are created within the same transaction, so the latter one will fail
|
|
785
|
+
with a 409 conflict error. The former one will succeed and the latter one should retry to get the
|
|
786
|
+
existing cluster env.
|
|
787
|
+
"""
|
|
788
|
+
existing_cluster_env = self.get_cluster_env_by_name(cluster_env_name)
|
|
789
|
+
if existing_cluster_env is not None:
|
|
790
|
+
return existing_cluster_env
|
|
791
|
+
|
|
792
|
+
try:
|
|
793
|
+
if image_uri:
|
|
794
|
+
# For BYOD builds, we should create a build along with the cluster env.
|
|
795
|
+
cluster_environment = self._external_api_client.create_byod_cluster_environment(
|
|
796
|
+
CreateBYODClusterEnvironment(
|
|
797
|
+
name=cluster_env_name,
|
|
798
|
+
config_json=CreateBYODClusterEnvironmentConfigurationSchema(
|
|
799
|
+
docker_image=image_uri,
|
|
800
|
+
ray_version=ray_version
|
|
801
|
+
if ray_version
|
|
802
|
+
else LATEST_RAY_VERSION,
|
|
803
|
+
registry_login_secret=registry_login_secret,
|
|
804
|
+
),
|
|
805
|
+
anonymous=anonymous,
|
|
806
|
+
)
|
|
807
|
+
).result
|
|
808
|
+
else:
|
|
809
|
+
cluster_environment = self._external_api_client.create_cluster_environment(
|
|
810
|
+
CreateClusterEnvironment(name=cluster_env_name, anonymous=anonymous)
|
|
811
|
+
).result
|
|
812
|
+
return cluster_environment
|
|
813
|
+
except ExternalApiException as e:
|
|
814
|
+
if e.status != 409:
|
|
815
|
+
raise e from None
|
|
816
|
+
# Retry to get the existing cluster env because it might be created by another process.
|
|
817
|
+
existing_cluster_env = self.get_cluster_env_by_name(cluster_env_name)
|
|
818
|
+
if existing_cluster_env is None:
|
|
819
|
+
raise e from None
|
|
820
|
+
return existing_cluster_env
|
|
821
|
+
|
|
822
|
+
@handle_api_exceptions
|
|
823
|
+
def get_cluster_env_build_id_from_containerfile(
|
|
824
|
+
self,
|
|
825
|
+
cluster_env_name: str,
|
|
826
|
+
containerfile: str,
|
|
827
|
+
anonymous: bool = True,
|
|
828
|
+
ray_version: Optional[str] = None,
|
|
829
|
+
) -> str:
|
|
830
|
+
cluster_env = self._find_or_create_cluster_env(
|
|
831
|
+
cluster_env_name, anonymous=anonymous
|
|
832
|
+
)
|
|
833
|
+
for build in self.list_cluster_env_builds(cluster_env.id):
|
|
834
|
+
if (
|
|
835
|
+
build.status == ClusterEnvironmentBuildStatus.SUCCEEDED
|
|
836
|
+
and build.containerfile == containerfile
|
|
837
|
+
# we don't need to check the ray_version because checking the containerfile is enough.
|
|
838
|
+
):
|
|
839
|
+
return build.id
|
|
840
|
+
|
|
841
|
+
try:
|
|
842
|
+
build_op = self._external_api_client.create_cluster_environment_build(
|
|
843
|
+
CreateClusterEnvironmentBuild(
|
|
844
|
+
cluster_environment_id=cluster_env.id,
|
|
845
|
+
containerfile=containerfile,
|
|
846
|
+
ray_version=ray_version, # we don't use the latest version here if ray_version is Noneb/c the backend will try to parse the base image to decide the ray version.
|
|
847
|
+
)
|
|
848
|
+
).result
|
|
849
|
+
except ExternalApiException as e:
|
|
850
|
+
if e.status == 400:
|
|
851
|
+
raise RuntimeError(
|
|
852
|
+
"Invalid containerfile. Please check the syntax and try again.",
|
|
853
|
+
e.body,
|
|
854
|
+
) from None
|
|
855
|
+
|
|
856
|
+
build_url = self.get_build_ui_url(
|
|
857
|
+
cluster_env.id, build_op.cluster_environment_build_id
|
|
858
|
+
)
|
|
859
|
+
self.logger.info(f"Building image. View it in the UI: {build_url}")
|
|
860
|
+
self._wait_for_build_to_succeed(build_op.cluster_environment_build_id)
|
|
861
|
+
self.logger.info("Image build succeeded.")
|
|
862
|
+
|
|
863
|
+
return build_op.cluster_environment_build_id
|
|
864
|
+
|
|
865
|
+
@handle_api_exceptions
|
|
866
|
+
def get_cluster_env_build_id_from_image_uri(
|
|
867
|
+
self,
|
|
868
|
+
image_uri: ImageURI,
|
|
869
|
+
registry_login_secret: Optional[str] = None,
|
|
870
|
+
ray_version: Optional[str] = None,
|
|
871
|
+
name: Optional[str] = None,
|
|
872
|
+
) -> str:
|
|
873
|
+
if image_uri.is_cluster_env_image():
|
|
874
|
+
identifier = image_uri.to_cluster_env_identifier()
|
|
875
|
+
try:
|
|
876
|
+
build = self._external_api_client.find_cluster_environment_build_by_identifier(
|
|
877
|
+
identifier=identifier
|
|
878
|
+
).result
|
|
879
|
+
if build.status == ClusterEnvironmentBuildStatus.SUCCEEDED:
|
|
880
|
+
return build.id
|
|
881
|
+
else:
|
|
882
|
+
raise RuntimeError(
|
|
883
|
+
f"Legacy cluster environment build '{identifier}' is not a successful build."
|
|
884
|
+
)
|
|
885
|
+
except ExternalApiException as e:
|
|
886
|
+
if e.status == 404:
|
|
887
|
+
raise RuntimeError(
|
|
888
|
+
f"Legacy cluster environment '{identifier}' is not found."
|
|
889
|
+
)
|
|
890
|
+
elif image_uri.is_default_image():
|
|
891
|
+
# Default image
|
|
892
|
+
cluster_envs = self._internal_api_client.list_application_templates_api_v2_application_templates_get(
|
|
893
|
+
image_name_contains=image_uri.image_uri
|
|
894
|
+
).results
|
|
895
|
+
for cluster_env in cluster_envs:
|
|
896
|
+
if (
|
|
897
|
+
cluster_env.latest_build is not None
|
|
898
|
+
and cluster_env.latest_build.docker_image_name
|
|
899
|
+
== image_uri.image_uri
|
|
900
|
+
):
|
|
901
|
+
return cluster_env.latest_build.id
|
|
902
|
+
raise RuntimeError(f"Default image '{image_uri.image_uri}' is not found.")
|
|
903
|
+
|
|
904
|
+
# BYOD image
|
|
905
|
+
cluster_env_name = name if name else image_uri.to_cluster_env_name()
|
|
906
|
+
image_uri_str = str(image_uri)
|
|
907
|
+
cluster_env = self._find_or_create_cluster_env(
|
|
908
|
+
cluster_env_name,
|
|
909
|
+
anonymous=not name,
|
|
910
|
+
image_uri=image_uri_str,
|
|
911
|
+
registry_login_secret=registry_login_secret,
|
|
912
|
+
ray_version=ray_version,
|
|
913
|
+
)
|
|
914
|
+
for build in self.list_cluster_env_builds(cluster_env.id):
|
|
915
|
+
if (
|
|
916
|
+
build.docker_image_name == image_uri_str
|
|
917
|
+
and build.registry_login_secret == registry_login_secret
|
|
918
|
+
and build.status == ClusterEnvironmentBuildStatus.SUCCEEDED
|
|
919
|
+
and (ray_version is None or build.ray_version == ray_version)
|
|
920
|
+
):
|
|
921
|
+
return build.id
|
|
922
|
+
|
|
923
|
+
# Still create a new build if the cluster env already exists but the build does not match the image_uri.
|
|
924
|
+
result = self._external_api_client.create_cluster_environment_build(
|
|
925
|
+
CreateClusterEnvironmentBuild(
|
|
926
|
+
# For historical reasons, we have to use docker_image_name instead of image_uri; but it is just a URI to the image.
|
|
927
|
+
cluster_environment_id=cluster_env.id,
|
|
928
|
+
docker_image_name=image_uri_str,
|
|
929
|
+
registry_login_secret=registry_login_secret,
|
|
930
|
+
ray_version=ray_version if ray_version else LATEST_RAY_VERSION,
|
|
931
|
+
)
|
|
932
|
+
).result
|
|
933
|
+
|
|
934
|
+
assert result.completed
|
|
935
|
+
return result.cluster_environment_build_id
|
|
936
|
+
|
|
937
|
+
@handle_api_exceptions
|
|
938
|
+
def send_workspace_notification(
|
|
939
|
+
self, notification: WorkspaceNotification,
|
|
940
|
+
):
|
|
941
|
+
if not self.inside_workspace():
|
|
942
|
+
return
|
|
943
|
+
|
|
944
|
+
try:
|
|
945
|
+
r = requests.post(WORKSPACE_NOTIFICATION_ADDRESS, json=notification.dict())
|
|
946
|
+
r.raise_for_status()
|
|
947
|
+
except Exception:
|
|
948
|
+
internal_logger.exception(
|
|
949
|
+
"Failed to send workspace notification. "
|
|
950
|
+
"This should not happen, so please contact Anyscale support."
|
|
951
|
+
)
|
|
952
|
+
|
|
953
|
+
@handle_api_exceptions
|
|
954
|
+
def get_service(
|
|
955
|
+
self, name: str, *, cloud: Optional[str], project: Optional[str]
|
|
956
|
+
) -> Optional[ServiceModel]:
|
|
957
|
+
# TODO(edoakes): this endpoint is very slow and there's no reason we should need
|
|
958
|
+
# to use this complex list endpoint just to fetch a service by name.
|
|
959
|
+
paging_token = None
|
|
960
|
+
cloud_id = self.get_cloud_id(cloud_name=cloud)
|
|
961
|
+
project_id = self.get_project_id(parent_cloud_id=cloud_id, name=project)
|
|
962
|
+
service: Optional[ServiceModel] = None
|
|
963
|
+
while True:
|
|
964
|
+
resp = self._external_api_client.list_services(
|
|
965
|
+
project_id=project_id,
|
|
966
|
+
name=name,
|
|
967
|
+
count=self.LIST_ENDPOINT_COUNT,
|
|
968
|
+
paging_token=paging_token,
|
|
969
|
+
)
|
|
970
|
+
for result in resp.results:
|
|
971
|
+
if result.name == name:
|
|
972
|
+
service = result
|
|
973
|
+
break
|
|
974
|
+
|
|
975
|
+
paging_token = resp.metadata.next_paging_token
|
|
976
|
+
if service is not None or paging_token is None:
|
|
977
|
+
break
|
|
978
|
+
|
|
979
|
+
return service
|
|
980
|
+
|
|
981
|
+
@handle_api_exceptions
|
|
982
|
+
def get_project(self, project_id: str) -> Optional[Project]:
|
|
983
|
+
return self._internal_api_client.get_project_api_v2_projects_project_id_get(
|
|
984
|
+
project_id
|
|
985
|
+
).result
|
|
986
|
+
|
|
987
|
+
@handle_api_exceptions
|
|
988
|
+
def get_job(
|
|
989
|
+
self,
|
|
990
|
+
*,
|
|
991
|
+
name: Optional[str],
|
|
992
|
+
job_id: Optional[str],
|
|
993
|
+
cloud: Optional[str],
|
|
994
|
+
project: Optional[str],
|
|
995
|
+
) -> Optional[ProductionJob]:
|
|
996
|
+
if job_id is not None:
|
|
997
|
+
try:
|
|
998
|
+
return self._external_api_client.get_production_job(job_id).result
|
|
999
|
+
except ExternalApiException as e:
|
|
1000
|
+
if e.status == 404:
|
|
1001
|
+
return None
|
|
1002
|
+
raise e from None
|
|
1003
|
+
else:
|
|
1004
|
+
paging_token = None
|
|
1005
|
+
cloud_id = self.get_cloud_id(cloud_name=cloud)
|
|
1006
|
+
project_id = self.get_project_id(parent_cloud_id=cloud_id, name=project)
|
|
1007
|
+
result: Optional[ProductionJob] = None
|
|
1008
|
+
while True:
|
|
1009
|
+
resp = self._external_api_client.list_production_jobs(
|
|
1010
|
+
project_id=project_id,
|
|
1011
|
+
name=name,
|
|
1012
|
+
count=self.LIST_ENDPOINT_COUNT,
|
|
1013
|
+
paging_token=paging_token,
|
|
1014
|
+
)
|
|
1015
|
+
for job in resp.results:
|
|
1016
|
+
if (
|
|
1017
|
+
job is not None
|
|
1018
|
+
and job.name == name
|
|
1019
|
+
and (result is None or job.created_at > result.created_at)
|
|
1020
|
+
):
|
|
1021
|
+
result = job
|
|
1022
|
+
|
|
1023
|
+
paging_token = resp.metadata.next_paging_token
|
|
1024
|
+
if paging_token is None:
|
|
1025
|
+
break
|
|
1026
|
+
|
|
1027
|
+
return result
|
|
1028
|
+
|
|
1029
|
+
@handle_api_exceptions
|
|
1030
|
+
def get_job_runs(self, job_id: str) -> List[APIJobRun]:
|
|
1031
|
+
job_runs: List[APIJobRun] = search_entities(
|
|
1032
|
+
self._external_api_client.search_jobs,
|
|
1033
|
+
JobsQuery(
|
|
1034
|
+
ha_job_id=job_id,
|
|
1035
|
+
show_ray_client_runs_only=False,
|
|
1036
|
+
sort_by_clauses=[
|
|
1037
|
+
SortByClauseJobsSortField(
|
|
1038
|
+
sort_field=JobsSortField.CREATED_AT, sort_order=SortOrder.ASC,
|
|
1039
|
+
)
|
|
1040
|
+
],
|
|
1041
|
+
paging=PageQuery(),
|
|
1042
|
+
),
|
|
1043
|
+
)
|
|
1044
|
+
return job_runs
|
|
1045
|
+
|
|
1046
|
+
@handle_api_exceptions
|
|
1047
|
+
def rollout_service(self, model: ApplyServiceModel) -> ServiceModel:
|
|
1048
|
+
result: ServiceModel = self._external_api_client.rollout_service(model).result
|
|
1049
|
+
return result
|
|
1050
|
+
|
|
1051
|
+
@handle_api_exceptions
|
|
1052
|
+
def rollback_service(
|
|
1053
|
+
self, service_id: str, *, max_surge_percent: Optional[int] = None
|
|
1054
|
+
) -> ServiceModel:
|
|
1055
|
+
result: ServiceModel = self._external_api_client.rollback_service(
|
|
1056
|
+
service_id,
|
|
1057
|
+
rollback_service_model=RollbackServiceModel(
|
|
1058
|
+
max_surge_percent=max_surge_percent
|
|
1059
|
+
),
|
|
1060
|
+
)
|
|
1061
|
+
return result
|
|
1062
|
+
|
|
1063
|
+
@handle_api_exceptions
|
|
1064
|
+
def terminate_service(self, service_id: str) -> ServiceModel:
|
|
1065
|
+
result: ServiceModel = self._external_api_client.terminate_service(service_id)
|
|
1066
|
+
return result
|
|
1067
|
+
|
|
1068
|
+
@handle_api_exceptions
|
|
1069
|
+
def submit_job(self, model: CreateInternalProductionJob) -> InternalProductionJob:
|
|
1070
|
+
job: InternalProductionJob = self._internal_api_client.create_job_api_v2_decorated_ha_jobs_create_post(
|
|
1071
|
+
model,
|
|
1072
|
+
).result
|
|
1073
|
+
return job
|
|
1074
|
+
|
|
1075
|
+
@handle_api_exceptions
|
|
1076
|
+
def terminate_job(self, job_id: str):
|
|
1077
|
+
self._external_api_client.terminate_job(job_id)
|
|
1078
|
+
|
|
1079
|
+
@handle_api_exceptions
|
|
1080
|
+
def archive_job(self, job_id: str):
|
|
1081
|
+
self._internal_api_client.archive_job_api_v2_decorated_ha_jobs_production_job_id_archive_post(
|
|
1082
|
+
job_id
|
|
1083
|
+
)
|
|
1084
|
+
|
|
1085
|
+
@handle_api_exceptions
|
|
1086
|
+
def upload_local_dir_to_cloud_storage(
|
|
1087
|
+
self,
|
|
1088
|
+
local_dir: str,
|
|
1089
|
+
*,
|
|
1090
|
+
cloud_id: str,
|
|
1091
|
+
excludes: Optional[List[str]] = None,
|
|
1092
|
+
overwrite_existing_file: bool = OVERWRITE_EXISTING_CLOUD_STORAGE_FILES,
|
|
1093
|
+
) -> str:
|
|
1094
|
+
if not pathlib.Path(local_dir).is_dir():
|
|
1095
|
+
raise RuntimeError(f"Path '{local_dir}' is not a valid directory.")
|
|
1096
|
+
|
|
1097
|
+
with zip_local_dir(local_dir, excludes=excludes) as (
|
|
1098
|
+
_,
|
|
1099
|
+
zip_file_bytes,
|
|
1100
|
+
content_hash,
|
|
1101
|
+
):
|
|
1102
|
+
file_name = RUNTIME_ENV_PACKAGE_FORMAT.format(content_hash=content_hash)
|
|
1103
|
+
request = CloudDataBucketPresignedUrlRequest(
|
|
1104
|
+
file_type=CloudDataBucketFileType.RUNTIME_ENV_PACKAGES,
|
|
1105
|
+
file_name=file_name,
|
|
1106
|
+
access_mode=CloudDataBucketAccessMode.WRITE,
|
|
1107
|
+
)
|
|
1108
|
+
info: CloudDataBucketPresignedUrlResponse = self._internal_api_client.generate_cloud_data_bucket_presigned_url_api_v2_clouds_cloud_id_generate_cloud_data_bucket_presigned_url_post(
|
|
1109
|
+
cloud_id, request
|
|
1110
|
+
).result
|
|
1111
|
+
|
|
1112
|
+
# Skip the upload entirely if the file already exists.
|
|
1113
|
+
if info.file_exists and not overwrite_existing_file:
|
|
1114
|
+
internal_logger.debug(
|
|
1115
|
+
f"Skipping file upload for '{file_name}' because it already exists in cloud storage."
|
|
1116
|
+
)
|
|
1117
|
+
return info.file_uri
|
|
1118
|
+
|
|
1119
|
+
if info.url_scheme == CloudDataBucketPresignedUrlScheme.SMART_OPEN:
|
|
1120
|
+
# If the presigned URL scheme is SMART_OPEN, upload to cloud storage using the provided bucket name, path, & environment, and the smart_open library.
|
|
1121
|
+
bucket_name = info.bucket_name
|
|
1122
|
+
bucket_path = info.bucket_path
|
|
1123
|
+
file_uri = info.file_uri
|
|
1124
|
+
|
|
1125
|
+
if file_uri and file_uri.startswith("azure"):
|
|
1126
|
+
|
|
1127
|
+
from anyscale.utils.imports.azure import (
|
|
1128
|
+
try_import_azure_storage_blob_BlobServiceClient,
|
|
1129
|
+
)
|
|
1130
|
+
|
|
1131
|
+
# Smartopen needs transport_params to be passed in for Azure.
|
|
1132
|
+
blob_service_client = (
|
|
1133
|
+
try_import_azure_storage_blob_BlobServiceClient()
|
|
1134
|
+
)
|
|
1135
|
+
|
|
1136
|
+
transport_params = {
|
|
1137
|
+
"client": blob_service_client.from_connection_string(info.url),
|
|
1138
|
+
}
|
|
1139
|
+
with smart_open.open(
|
|
1140
|
+
f"{file_uri}", "wb", transport_params=transport_params,
|
|
1141
|
+
) as fout:
|
|
1142
|
+
fout.write(zip_file_bytes)
|
|
1143
|
+
else:
|
|
1144
|
+
env_vars: Dict[str, str] = {
|
|
1145
|
+
"AWS_ENDPOINT_URL": info.url,
|
|
1146
|
+
}
|
|
1147
|
+
with set_env(**env_vars), smart_open.open(
|
|
1148
|
+
f"{bucket_name}/{bucket_path}", "wb",
|
|
1149
|
+
) as fout:
|
|
1150
|
+
fout.write(zip_file_bytes)
|
|
1151
|
+
|
|
1152
|
+
else:
|
|
1153
|
+
# Default to HTTP PUT.
|
|
1154
|
+
internal_logger.debug(f"Uploading file '{file_name}' to cloud storage.")
|
|
1155
|
+
requests.put(info.url, data=zip_file_bytes).raise_for_status()
|
|
1156
|
+
|
|
1157
|
+
return info.file_uri
|
|
1158
|
+
|
|
1159
|
+
def _fetch_log_chunks(self, job_run_id: str) -> Tuple[List[str], Any]:
|
|
1160
|
+
all_log_chunk_urls = []
|
|
1161
|
+
MAX_PAGE_SIZE = 1000
|
|
1162
|
+
next_page_token = None
|
|
1163
|
+
bearer_token = None
|
|
1164
|
+
while True:
|
|
1165
|
+
log_download_result = self._internal_api_client.get_job_logs_download_v2_api_v2_logs_job_logs_download_v2_job_id_get(
|
|
1166
|
+
job_id=job_run_id,
|
|
1167
|
+
next_page_token=next_page_token,
|
|
1168
|
+
page_size=MAX_PAGE_SIZE,
|
|
1169
|
+
).result
|
|
1170
|
+
|
|
1171
|
+
if bearer_token is None:
|
|
1172
|
+
bearer_token = log_download_result.bearer_token
|
|
1173
|
+
|
|
1174
|
+
all_log_chunk_urls.extend(
|
|
1175
|
+
[chunk.chunk_url for chunk in log_download_result.log_chunks]
|
|
1176
|
+
)
|
|
1177
|
+
|
|
1178
|
+
next_page_token = log_download_result.next_page_token
|
|
1179
|
+
if next_page_token is None:
|
|
1180
|
+
break
|
|
1181
|
+
|
|
1182
|
+
return all_log_chunk_urls, bearer_token
|
|
1183
|
+
|
|
1184
|
+
def _fetch_log_chunks_for_controller_logs(
|
|
1185
|
+
self, cluster_id: str
|
|
1186
|
+
) -> Tuple[List[str], Any]:
|
|
1187
|
+
all_log_chunk_urls = []
|
|
1188
|
+
MAX_PAGE_SIZE = 1000
|
|
1189
|
+
next_page_token = None
|
|
1190
|
+
bearer_token = None
|
|
1191
|
+
while True:
|
|
1192
|
+
log_download_result = self._internal_api_client.get_serve_logs_download_api_v2_logs_serve_logs_download_cluster_id_get(
|
|
1193
|
+
cluster_id=cluster_id,
|
|
1194
|
+
next_page_token=next_page_token,
|
|
1195
|
+
page_size=MAX_PAGE_SIZE,
|
|
1196
|
+
).result
|
|
1197
|
+
|
|
1198
|
+
if bearer_token is None:
|
|
1199
|
+
bearer_token = log_download_result.bearer_token
|
|
1200
|
+
|
|
1201
|
+
all_log_chunk_urls.extend(
|
|
1202
|
+
[chunk.chunk_url for chunk in log_download_result.log_chunks]
|
|
1203
|
+
)
|
|
1204
|
+
|
|
1205
|
+
next_page_token = log_download_result.next_page_token
|
|
1206
|
+
if next_page_token is None:
|
|
1207
|
+
break
|
|
1208
|
+
|
|
1209
|
+
return all_log_chunk_urls, bearer_token
|
|
1210
|
+
|
|
1211
|
+
def _read_log_lines(
|
|
1212
|
+
self,
|
|
1213
|
+
log_chunk_urls: List[str],
|
|
1214
|
+
head: bool,
|
|
1215
|
+
bearer_token: Any,
|
|
1216
|
+
max_lines: Optional[int],
|
|
1217
|
+
parse_json: Optional[bool] = None,
|
|
1218
|
+
) -> str:
|
|
1219
|
+
# TODO(mowen): Would be nice to return some placeholder here to allow all new
|
|
1220
|
+
# logs to be read from a particular point onwards.
|
|
1221
|
+
# TODO(aguo): Change this to be a generator to avoid loading all logs into memory at once
|
|
1222
|
+
# and to gradually load logs as needed.
|
|
1223
|
+
|
|
1224
|
+
def parse_json_line(line: str) -> str:
|
|
1225
|
+
json_line = json.loads(line)
|
|
1226
|
+
# This is the default schema for ray core logger but users
|
|
1227
|
+
# could technically use any schema they want for structured logs.
|
|
1228
|
+
# Fall back to spitting out the json in the worst-case scenario.
|
|
1229
|
+
if "asctime" in json_line and "message" in json_line:
|
|
1230
|
+
return f"{json_line['asctime']} {json_line['message']}"
|
|
1231
|
+
elif "message" in json_line:
|
|
1232
|
+
return json_line["message"]
|
|
1233
|
+
# This is the worst-case scenario but very unlikely. Users would
|
|
1234
|
+
# not likely be changing "message" to something-else.
|
|
1235
|
+
return line
|
|
1236
|
+
|
|
1237
|
+
result_lines: List[str] = []
|
|
1238
|
+
step = 1 if head else -1
|
|
1239
|
+
line_count = 0
|
|
1240
|
+
for chunk_url in log_chunk_urls[::step]:
|
|
1241
|
+
log_lines = _download_log_from_s3_url_sync(
|
|
1242
|
+
chunk_url, bearer_token=bearer_token
|
|
1243
|
+
).splitlines()
|
|
1244
|
+
|
|
1245
|
+
if max_lines is not None:
|
|
1246
|
+
num_lines_to_add = min(len(log_lines), max_lines - line_count)
|
|
1247
|
+
else:
|
|
1248
|
+
num_lines_to_add = len(log_lines)
|
|
1249
|
+
line_count += num_lines_to_add
|
|
1250
|
+
|
|
1251
|
+
if head:
|
|
1252
|
+
lines_to_add = log_lines[:num_lines_to_add]
|
|
1253
|
+
else:
|
|
1254
|
+
lines_to_add = log_lines[-1 * num_lines_to_add :]
|
|
1255
|
+
|
|
1256
|
+
if parse_json is not False:
|
|
1257
|
+
try:
|
|
1258
|
+
lines_to_add = [parse_json_line(line) for line in lines_to_add]
|
|
1259
|
+
except json.JSONDecodeError:
|
|
1260
|
+
if parse_json is True:
|
|
1261
|
+
raise ValueError(
|
|
1262
|
+
"Failed to parse logs as JSON. Logs are not all in JSON format."
|
|
1263
|
+
)
|
|
1264
|
+
# If we fail to parse_json, we should always just use plain text going forward.
|
|
1265
|
+
parse_json = False
|
|
1266
|
+
# lines_to_add should already be plain text, so continue on...
|
|
1267
|
+
|
|
1268
|
+
if head:
|
|
1269
|
+
result_lines = result_lines + lines_to_add
|
|
1270
|
+
else:
|
|
1271
|
+
result_lines = lines_to_add + result_lines
|
|
1272
|
+
|
|
1273
|
+
if line_count == max_lines:
|
|
1274
|
+
break
|
|
1275
|
+
|
|
1276
|
+
return "\n".join(result_lines) + "\n"
|
|
1277
|
+
|
|
1278
|
+
@handle_api_exceptions
|
|
1279
|
+
def logs_for_job_run(
|
|
1280
|
+
self,
|
|
1281
|
+
job_run_id: str,
|
|
1282
|
+
head: bool = False,
|
|
1283
|
+
max_lines: Optional[int] = None,
|
|
1284
|
+
parse_json: Optional[bool] = None,
|
|
1285
|
+
) -> str:
|
|
1286
|
+
"""
|
|
1287
|
+
Retrieves logs from the streaming job logs folder in S3/GCS
|
|
1288
|
+
Args:
|
|
1289
|
+
- parse_json: If true, we will always attempt to parse the logs as JSON.
|
|
1290
|
+
If false, we will always attempt to parse the logs as text. If None, we
|
|
1291
|
+
will attempt to parse the logs as JSON and fall back to text if parsing
|
|
1292
|
+
fails.
|
|
1293
|
+
"""
|
|
1294
|
+
|
|
1295
|
+
all_log_chunk_urls, bearer_token = self._fetch_log_chunks(job_run_id)
|
|
1296
|
+
|
|
1297
|
+
logs = self._read_log_lines(
|
|
1298
|
+
all_log_chunk_urls, head, bearer_token, max_lines, parse_json=parse_json
|
|
1299
|
+
)
|
|
1300
|
+
return logs
|
|
1301
|
+
|
|
1302
|
+
@handle_api_exceptions
|
|
1303
|
+
def controller_logs_for_service_version(
|
|
1304
|
+
self,
|
|
1305
|
+
service_version: ProductionServiceV2VersionModel,
|
|
1306
|
+
head: bool = False,
|
|
1307
|
+
max_lines: Optional[int] = None,
|
|
1308
|
+
parse_json: Optional[bool] = None,
|
|
1309
|
+
) -> str:
|
|
1310
|
+
"""
|
|
1311
|
+
Returns the controller logs associated with a particular service version.
|
|
1312
|
+
|
|
1313
|
+
Args:
|
|
1314
|
+
- parse_json: If true, we will always attempt to parse the logs as JSON.
|
|
1315
|
+
If false, we will always attempt to parse the logs as text. If None, we
|
|
1316
|
+
will attempt to parse the logs as JSON and fall back to text if parsing
|
|
1317
|
+
fails.
|
|
1318
|
+
"""
|
|
1319
|
+
service_version_id = service_version.id[-8:]
|
|
1320
|
+
if not len(service_version.production_job_ids):
|
|
1321
|
+
raise ValueError(
|
|
1322
|
+
f"Service version '{service_version_id}' canary version is not ready. Please try again later..."
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
job = self._internal_api_client.get_job_api_v2_decorated_ha_jobs_production_job_id_get(
|
|
1326
|
+
service_version.production_job_ids[0]
|
|
1327
|
+
).result
|
|
1328
|
+
|
|
1329
|
+
if not job:
|
|
1330
|
+
raise ValueError(
|
|
1331
|
+
f"Service version '{service_version_id}' canary version is not ready. Please try again later..."
|
|
1332
|
+
)
|
|
1333
|
+
|
|
1334
|
+
if not job.state.cluster:
|
|
1335
|
+
raise ValueError(
|
|
1336
|
+
f"Service version '{service_version_id}' canary version is not ready. Please try again later..."
|
|
1337
|
+
)
|
|
1338
|
+
|
|
1339
|
+
cluster_id = job.state.cluster.id
|
|
1340
|
+
|
|
1341
|
+
all_log_chunk_urls, bearer_token = self._fetch_log_chunks_for_controller_logs(
|
|
1342
|
+
cluster_id
|
|
1343
|
+
)
|
|
1344
|
+
|
|
1345
|
+
logs = self._read_log_lines(
|
|
1346
|
+
all_log_chunk_urls, head, bearer_token, max_lines, parse_json=parse_json
|
|
1347
|
+
)
|
|
1348
|
+
return logs
|
|
1349
|
+
|
|
1350
|
+
@handle_api_exceptions
|
|
1351
|
+
def apply_schedule(self, model: CreateSchedule) -> DecoratedSchedule:
|
|
1352
|
+
return self._internal_api_client.create_or_update_job_api_v2_experimental_cron_jobs_put(
|
|
1353
|
+
model
|
|
1354
|
+
).result
|
|
1355
|
+
|
|
1356
|
+
@handle_api_exceptions
|
|
1357
|
+
def get_schedule(
|
|
1358
|
+
self,
|
|
1359
|
+
*,
|
|
1360
|
+
name: Optional[str],
|
|
1361
|
+
id: Optional[str], # noqa: A002
|
|
1362
|
+
cloud: Optional[str],
|
|
1363
|
+
project: Optional[str],
|
|
1364
|
+
) -> Optional[DecoratedSchedule]:
|
|
1365
|
+
if id is not None:
|
|
1366
|
+
try:
|
|
1367
|
+
return self._internal_api_client.get_cron_job_api_v2_experimental_cron_jobs_cron_job_id_get(
|
|
1368
|
+
id
|
|
1369
|
+
).result
|
|
1370
|
+
except ExternalApiException as e:
|
|
1371
|
+
if e.status == 404:
|
|
1372
|
+
return None
|
|
1373
|
+
raise e from None
|
|
1374
|
+
else:
|
|
1375
|
+
paging_token = None
|
|
1376
|
+
cloud_id = self.get_cloud_id(cloud_name=cloud)
|
|
1377
|
+
project_id = self.get_project_id(parent_cloud_id=cloud_id, name=project)
|
|
1378
|
+
result: Optional[DecoratedSchedule] = None
|
|
1379
|
+
while True:
|
|
1380
|
+
resp = self._internal_api_client.list_cron_jobs_api_v2_experimental_cron_jobs_get(
|
|
1381
|
+
project_id=project_id,
|
|
1382
|
+
name=name,
|
|
1383
|
+
count=self.LIST_ENDPOINT_COUNT,
|
|
1384
|
+
paging_token=paging_token,
|
|
1385
|
+
)
|
|
1386
|
+
for schedule in resp.results:
|
|
1387
|
+
if schedule is not None and schedule.name == name:
|
|
1388
|
+
result = schedule
|
|
1389
|
+
break
|
|
1390
|
+
|
|
1391
|
+
paging_token = resp.metadata.next_paging_token
|
|
1392
|
+
if paging_token is None:
|
|
1393
|
+
break
|
|
1394
|
+
|
|
1395
|
+
return result
|
|
1396
|
+
|
|
1397
|
+
@handle_api_exceptions
|
|
1398
|
+
def set_schedule_state(self, id: str, is_paused: bool): # noqa: A002
|
|
1399
|
+
self._internal_api_client.pause_cron_job_api_v2_experimental_cron_jobs_cron_job_id_pause_post(
|
|
1400
|
+
id, {"is_paused": is_paused}
|
|
1401
|
+
).result
|
|
1402
|
+
|
|
1403
|
+
@handle_api_exceptions
|
|
1404
|
+
def trigger_schedule(self, id: str): # noqa: A002
|
|
1405
|
+
self._internal_api_client.trigger_cron_job_api_v2_experimental_cron_jobs_cron_job_id_trigger_post(
|
|
1406
|
+
id
|
|
1407
|
+
)
|
|
1408
|
+
|
|
1409
|
+
@handle_api_exceptions
|
|
1410
|
+
def get_dataset(self, name: str, version: Optional[int], project: Optional[str]):
|
|
1411
|
+
project_id = self._source_project_id(project)
|
|
1412
|
+
internal_dataset = self._internal_api_client.find_dataset_api_v2_datasets_find_get(
|
|
1413
|
+
name=name, version=version, project_id=project_id
|
|
1414
|
+
).result
|
|
1415
|
+
from anyscale.llm.dataset._private.models import Dataset
|
|
1416
|
+
|
|
1417
|
+
dataset = Dataset.parse_from_internal_model(internal_dataset)
|
|
1418
|
+
return dataset
|
|
1419
|
+
|
|
1420
|
+
def upload_dataset(
|
|
1421
|
+
self,
|
|
1422
|
+
dataset_file: str,
|
|
1423
|
+
name: Optional[str],
|
|
1424
|
+
description: Optional[str],
|
|
1425
|
+
cloud: Optional[str],
|
|
1426
|
+
project: Optional[str],
|
|
1427
|
+
):
|
|
1428
|
+
# Resolve `~/.../file` to `/home/user/.../file`
|
|
1429
|
+
dataset_file = os.path.expanduser(dataset_file)
|
|
1430
|
+
|
|
1431
|
+
if not os.path.isfile(dataset_file):
|
|
1432
|
+
raise ValueError(f"Path '{dataset_file}' is not a valid file.")
|
|
1433
|
+
dataset_file_size = os.path.getsize(dataset_file)
|
|
1434
|
+
if dataset_file_size > 5 * Bytes.GB:
|
|
1435
|
+
raise ValueError(
|
|
1436
|
+
f"File '{dataset_file}' is too large to upload. The maximum size is 5 GB."
|
|
1437
|
+
)
|
|
1438
|
+
project_id = self._get_project_id_by_name(name=project) if project else None
|
|
1439
|
+
cloud_id = self.get_cloud_id(cloud_name=cloud) if cloud else None
|
|
1440
|
+
|
|
1441
|
+
with FileDownloadProgress() as progress:
|
|
1442
|
+
task_id = progress.add_task(
|
|
1443
|
+
description=f"Creating an upload request for '{dataset_file}'",
|
|
1444
|
+
total=dataset_file_size,
|
|
1445
|
+
)
|
|
1446
|
+
_, project_id = source_cloud_id_and_project_id(
|
|
1447
|
+
internal_api=self._internal_api_client,
|
|
1448
|
+
external_api=self._external_api_client,
|
|
1449
|
+
cloud_id=cloud_id,
|
|
1450
|
+
project_id=project_id,
|
|
1451
|
+
)
|
|
1452
|
+
dataset_upload: DatasetUpload = self._internal_api_client.create_dataset_upload_api_v2_datasets_upload_post(
|
|
1453
|
+
create_dataset=CreateDataset(
|
|
1454
|
+
filename=os.path.basename(dataset_file),
|
|
1455
|
+
description=description,
|
|
1456
|
+
name=name,
|
|
1457
|
+
project_id=project_id,
|
|
1458
|
+
)
|
|
1459
|
+
).result
|
|
1460
|
+
|
|
1461
|
+
progress.update(task_id, description=f"Uploading '{dataset_file}'")
|
|
1462
|
+
|
|
1463
|
+
with open(dataset_file, "rb") as file_reader:
|
|
1464
|
+
progress_reader = ProgressFileReader(file_reader, progress, task_id)
|
|
1465
|
+
response = requests.put(
|
|
1466
|
+
dataset_upload.upload_url, data=progress_reader,
|
|
1467
|
+
)
|
|
1468
|
+
response.raise_for_status()
|
|
1469
|
+
|
|
1470
|
+
progress.update(task_id, completed=os.path.getsize(dataset_file))
|
|
1471
|
+
progress.console.print(
|
|
1472
|
+
"Upload complete!", style=Style(bold=True, color="green")
|
|
1473
|
+
)
|
|
1474
|
+
internal_dataset = dataset_upload.dataset
|
|
1475
|
+
from anyscale.llm.dataset._private.models import Dataset
|
|
1476
|
+
|
|
1477
|
+
dataset = Dataset.parse_from_internal_model(internal_dataset)
|
|
1478
|
+
return dataset
|
|
1479
|
+
|
|
1480
|
+
@handle_api_exceptions
|
|
1481
|
+
def download_dataset(
|
|
1482
|
+
self, name: str, version: Optional[int], project: Optional[str]
|
|
1483
|
+
) -> bytes:
|
|
1484
|
+
project_id = self._source_project_id(project)
|
|
1485
|
+
with FileDownloadProgress() as progress:
|
|
1486
|
+
task_id = progress.add_task(
|
|
1487
|
+
description=f"Getting download info for '{name}'",
|
|
1488
|
+
)
|
|
1489
|
+
download_url: str = self._internal_api_client.get_dataset_download_url_api_v2_datasets_download_get(
|
|
1490
|
+
name, version=version, project_id=project_id,
|
|
1491
|
+
)
|
|
1492
|
+
progress.update(task_id, description=f"Downloading '{name}'")
|
|
1493
|
+
response = requests.get(download_url, stream=True)
|
|
1494
|
+
total_size = int(response.headers.get("content-length", 0))
|
|
1495
|
+
progress.update(task_id, total=total_size)
|
|
1496
|
+
|
|
1497
|
+
# For CLI, consider writing to disk instead of loading the entire file into memory.
|
|
1498
|
+
dataset_bytes = b""
|
|
1499
|
+
for data in response.iter_content(Bytes.MB):
|
|
1500
|
+
dataset_bytes += data
|
|
1501
|
+
progress.update(task_id, advance=len(data))
|
|
1502
|
+
|
|
1503
|
+
progress.update(task_id, completed=total_size)
|
|
1504
|
+
progress.console.print(
|
|
1505
|
+
"Download complete!", style=Style(bold=True, color="green")
|
|
1506
|
+
)
|
|
1507
|
+
|
|
1508
|
+
return dataset_bytes
|
|
1509
|
+
|
|
1510
|
+
@handle_api_exceptions
|
|
1511
|
+
def list_datasets(
|
|
1512
|
+
self,
|
|
1513
|
+
limit: Optional[int] = None,
|
|
1514
|
+
after: Optional[str] = None, # Unique ID to start listing after
|
|
1515
|
+
name_contains: Optional[str] = None,
|
|
1516
|
+
cloud: Optional[str] = None,
|
|
1517
|
+
project: Optional[str] = None,
|
|
1518
|
+
):
|
|
1519
|
+
project_id = self._source_project_id(project)
|
|
1520
|
+
cloud_id = self.get_cloud_id(cloud_name=cloud) if cloud else None
|
|
1521
|
+
|
|
1522
|
+
def get_next_page(
|
|
1523
|
+
after_id: Optional[str],
|
|
1524
|
+
) -> InternalListResponse[InternalDataset]:
|
|
1525
|
+
internal_datasets: InternalListResponse = self._internal_api_client.list_datasets_api_v2_datasets_get(
|
|
1526
|
+
project_id=project_id,
|
|
1527
|
+
cloud_id=cloud_id,
|
|
1528
|
+
name_contains=name_contains,
|
|
1529
|
+
after=after_id,
|
|
1530
|
+
)
|
|
1531
|
+
return internal_datasets
|
|
1532
|
+
|
|
1533
|
+
from anyscale.llm.dataset._private.models import Dataset
|
|
1534
|
+
|
|
1535
|
+
list_response = ListResponse(
|
|
1536
|
+
after=after, limit=limit, get_next_page=get_next_page, cls=Dataset,
|
|
1537
|
+
)
|
|
1538
|
+
return list_response
|
|
1539
|
+
|
|
1540
|
+
def _source_project_id(self, project_name: Optional[str]) -> Optional[str]:
|
|
1541
|
+
"""Sources a optional project ID from an optionally-provided project name."""
|
|
1542
|
+
if project_name:
|
|
1543
|
+
project_id = self._get_project_id_by_name(name=project_name)
|
|
1544
|
+
else:
|
|
1545
|
+
project_id = None
|
|
1546
|
+
return project_id
|
|
1547
|
+
|
|
1548
|
+
@handle_api_exceptions
|
|
1549
|
+
def get_finetuned_model(
|
|
1550
|
+
self, model_id: Optional[str], job_id: Optional[str]
|
|
1551
|
+
) -> FineTunedModel:
|
|
1552
|
+
if model_id:
|
|
1553
|
+
return self._internal_api_client.get_model_api_v2_llm_models_model_id_get(
|
|
1554
|
+
model_id
|
|
1555
|
+
).result
|
|
1556
|
+
elif job_id:
|
|
1557
|
+
return self._internal_api_client.get_model_by_job_id_api_v2_llm_models_get_by_job_id_job_id_get(
|
|
1558
|
+
job_id
|
|
1559
|
+
).result
|
|
1560
|
+
else:
|
|
1561
|
+
raise ValueError("Atleast one of `model_id` or `job_id` must be provided")
|
|
1562
|
+
|
|
1563
|
+
@handle_api_exceptions
|
|
1564
|
+
def create_workspace(self, model: CreateExperimentalWorkspace) -> str:
|
|
1565
|
+
return self._internal_api_client.create_workspace_api_v2_experimental_workspaces_post(
|
|
1566
|
+
create_experimental_workspace=model,
|
|
1567
|
+
).result.id
|
|
1568
|
+
|
|
1569
|
+
@handle_api_exceptions
|
|
1570
|
+
def get_workspace(
|
|
1571
|
+
self,
|
|
1572
|
+
*,
|
|
1573
|
+
id: Optional[str] = None, # noqa: A002
|
|
1574
|
+
name: Optional[str] = None,
|
|
1575
|
+
cloud: Optional[str] = None,
|
|
1576
|
+
project: Optional[str] = None,
|
|
1577
|
+
) -> Optional[ExperimentalWorkspace]:
|
|
1578
|
+
"""Get a workspace by either name or id. Filter by cloud and project.
|
|
1579
|
+
|
|
1580
|
+
Returns None if not found.
|
|
1581
|
+
"""
|
|
1582
|
+
if id is not None:
|
|
1583
|
+
try:
|
|
1584
|
+
return self._internal_api_client.get_workspace_api_v2_experimental_workspaces_workspace_id_get(
|
|
1585
|
+
id
|
|
1586
|
+
).result
|
|
1587
|
+
except ExternalApiException as e:
|
|
1588
|
+
if e.status == 404:
|
|
1589
|
+
return None
|
|
1590
|
+
raise e from None
|
|
1591
|
+
else:
|
|
1592
|
+
paging_token = None
|
|
1593
|
+
cloud_id = self.get_cloud_id(cloud_name=cloud)
|
|
1594
|
+
project_id = self.get_project_id(parent_cloud_id=cloud_id, name=project)
|
|
1595
|
+
resp = self._internal_api_client.list_workspaces_api_v2_experimental_workspaces_get(
|
|
1596
|
+
project_id=project_id,
|
|
1597
|
+
name=name,
|
|
1598
|
+
count=self.LIST_ENDPOINT_COUNT,
|
|
1599
|
+
paging_token=paging_token,
|
|
1600
|
+
)
|
|
1601
|
+
|
|
1602
|
+
if len(resp.results) == 0:
|
|
1603
|
+
return None
|
|
1604
|
+
|
|
1605
|
+
workspace = resp.results[0]
|
|
1606
|
+
return workspace
|
|
1607
|
+
|
|
1608
|
+
@handle_api_exceptions
|
|
1609
|
+
def update_workspace(
|
|
1610
|
+
self,
|
|
1611
|
+
*,
|
|
1612
|
+
workspace_id: str,
|
|
1613
|
+
name: Optional[str] = None,
|
|
1614
|
+
compute_config_id: Optional[str] = None,
|
|
1615
|
+
cluster_environment_build_id: Optional[str] = None,
|
|
1616
|
+
idle_timeout_minutes: Optional[int] = None,
|
|
1617
|
+
):
|
|
1618
|
+
workspace = self.get_workspace(id=workspace_id)
|
|
1619
|
+
if not workspace:
|
|
1620
|
+
raise ValueError(f"Workspace '{workspace_id}' not found.")
|
|
1621
|
+
|
|
1622
|
+
if name:
|
|
1623
|
+
# Update the workspace name with workspaces patch API
|
|
1624
|
+
self._internal_api_client.patch_workspace_api_v2_experimental_workspaces_workspace_id_patch(
|
|
1625
|
+
workspace_id=workspace_id,
|
|
1626
|
+
json_patch_operation=[
|
|
1627
|
+
{"op": "replace", "path": "/name", "value": name,},
|
|
1628
|
+
],
|
|
1629
|
+
)
|
|
1630
|
+
|
|
1631
|
+
if compute_config_id or cluster_environment_build_id or idle_timeout_minutes:
|
|
1632
|
+
# Update cluster with external cluster API
|
|
1633
|
+
self._external_api_client.update_cluster(
|
|
1634
|
+
cluster_id=workspace.cluster_id,
|
|
1635
|
+
update_cluster=UpdateCluster(
|
|
1636
|
+
idle_timeout_minutes=idle_timeout_minutes,
|
|
1637
|
+
cluster_environment_build_id=cluster_environment_build_id,
|
|
1638
|
+
cluster_compute_id=compute_config_id,
|
|
1639
|
+
),
|
|
1640
|
+
)
|
|
1641
|
+
|
|
1642
|
+
@handle_api_exceptions
|
|
1643
|
+
def update_workspace_dependencies_offline_only(
|
|
1644
|
+
self, workspace_id: str, requirements: List[str]
|
|
1645
|
+
):
|
|
1646
|
+
return self._internal_api_client.put_workspace_proxied_dataplane_artifacts_api_v2_experimental_workspaces_workspace_id_proxied_dataplane_artifacts_put(
|
|
1647
|
+
workspace_id=workspace_id,
|
|
1648
|
+
workspace_dataplane_proxied_artifacts={
|
|
1649
|
+
"requirements": "\n".join(requirements),
|
|
1650
|
+
},
|
|
1651
|
+
)
|
|
1652
|
+
|
|
1653
|
+
@handle_api_exceptions
|
|
1654
|
+
def update_workspace_env_vars_offline_only(
|
|
1655
|
+
self, workspace_id: str, env_vars: Dict[str, str]
|
|
1656
|
+
):
|
|
1657
|
+
return self._internal_api_client.put_workspace_proxied_dataplane_artifacts_api_v2_experimental_workspaces_workspace_id_proxied_dataplane_artifacts_put(
|
|
1658
|
+
workspace_id=workspace_id,
|
|
1659
|
+
workspace_dataplane_proxied_artifacts={
|
|
1660
|
+
"environment_variables": [
|
|
1661
|
+
f"{key}={value}" for key, value in env_vars.items()
|
|
1662
|
+
],
|
|
1663
|
+
},
|
|
1664
|
+
)
|
|
1665
|
+
|
|
1666
|
+
@handle_api_exceptions
|
|
1667
|
+
def get_workspace_proxied_dataplane_artifacts(
|
|
1668
|
+
self, workspace_id: str
|
|
1669
|
+
) -> WorkspaceDataplaneProxiedArtifacts:
|
|
1670
|
+
return self._internal_api_client.get_workspace_proxied_dataplane_artifacts_api_v2_experimental_workspaces_workspace_id_proxied_dataplane_artifacts_get(
|
|
1671
|
+
workspace_id
|
|
1672
|
+
).result
|
|
1673
|
+
|
|
1674
|
+
@handle_api_exceptions
|
|
1675
|
+
def start_workspace(self, workspace_id: str):
|
|
1676
|
+
"""Start a workspace."""
|
|
1677
|
+
workspace_model = self.get_workspace(id=workspace_id)
|
|
1678
|
+
if workspace_model is None:
|
|
1679
|
+
raise ValueError(f"Workspace '{workspace_id}' not found.")
|
|
1680
|
+
|
|
1681
|
+
return self._internal_api_client.start_session_api_v2_sessions_session_id_start_post(
|
|
1682
|
+
session_id=workspace_model.cluster_id,
|
|
1683
|
+
start_session_options=StartSessionOptions(),
|
|
1684
|
+
)
|
|
1685
|
+
|
|
1686
|
+
@handle_api_exceptions
|
|
1687
|
+
def terminate_workspace(self, workspace_id: str):
|
|
1688
|
+
"""Terminate a workspace."""
|
|
1689
|
+
workspace_model = self.get_workspace(id=workspace_id)
|
|
1690
|
+
if workspace_model is None:
|
|
1691
|
+
raise ValueError(f"Workspace '{workspace_id}' not found.")
|
|
1692
|
+
|
|
1693
|
+
options = StopSessionOptions(
|
|
1694
|
+
terminate=True,
|
|
1695
|
+
workers_only=False,
|
|
1696
|
+
keep_min_workers=False,
|
|
1697
|
+
take_snapshot=False,
|
|
1698
|
+
)
|
|
1699
|
+
return self._internal_api_client.stop_session_api_v2_sessions_session_id_stop_post(
|
|
1700
|
+
session_id=workspace_model.cluster_id, stop_session_options=options,
|
|
1701
|
+
)
|
|
1702
|
+
|
|
1703
|
+
@handle_api_exceptions
|
|
1704
|
+
def get_workspace_cluster(self, workspace_id: str) -> Optional[DecoratedSession]:
|
|
1705
|
+
workspace = self.get_workspace(id=workspace_id)
|
|
1706
|
+
if not workspace:
|
|
1707
|
+
raise ValueError(f"Workspace '{workspace_id}' not found.")
|
|
1708
|
+
|
|
1709
|
+
result = self._internal_api_client.get_decorated_cluster_api_v2_decorated_sessions_cluster_id_get(
|
|
1710
|
+
workspace.cluster_id
|
|
1711
|
+
)
|
|
1712
|
+
return result.result
|
|
1713
|
+
|
|
1714
|
+
@handle_api_exceptions
|
|
1715
|
+
def get_cluster_head_node_ip(self, cluster_id: str) -> str:
|
|
1716
|
+
head_ip = self._internal_api_client.get_session_head_ip_api_v2_sessions_session_id_head_ip_get(
|
|
1717
|
+
cluster_id
|
|
1718
|
+
).result.head_ip
|
|
1719
|
+
return head_ip
|
|
1720
|
+
|
|
1721
|
+
@handle_api_exceptions
|
|
1722
|
+
def get_cluster_ssh_key(self, cluster_id: str) -> SessionSshKey:
|
|
1723
|
+
return self._internal_api_client.get_session_ssh_key_api_v2_sessions_session_id_ssh_key_get(
|
|
1724
|
+
cluster_id
|
|
1725
|
+
).result
|
|
1726
|
+
|
|
1727
|
+
@handle_api_exceptions
|
|
1728
|
+
def get_workspace_default_dir_name(self, workspace_id) -> str:
|
|
1729
|
+
workspace = self.get_workspace(id=workspace_id)
|
|
1730
|
+
assert workspace, f"Workspace '{workspace_id}' not found."
|
|
1731
|
+
project = self._internal_api_client.get_project_api_v2_projects_project_id_get(
|
|
1732
|
+
workspace.project_id
|
|
1733
|
+
).result
|
|
1734
|
+
if self._internal_api_client.check_is_feature_flag_on_api_v2_userinfo_check_is_feature_flag_on_get(
|
|
1735
|
+
FLAG_DEFAULT_WORKING_DIR_FOR_PROJ
|
|
1736
|
+
).result.is_on:
|
|
1737
|
+
return project.directory_name
|
|
1738
|
+
else:
|
|
1739
|
+
return project.name
|
|
1740
|
+
|
|
1741
|
+
@handle_api_exceptions
|
|
1742
|
+
def delete_finetuned_model(self, model_id: str) -> DeletedPlatformFineTunedModel:
|
|
1743
|
+
deleted_model = self._internal_api_client.delete_model_api_v2_llm_models_model_id_delete(
|
|
1744
|
+
model_id
|
|
1745
|
+
).result
|
|
1746
|
+
return deleted_model
|
|
1747
|
+
|
|
1748
|
+
@handle_api_exceptions
|
|
1749
|
+
def list_finetuned_models(
|
|
1750
|
+
self, cloud_id: Optional[str], project_id: Optional[str], max_items: int,
|
|
1751
|
+
) -> List[FineTunedModel]:
|
|
1752
|
+
if self.inside_workspace():
|
|
1753
|
+
# Resolve `cloud_id` and `project_id`. If not provided and if this is being run in a workspace,
|
|
1754
|
+
# we use the `cloud_id` and `project_id` of the workspace
|
|
1755
|
+
cloud_id, project_id = source_cloud_id_and_project_id(
|
|
1756
|
+
internal_api=self._internal_api_client,
|
|
1757
|
+
external_api=self._external_api_client,
|
|
1758
|
+
cloud_id=cloud_id,
|
|
1759
|
+
project_id=project_id,
|
|
1760
|
+
)
|
|
1761
|
+
|
|
1762
|
+
paging_token = None
|
|
1763
|
+
results = []
|
|
1764
|
+
while True:
|
|
1765
|
+
count = min(self.LIST_ENDPOINT_COUNT, max_items)
|
|
1766
|
+
resp: FinetunedmodelListResponse = self._internal_api_client.list_models_api_v2_llm_models_get(
|
|
1767
|
+
cloud_id=cloud_id,
|
|
1768
|
+
project_id=project_id,
|
|
1769
|
+
paging_token=paging_token,
|
|
1770
|
+
count=count,
|
|
1771
|
+
)
|
|
1772
|
+
models = resp.results
|
|
1773
|
+
results.extend(models)
|
|
1774
|
+
if not len(models) or not resp.metadata.next_paging_token:
|
|
1775
|
+
break
|
|
1776
|
+
|
|
1777
|
+
if max_items and len(results) >= max_items:
|
|
1778
|
+
break
|
|
1779
|
+
paging_token = resp.metadata.next_paging_token
|
|
1780
|
+
|
|
1781
|
+
return results[:max_items] if max_items else results
|
|
1782
|
+
|
|
1783
|
+
@handle_api_exceptions
|
|
1784
|
+
def download_aggregated_instance_usage_csv(
|
|
1785
|
+
self,
|
|
1786
|
+
start_date,
|
|
1787
|
+
end_date,
|
|
1788
|
+
cloud_id=None,
|
|
1789
|
+
project_id=None,
|
|
1790
|
+
directory=None,
|
|
1791
|
+
hide_progress_bar=False,
|
|
1792
|
+
) -> str:
|
|
1793
|
+
with FileDownloadProgress() as progress:
|
|
1794
|
+
task_id = progress.add_task(
|
|
1795
|
+
description="Preparing aggregated instance usage CSV",
|
|
1796
|
+
visible=not hide_progress_bar,
|
|
1797
|
+
)
|
|
1798
|
+
|
|
1799
|
+
resp = self._internal_api_client.download_aggregated_instance_usage_csv_api_v2_aggregated_instance_usage_download_csv_get(
|
|
1800
|
+
start_date=start_date,
|
|
1801
|
+
end_date=end_date,
|
|
1802
|
+
cloud_id=cloud_id,
|
|
1803
|
+
project_id=project_id,
|
|
1804
|
+
_preload_content=False,
|
|
1805
|
+
)
|
|
1806
|
+
|
|
1807
|
+
progress.update(
|
|
1808
|
+
task_id, description="Downloading aggregated instance usage CSV"
|
|
1809
|
+
)
|
|
1810
|
+
|
|
1811
|
+
# Set the total size of the download for the progress bar
|
|
1812
|
+
total_size = int(resp.headers.get("content-length", 0))
|
|
1813
|
+
progress.update(task_id, total=total_size)
|
|
1814
|
+
|
|
1815
|
+
# Construct the filepath to save the downloaded file
|
|
1816
|
+
content_disposition = resp.headers.get("content-disposition", "")
|
|
1817
|
+
filename_regex = re.search(
|
|
1818
|
+
r'filename="?(?P<filename>[^"]+)"?', content_disposition
|
|
1819
|
+
)
|
|
1820
|
+
|
|
1821
|
+
if not filename_regex:
|
|
1822
|
+
filename = f"aggregated_instance_usage_{start_date}_{end_date}.zip"
|
|
1823
|
+
else:
|
|
1824
|
+
filename = filename_regex.group("filename")
|
|
1825
|
+
|
|
1826
|
+
if directory:
|
|
1827
|
+
filepath = os.path.join(directory, filename)
|
|
1828
|
+
else:
|
|
1829
|
+
filepath = filename
|
|
1830
|
+
|
|
1831
|
+
# Download the file
|
|
1832
|
+
try:
|
|
1833
|
+
with open(filepath, "wb") as f:
|
|
1834
|
+
for chunk in resp.stream(Bytes.MB):
|
|
1835
|
+
if chunk:
|
|
1836
|
+
f.write(chunk)
|
|
1837
|
+
progress.update(task_id, advance=len(chunk))
|
|
1838
|
+
except Exception as e: # noqa: BLE001
|
|
1839
|
+
raise ValueError(f"Failed to download to '{filepath}': {e}") from None
|
|
1840
|
+
|
|
1841
|
+
progress.update(task_id, completed=total_size)
|
|
1842
|
+
progress.console.print(
|
|
1843
|
+
f"Download complete! File saved to '{filepath}'",
|
|
1844
|
+
style=Style(bold=True, color="green"),
|
|
1845
|
+
)
|
|
1846
|
+
|
|
1847
|
+
return filepath
|