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/api/studio_api.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
-
import tempfile
|
|
4
3
|
import time
|
|
5
|
-
import
|
|
4
|
+
from pathlib import Path
|
|
6
5
|
from threading import Event, Thread
|
|
7
6
|
from typing import Any, Dict, Generator, List, Mapping, Optional, Tuple, Union
|
|
8
7
|
|
|
@@ -12,6 +11,7 @@ from tqdm import tqdm
|
|
|
12
11
|
|
|
13
12
|
from lightning_sdk.api.utils import (
|
|
14
13
|
_create_app,
|
|
14
|
+
_download_teamspace_files,
|
|
15
15
|
_DummyBody,
|
|
16
16
|
_DummyResponse,
|
|
17
17
|
_FileUploader,
|
|
@@ -25,10 +25,12 @@ from lightning_sdk.constants import _LIGHTNING_DEBUG
|
|
|
25
25
|
from lightning_sdk.lightning_cloud.login import Auth
|
|
26
26
|
from lightning_sdk.lightning_cloud.openapi import (
|
|
27
27
|
CloudspaceIdRunsBody,
|
|
28
|
+
CloudspacesIdBody,
|
|
28
29
|
Externalv1LightningappInstance,
|
|
29
30
|
IdCodeconfigBody,
|
|
30
31
|
IdExecuteBody1,
|
|
31
32
|
IdForkBody1,
|
|
33
|
+
IdSleepconfigBody,
|
|
32
34
|
IdStartBody,
|
|
33
35
|
ProjectIdCloudspacesBody,
|
|
34
36
|
V1Assistant,
|
|
@@ -39,6 +41,7 @@ from lightning_sdk.lightning_cloud.openapi import (
|
|
|
39
41
|
V1CloudSpaceState,
|
|
40
42
|
V1ClusterAccelerator,
|
|
41
43
|
V1EndpointType,
|
|
44
|
+
V1EnvVar,
|
|
42
45
|
V1GetCloudSpaceInstanceStatusResponse,
|
|
43
46
|
V1GetLongRunningCommandInCloudSpaceResponse,
|
|
44
47
|
V1LoginRequest,
|
|
@@ -204,6 +207,32 @@ class StudioApi:
|
|
|
204
207
|
instance_id = code_status.in_use.cloud_space_instance_id
|
|
205
208
|
print(f"Studio started | {teamspace_id=} {studio_id=} {instance_id=}")
|
|
206
209
|
|
|
210
|
+
def start_studio_async(
|
|
211
|
+
self,
|
|
212
|
+
studio_id: str,
|
|
213
|
+
teamspace_id: str,
|
|
214
|
+
machine: Union[Machine, str],
|
|
215
|
+
interruptible: bool = False,
|
|
216
|
+
max_runtime: Optional[int] = None,
|
|
217
|
+
) -> None:
|
|
218
|
+
"""Start an existing Studio without blocking."""
|
|
219
|
+
# need to go via kwargs for typing compatibility since autogenerated apis accept None but aren't typed with None
|
|
220
|
+
optional_kwargs_compute_body = {}
|
|
221
|
+
|
|
222
|
+
if max_runtime is not None:
|
|
223
|
+
optional_kwargs_compute_body["requested_run_duration_seconds"] = str(max_runtime)
|
|
224
|
+
self._client.cloud_space_service_start_cloud_space_instance(
|
|
225
|
+
IdStartBody(
|
|
226
|
+
compute_config=V1UserRequestedComputeConfig(
|
|
227
|
+
name=_machine_to_compute_name(machine),
|
|
228
|
+
spot=interruptible,
|
|
229
|
+
**optional_kwargs_compute_body,
|
|
230
|
+
)
|
|
231
|
+
),
|
|
232
|
+
teamspace_id,
|
|
233
|
+
studio_id,
|
|
234
|
+
)
|
|
235
|
+
|
|
207
236
|
def stop_studio(self, studio_id: str, teamspace_id: str) -> None:
|
|
208
237
|
"""Stop an existing Studio."""
|
|
209
238
|
self.stop_keeping_alive(teamspace_id=teamspace_id, studio_id=studio_id)
|
|
@@ -232,12 +261,19 @@ class StudioApi:
|
|
|
232
261
|
return getattr(getattr(studio.code_status, "in_use", None), "phase", None)
|
|
233
262
|
|
|
234
263
|
def _request_switch(
|
|
235
|
-
self,
|
|
264
|
+
self,
|
|
265
|
+
studio_id: str,
|
|
266
|
+
teamspace_id: str,
|
|
267
|
+
machine: Union[Machine, str],
|
|
268
|
+
interruptible: bool,
|
|
269
|
+
cloud_account: Optional[str],
|
|
236
270
|
) -> None:
|
|
237
271
|
"""Switches given Studio to a new machine type."""
|
|
238
272
|
compute_name = _machine_to_compute_name(machine)
|
|
239
273
|
# TODO: UI sends disk size here, maybe we need to also?
|
|
240
274
|
body = IdCodeconfigBody(compute_config=V1UserRequestedComputeConfig(name=compute_name, spot=interruptible))
|
|
275
|
+
if cloud_account:
|
|
276
|
+
body.compute_config.cluster_override = cloud_account
|
|
241
277
|
self._client.cloud_space_service_update_cloud_space_instance_config(
|
|
242
278
|
id=studio_id,
|
|
243
279
|
project_id=teamspace_id,
|
|
@@ -245,29 +281,136 @@ class StudioApi:
|
|
|
245
281
|
)
|
|
246
282
|
|
|
247
283
|
def switch_studio_machine(
|
|
248
|
-
self,
|
|
284
|
+
self,
|
|
285
|
+
studio_id: str,
|
|
286
|
+
teamspace_id: str,
|
|
287
|
+
machine: Union[Machine, str],
|
|
288
|
+
interruptible: bool,
|
|
289
|
+
cloud_account: Optional[str],
|
|
249
290
|
) -> None:
|
|
250
291
|
"""Switches given Studio to a new machine type."""
|
|
251
292
|
self._request_switch(
|
|
252
|
-
studio_id=studio_id,
|
|
293
|
+
studio_id=studio_id,
|
|
294
|
+
teamspace_id=teamspace_id,
|
|
295
|
+
machine=machine,
|
|
296
|
+
interruptible=interruptible,
|
|
297
|
+
cloud_account=cloud_account,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# Wait until it's time to switch
|
|
301
|
+
requested_was_found = False
|
|
302
|
+
startup_status = None
|
|
303
|
+
while True:
|
|
304
|
+
status = self.get_studio_status(studio_id, teamspace_id)
|
|
305
|
+
requested_machine = status.requested
|
|
306
|
+
|
|
307
|
+
if requested_machine is not None:
|
|
308
|
+
requested_was_found = True
|
|
309
|
+
startup_status = requested_machine.startup_status
|
|
310
|
+
|
|
311
|
+
# if the requested machine was found in the past, use the in_use status instead.
|
|
312
|
+
# it might be that it either was cancelled or it actually is ready.
|
|
313
|
+
# Either way, since we're actually blocking below for the in use startup status
|
|
314
|
+
# it's safe to switch at this point
|
|
315
|
+
elif requested_was_found:
|
|
316
|
+
in_use_machine = status.in_use
|
|
317
|
+
if in_use_machine is not None:
|
|
318
|
+
startup_status = in_use_machine.startup_status
|
|
319
|
+
|
|
320
|
+
if startup_status and startup_status.initial_restore_finished:
|
|
321
|
+
break
|
|
322
|
+
time.sleep(1)
|
|
323
|
+
|
|
324
|
+
self._client.cloud_space_service_switch_cloud_space_instance(teamspace_id, studio_id)
|
|
325
|
+
|
|
326
|
+
# Wait until the new machine is ready to use
|
|
327
|
+
while True:
|
|
328
|
+
in_use = self.get_studio_status(studio_id, teamspace_id).in_use
|
|
329
|
+
if in_use is None:
|
|
330
|
+
continue
|
|
331
|
+
startup_status = in_use.startup_status
|
|
332
|
+
if startup_status and startup_status.top_up_restore_finished:
|
|
333
|
+
break
|
|
334
|
+
time.sleep(1)
|
|
335
|
+
|
|
336
|
+
def switch_studio_machine_with_progress(
|
|
337
|
+
self,
|
|
338
|
+
studio_id: str,
|
|
339
|
+
teamspace_id: str,
|
|
340
|
+
machine: Union[Machine, str],
|
|
341
|
+
interruptible: bool,
|
|
342
|
+
progress: Any, # StudioProgressTracker - avoid circular import
|
|
343
|
+
cloud_account: Optional[str],
|
|
344
|
+
) -> None:
|
|
345
|
+
"""Switches given Studio to a new machine type with progress tracking."""
|
|
346
|
+
progress.update_progress(10, "Requesting machine switch...")
|
|
347
|
+
|
|
348
|
+
self._request_switch(
|
|
349
|
+
studio_id=studio_id,
|
|
350
|
+
teamspace_id=teamspace_id,
|
|
351
|
+
machine=machine,
|
|
352
|
+
interruptible=interruptible,
|
|
353
|
+
cloud_account=cloud_account,
|
|
253
354
|
)
|
|
254
355
|
|
|
356
|
+
progress.update_progress(20, "Waiting for machine allocation...")
|
|
357
|
+
|
|
255
358
|
# Wait until it's time to switch
|
|
359
|
+
requested_was_found = False
|
|
360
|
+
startup_status = None
|
|
361
|
+
base_progress = 20
|
|
362
|
+
max_wait_progress = 60
|
|
363
|
+
wait_counter = 0
|
|
364
|
+
|
|
256
365
|
while True:
|
|
257
|
-
|
|
366
|
+
status = self.get_studio_status(studio_id, teamspace_id)
|
|
367
|
+
requested_machine = status.requested
|
|
368
|
+
|
|
369
|
+
if requested_machine is not None:
|
|
370
|
+
requested_was_found = True
|
|
371
|
+
startup_status = requested_machine.startup_status
|
|
372
|
+
|
|
373
|
+
# if the requested machine was found in the past, use the in_use status instead.
|
|
374
|
+
# it might be that it either was cancelled or it actually is ready.
|
|
375
|
+
# Either way, since we're actually blocking below for the in use startup status
|
|
376
|
+
# it's safe to switch at this point
|
|
377
|
+
elif requested_was_found:
|
|
378
|
+
in_use_machine = status.in_use
|
|
379
|
+
if in_use_machine is not None:
|
|
380
|
+
startup_status = in_use_machine.startup_status
|
|
381
|
+
|
|
258
382
|
if startup_status and startup_status.initial_restore_finished:
|
|
259
383
|
break
|
|
384
|
+
|
|
385
|
+
# Update progress gradually while waiting
|
|
386
|
+
wait_counter += 1
|
|
387
|
+
current_progress = min(base_progress + (wait_counter * 2), max_wait_progress)
|
|
388
|
+
progress.update_progress(current_progress, "Allocating new machine...")
|
|
260
389
|
time.sleep(1)
|
|
261
390
|
|
|
391
|
+
progress.update_progress(70, "Starting machine switch...")
|
|
262
392
|
self._client.cloud_space_service_switch_cloud_space_instance(teamspace_id, studio_id)
|
|
263
393
|
|
|
394
|
+
progress.update_progress(80, "Configuring new machine...")
|
|
395
|
+
|
|
264
396
|
# Wait until the new machine is ready to use
|
|
397
|
+
switch_counter = 0
|
|
265
398
|
while True:
|
|
266
|
-
|
|
399
|
+
in_use = self.get_studio_status(studio_id, teamspace_id).in_use
|
|
400
|
+
if in_use is None:
|
|
401
|
+
continue
|
|
402
|
+
startup_status = in_use.startup_status
|
|
267
403
|
if startup_status and startup_status.top_up_restore_finished:
|
|
268
404
|
break
|
|
405
|
+
|
|
406
|
+
# Update progress while waiting for machine to be ready
|
|
407
|
+
switch_counter += 1
|
|
408
|
+
current_progress = min(80 + switch_counter, 95)
|
|
409
|
+
progress.update_progress(current_progress, "Finalizing machine setup...")
|
|
269
410
|
time.sleep(1)
|
|
270
411
|
|
|
412
|
+
progress.complete("Machine switch completed successfully")
|
|
413
|
+
|
|
271
414
|
def get_machine(self, studio_id: str, teamspace_id: str, cloud_account_id: str, org_id: str) -> Machine:
|
|
272
415
|
"""Get the current machine type the given Studio is running on."""
|
|
273
416
|
response: V1CloudSpaceInstanceConfig = self._client.cloud_space_service_get_cloud_space_instance_config(
|
|
@@ -283,7 +426,7 @@ class StudioApi:
|
|
|
283
426
|
accelerator.slug_multi_cloud,
|
|
284
427
|
accelerator.instance_id,
|
|
285
428
|
):
|
|
286
|
-
return Machine.
|
|
429
|
+
return Machine._from_accelerator(accelerator)
|
|
287
430
|
|
|
288
431
|
return Machine.from_str(response.compute_config.name)
|
|
289
432
|
|
|
@@ -295,6 +438,14 @@ class StudioApi:
|
|
|
295
438
|
|
|
296
439
|
return response.compute_config.spot
|
|
297
440
|
|
|
441
|
+
def get_public_ip(self, studio_id: str, teamspace_id: str) -> Optional[str]:
|
|
442
|
+
"""Get the public IP address of the Studio."""
|
|
443
|
+
internal_status = self.get_studio_status(studio_id=studio_id, teamspace_id=teamspace_id).in_use
|
|
444
|
+
if internal_status is None:
|
|
445
|
+
return None
|
|
446
|
+
|
|
447
|
+
return internal_status.public_ip_address
|
|
448
|
+
|
|
298
449
|
def _get_machines_for_cloud_account(
|
|
299
450
|
self, teamspace_id: str, cloud_account_id: str, org_id: str
|
|
300
451
|
) -> List[V1ClusterAccelerator]:
|
|
@@ -423,25 +574,28 @@ class StudioApi:
|
|
|
423
574
|
self,
|
|
424
575
|
studio_id: str,
|
|
425
576
|
teamspace_id: str,
|
|
426
|
-
studio: V1CloudSpace,
|
|
427
577
|
enabled: Optional[bool] = None,
|
|
428
|
-
idle_shutdown_seconds: int =
|
|
429
|
-
) ->
|
|
430
|
-
"""Update the autoshutdown time of the given Studio."""
|
|
431
|
-
|
|
432
|
-
enabled
|
|
433
|
-
body = IdCodeconfigBody(
|
|
434
|
-
disable_auto_shutdown=not enabled,
|
|
578
|
+
idle_shutdown_seconds: Optional[int] = None,
|
|
579
|
+
) -> V1CloudSpaceInstanceConfig:
|
|
580
|
+
"""Update the autoshutdown time and behaviour of the given Studio."""
|
|
581
|
+
body = IdSleepconfigBody(
|
|
582
|
+
disable_auto_shutdown=not enabled if enabled is not None else None,
|
|
435
583
|
idle_shutdown_seconds=idle_shutdown_seconds,
|
|
436
|
-
compute_config=studio.code_config.compute_config,
|
|
437
584
|
)
|
|
438
|
-
self._client.
|
|
585
|
+
return self._client.cloud_space_service_update_cloud_space_sleep_config(
|
|
439
586
|
id=studio_id,
|
|
440
587
|
project_id=teamspace_id,
|
|
441
588
|
body=body,
|
|
442
589
|
)
|
|
443
590
|
|
|
444
|
-
def duplicate_studio(
|
|
591
|
+
def duplicate_studio(
|
|
592
|
+
self,
|
|
593
|
+
studio_id: str,
|
|
594
|
+
teamspace_id: str,
|
|
595
|
+
target_teamspace_id: str,
|
|
596
|
+
machine: Machine = Machine.CPU,
|
|
597
|
+
new_name: Optional[str] = None,
|
|
598
|
+
) -> Dict[str, Any]:
|
|
445
599
|
"""Duplicates the given Studio from a given Teamspace into a given target Teamspace."""
|
|
446
600
|
target_teamspace = self._client.projects_service_get_project(target_teamspace_id)
|
|
447
601
|
init_kwargs = {}
|
|
@@ -455,7 +609,7 @@ class StudioApi:
|
|
|
455
609
|
init_kwargs["org"] = OrgApi()._get_org_by_id(target_teamspace.owner_id).name
|
|
456
610
|
|
|
457
611
|
new_cloudspace = self._client.cloud_space_service_fork_cloud_space(
|
|
458
|
-
IdForkBody1(target_project_id=target_teamspace_id), project_id=teamspace_id, id=studio_id
|
|
612
|
+
IdForkBody1(target_project_id=target_teamspace_id, new_name=new_name), project_id=teamspace_id, id=studio_id
|
|
459
613
|
)
|
|
460
614
|
|
|
461
615
|
while self.get_studio_by_id(new_cloudspace.id, target_teamspace_id).state != V1CloudSpaceState.READY:
|
|
@@ -464,7 +618,7 @@ class StudioApi:
|
|
|
464
618
|
init_kwargs["name"] = new_cloudspace.name
|
|
465
619
|
init_kwargs["teamspace"] = target_teamspace.name
|
|
466
620
|
|
|
467
|
-
self.start_studio(new_cloudspace.id, target_teamspace_id,
|
|
621
|
+
self.start_studio(new_cloudspace.id, target_teamspace_id, machine, False, None)
|
|
468
622
|
return init_kwargs
|
|
469
623
|
|
|
470
624
|
def delete_studio(self, studio_id: str, teamspace_id: str) -> None:
|
|
@@ -550,46 +704,24 @@ class StudioApi:
|
|
|
550
704
|
progress_bar: bool = True,
|
|
551
705
|
) -> None:
|
|
552
706
|
"""Downloads a given folder from a Studio to a target location."""
|
|
553
|
-
# TODO:
|
|
707
|
+
# TODO: implement resumable downloads
|
|
554
708
|
auth = Auth()
|
|
555
709
|
auth.authenticate()
|
|
556
|
-
token = self._client.auth_service_login(V1LoginRequest(auth.api_key)).token
|
|
557
710
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
"
|
|
562
|
-
}
|
|
711
|
+
prefix = _sanitize_studio_remote_path(path, studio_id)
|
|
712
|
+
# ensure we only download as a directory and not the entire prefix
|
|
713
|
+
if prefix.endswith("/") is False:
|
|
714
|
+
prefix = prefix + "/"
|
|
563
715
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
716
|
+
_download_teamspace_files(
|
|
717
|
+
client=self._client,
|
|
718
|
+
teamspace_id=teamspace_id,
|
|
719
|
+
cluster_id=cloud_account,
|
|
720
|
+
prefix=prefix,
|
|
721
|
+
download_dir=Path(target_path),
|
|
722
|
+
progress_bar=progress_bar,
|
|
568
723
|
)
|
|
569
724
|
|
|
570
|
-
if progress_bar:
|
|
571
|
-
pbar = tqdm(
|
|
572
|
-
desc=f"Downloading {os.path.split(path)[1]}",
|
|
573
|
-
unit="B",
|
|
574
|
-
unit_scale=True,
|
|
575
|
-
unit_divisor=1000,
|
|
576
|
-
)
|
|
577
|
-
|
|
578
|
-
pbar_update = pbar.update
|
|
579
|
-
else:
|
|
580
|
-
pbar_update = lambda x: None
|
|
581
|
-
|
|
582
|
-
if target_path:
|
|
583
|
-
os.makedirs(target_path, exist_ok=True)
|
|
584
|
-
|
|
585
|
-
with tempfile.TemporaryFile() as f:
|
|
586
|
-
for chunk in r.iter_content(chunk_size=4096 * 8):
|
|
587
|
-
f.write(chunk)
|
|
588
|
-
pbar_update(len(chunk))
|
|
589
|
-
|
|
590
|
-
with zipfile.ZipFile(f) as z:
|
|
591
|
-
z.extractall(target_path)
|
|
592
|
-
|
|
593
725
|
def install_plugin(self, studio_id: str, teamspace_id: str, plugin_name: str) -> str:
|
|
594
726
|
"""Installs the given plugin."""
|
|
595
727
|
resp: V1Plugin = self._client.cloud_space_service_install_plugin(
|
|
@@ -844,3 +976,68 @@ class StudioApi:
|
|
|
844
976
|
plugin_type=plugin_type,
|
|
845
977
|
**other_arguments,
|
|
846
978
|
)
|
|
979
|
+
|
|
980
|
+
def _update_cloudspace(self, studio: V1CloudSpace, teamspace_id: str, key: str, value: Any) -> None:
|
|
981
|
+
body = CloudspacesIdBody(
|
|
982
|
+
code_url=studio.code_url,
|
|
983
|
+
data_connection_mounts=studio.data_connection_mounts,
|
|
984
|
+
description=studio.description,
|
|
985
|
+
display_name=studio.display_name,
|
|
986
|
+
env=studio.env,
|
|
987
|
+
featured=studio.featured,
|
|
988
|
+
hide_files=studio.hide_files,
|
|
989
|
+
is_cloudspace_private=studio.is_cloudspace_private,
|
|
990
|
+
is_code_private=studio.is_code_private,
|
|
991
|
+
is_favorite=studio.is_favorite,
|
|
992
|
+
is_published=studio.is_published,
|
|
993
|
+
license=studio.license,
|
|
994
|
+
license_url=studio.license_url,
|
|
995
|
+
message=studio.message,
|
|
996
|
+
multi_user_edit=studio.multi_user_edit,
|
|
997
|
+
operating_cost=studio.operating_cost,
|
|
998
|
+
paper_authors=studio.paper_authors,
|
|
999
|
+
paper_org=studio.paper_org,
|
|
1000
|
+
paper_org_avatar_url=studio.paper_org_avatar_url,
|
|
1001
|
+
paper_url=studio.paper_url,
|
|
1002
|
+
switch_to_default_machine_on_idle=studio.switch_to_default_machine_on_idle,
|
|
1003
|
+
tags=studio.tags,
|
|
1004
|
+
thumbnail_file_type=studio.thumbnail_file_type,
|
|
1005
|
+
user_metadata=studio.user_metadata,
|
|
1006
|
+
)
|
|
1007
|
+
|
|
1008
|
+
setattr(body, key, value)
|
|
1009
|
+
|
|
1010
|
+
self._client.cloud_space_service_update_cloud_space(
|
|
1011
|
+
id=studio.id,
|
|
1012
|
+
project_id=teamspace_id,
|
|
1013
|
+
body=body,
|
|
1014
|
+
)
|
|
1015
|
+
|
|
1016
|
+
def set_env(
|
|
1017
|
+
self,
|
|
1018
|
+
studio: V1CloudSpace,
|
|
1019
|
+
teamspace_id: str,
|
|
1020
|
+
new_env: Dict[str, str],
|
|
1021
|
+
partial: bool = True,
|
|
1022
|
+
) -> None:
|
|
1023
|
+
"""Set the environment variables for the Studio.
|
|
1024
|
+
|
|
1025
|
+
Args:
|
|
1026
|
+
new_env: The new environment variables to set.
|
|
1027
|
+
partial: Whether to only set the environment variables that are provided.
|
|
1028
|
+
If False, existing environment variables that are not in new_env will be removed.
|
|
1029
|
+
If True, existing environment variables that are not in new_env will be kept.
|
|
1030
|
+
"""
|
|
1031
|
+
updated_env_dict = {}
|
|
1032
|
+
if partial:
|
|
1033
|
+
updated_env_dict = {env.name: env.value for env in studio.env}
|
|
1034
|
+
updated_env_dict.update(new_env)
|
|
1035
|
+
else:
|
|
1036
|
+
updated_env_dict = new_env
|
|
1037
|
+
|
|
1038
|
+
updated_env = [V1EnvVar(name=key, value=value) for key, value in updated_env_dict.items()]
|
|
1039
|
+
|
|
1040
|
+
self._update_cloudspace(studio, teamspace_id, "env", updated_env)
|
|
1041
|
+
|
|
1042
|
+
def get_env(self, studio: V1CloudSpace) -> Dict[str, str]:
|
|
1043
|
+
return {env.name: env.value for env in studio.env}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import
|
|
3
|
-
import zipfile
|
|
2
|
+
import re
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
from typing import Dict, List, Optional, Tuple
|
|
6
5
|
|
|
@@ -9,6 +8,7 @@ from tqdm.auto import tqdm
|
|
|
9
8
|
|
|
10
9
|
from lightning_sdk.api.utils import (
|
|
11
10
|
_download_model_files,
|
|
11
|
+
_download_teamspace_files,
|
|
12
12
|
_DummyBody,
|
|
13
13
|
_FileUploader,
|
|
14
14
|
_get_model_version,
|
|
@@ -17,15 +17,21 @@ from lightning_sdk.api.utils import (
|
|
|
17
17
|
)
|
|
18
18
|
from lightning_sdk.lightning_cloud.login import Auth
|
|
19
19
|
from lightning_sdk.lightning_cloud.openapi import (
|
|
20
|
+
Create,
|
|
20
21
|
Externalv1LightningappInstance,
|
|
21
22
|
ModelIdVersionsBody,
|
|
22
23
|
ModelsStoreApi,
|
|
23
24
|
ProjectIdAgentsBody,
|
|
24
25
|
ProjectIdModelsBody,
|
|
26
|
+
ProjectIdSecretsBody,
|
|
27
|
+
SecretsIdBody,
|
|
25
28
|
V1Assistant,
|
|
26
29
|
V1CloudSpace,
|
|
27
30
|
V1ClusterAccelerator,
|
|
31
|
+
V1EfsConfig,
|
|
28
32
|
V1Endpoint,
|
|
33
|
+
V1ExternalCluster,
|
|
34
|
+
V1GCSFolderDataConnection,
|
|
29
35
|
V1Job,
|
|
30
36
|
V1LoginRequest,
|
|
31
37
|
V1Model,
|
|
@@ -34,6 +40,10 @@ from lightning_sdk.lightning_cloud.openapi import (
|
|
|
34
40
|
V1Project,
|
|
35
41
|
V1ProjectClusterBinding,
|
|
36
42
|
V1PromptSuggestion,
|
|
43
|
+
V1R2DataConnection,
|
|
44
|
+
V1S3FolderDataConnection,
|
|
45
|
+
V1Secret,
|
|
46
|
+
V1SecretType,
|
|
37
47
|
V1UpstreamOpenAI,
|
|
38
48
|
)
|
|
39
49
|
from lightning_sdk.lightning_cloud.rest_client import LightningClient
|
|
@@ -63,7 +73,7 @@ class TeamspaceApi:
|
|
|
63
73
|
def _get_teamspace_by_id(self, teamspace_id: str) -> V1Project:
|
|
64
74
|
return self._client.projects_service_get_project(teamspace_id)
|
|
65
75
|
|
|
66
|
-
def list_teamspaces(self, owner_id: str, name: Optional[str] = None) -> Optional[V1Project]:
|
|
76
|
+
def list_teamspaces(self, owner_id: str, name: Optional[str] = None) -> Optional[List[V1Project]]:
|
|
67
77
|
"""Lists teamspaces from owner.
|
|
68
78
|
|
|
69
79
|
If name is passed only teamspaces matching that name will be returned
|
|
@@ -386,6 +396,8 @@ class TeamspaceApi:
|
|
|
386
396
|
params=query_params,
|
|
387
397
|
stream=True,
|
|
388
398
|
)
|
|
399
|
+
if r.status_code == 404:
|
|
400
|
+
raise FileNotFoundError(f"File {path} not found")
|
|
389
401
|
total_length = int(r.headers.get("content-length"))
|
|
390
402
|
|
|
391
403
|
if progress_bar:
|
|
@@ -421,39 +433,103 @@ class TeamspaceApi:
|
|
|
421
433
|
# TODO: Update this endpoint to permit basic auth
|
|
422
434
|
auth = Auth()
|
|
423
435
|
auth.authenticate()
|
|
424
|
-
token = self._client.auth_service_login(V1LoginRequest(auth.api_key)).token
|
|
425
436
|
|
|
426
|
-
|
|
427
|
-
"clusterId": cloud_account,
|
|
428
|
-
"prefix": _resolve_teamspace_remote_path(path),
|
|
429
|
-
"token": token,
|
|
430
|
-
}
|
|
437
|
+
prefix = _resolve_teamspace_remote_path(path)
|
|
431
438
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
439
|
+
# ensure we only download as a directory and not the entire prefix
|
|
440
|
+
if prefix.endswith("/") is False:
|
|
441
|
+
prefix = prefix + "/"
|
|
442
|
+
|
|
443
|
+
_download_teamspace_files(
|
|
444
|
+
client=self._client,
|
|
445
|
+
teamspace_id=teamspace_id,
|
|
446
|
+
cluster_id=cloud_account,
|
|
447
|
+
prefix=prefix,
|
|
448
|
+
download_dir=Path(target_path),
|
|
449
|
+
progress_bar=progress_bar,
|
|
436
450
|
)
|
|
437
451
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
452
|
+
def get_secrets(self, teamspace_id: str) -> Dict[str, str]:
|
|
453
|
+
"""Get all secrets for a teamspace."""
|
|
454
|
+
secrets = self._get_secrets(teamspace_id)
|
|
455
|
+
# this returns encrypted values for security. It doesn't make sense to show them,
|
|
456
|
+
# so we just return a placeholder
|
|
457
|
+
# not a security issue to replace in the client as we get the encrypted values from the server.
|
|
458
|
+
return {secret.name: "***REDACTED***" for secret in secrets if secret.type == V1SecretType.UNSPECIFIED}
|
|
445
459
|
|
|
446
|
-
|
|
460
|
+
def set_secret(self, teamspace_id: str, key: str, value: str) -> None:
|
|
461
|
+
"""Set a secret for a teamspace.
|
|
462
|
+
|
|
463
|
+
This will replace the existing secret if it exists and create a new one if it doesn't.
|
|
464
|
+
"""
|
|
465
|
+
secrets = self._get_secrets(teamspace_id)
|
|
466
|
+
for secret in secrets:
|
|
467
|
+
if secret.name == key:
|
|
468
|
+
return self._update_secret(teamspace_id, secret.id, value)
|
|
469
|
+
return self._create_secret(teamspace_id, key, value)
|
|
470
|
+
|
|
471
|
+
def _get_secrets(self, teamspace_id: str) -> List[V1Secret]:
|
|
472
|
+
return self._client.secret_service_list_secrets(project_id=teamspace_id).secrets
|
|
473
|
+
|
|
474
|
+
def _update_secret(self, teamspace_id: str, secret_id: str, value: str) -> None:
|
|
475
|
+
self._client.secret_service_update_secret(
|
|
476
|
+
body=SecretsIdBody(value=value),
|
|
477
|
+
project_id=teamspace_id,
|
|
478
|
+
id=secret_id,
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
def _create_secret(
|
|
482
|
+
self,
|
|
483
|
+
teamspace_id: str,
|
|
484
|
+
key: str,
|
|
485
|
+
value: str,
|
|
486
|
+
) -> None:
|
|
487
|
+
self._client.secret_service_create_secret(
|
|
488
|
+
body=ProjectIdSecretsBody(name=key, value=value, type=V1SecretType.UNSPECIFIED), project_id=teamspace_id
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
def verify_secret_name(self, name: str) -> bool:
|
|
492
|
+
"""Verify if a secret name is valid.
|
|
493
|
+
|
|
494
|
+
A valid secret name starts with a letter or underscore, followed by letters, digits, or underscores.
|
|
495
|
+
"""
|
|
496
|
+
pattern = r"^[A-Za-z_][A-Za-z0-9_]*$"
|
|
497
|
+
return re.match(pattern, name) is not None
|
|
498
|
+
|
|
499
|
+
def new_folder(self, teamspace_id: str, name: str, cluster: Optional[V1ExternalCluster]) -> None:
|
|
500
|
+
create_request = Create(
|
|
501
|
+
name=name,
|
|
502
|
+
create_resources=True,
|
|
503
|
+
force=True,
|
|
504
|
+
writable=True,
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
if cluster is None:
|
|
508
|
+
create_request.r2 = V1R2DataConnection(name=name)
|
|
447
509
|
else:
|
|
448
|
-
|
|
510
|
+
create_request.cluster_id = cluster.id
|
|
511
|
+
create_request.access_cluster_ids = [cluster.id]
|
|
449
512
|
|
|
450
|
-
|
|
451
|
-
|
|
513
|
+
if cluster.spec.aws_v1:
|
|
514
|
+
create_request.s3_folder = V1S3FolderDataConnection()
|
|
515
|
+
elif cluster.spec.google_cloud_v1:
|
|
516
|
+
create_request.gcs_folder = V1GCSFolderDataConnection()
|
|
452
517
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
518
|
+
self._client.data_connection_service_create_data_connection(create_request, teamspace_id)
|
|
519
|
+
|
|
520
|
+
def new_connection(
|
|
521
|
+
self, teamspace_id: str, name: str, source: str, cluster: V1ExternalCluster, writable: bool, region: str
|
|
522
|
+
) -> None:
|
|
523
|
+
create_request = Create(
|
|
524
|
+
name=name,
|
|
525
|
+
create_resources=False,
|
|
526
|
+
force=True,
|
|
527
|
+
writable=writable,
|
|
528
|
+
cluster_id=cluster.id,
|
|
529
|
+
access_cluster_ids=[cluster.id],
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
# TODO: Add support for other connection types
|
|
533
|
+
create_request.efs = V1EfsConfig(file_system_id=source, region=region)
|
|
457
534
|
|
|
458
|
-
|
|
459
|
-
z.extractall(target_path)
|
|
535
|
+
self._client.data_connection_service_create_data_connection(create_request, teamspace_id)
|