lightning-sdk 2025.8.6rc2__py3-none-any.whl → 2025.11.5__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.
- lightning_sdk/__init__.py +13 -7
- lightning_sdk/__version__.py +3 -0
- lightning_sdk/agents.py +2 -1
- lightning_sdk/ai_hub.py +2 -1
- lightning_sdk/api/__init__.py +2 -0
- lightning_sdk/api/base_studio_api.py +13 -9
- lightning_sdk/api/cloud_account_api.py +31 -10
- lightning_sdk/api/deployment_api.py +27 -1
- lightning_sdk/api/job_api.py +16 -12
- lightning_sdk/api/license_api.py +26 -59
- lightning_sdk/api/llm_api.py +25 -2
- lightning_sdk/api/mmt_api.py +5 -2
- lightning_sdk/api/studio_api.py +252 -55
- lightning_sdk/api/teamspace_api.py +106 -30
- lightning_sdk/api/user_api.py +56 -2
- lightning_sdk/api/utils.py +108 -18
- lightning_sdk/base_studio.py +51 -27
- lightning_sdk/cli/__init__.py +1 -0
- lightning_sdk/cli/base_studio/__init__.py +10 -0
- lightning_sdk/cli/base_studio/list.py +43 -0
- lightning_sdk/cli/config/__init__.py +14 -0
- lightning_sdk/cli/config/get.py +57 -0
- lightning_sdk/cli/config/set.py +92 -0
- lightning_sdk/cli/config/show.py +9 -0
- lightning_sdk/cli/entrypoint.py +71 -71
- lightning_sdk/cli/groups.py +56 -0
- lightning_sdk/cli/job/__init__.py +7 -0
- lightning_sdk/cli/{clusters_menu.py → legacy/clusters_menu.py} +9 -6
- lightning_sdk/cli/{configure.py → legacy/configure.py} +2 -2
- lightning_sdk/cli/{connect.py → legacy/connect.py} +2 -2
- lightning_sdk/cli/{create.py → legacy/create.py} +12 -14
- lightning_sdk/cli/{delete.py → legacy/delete.py} +5 -5
- lightning_sdk/cli/legacy/deploy/__init__.py +0 -0
- lightning_sdk/cli/{deploy → legacy/deploy}/_auth.py +5 -6
- lightning_sdk/cli/{deploy → legacy/deploy}/devbox.py +8 -2
- lightning_sdk/cli/{deploy → legacy/deploy}/serve.py +19 -8
- lightning_sdk/cli/{download.py → legacy/download.py} +14 -15
- lightning_sdk/cli/legacy/entrypoint.py +110 -0
- lightning_sdk/cli/{generate.py → legacy/generate.py} +1 -1
- lightning_sdk/cli/{inspection.py → legacy/inspection.py} +1 -1
- lightning_sdk/cli/{job_and_mmt_action.py → legacy/job_and_mmt_action.py} +6 -6
- lightning_sdk/cli/{jobs_menu.py → legacy/jobs_menu.py} +1 -1
- lightning_sdk/cli/{list.py → legacy/list.py} +12 -13
- lightning_sdk/cli/{mmts_menu.py → legacy/mmts_menu.py} +1 -1
- lightning_sdk/cli/{open.py → legacy/open.py} +4 -4
- lightning_sdk/cli/{start.py → legacy/start.py} +1 -0
- lightning_sdk/cli/{stop.py → legacy/stop.py} +1 -1
- lightning_sdk/cli/{switch.py → legacy/switch.py} +1 -0
- lightning_sdk/cli/{teamspace_menu.py → legacy/teamspace_menu.py} +1 -1
- lightning_sdk/cli/{upload.py → legacy/upload.py} +7 -8
- lightning_sdk/cli/license/__init__.py +14 -0
- lightning_sdk/cli/license/get.py +15 -0
- lightning_sdk/cli/license/list.py +45 -0
- lightning_sdk/cli/license/set.py +13 -0
- lightning_sdk/cli/mmt/__init__.py +7 -0
- lightning_sdk/cli/studio/__init__.py +24 -0
- lightning_sdk/cli/studio/connect.py +139 -0
- lightning_sdk/cli/studio/create.py +96 -0
- lightning_sdk/cli/studio/delete.py +49 -0
- lightning_sdk/cli/studio/list.py +85 -0
- lightning_sdk/cli/studio/ssh.py +64 -0
- lightning_sdk/cli/studio/start.py +115 -0
- lightning_sdk/cli/studio/stop.py +45 -0
- lightning_sdk/cli/studio/switch.py +66 -0
- lightning_sdk/cli/utils/__init__.py +7 -0
- lightning_sdk/cli/utils/cloud_account_map.py +10 -0
- lightning_sdk/cli/utils/get_base_studio.py +24 -0
- lightning_sdk/cli/utils/handle_machine_and_gpus_args.py +69 -0
- lightning_sdk/cli/utils/logging.py +122 -0
- lightning_sdk/cli/utils/owner_selection.py +110 -0
- lightning_sdk/cli/utils/resolve.py +28 -0
- lightning_sdk/cli/utils/richt_print.py +35 -0
- lightning_sdk/cli/utils/save_to_config.py +27 -0
- lightning_sdk/cli/utils/ssh_connection.py +59 -0
- lightning_sdk/cli/utils/studio_selection.py +113 -0
- lightning_sdk/cli/utils/teamspace_selection.py +125 -0
- lightning_sdk/cli/vm/__init__.py +20 -0
- lightning_sdk/cli/vm/create.py +33 -0
- lightning_sdk/cli/vm/delete.py +25 -0
- lightning_sdk/cli/vm/list.py +30 -0
- lightning_sdk/cli/vm/ssh.py +31 -0
- lightning_sdk/cli/vm/start.py +60 -0
- lightning_sdk/cli/vm/stop.py +25 -0
- lightning_sdk/cli/vm/switch.py +38 -0
- lightning_sdk/constants.py +1 -0
- lightning_sdk/deployment/__init__.py +2 -0
- lightning_sdk/deployment/deployment.py +17 -2
- lightning_sdk/helpers.py +56 -37
- lightning_sdk/job/base.py +21 -6
- lightning_sdk/job/job.py +13 -0
- lightning_sdk/job/v1.py +11 -0
- lightning_sdk/job/v2.py +12 -0
- lightning_sdk/lightning_cloud/login.py +320 -10
- lightning_sdk/lightning_cloud/openapi/__init__.py +113 -3
- lightning_sdk/lightning_cloud/openapi/api/__init__.py +3 -0
- lightning_sdk/lightning_cloud/openapi/api/assistants_service_api.py +713 -75
- lightning_sdk/lightning_cloud/openapi/api/auth_service_api.py +376 -0
- lightning_sdk/lightning_cloud/openapi/api/billing_service_api.py +191 -1
- lightning_sdk/lightning_cloud/openapi/api/cloud_space_environment_template_service_api.py +5 -1
- lightning_sdk/lightning_cloud/openapi/api/cloud_space_service_api.py +420 -0
- lightning_sdk/lightning_cloud/openapi/api/cloudy_service_api.py +0 -97
- lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +420 -0
- lightning_sdk/lightning_cloud/openapi/api/data_connection_service_api.py +101 -0
- lightning_sdk/lightning_cloud/openapi/api/incidents_service_api.py +1058 -0
- lightning_sdk/lightning_cloud/openapi/api/jobs_service_api.py +121 -0
- lightning_sdk/lightning_cloud/openapi/api/k8_s_cluster_service_api.py +1742 -94
- lightning_sdk/lightning_cloud/openapi/api/markets_service_api.py +145 -0
- lightning_sdk/lightning_cloud/openapi/api/models_store_api.py +4 -4
- lightning_sdk/lightning_cloud/openapi/api/product_license_service_api.py +108 -108
- lightning_sdk/lightning_cloud/openapi/api/projects_service_api.py +105 -0
- lightning_sdk/lightning_cloud/openapi/api/schedules_service_api.py +347 -0
- lightning_sdk/lightning_cloud/openapi/api/sdk_command_history_service_api.py +141 -0
- lightning_sdk/lightning_cloud/openapi/api/storage_service_api.py +761 -1
- lightning_sdk/lightning_cloud/openapi/configuration.py +3 -19
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +110 -3
- lightning_sdk/lightning_cloud/openapi/models/assistant_id_conversations_body.py +15 -15
- lightning_sdk/lightning_cloud/openapi/models/cloudspace_id_visibility_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/cluster_id_kubernetestemplates_body.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/cluster_id_metrics_body.py +131 -1
- lightning_sdk/lightning_cloud/openapi/models/create.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/create_machine_request_represents_the_request_to_create_a_machine.py +461 -0
- lightning_sdk/lightning_cloud/openapi/models/deployments_id_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/externalv1_cloud_space_instance_status.py +105 -1
- lightning_sdk/lightning_cloud/openapi/models/externalv1_user_status.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/id_codeconfig_body.py +3 -81
- lightning_sdk/lightning_cloud/openapi/models/id_fork_body1.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/id_render_body.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/id_sleepconfig_body.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/id_transfer_body.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/incident_id_messages_body.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/incidents_id_body.py +279 -0
- lightning_sdk/lightning_cloud/openapi/models/job_id_reportroutingtelemetry_body.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/kubernetestemplates_id_body.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/license_key_validate_body.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/message_id_actions_body.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/messages_message_id_body.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/metricsstream_create_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/models_model_id_body.py +109 -31
- lightning_sdk/lightning_cloud/openapi/models/models_model_id_body1.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/orgs_id_body.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/project_id_storage_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/project_id_storagetransfers_body.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/project_tab_management_messages.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/projects_id_body.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/storage_complete_body.py +41 -15
- lightning_sdk/lightning_cloud/openapi/models/storagetransfers_validate_body.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/update1.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/uploads_upload_id_body1.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/user_id_affiliatelinks_body.py +107 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_abort_storage_transfer_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_aggregated_pod_metrics.py +799 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_ai_pod_v1.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_assistant_session_daily_aggregated.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_billing_tier.py +0 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cancel_running_cloud_space_instance_transfer_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_provider.py +2 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template_config.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_specialized_view.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_transfer_metadata.py +147 -17
- lightning_sdk/lightning_cloud/openapi/models/v1_cloudflare_v1.py +3 -29
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_accelerator.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_capacity_reservation.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_metrics.py +1527 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_security_options.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_type.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_container_metrics.py +21 -21
- lightning_sdk/lightning_cloud/openapi/models/v1_create_incident_request.py +305 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_license_request.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_machine_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_model_metrics_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_project_request.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_sdk_command_history_request.py +253 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_sdk_command_history_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_daily_model_metrics.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_data_connection.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_incident_message_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_incident_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_kubernetes_template_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_license_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_machine_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_deployment.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_external_cluster_spec.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_external_search_user.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_filestore_data_connection.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_filesystem_metric.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_firewall_rule.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_cloud_space_required_balance_status_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_cloud_space_transfer_estimate_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_latest_model_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_machine_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_market_pricing_response.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_model_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_temp_bucket_credentials_response.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +41 -15
- lightning_sdk/lightning_cloud/openapi/models/v1_google_cloud_direct_v1.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_group_node_metrics.py +1215 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_group_pod_metrics.py +1241 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_guest_login_request.py +177 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_guest_login_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_guest_user.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_incident.py +565 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_incident_detail.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_incident_event.py +591 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_incident_message.py +253 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_incident_severity.py +105 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_incident_type.py +108 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_job.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_job_spec.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_k8s_incident_indexes.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_kai_scheduler_queue_metrics.py +627 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_kubernetes_aws_config.py +279 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_kubernetes_direct_settings_v1.py +253 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_kubernetes_direct_v1.py +107 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_kubernetes_template.py +357 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_kubernetes_template_property.py +227 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_lambda_labs_direct_v1.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_license.py +227 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_lightning_elastic_cluster_v1.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_aggregated_pod_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_cluster_metric_timestamps_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_cluster_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_cluster_namespace_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_cluster_namespace_user_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_conversation_message_actions_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_filesystem_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_group_pod_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_incident_events_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_incident_messages_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_incidents_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_kai_scheduler_queues_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_kubernetes_templates_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/{v1_list_product_licenses_response.py → v1_list_license_response.py} +16 -16
- lightning_sdk/lightning_cloud/openapi/models/v1_list_machines_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_platform_notifications_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_schedule_runs_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_storage_transfers_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_lustre_data_connection.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_machine.py +617 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_machine_direct_v1.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_magic_link_login_request.py +1 -27
- lightning_sdk/lightning_cloud/openapi/models/v1_magic_link_login_response.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_managed_model.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_market_price.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_membership.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_message_action.py +279 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_metrics_stream.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_model_metrics.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_namespace_metrics.py +591 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_namespace_user_metrics.py +435 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_nebius_direct_v1.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_node_metrics.py +361 -23
- lightning_sdk/lightning_cloud/openapi/models/v1_notification_type.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_organization.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_pause_storage_transfer_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_platform_notification.py +279 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_pod_metrics.py +335 -23
- lightning_sdk/lightning_cloud/openapi/models/v1_project.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_project_cluster_binding.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_project_membership.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_project_settings.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_project_tab.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/{v1_product_license_check_response.py → v1_purchase_annual_upsell_request.py} +23 -23
- lightning_sdk/lightning_cloud/openapi/models/v1_purchase_annual_upsell_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_quote_annual_upsell_response.py +227 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_render_kubernetes_template_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_report_deployment_routing_telemetry_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_required_balance_reason.py +107 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_reset_api_key_request.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_reset_api_key_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_resource_visibility.py +1 -27
- lightning_sdk/lightning_cloud/openapi/models/v1_resume_storage_transfer_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_routing_telemetry.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_rule_resource.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_schedule_run.py +357 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_sdk_command_history_severity.py +104 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_sdk_command_history_type.py +104 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_secret_type.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_server_alert_type.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_slack_notifier.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_slack_notifier_type.py +105 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_storage_asset.py +133 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_storage_transfer.py +435 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_storage_transfer_status.py +108 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_token_login_request.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_token_login_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_token_owner_type.py +104 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_update_cloud_space_instance_config_request.py +3 -81
- lightning_sdk/lightning_cloud/openapi/models/v1_update_project_tab_order_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_update_user_request.py +41 -15
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +444 -600
- lightning_sdk/lightning_cloud/openapi/models/v1_validate_license_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_validate_storage_transfer_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_voltage_park_direct_v1.py +29 -3
- lightning_sdk/lightning_cloud/rest_client.py +48 -45
- lightning_sdk/lightning_cloud/utils/data_connection.py +51 -1
- lightning_sdk/llm/llm.py +175 -56
- lightning_sdk/llm/public_assistants.py +44 -7
- lightning_sdk/machine.py +21 -2
- lightning_sdk/mmt/base.py +7 -0
- lightning_sdk/mmt/mmt.py +11 -3
- lightning_sdk/mmt/v1.py +3 -1
- lightning_sdk/mmt/v2.py +4 -0
- lightning_sdk/owner.py +2 -1
- lightning_sdk/pipeline/steps.py +6 -0
- lightning_sdk/plugin.py +6 -1
- lightning_sdk/studio.py +294 -53
- lightning_sdk/teamspace.py +167 -7
- lightning_sdk/user.py +19 -1
- lightning_sdk/utils/config.py +179 -0
- lightning_sdk/utils/license.py +13 -0
- lightning_sdk/utils/logging.py +79 -0
- lightning_sdk/utils/names.py +1179 -0
- lightning_sdk/utils/progress.py +283 -0
- lightning_sdk/utils/resolve.py +82 -7
- {lightning_sdk-2025.8.6rc2.dist-info → lightning_sdk-2025.11.5.dist-info}/METADATA +2 -1
- {lightning_sdk-2025.8.6rc2.dist-info → lightning_sdk-2025.11.5.dist-info}/RECORD +328 -169
- {lightning_sdk-2025.8.6rc2.dist-info → lightning_sdk-2025.11.5.dist-info}/entry_points.txt +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_product_license.py +0 -435
- lightning_sdk/services/license.py +0 -363
- /lightning_sdk/cli/{deploy → legacy}/__init__.py +0 -0
- /lightning_sdk/cli/{ai_hub.py → legacy/ai_hub.py} +0 -0
- /lightning_sdk/cli/{docker_cli.py → legacy/docker_cli.py} +0 -0
- /lightning_sdk/cli/{exceptions.py → legacy/exceptions.py} +0 -0
- /lightning_sdk/cli/{run.py → legacy/run.py} +0 -0
- /lightning_sdk/cli/{studios_menu.py → legacy/studios_menu.py} +0 -0
- /lightning_sdk/cli/{coloring.py → utils/coloring.py} +0 -0
- {lightning_sdk-2025.8.6rc2.dist-info → lightning_sdk-2025.11.5.dist-info}/LICENSE +0 -0
- {lightning_sdk-2025.8.6rc2.dist-info → lightning_sdk-2025.11.5.dist-info}/WHEEL +0 -0
- {lightning_sdk-2025.8.6rc2.dist-info → lightning_sdk-2025.11.5.dist-info}/top_level.txt +0 -0
lightning_sdk/plugin.py
CHANGED
|
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, Generator, Optional, Protocol, Union, run
|
|
|
9
9
|
from lightning_sdk.job import Job
|
|
10
10
|
from lightning_sdk.machine import Machine
|
|
11
11
|
from lightning_sdk.studio import Studio
|
|
12
|
+
from lightning_sdk.utils.logging import TrackCallsABCMeta
|
|
12
13
|
from lightning_sdk.utils.resolve import (
|
|
13
14
|
_LIGHTNING_SERVICE_EXECUTION_ID_KEY,
|
|
14
15
|
_resolve_deprecated_cloud_compute,
|
|
@@ -22,7 +23,7 @@ if TYPE_CHECKING:
|
|
|
22
23
|
_logger = _setup_logger(__name__)
|
|
23
24
|
|
|
24
25
|
|
|
25
|
-
class _Plugin(ABC):
|
|
26
|
+
class _Plugin(ABC, metaclass=TrackCallsABCMeta):
|
|
26
27
|
"""Abstract Plugin class defining the API.
|
|
27
28
|
|
|
28
29
|
Args:
|
|
@@ -128,6 +129,7 @@ class JobsPlugin(_Plugin):
|
|
|
128
129
|
machine: Machine = Machine.CPU,
|
|
129
130
|
cloud_compute: Optional[Machine] = None,
|
|
130
131
|
interruptible: bool = False,
|
|
132
|
+
reuse_snapshot: bool = True,
|
|
131
133
|
) -> Job:
|
|
132
134
|
"""Launches an asynchronous job.
|
|
133
135
|
|
|
@@ -137,6 +139,8 @@ class JobsPlugin(_Plugin):
|
|
|
137
139
|
machine: The machine to run the job on.
|
|
138
140
|
interruptible: Whether to run the job on an interruptible machine.
|
|
139
141
|
These are cheaper but can be preempted at any time.
|
|
142
|
+
reuse_snapshot: Whether the job should reuse a Studio snapshot when multiple jobs for the same Studio are
|
|
143
|
+
submitted. Turning this off may result in longer job startup times. Defaults to True.
|
|
140
144
|
"""
|
|
141
145
|
if not name:
|
|
142
146
|
name = _run_name("job")
|
|
@@ -151,6 +155,7 @@ class JobsPlugin(_Plugin):
|
|
|
151
155
|
teamspace=self._studio.teamspace,
|
|
152
156
|
cloud_account=self._studio.cloud_account,
|
|
153
157
|
interruptible=interruptible,
|
|
158
|
+
reuse_snapshot=reuse_snapshot,
|
|
154
159
|
)
|
|
155
160
|
|
|
156
161
|
|
lightning_sdk/studio.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import glob
|
|
2
2
|
import os
|
|
3
|
+
import threading
|
|
3
4
|
import warnings
|
|
4
5
|
from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Tuple, Union
|
|
5
6
|
|
|
@@ -7,13 +8,17 @@ from tqdm.auto import tqdm
|
|
|
7
8
|
|
|
8
9
|
from lightning_sdk.api.cloud_account_api import CloudAccountApi
|
|
9
10
|
from lightning_sdk.api.studio_api import StudioApi
|
|
11
|
+
from lightning_sdk.base_studio import BaseStudio
|
|
10
12
|
from lightning_sdk.constants import _LIGHTNING_DEBUG
|
|
11
|
-
from lightning_sdk.
|
|
13
|
+
from lightning_sdk.lightning_cloud.openapi import V1ClusterType
|
|
14
|
+
from lightning_sdk.machine import DEFAULT_MACHINE, CloudProvider, Machine
|
|
12
15
|
from lightning_sdk.organization import Organization
|
|
13
16
|
from lightning_sdk.owner import Owner
|
|
14
17
|
from lightning_sdk.status import Status
|
|
15
18
|
from lightning_sdk.teamspace import Teamspace
|
|
16
19
|
from lightning_sdk.user import User
|
|
20
|
+
from lightning_sdk.utils.logging import TrackCallsMeta
|
|
21
|
+
from lightning_sdk.utils.names import random_unique_name
|
|
17
22
|
from lightning_sdk.utils.resolve import (
|
|
18
23
|
_get_org_id,
|
|
19
24
|
_resolve_deprecated_cluster,
|
|
@@ -30,7 +35,7 @@ if TYPE_CHECKING:
|
|
|
30
35
|
_logger = _setup_logger(__name__)
|
|
31
36
|
|
|
32
37
|
|
|
33
|
-
class Studio:
|
|
38
|
+
class Studio(metaclass=TrackCallsMeta):
|
|
34
39
|
"""A single Lightning AI Studio.
|
|
35
40
|
|
|
36
41
|
Allows to fully control a studio, including retrieving the status, running commands
|
|
@@ -45,9 +50,11 @@ class Studio:
|
|
|
45
50
|
Doesn't matter when the studio already exists.
|
|
46
51
|
cloud_account_provider: The provider to select the cloud-account from.
|
|
47
52
|
If set, must be in agreement with the provider from the cloud_account (if specified).
|
|
48
|
-
If not specified, falls
|
|
53
|
+
If not specified, falls back to the teamspace default cloud account.
|
|
49
54
|
create_ok: whether the studio will be created if it does not yet exist. Defaults to True
|
|
50
55
|
provider: the provider of the machine, the studio should be created on.
|
|
56
|
+
studio_type: Type of studio to create. Only effective during initial creation;
|
|
57
|
+
ignored for existing studios.
|
|
51
58
|
|
|
52
59
|
Note:
|
|
53
60
|
Since a teamspace can either be owned by an org or by a user directly,
|
|
@@ -56,7 +63,11 @@ class Studio:
|
|
|
56
63
|
"""
|
|
57
64
|
|
|
58
65
|
# skips init of studio, only set when using this as a shell for names, ids etc.
|
|
59
|
-
_skip_init =
|
|
66
|
+
_skip_init = threading.local()
|
|
67
|
+
_skip_setup = threading.local()
|
|
68
|
+
|
|
69
|
+
# whether to show progress bars during operations
|
|
70
|
+
show_progress = False
|
|
60
71
|
|
|
61
72
|
def __init__(
|
|
62
73
|
self,
|
|
@@ -71,56 +82,117 @@ class Studio:
|
|
|
71
82
|
source: Optional[str] = None,
|
|
72
83
|
disable_secrets: bool = False,
|
|
73
84
|
provider: Optional[Union[CloudProvider, str]] = None, # deprecated in favor of cloud_provider
|
|
85
|
+
studio_type: Optional[str] = None, # for base studio templates
|
|
74
86
|
) -> None:
|
|
75
87
|
self._studio_api = StudioApi()
|
|
76
88
|
self._cloud_account_api = CloudAccountApi()
|
|
77
89
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
90
|
+
self._prevent_refetch = False
|
|
91
|
+
self._teamspace = None
|
|
92
|
+
|
|
93
|
+
# don't resolve anything if we're skipping init
|
|
94
|
+
if not getattr(self._skip_init, "value", False):
|
|
95
|
+
_teamspace = _resolve_teamspace(teamspace=teamspace, org=org, user=user)
|
|
96
|
+
if _teamspace is None:
|
|
97
|
+
raise ValueError("Couldn't resolve teamspace from the provided name, org, or user")
|
|
81
98
|
|
|
82
|
-
|
|
83
|
-
self._cloud_account = _resolve_deprecated_cluster(cloud_account, cluster)
|
|
99
|
+
self._teamspace = _teamspace
|
|
84
100
|
|
|
85
|
-
self._setup_done = False
|
|
101
|
+
self._setup_done = getattr(self._skip_setup, "value", False)
|
|
86
102
|
self._disable_secrets = disable_secrets
|
|
87
103
|
|
|
88
104
|
self._plugins = {}
|
|
105
|
+
self._studio = None
|
|
106
|
+
|
|
107
|
+
# Check to see if we're inside a studio
|
|
108
|
+
current_studio = None
|
|
109
|
+
studio_id = os.environ.get("LIGHTNING_CLOUD_SPACE_ID", None)
|
|
110
|
+
if studio_id is not None and self._teamspace is not None:
|
|
111
|
+
# We're inside a studio, get it by ID
|
|
112
|
+
current_studio = self._studio_api.get_studio_by_id(studio_id=studio_id, teamspace_id=self._teamspace.id)
|
|
113
|
+
|
|
114
|
+
if cloud_account or not cloud_provider:
|
|
115
|
+
cloud_account = _resolve_deprecated_cluster(
|
|
116
|
+
cloud_account, cluster, current_studio.cluster_id if current_studio else None
|
|
117
|
+
)
|
|
118
|
+
cloud_provider = _resolve_deprecated_provider(cloud_provider, provider)
|
|
119
|
+
else:
|
|
120
|
+
cloud_provider = _resolve_deprecated_provider(cloud_provider, provider)
|
|
89
121
|
|
|
90
|
-
|
|
91
|
-
cloud_provider = _resolve_deprecated_provider(cloud_provider, provider)
|
|
122
|
+
cls_name = self._cls_name
|
|
92
123
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
124
|
+
# if we're skipping init, we don't need to resolve the cloud account as then we're not creating a studio
|
|
125
|
+
if self._teamspace is not None:
|
|
126
|
+
_cloud_account = self._cloud_account_api.resolve_cloud_account(
|
|
127
|
+
self._teamspace.id,
|
|
128
|
+
cloud_account=cloud_account,
|
|
129
|
+
cloud_provider=cloud_provider,
|
|
130
|
+
default_cloud_account=self._teamspace.default_cloud_account,
|
|
131
|
+
)
|
|
99
132
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
133
|
+
self._studio_type = None
|
|
134
|
+
if studio_type:
|
|
135
|
+
self._base_studio = BaseStudio(teamspace=self._teamspace)
|
|
136
|
+
self._available_base_studios = self._base_studio.list()
|
|
137
|
+
for bst in self._available_base_studios:
|
|
138
|
+
if (
|
|
139
|
+
bst.id == studio_type
|
|
140
|
+
or bst.name == studio_type
|
|
141
|
+
or bst.name.lower().replace(" ", "-") == studio_type
|
|
142
|
+
):
|
|
143
|
+
self._studio_type = bst.id
|
|
144
|
+
|
|
145
|
+
if not self._studio_type:
|
|
146
|
+
raise ValueError(
|
|
147
|
+
f"Could not find studio type with ID or name '{studio_type}'. "
|
|
148
|
+
f"Available studio types: "
|
|
149
|
+
f"{[bst.name.lower().replace(' ', '-') for bst in self._available_base_studios]}"
|
|
150
|
+
)
|
|
105
151
|
else:
|
|
152
|
+
if current_studio:
|
|
153
|
+
self._studio_type = current_studio.environment_template_id
|
|
154
|
+
|
|
155
|
+
# Resolve studio name if not provided: explicit → env (LIGHTNING_CLOUD_SPACE_ID) → config defaults
|
|
156
|
+
if name is None and not getattr(self._skip_init, "value", False):
|
|
157
|
+
if current_studio:
|
|
158
|
+
name = current_studio.name
|
|
159
|
+
else:
|
|
160
|
+
# Try config defaults
|
|
161
|
+
from lightning_sdk.utils.config import Config, DefaultConfigKeys
|
|
162
|
+
|
|
163
|
+
config = Config()
|
|
164
|
+
name = config.get_value(DefaultConfigKeys.studio)
|
|
165
|
+
if name is None and not create_ok:
|
|
166
|
+
raise ValueError(
|
|
167
|
+
f"Cannot autodetect {cls_name}. Either use the SDK from within a {cls_name} or pass a name!"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if self._studio is None and not getattr(self._skip_init, "value", False):
|
|
171
|
+
# If we have a name (explicit or from config), get studio by name
|
|
106
172
|
try:
|
|
173
|
+
if name is None:
|
|
174
|
+
# if we don't have a name, raise an error to get
|
|
175
|
+
# to the exception path and optionally create a studio
|
|
176
|
+
raise ValueError(
|
|
177
|
+
f"Cannot autodetect {cls_name}. Either use the SDK from within a {cls_name} or pass a name!"
|
|
178
|
+
)
|
|
107
179
|
self._studio = self._studio_api.get_studio(name, self._teamspace.id)
|
|
108
180
|
except ValueError as e:
|
|
109
181
|
if create_ok:
|
|
182
|
+
name = name or random_unique_name()
|
|
110
183
|
self._studio = self._studio_api.create_studio(
|
|
111
184
|
name,
|
|
112
185
|
self._teamspace.id,
|
|
113
|
-
cloud_account=
|
|
186
|
+
cloud_account=_cloud_account,
|
|
114
187
|
source=source,
|
|
115
188
|
disable_secrets=self._disable_secrets,
|
|
189
|
+
cloud_space_environment_template_id=self._studio_type,
|
|
116
190
|
)
|
|
117
191
|
else:
|
|
118
|
-
raise
|
|
119
|
-
|
|
120
|
-
self._cloud_account = self._studio.cluster_id
|
|
192
|
+
raise e
|
|
121
193
|
|
|
122
194
|
if (
|
|
123
|
-
not self._skip_init
|
|
195
|
+
not getattr(self._skip_init, "value", False)
|
|
124
196
|
and _internal_status_to_external_status(
|
|
125
197
|
self._studio_api._get_studio_instance_status_from_object(self._studio)
|
|
126
198
|
)
|
|
@@ -183,6 +255,14 @@ class Studio:
|
|
|
183
255
|
_get_org_id(self._teamspace),
|
|
184
256
|
)
|
|
185
257
|
|
|
258
|
+
@property
|
|
259
|
+
def public_ip(self) -> Optional[str]:
|
|
260
|
+
"""Returns the public IP address of the machine the Studio is running on."""
|
|
261
|
+
return self._studio_api.get_public_ip(
|
|
262
|
+
self._studio.id,
|
|
263
|
+
self._teamspace.id,
|
|
264
|
+
)
|
|
265
|
+
|
|
186
266
|
@property
|
|
187
267
|
def interruptible(self) -> bool:
|
|
188
268
|
"""Returns whether the Studio is running on a interruptible instance."""
|
|
@@ -194,7 +274,9 @@ class Studio:
|
|
|
194
274
|
@property
|
|
195
275
|
def cluster(self) -> str:
|
|
196
276
|
"""Returns the cluster the Studio is running on."""
|
|
197
|
-
warnings.warn(
|
|
277
|
+
warnings.warn(
|
|
278
|
+
f"{self._cls_name}.cluster is deprecated. Use {self._cls_name}.cloud_account instead", DeprecationWarning
|
|
279
|
+
)
|
|
198
280
|
return self.cloud_account
|
|
199
281
|
|
|
200
282
|
@property
|
|
@@ -203,7 +285,7 @@ class Studio:
|
|
|
203
285
|
|
|
204
286
|
def start(
|
|
205
287
|
self,
|
|
206
|
-
machine: Union[Machine, str] =
|
|
288
|
+
machine: Optional[Union[Machine, str]] = None,
|
|
207
289
|
interruptible: Optional[bool] = None,
|
|
208
290
|
max_runtime: Optional[int] = None,
|
|
209
291
|
) -> None:
|
|
@@ -218,6 +300,22 @@ class Studio:
|
|
|
218
300
|
Defaults to 3h
|
|
219
301
|
|
|
220
302
|
"""
|
|
303
|
+
# Check to see if we're inside a studio and if its running
|
|
304
|
+
current_studio_machine = None
|
|
305
|
+
studio_id = os.environ.get("LIGHTNING_CLOUD_SPACE_ID", None)
|
|
306
|
+
if studio_id is not None:
|
|
307
|
+
# We're inside a studio, get the machine if it is running
|
|
308
|
+
current_studio = self._studio_api.get_studio_by_id(studio_id=studio_id, teamspace_id=self._teamspace.id)
|
|
309
|
+
current_status = self._studio_api._get_studio_instance_status_from_object(current_studio)
|
|
310
|
+
|
|
311
|
+
if current_status and _internal_status_to_external_status(current_status) == Status.Running:
|
|
312
|
+
current_studio_machine = self._studio_api.get_machine(
|
|
313
|
+
current_studio.id,
|
|
314
|
+
self._teamspace.id,
|
|
315
|
+
current_studio.cluster_id,
|
|
316
|
+
_get_org_id(self._teamspace),
|
|
317
|
+
)
|
|
318
|
+
|
|
221
319
|
status = self.status
|
|
222
320
|
|
|
223
321
|
if interruptible is None:
|
|
@@ -227,23 +325,53 @@ class Studio:
|
|
|
227
325
|
else:
|
|
228
326
|
interruptible = self.teamspace.start_studios_on_interruptible
|
|
229
327
|
|
|
230
|
-
|
|
328
|
+
new_machine = DEFAULT_MACHINE
|
|
329
|
+
if machine is not None:
|
|
231
330
|
new_machine = machine
|
|
232
|
-
|
|
233
|
-
|
|
331
|
+
elif current_studio_machine is not None:
|
|
332
|
+
new_machine = current_studio_machine
|
|
333
|
+
|
|
334
|
+
if not isinstance(new_machine, Machine):
|
|
335
|
+
new_machine = Machine.from_str(new_machine)
|
|
336
|
+
|
|
337
|
+
if status == Status.Running:
|
|
234
338
|
if new_machine != self.machine:
|
|
235
339
|
raise RuntimeError(
|
|
236
|
-
f"Requested to start
|
|
340
|
+
f"Requested to start {self._cls_name} on {new_machine}, "
|
|
341
|
+
"but {self._cls_name} is already running on {self.machine}."
|
|
237
342
|
" Consider switching instead!"
|
|
238
343
|
)
|
|
239
|
-
_logger.info(f"
|
|
344
|
+
_logger.info(f"{self._cls_name} {self.name} is already running")
|
|
240
345
|
return
|
|
241
346
|
|
|
242
347
|
if status != Status.Stopped:
|
|
243
|
-
raise RuntimeError(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
348
|
+
raise RuntimeError(
|
|
349
|
+
f"Cannot start a {self._cls_name} that is not stopped. {self._cls_name} {self.name} is {status}."
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# Show progress bar during startup
|
|
353
|
+
if self.show_progress:
|
|
354
|
+
from lightning_sdk.utils.progress import StudioProgressTracker
|
|
355
|
+
|
|
356
|
+
with StudioProgressTracker("start", show_progress=True) as progress:
|
|
357
|
+
# Start the studio without blocking
|
|
358
|
+
self._studio_api.start_studio_async(
|
|
359
|
+
self._studio.id,
|
|
360
|
+
self._teamspace.id,
|
|
361
|
+
new_machine,
|
|
362
|
+
interruptible=interruptible,
|
|
363
|
+
max_runtime=max_runtime,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
# Track progress through completion
|
|
367
|
+
progress.track_startup_phases(
|
|
368
|
+
lambda: self._studio_api.get_studio_status(self._studio.id, self._teamspace.id)
|
|
369
|
+
)
|
|
370
|
+
else:
|
|
371
|
+
# Use the blocking version if no progress is needed
|
|
372
|
+
self._studio_api.start_studio(
|
|
373
|
+
self._studio.id, self._teamspace.id, new_machine, interruptible=interruptible, max_runtime=max_runtime
|
|
374
|
+
)
|
|
247
375
|
|
|
248
376
|
self._setup()
|
|
249
377
|
|
|
@@ -258,13 +386,20 @@ class Studio:
|
|
|
258
386
|
"""Deletes the current Studio."""
|
|
259
387
|
self._studio_api.delete_studio(self._studio.id, self._teamspace.id)
|
|
260
388
|
|
|
261
|
-
def duplicate(
|
|
389
|
+
def duplicate(
|
|
390
|
+
self,
|
|
391
|
+
target_teamspace: Optional[Union["Teamspace", str]] = None,
|
|
392
|
+
machine: Machine = Machine.CPU,
|
|
393
|
+
name: Optional[str] = None,
|
|
394
|
+
) -> "Studio":
|
|
262
395
|
"""Duplicates the existing Studio.
|
|
263
396
|
|
|
264
397
|
Args:
|
|
265
398
|
target_teamspace: the teamspace to duplicate the studio to.
|
|
266
399
|
Must have the same owner as the source teamspace.
|
|
267
400
|
If not provided, defaults to current teamspace.
|
|
401
|
+
machine: the machine to start the duplicated studio on.
|
|
402
|
+
Defaults to CPU
|
|
268
403
|
"""
|
|
269
404
|
if target_teamspace is None:
|
|
270
405
|
target_teamspace_id = self._teamspace.id
|
|
@@ -284,16 +419,23 @@ class Studio:
|
|
|
284
419
|
target_teamspace_id = target_teamspace.id
|
|
285
420
|
|
|
286
421
|
kwargs = self._studio_api.duplicate_studio(
|
|
287
|
-
studio_id=self._studio.id,
|
|
422
|
+
studio_id=self._studio.id,
|
|
423
|
+
teamspace_id=self._teamspace.id,
|
|
424
|
+
target_teamspace_id=target_teamspace_id,
|
|
425
|
+
machine=machine,
|
|
426
|
+
new_name=name,
|
|
288
427
|
)
|
|
289
428
|
return Studio(**kwargs)
|
|
290
429
|
|
|
291
|
-
def switch_machine(
|
|
430
|
+
def switch_machine(
|
|
431
|
+
self, machine: Union[Machine, str], interruptible: bool = False, cloud_provider: Optional[CloudProvider] = None
|
|
432
|
+
) -> None:
|
|
292
433
|
"""Switches machine to the provided machine type/.
|
|
293
434
|
|
|
294
435
|
Args:
|
|
295
436
|
machine: the new machine type to switch to
|
|
296
437
|
interruptible: determines whether to switch to an interruptible instance
|
|
438
|
+
cloud_provider: the cloud provider to switch to, has no effect if the Studio is not on Lightning Cloud
|
|
297
439
|
|
|
298
440
|
Note:
|
|
299
441
|
this call is blocking until the new machine is provisioned
|
|
@@ -302,12 +444,49 @@ class Studio:
|
|
|
302
444
|
status = self.status
|
|
303
445
|
if status != Status.Running:
|
|
304
446
|
raise RuntimeError(
|
|
305
|
-
f"Cannot switch machine on a
|
|
447
|
+
f"Cannot switch machine on a {self._cls_name} that is not running. "
|
|
448
|
+
"{self._cls_name} {self.name} is {status}."
|
|
306
449
|
)
|
|
307
|
-
|
|
308
|
-
|
|
450
|
+
|
|
451
|
+
current_cloud = self._cloud_account_api.get_cloud_account_non_org(
|
|
452
|
+
self._teamspace.id,
|
|
453
|
+
self._studio.cluster_id,
|
|
309
454
|
)
|
|
310
455
|
|
|
456
|
+
cloud_account = ""
|
|
457
|
+
if cloud_provider is not None and current_cloud.spec.cluster_type == V1ClusterType.GLOBAL:
|
|
458
|
+
cloud_account = self._cloud_account_api.resolve_cloud_account(
|
|
459
|
+
self._teamspace.id,
|
|
460
|
+
cloud_account=None,
|
|
461
|
+
cloud_provider=cloud_provider,
|
|
462
|
+
default_cloud_account=None,
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
if self.show_progress:
|
|
466
|
+
from lightning_sdk.utils.progress import StudioProgressTracker
|
|
467
|
+
|
|
468
|
+
with StudioProgressTracker("switch", show_progress=True) as progress:
|
|
469
|
+
# Update progress before starting the switch
|
|
470
|
+
progress.update_progress(5, "Initiating machine switch...")
|
|
471
|
+
|
|
472
|
+
# Start the switch operation with progress tracking
|
|
473
|
+
self._studio_api.switch_studio_machine_with_progress(
|
|
474
|
+
self._studio.id,
|
|
475
|
+
self._teamspace.id,
|
|
476
|
+
machine,
|
|
477
|
+
interruptible=interruptible,
|
|
478
|
+
progress=progress,
|
|
479
|
+
cloud_account=cloud_account,
|
|
480
|
+
)
|
|
481
|
+
else:
|
|
482
|
+
self._studio_api.switch_studio_machine(
|
|
483
|
+
self._studio.id, self._teamspace.id, machine, interruptible=interruptible, cloud_account=cloud_account
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
if self._studio and cloud_account:
|
|
487
|
+
# TODO: get this from the API
|
|
488
|
+
self._studio.cluster_id = cloud_account
|
|
489
|
+
|
|
311
490
|
def run_and_detach(self, *commands: str, timeout: float = 10, check_interval: float = 1) -> str:
|
|
312
491
|
"""Runs given commands on the Studio and returns immediately.
|
|
313
492
|
|
|
@@ -324,7 +503,10 @@ class Studio:
|
|
|
324
503
|
print(f"Running {commands=}")
|
|
325
504
|
status = self.status
|
|
326
505
|
if status != Status.Running:
|
|
327
|
-
raise RuntimeError(
|
|
506
|
+
raise RuntimeError(
|
|
507
|
+
f"Cannot run a command in a {self._cls_name} that is not running. "
|
|
508
|
+
"{self._cls_name} {self.name} is {status}."
|
|
509
|
+
)
|
|
328
510
|
|
|
329
511
|
iter_output = self._studio_api.run_studio_commands_and_yield(
|
|
330
512
|
self._studio.id, self._teamspace.id, *commands, timeout=timeout, check_interval=check_interval
|
|
@@ -350,7 +532,10 @@ class Studio:
|
|
|
350
532
|
|
|
351
533
|
status = self.status
|
|
352
534
|
if status != Status.Running:
|
|
353
|
-
raise RuntimeError(
|
|
535
|
+
raise RuntimeError(
|
|
536
|
+
f"Cannot run a command in a {self._cls_name} that is not running. "
|
|
537
|
+
"{self._cls_name} {self.name} is {status}."
|
|
538
|
+
)
|
|
354
539
|
output, exit_code = self._studio_api.run_studio_commands(self._studio.id, self._teamspace.id, *commands)
|
|
355
540
|
output = output.strip()
|
|
356
541
|
|
|
@@ -446,6 +631,7 @@ class Studio:
|
|
|
446
631
|
command: str,
|
|
447
632
|
env: Optional[Dict[str, str]] = None,
|
|
448
633
|
interruptible: bool = False,
|
|
634
|
+
reuse_snapshot: bool = True,
|
|
449
635
|
) -> "Job":
|
|
450
636
|
"""Run async workloads using the compute environment from your studio.
|
|
451
637
|
|
|
@@ -455,6 +641,8 @@ class Studio:
|
|
|
455
641
|
command: The command to run inside your job.
|
|
456
642
|
env: Environment variables to set inside the job.
|
|
457
643
|
interruptible: Whether the job should run on interruptible instances. They are cheaper but can be preempted.
|
|
644
|
+
reuse_snapshot: Whether the job should reuse a Studio snapshot when multiple jobs for the same Studio are
|
|
645
|
+
submitted. Turning this off may result in longer job startup times. Defaults to True.
|
|
458
646
|
"""
|
|
459
647
|
from lightning_sdk.job import Job
|
|
460
648
|
|
|
@@ -468,6 +656,7 @@ class Studio:
|
|
|
468
656
|
cloud_account=self.cloud_account,
|
|
469
657
|
env=env,
|
|
470
658
|
interruptible=interruptible,
|
|
659
|
+
reuse_snapshot=reuse_snapshot,
|
|
471
660
|
)
|
|
472
661
|
|
|
473
662
|
def run_mmt(
|
|
@@ -512,6 +701,14 @@ class Studio:
|
|
|
512
701
|
self._assistant_id = assistant.id
|
|
513
702
|
_logger.info(assistant_info)
|
|
514
703
|
|
|
704
|
+
def rename(self, new_name: str) -> None:
|
|
705
|
+
"""Renames the current Studio to the provided new name."""
|
|
706
|
+
if new_name == self._studio.name:
|
|
707
|
+
return
|
|
708
|
+
|
|
709
|
+
self._studio_api._update_cloudspace(self._studio, self._teamspace.id, "display_name", new_name)
|
|
710
|
+
self._update_studio_reference()
|
|
711
|
+
|
|
515
712
|
@property
|
|
516
713
|
def auto_sleep(self) -> bool:
|
|
517
714
|
"""Returns if a Studio has auto-sleep enabled."""
|
|
@@ -520,8 +717,8 @@ class Studio:
|
|
|
520
717
|
@auto_sleep.setter
|
|
521
718
|
def auto_sleep(self, value: bool) -> None:
|
|
522
719
|
if not value and self.machine == Machine.CPU:
|
|
523
|
-
warnings.warn("Disabling auto-sleep will convert the
|
|
524
|
-
self._studio_api.update_autoshutdown(self._studio.id, self._teamspace.id, enabled=value
|
|
720
|
+
warnings.warn(f"Disabling auto-sleep will convert the {self._cls_name} from free to paid!")
|
|
721
|
+
self._studio_api.update_autoshutdown(self._studio.id, self._teamspace.id, enabled=value)
|
|
525
722
|
self._update_studio_reference()
|
|
526
723
|
|
|
527
724
|
@property
|
|
@@ -531,10 +728,8 @@ class Studio:
|
|
|
531
728
|
|
|
532
729
|
@auto_sleep_time.setter
|
|
533
730
|
def auto_sleep_time(self, value: int) -> None:
|
|
534
|
-
warnings.warn("Setting auto-sleep time will convert the
|
|
535
|
-
self._studio_api.update_autoshutdown(
|
|
536
|
-
self._studio.id, self._teamspace.id, idle_shutdown_seconds=value, studio=self._studio
|
|
537
|
-
)
|
|
731
|
+
warnings.warn(f"Setting auto-sleep time will convert the {self._cls_name} from free to paid!")
|
|
732
|
+
self._studio_api.update_autoshutdown(self._studio.id, self._teamspace.id, idle_shutdown_seconds=value)
|
|
538
733
|
self._update_studio_reference()
|
|
539
734
|
|
|
540
735
|
@property
|
|
@@ -557,6 +752,22 @@ class Studio:
|
|
|
557
752
|
warnings.warn("auto_shutdown_time is deprecated. Use auto_sleep_time instead", DeprecationWarning)
|
|
558
753
|
self.auto_sleep_time = value
|
|
559
754
|
|
|
755
|
+
@property
|
|
756
|
+
def env(self) -> Dict[str, str]:
|
|
757
|
+
self._update_studio_reference()
|
|
758
|
+
return self._studio_api.get_env(self._studio)
|
|
759
|
+
|
|
760
|
+
def set_env(self, new_env: Dict[str, str], partial: bool = True) -> None:
|
|
761
|
+
"""Set the environment variables for the Studio.
|
|
762
|
+
|
|
763
|
+
Args:
|
|
764
|
+
new_env: The new environment variables to set.
|
|
765
|
+
partial: Whether to only set the environment variables that are provided.
|
|
766
|
+
If False, existing environment variables that are not in new_env will be removed.
|
|
767
|
+
If True, existing environment variables that are not in new_env will be kept.
|
|
768
|
+
"""
|
|
769
|
+
self._studio_api.set_env(self._studio, self._teamspace.id, new_env, partial=partial)
|
|
770
|
+
|
|
560
771
|
@property
|
|
561
772
|
def available_plugins(self) -> Mapping[str, str]:
|
|
562
773
|
"""All available plugins to install in the current Studio."""
|
|
@@ -648,6 +859,36 @@ class Studio:
|
|
|
648
859
|
def _update_studio_reference(self) -> None:
|
|
649
860
|
self._studio = self._studio_api.get_studio_by_id(studio_id=self._studio.id, teamspace_id=self._teamspace.id)
|
|
650
861
|
|
|
862
|
+
@property
|
|
863
|
+
def _cls_name(self) -> str:
|
|
864
|
+
return self.__class__.__qualname__
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
class VM(Studio):
|
|
868
|
+
"""A single Lightning AI VM.
|
|
869
|
+
|
|
870
|
+
Allows to fully control a vm, including retrieving the status, running commands
|
|
871
|
+
and switching machine types.
|
|
872
|
+
|
|
873
|
+
Args:
|
|
874
|
+
name: the name of the vm
|
|
875
|
+
teamspace: the name of the teamspace the vm is contained by
|
|
876
|
+
org: the name of the organization owning the :param`teamspace` in case it is owned by an org
|
|
877
|
+
user: the name of the user owning the :param`teamspace` in case it is owned directly by a user instead of an org
|
|
878
|
+
cloud_account: the name of the cloud account, the vm should be created on.
|
|
879
|
+
Doesn't matter when the vm already exists.
|
|
880
|
+
cloud_account_provider: The provider to select the cloud-account from.
|
|
881
|
+
If set, must be in agreement with the provider from the cloud_account (if specified).
|
|
882
|
+
If not specified, falls backto the teamspace default cloud account.
|
|
883
|
+
create_ok: whether the vm will be created if it does not yet exist. Defaults to True
|
|
884
|
+
provider: the provider of the machine, the vm should be created on.
|
|
885
|
+
|
|
886
|
+
Note:
|
|
887
|
+
Since a teamspace can either be owned by an org or by a user directly,
|
|
888
|
+
only one of the arguments can be provided.
|
|
889
|
+
|
|
890
|
+
"""
|
|
891
|
+
|
|
651
892
|
|
|
652
893
|
def _internal_status_to_external_status(internal_status: str) -> Status:
|
|
653
894
|
"""Converts internal status strings from HTTP requests to external enums."""
|