blaxel 0.2.33__py3-none-any.whl → 0.2.35__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.
- blaxel/__init__.py +2 -2
- blaxel/core/__init__.py +2 -1
- blaxel/core/client/api/agents/create_agent.py +64 -19
- blaxel/core/client/api/agents/delete_agent.py +44 -15
- blaxel/core/client/api/agents/get_agent.py +43 -14
- blaxel/core/client/api/agents/list_agents.py +40 -11
- blaxel/core/client/api/agents/update_agent.py +60 -19
- blaxel/core/client/api/compute/create_sandbox.py +60 -23
- blaxel/core/client/api/compute/delete_sandbox.py +40 -19
- blaxel/core/client/api/compute/get_sandbox.py +39 -18
- blaxel/core/client/api/compute/list_sandboxes.py +40 -19
- blaxel/core/client/api/compute/update_sandbox.py +56 -23
- blaxel/core/client/api/configurations/get_configuration.py +16 -4
- blaxel/core/client/api/customdomains/create_custom_domain.py +12 -0
- blaxel/core/client/api/customdomains/list_custom_domains.py +16 -4
- blaxel/core/client/api/default/get_template.py +8 -4
- blaxel/core/client/api/functions/create_function.py +62 -19
- blaxel/core/client/api/functions/delete_function.py +46 -15
- blaxel/core/client/api/functions/get_function.py +45 -14
- blaxel/core/client/api/functions/list_functions.py +44 -15
- blaxel/core/client/api/functions/update_function.py +62 -19
- blaxel/core/client/api/images/cleanup_images.py +12 -12
- blaxel/core/client/api/images/delete_image.py +12 -8
- blaxel/core/client/api/images/delete_image_tag.py +12 -8
- blaxel/core/client/api/images/get_image.py +12 -8
- blaxel/core/client/api/images/list_images.py +12 -8
- blaxel/core/client/api/integrations/create_integration_connection.py +56 -23
- blaxel/core/client/api/integrations/delete_integration_connection.py +48 -19
- blaxel/core/client/api/integrations/get_integration.py +12 -8
- blaxel/core/client/api/integrations/get_integration_connection.py +44 -19
- blaxel/core/client/api/integrations/get_integration_connection_model.py +2 -2
- blaxel/core/client/api/integrations/list_integration_connections.py +36 -19
- blaxel/core/client/api/integrations/update_integration_connection.py +52 -19
- blaxel/core/client/api/jobs/create_job.py +20 -12
- blaxel/core/client/api/jobs/create_job_execution.py +22 -16
- blaxel/core/client/api/jobs/delete_job.py +12 -8
- blaxel/core/client/api/jobs/delete_job_execution.py +12 -8
- blaxel/core/client/api/jobs/get_job.py +24 -20
- blaxel/core/client/api/jobs/get_job_execution.py +8 -4
- blaxel/core/client/api/jobs/list_job_executions.py +8 -4
- blaxel/core/client/api/jobs/list_jobs.py +12 -8
- blaxel/core/client/api/jobs/update_job.py +20 -12
- blaxel/core/client/api/locations/list_locations.py +12 -8
- blaxel/core/client/api/{default → mcphub}/list_mcp_hub_definitions.py +20 -4
- blaxel/core/client/api/models/create_model.py +52 -23
- blaxel/core/client/api/models/delete_model.py +40 -19
- blaxel/core/client/api/models/get_model.py +40 -19
- blaxel/core/client/api/models/list_models.py +40 -19
- blaxel/core/client/api/models/update_model.py +52 -23
- blaxel/core/client/api/policies/create_policy.py +12 -8
- blaxel/core/client/api/policies/delete_policy.py +12 -8
- blaxel/core/client/api/policies/get_policy.py +12 -8
- blaxel/core/client/api/policies/list_policies.py +12 -8
- blaxel/core/client/api/policies/update_policy.py +12 -8
- blaxel/core/client/api/public_ipslist/list_public_ips.py +37 -5
- blaxel/core/client/api/sandboxhub/__init__.py +0 -0
- blaxel/core/client/api/{default → sandboxhub}/list_sandbox_hub_definitions.py +20 -4
- blaxel/core/client/api/service_accounts/create_api_key_for_service_account.py +12 -8
- blaxel/core/client/api/service_accounts/create_workspace_service_account.py +12 -8
- blaxel/core/client/api/service_accounts/delete_api_key_for_service_account.py +6 -4
- blaxel/core/client/api/service_accounts/delete_workspace_service_account.py +12 -8
- blaxel/core/client/api/service_accounts/get_workspace_service_accounts.py +12 -8
- blaxel/core/client/api/service_accounts/list_api_keys_for_service_account.py +12 -8
- blaxel/core/client/api/service_accounts/update_workspace_service_account.py +8 -8
- blaxel/core/client/api/templates/list_templates.py +12 -8
- blaxel/core/client/api/volume_templates/create_volume_template.py +8 -4
- blaxel/core/client/api/volume_templates/list_volume_templates.py +8 -4
- blaxel/core/client/api/volumes/create_volume.py +56 -23
- blaxel/core/client/api/volumes/delete_volume.py +44 -19
- blaxel/core/client/api/volumes/get_volume.py +40 -19
- blaxel/core/client/api/volumes/list_volumes.py +40 -19
- blaxel/core/client/api/workspaces/create_workspace.py +54 -23
- blaxel/core/client/api/workspaces/delete_workspace.py +42 -19
- blaxel/core/client/api/workspaces/get_workspace.py +42 -19
- blaxel/core/client/api/workspaces/invite_workspace_user.py +8 -4
- blaxel/core/client/api/workspaces/list_workspace_users.py +12 -8
- blaxel/core/client/api/workspaces/list_workspaces.py +36 -19
- blaxel/core/client/api/workspaces/update_workspace.py +50 -19
- blaxel/core/client/models/__init__.py +76 -146
- blaxel/core/client/models/agent.py +43 -47
- blaxel/core/client/models/agent_runtime.py +139 -0
- blaxel/core/client/models/agent_runtime_generation.py +18 -0
- blaxel/core/client/models/agent_spec.py +33 -110
- blaxel/core/client/models/api_key.py +5 -4
- blaxel/core/client/models/core_event.py +5 -5
- blaxel/core/client/models/create_api_key_for_service_account_body.py +2 -1
- blaxel/core/client/models/create_job_execution_request.py +1 -1
- blaxel/core/client/models/create_job_execution_response.py +13 -9
- blaxel/core/client/models/custom_domain.py +19 -36
- blaxel/core/client/models/custom_domain_metadata.py +4 -3
- blaxel/core/client/models/custom_domain_spec.py +14 -5
- blaxel/core/client/models/custom_domain_spec_status.py +19 -0
- blaxel/core/client/models/entrypoint.py +39 -13
- blaxel/core/client/models/{workspace_labels.py → entrypoint_args_item.py} +6 -6
- blaxel/core/client/models/entrypoint_env.py +3 -3
- blaxel/core/client/models/{job_metrics_executions_total.py → entrypoint_super_gateway_args_item.py} +6 -6
- blaxel/core/client/models/{spec_configuration.py → env.py} +17 -8
- blaxel/core/{sandbox/client/models/welcome_response.py → client/models/error.py} +26 -23
- blaxel/core/client/models/expiration_policy.py +30 -11
- blaxel/core/client/models/expiration_policy_action.py +17 -0
- blaxel/core/client/models/expiration_policy_type.py +19 -0
- blaxel/core/client/models/flavor.py +13 -5
- blaxel/core/client/models/flavor_type.py +18 -0
- blaxel/core/client/models/form.py +6 -6
- blaxel/core/client/models/function.py +43 -47
- blaxel/core/client/models/function_runtime.py +138 -0
- blaxel/core/client/models/function_runtime_generation.py +18 -0
- blaxel/core/client/models/function_spec.py +27 -73
- blaxel/core/client/models/function_spec_transport.py +18 -0
- blaxel/core/client/models/image.py +19 -36
- blaxel/core/client/models/integration_connection.py +25 -39
- blaxel/core/client/models/integration_connection_spec.py +8 -5
- blaxel/core/client/models/integration_connection_spec_config.py +1 -1
- blaxel/core/client/models/integration_connection_spec_secret.py +1 -1
- blaxel/core/client/models/integration_endpoint.py +41 -11
- blaxel/core/client/models/integration_endpoint_ignore_models_item.py +45 -0
- blaxel/core/client/models/{mcp_definition_entrypoint.py → integration_endpoint_models_item.py} +6 -6
- blaxel/core/client/models/job.py +43 -47
- blaxel/core/client/models/job_execution.py +30 -37
- blaxel/core/client/models/job_execution_metadata.py +3 -3
- blaxel/core/client/models/job_execution_spec.py +2 -2
- blaxel/core/client/models/job_execution_stats.py +5 -5
- blaxel/core/client/models/job_execution_status.py +24 -0
- blaxel/core/client/models/job_execution_task.py +12 -4
- blaxel/core/client/models/job_execution_task_metadata.py +1 -1
- blaxel/core/client/models/job_execution_task_spec.py +2 -2
- blaxel/core/client/models/job_execution_task_status.py +23 -0
- blaxel/core/client/models/job_runtime.py +172 -0
- blaxel/core/client/models/job_runtime_generation.py +18 -0
- blaxel/core/client/models/job_spec.py +20 -88
- blaxel/core/client/models/location_response.py +5 -5
- blaxel/core/client/models/mcp_definition.py +30 -17
- blaxel/core/client/models/{job_metrics_tasks_total.py → mcp_definition_categories_item.py} +6 -6
- blaxel/core/client/models/metadata.py +23 -17
- blaxel/core/client/models/metadata_labels.py +4 -1
- blaxel/core/client/models/model.py +43 -47
- blaxel/core/client/models/model_runtime.py +99 -0
- blaxel/core/client/models/model_runtime_type.py +34 -0
- blaxel/core/client/models/model_spec.py +12 -58
- blaxel/core/client/models/o_auth.py +23 -6
- blaxel/core/client/models/{form_oauth.py → o_auth_scope_item.py} +6 -6
- blaxel/core/client/models/pending_invitation_accept.py +2 -1
- blaxel/core/client/models/pending_invitation_workspace_details.py +27 -6
- blaxel/core/client/models/{metrics_request_total_per_code.py → pending_invitation_workspace_details_emails_item.py} +6 -6
- blaxel/core/client/models/policy.py +20 -36
- blaxel/core/client/models/policy_location.py +13 -5
- blaxel/core/client/models/policy_location_type.py +19 -0
- blaxel/core/client/models/policy_max_tokens.py +6 -6
- blaxel/core/client/models/policy_resource_type.py +20 -0
- blaxel/core/client/models/policy_spec.py +31 -10
- blaxel/core/client/models/policy_spec_type.py +19 -0
- blaxel/core/client/models/port.py +25 -15
- blaxel/core/client/models/port_protocol.py +19 -0
- blaxel/core/client/models/preview.py +19 -36
- blaxel/core/client/models/preview_metadata.py +12 -10
- blaxel/core/client/models/preview_token.py +19 -36
- blaxel/core/client/models/preview_token_metadata.py +8 -6
- blaxel/core/client/models/repository.py +2 -2
- blaxel/core/client/models/revision_configuration.py +3 -3
- blaxel/core/client/models/sandbox.py +45 -58
- blaxel/core/client/models/sandbox_definition.py +37 -22
- blaxel/core/client/models/sandbox_definition_categories_item.py +45 -0
- blaxel/core/client/models/sandbox_error.py +148 -0
- blaxel/core/client/models/sandbox_error_details.py +45 -0
- blaxel/core/client/models/sandbox_lifecycle.py +3 -2
- blaxel/core/client/models/sandbox_runtime.py +145 -0
- blaxel/core/client/models/sandbox_spec.py +33 -134
- blaxel/core/client/models/status.py +25 -0
- blaxel/core/client/models/template.py +8 -7
- blaxel/core/client/models/template_variable.py +5 -5
- blaxel/core/client/models/trigger.py +14 -6
- blaxel/core/client/models/trigger_configuration.py +7 -6
- blaxel/core/client/models/trigger_type.py +19 -0
- blaxel/core/client/models/volume.py +35 -47
- blaxel/core/client/models/volume_attachment.py +6 -4
- blaxel/core/client/models/volume_spec.py +7 -4
- blaxel/core/client/models/volume_state.py +3 -3
- blaxel/core/client/models/volume_template.py +19 -33
- blaxel/core/client/models/volume_template_state.py +12 -4
- blaxel/core/client/models/volume_template_state_status.py +19 -0
- blaxel/core/client/models/volume_template_version.py +12 -4
- blaxel/core/client/models/volume_template_version_status.py +19 -0
- blaxel/core/client/models/workspace.py +35 -25
- blaxel/core/client/models/workspace_runtime.py +3 -2
- blaxel/core/client/models/workspace_status.py +22 -0
- blaxel/core/common/__init__.py +1 -1
- blaxel/core/jobs/__init__.py +0 -1
- blaxel/core/sandbox/__init__.py +2 -0
- blaxel/core/sandbox/client/api/process/post_process.py +8 -4
- blaxel/core/sandbox/client/models/__init__.py +0 -2
- blaxel/core/sandbox/client/models/process_response.py +16 -0
- blaxel/core/sandbox/client/models/process_response_status.py +9 -0
- blaxel/core/sandbox/default/__init__.py +2 -0
- blaxel/core/sandbox/default/interpreter.py +5 -1
- blaxel/core/sandbox/default/preview.py +3 -1
- blaxel/core/sandbox/default/sandbox.py +85 -26
- blaxel/core/sandbox/sync/process.py +2 -1
- blaxel/core/sandbox/sync/sandbox.py +68 -23
- blaxel/core/sandbox/types.py +3 -0
- blaxel/core/tools/common.py +16 -2
- blaxel/core/volume/__init__.py +2 -2
- blaxel/core/volume/volume.py +227 -11
- blaxel/langgraph/tools.py +34 -2
- blaxel/openai/tools.py +33 -1
- {blaxel-0.2.33.dist-info → blaxel-0.2.35.dist-info}/METADATA +3 -3
- {blaxel-0.2.33.dist-info → blaxel-0.2.35.dist-info}/RECORD +209 -248
- blaxel/core/client/models/acl.py +0 -133
- blaxel/core/client/models/billable_time_metric.py +0 -89
- blaxel/core/client/models/core_spec.py +0 -194
- blaxel/core/client/models/core_spec_configurations.py +0 -77
- blaxel/core/client/models/histogram_bucket.py +0 -79
- blaxel/core/client/models/histogram_stats.py +0 -88
- blaxel/core/client/models/integration_model.py +0 -162
- blaxel/core/client/models/job_execution_config.py +0 -79
- blaxel/core/client/models/job_metrics.py +0 -262
- blaxel/core/client/models/jobs_chart_value.py +0 -70
- blaxel/core/client/models/jobs_network_chart.py +0 -102
- blaxel/core/client/models/jobs_success_failed_chart.py +0 -147
- blaxel/core/client/models/jobs_total.py +0 -88
- blaxel/core/client/models/last_n_requests_metric.py +0 -97
- blaxel/core/client/models/latency_metric.py +0 -148
- blaxel/core/client/models/logs_response.py +0 -63
- blaxel/core/client/models/logs_response_data.py +0 -99
- blaxel/core/client/models/mcp_definition_form.py +0 -45
- blaxel/core/client/models/memory_allocation_by_name.py +0 -70
- blaxel/core/client/models/memory_allocation_metric.py +0 -61
- blaxel/core/client/models/metric.py +0 -79
- blaxel/core/client/models/metrics.py +0 -273
- blaxel/core/client/models/metrics_models.py +0 -45
- blaxel/core/client/models/metrics_rps_per_code.py +0 -45
- blaxel/core/client/models/pod_template_spec.py +0 -45
- blaxel/core/client/models/request_duration_over_time_metric.py +0 -97
- blaxel/core/client/models/request_duration_over_time_metrics.py +0 -84
- blaxel/core/client/models/request_total_by_origin_metric.py +0 -129
- blaxel/core/client/models/request_total_by_origin_metric_request_total_by_origin.py +0 -45
- blaxel/core/client/models/request_total_by_origin_metric_request_total_by_origin_and_code.py +0 -45
- blaxel/core/client/models/request_total_metric.py +0 -155
- blaxel/core/client/models/request_total_metric_request_total_per_code.py +0 -45
- blaxel/core/client/models/request_total_metric_rps_per_code.py +0 -45
- blaxel/core/client/models/request_total_response_data.py +0 -97
- blaxel/core/client/models/resource.py +0 -99
- blaxel/core/client/models/resource_log.py +0 -88
- blaxel/core/client/models/resource_log_chart.py +0 -133
- blaxel/core/client/models/resource_log_response.py +0 -83
- blaxel/core/client/models/resource_metrics.py +0 -618
- blaxel/core/client/models/resource_metrics_request_total_per_code.py +0 -45
- blaxel/core/client/models/resource_metrics_request_total_per_code_previous.py +0 -45
- blaxel/core/client/models/resource_metrics_rps_per_code.py +0 -45
- blaxel/core/client/models/resource_metrics_rps_per_code_previous.py +0 -45
- blaxel/core/client/models/resource_trace.py +0 -97
- blaxel/core/client/models/runtime.py +0 -317
- blaxel/core/client/models/runtime_configuration.py +0 -45
- blaxel/core/client/models/runtime_startup_probe.py +0 -45
- blaxel/core/client/models/sandbox_metrics.py +0 -88
- blaxel/core/client/models/serverless_config.py +0 -117
- blaxel/core/client/models/serverless_config_configuration.py +0 -45
- blaxel/core/client/models/start_sandbox.py +0 -98
- blaxel/core/client/models/stop_sandbox.py +0 -98
- blaxel/core/client/models/store_agent.py +0 -181
- blaxel/core/client/models/store_agent_labels.py +0 -45
- blaxel/core/client/models/store_configuration.py +0 -156
- blaxel/core/client/models/store_configuration_option.py +0 -79
- blaxel/core/client/models/time_to_first_token_over_time_metrics.py +0 -87
- blaxel/core/client/models/token_rate_metric.py +0 -106
- blaxel/core/client/models/token_rate_metrics.py +0 -124
- blaxel/core/client/models/token_total_metric.py +0 -112
- blaxel/core/client/models/trace_ids_response.py +0 -45
- blaxel/core/client/models/websocket_channel.py +0 -97
- blaxel/core/client/models/websocket_message.py +0 -106
- blaxel/core/sandbox/client/api/root/delete.py +0 -130
- blaxel/core/sandbox/client/api/root/get.py +0 -130
- blaxel/core/sandbox/client/api/root/options.py +0 -130
- blaxel/core/sandbox/client/api/root/patch.py +0 -130
- blaxel/core/sandbox/client/api/root/post.py +0 -130
- blaxel/core/sandbox/client/api/root/put.py +0 -130
- /blaxel/core/{sandbox/client/api/root → client/api/mcphub}/__init__.py +0 -0
- {blaxel-0.2.33.dist-info → blaxel-0.2.35.dist-info}/WHEEL +0 -0
- {blaxel-0.2.33.dist-info → blaxel-0.2.35.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class WorkspaceStatus(str, Enum):
|
|
5
|
+
ACCOUNT_BINDED = "account_binded"
|
|
6
|
+
ACCOUNT_CONFIGURED = "account_configured"
|
|
7
|
+
CREATED = "created"
|
|
8
|
+
ERROR = "error"
|
|
9
|
+
READY = "ready"
|
|
10
|
+
WORKSPACE_CONFIGURED = "workspace_configured"
|
|
11
|
+
|
|
12
|
+
def __str__(self) -> str:
|
|
13
|
+
return str(self.value)
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def _missing_(cls, value: object) -> "WorkspaceStatus | None":
|
|
17
|
+
if isinstance(value, str):
|
|
18
|
+
upper_value = value.upper()
|
|
19
|
+
for member in cls:
|
|
20
|
+
if member.value.upper() == upper_value:
|
|
21
|
+
return member
|
|
22
|
+
return None
|
blaxel/core/common/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from .autoload import autoload
|
|
2
2
|
from .env import env
|
|
3
|
-
from .sentry import capture_exception, flush_sentry, init_sentry, is_sentry_initialized
|
|
4
3
|
from .internal import get_alphanumeric_limited_hash, get_global_unique_hash
|
|
4
|
+
from .sentry import capture_exception, flush_sentry, init_sentry, is_sentry_initialized
|
|
5
5
|
from .settings import Settings, settings
|
|
6
6
|
from .webhook import (
|
|
7
7
|
AsyncSidecarCallback,
|
blaxel/core/jobs/__init__.py
CHANGED
blaxel/core/sandbox/__init__.py
CHANGED
|
@@ -6,6 +6,7 @@ from .client.models import (
|
|
|
6
6
|
)
|
|
7
7
|
from .default import (
|
|
8
8
|
CodeInterpreter,
|
|
9
|
+
SandboxAPIError,
|
|
9
10
|
SandboxCodegen,
|
|
10
11
|
SandboxFileSystem,
|
|
11
12
|
SandboxInstance,
|
|
@@ -34,6 +35,7 @@ from .types import (
|
|
|
34
35
|
|
|
35
36
|
__all__ = [
|
|
36
37
|
"SandboxInstance",
|
|
38
|
+
"SandboxAPIError",
|
|
37
39
|
"SessionCreateOptions",
|
|
38
40
|
"SessionWithToken",
|
|
39
41
|
"SandboxConfiguration",
|
|
@@ -77,7 +77,8 @@ def sync_detailed(
|
|
|
77
77
|
) -> Response[Union[ErrorResponse, ProcessResponse]]:
|
|
78
78
|
"""Execute a command
|
|
79
79
|
|
|
80
|
-
Execute a command and return process information
|
|
80
|
+
Execute a command and return process information. If Accept header is text/event-stream, streams
|
|
81
|
+
logs in SSE format and returns the process response as a final event.
|
|
81
82
|
|
|
82
83
|
Args:
|
|
83
84
|
body (ProcessRequest):
|
|
@@ -108,7 +109,8 @@ def sync(
|
|
|
108
109
|
) -> Union[ErrorResponse, ProcessResponse] | None:
|
|
109
110
|
"""Execute a command
|
|
110
111
|
|
|
111
|
-
Execute a command and return process information
|
|
112
|
+
Execute a command and return process information. If Accept header is text/event-stream, streams
|
|
113
|
+
logs in SSE format and returns the process response as a final event.
|
|
112
114
|
|
|
113
115
|
Args:
|
|
114
116
|
body (ProcessRequest):
|
|
@@ -134,7 +136,8 @@ async def asyncio_detailed(
|
|
|
134
136
|
) -> Response[Union[ErrorResponse, ProcessResponse]]:
|
|
135
137
|
"""Execute a command
|
|
136
138
|
|
|
137
|
-
Execute a command and return process information
|
|
139
|
+
Execute a command and return process information. If Accept header is text/event-stream, streams
|
|
140
|
+
logs in SSE format and returns the process response as a final event.
|
|
138
141
|
|
|
139
142
|
Args:
|
|
140
143
|
body (ProcessRequest):
|
|
@@ -163,7 +166,8 @@ async def asyncio(
|
|
|
163
166
|
) -> Union[ErrorResponse, ProcessResponse] | None:
|
|
164
167
|
"""Execute a command
|
|
165
168
|
|
|
166
|
-
Execute a command and return process information
|
|
169
|
+
Execute a command and return process information. If Accept header is text/event-stream, streams
|
|
170
|
+
logs in SSE format and returns the process response as a final event.
|
|
167
171
|
|
|
168
172
|
Args:
|
|
169
173
|
body (ProcessRequest):
|
|
@@ -41,7 +41,6 @@ from .subdirectory import Subdirectory
|
|
|
41
41
|
from .success_response import SuccessResponse
|
|
42
42
|
from .tree_request import TreeRequest
|
|
43
43
|
from .tree_request_files import TreeRequestFiles
|
|
44
|
-
from .welcome_response import WelcomeResponse
|
|
45
44
|
|
|
46
45
|
__all__ = (
|
|
47
46
|
"ApplyEditRequest",
|
|
@@ -83,5 +82,4 @@ __all__ = (
|
|
|
83
82
|
"SuccessResponse",
|
|
84
83
|
"TreeRequest",
|
|
85
84
|
"TreeRequestFiles",
|
|
86
|
-
"WelcomeResponse",
|
|
87
85
|
)
|
|
@@ -21,6 +21,8 @@ class ProcessResponse:
|
|
|
21
21
|
pid (str): Example: 1234.
|
|
22
22
|
started_at (str): Example: Wed, 01 Jan 2023 12:00:00 GMT.
|
|
23
23
|
status (ProcessResponseStatus): Example: running.
|
|
24
|
+
stderr (str): Example: stderr output.
|
|
25
|
+
stdout (str): Example: stdout output.
|
|
24
26
|
working_dir (str): Example: /home/user.
|
|
25
27
|
max_restarts (Union[Unset, int]): Example: 3.
|
|
26
28
|
restart_count (Union[Unset, int]): Example: 2.
|
|
@@ -35,6 +37,8 @@ class ProcessResponse:
|
|
|
35
37
|
pid: str
|
|
36
38
|
started_at: str
|
|
37
39
|
status: ProcessResponseStatus
|
|
40
|
+
stderr: str
|
|
41
|
+
stdout: str
|
|
38
42
|
working_dir: str
|
|
39
43
|
max_restarts: Union[Unset, int] = UNSET
|
|
40
44
|
restart_count: Union[Unset, int] = UNSET
|
|
@@ -58,6 +62,10 @@ class ProcessResponse:
|
|
|
58
62
|
|
|
59
63
|
status = self.status.value
|
|
60
64
|
|
|
65
|
+
stderr = self.stderr
|
|
66
|
+
|
|
67
|
+
stdout = self.stdout
|
|
68
|
+
|
|
61
69
|
working_dir = self.working_dir
|
|
62
70
|
|
|
63
71
|
max_restarts = self.max_restarts
|
|
@@ -78,6 +86,8 @@ class ProcessResponse:
|
|
|
78
86
|
"pid": pid,
|
|
79
87
|
"startedAt": started_at,
|
|
80
88
|
"status": status,
|
|
89
|
+
"stderr": stderr,
|
|
90
|
+
"stdout": stdout,
|
|
81
91
|
"workingDir": working_dir,
|
|
82
92
|
}
|
|
83
93
|
)
|
|
@@ -111,6 +121,10 @@ class ProcessResponse:
|
|
|
111
121
|
|
|
112
122
|
status = ProcessResponseStatus(d.pop("status"))
|
|
113
123
|
|
|
124
|
+
stderr = d.pop("stderr")
|
|
125
|
+
|
|
126
|
+
stdout = d.pop("stdout")
|
|
127
|
+
|
|
114
128
|
working_dir = d.pop("workingDir") if "workingDir" in d else d.pop("working_dir")
|
|
115
129
|
|
|
116
130
|
max_restarts = d.pop("maxRestarts", d.pop("max_restarts", UNSET))
|
|
@@ -128,6 +142,8 @@ class ProcessResponse:
|
|
|
128
142
|
pid=pid,
|
|
129
143
|
started_at=started_at,
|
|
130
144
|
status=status,
|
|
145
|
+
stderr=stderr,
|
|
146
|
+
stdout=stdout,
|
|
131
147
|
working_dir=working_dir,
|
|
132
148
|
max_restarts=max_restarts,
|
|
133
149
|
restart_count=restart_count,
|
|
@@ -10,3 +10,12 @@ class ProcessResponseStatus(str, Enum):
|
|
|
10
10
|
|
|
11
11
|
def __str__(self) -> str:
|
|
12
12
|
return str(self.value)
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def _missing_(cls, value: object) -> "ProcessResponseStatus | None":
|
|
16
|
+
if isinstance(value, str):
|
|
17
|
+
upper_value = value.upper()
|
|
18
|
+
for member in cls:
|
|
19
|
+
if member.value.upper() == upper_value:
|
|
20
|
+
return member
|
|
21
|
+
return None
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from .interpreter import CodeInterpreter
|
|
2
2
|
from .sandbox import (
|
|
3
|
+
SandboxAPIError,
|
|
3
4
|
SandboxCodegen,
|
|
4
5
|
SandboxFileSystem,
|
|
5
6
|
SandboxInstance,
|
|
@@ -9,6 +10,7 @@ from .sandbox import (
|
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
11
12
|
"SandboxInstance",
|
|
13
|
+
"SandboxAPIError",
|
|
12
14
|
"SandboxFileSystem",
|
|
13
15
|
"SandboxPreviews",
|
|
14
16
|
"SandboxProcess",
|
|
@@ -52,7 +52,7 @@ class CodeInterpreter(SandboxInstance):
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
# Whitelist a minimal set of fields that can be propagated from input
|
|
55
|
-
allowed_copy_keys = {"name", "envs", "memory", "region", "headers"}
|
|
55
|
+
allowed_copy_keys = {"name", "envs", "memory", "region", "headers", "labels"}
|
|
56
56
|
|
|
57
57
|
if isinstance(sandbox, dict):
|
|
58
58
|
for k in allowed_copy_keys:
|
|
@@ -67,10 +67,14 @@ class CodeInterpreter(SandboxInstance):
|
|
|
67
67
|
payload["memory"] = sandbox.memory
|
|
68
68
|
if getattr(sandbox, "region", None):
|
|
69
69
|
payload["region"] = sandbox.region
|
|
70
|
+
if getattr(sandbox, "labels", None):
|
|
71
|
+
payload["labels"] = sandbox.labels
|
|
70
72
|
elif isinstance(sandbox, Sandbox):
|
|
71
73
|
# Extract a few basics if available
|
|
72
74
|
if sandbox.metadata and getattr(sandbox.metadata, "name", None):
|
|
73
75
|
payload["name"] = sandbox.metadata.name
|
|
76
|
+
if sandbox.metadata and getattr(sandbox.metadata, "labels", None):
|
|
77
|
+
payload["labels"] = sandbox.metadata.labels
|
|
74
78
|
if sandbox.spec and sandbox.spec.runtime:
|
|
75
79
|
if getattr(sandbox.spec.runtime, "envs", None):
|
|
76
80
|
payload["envs"] = sandbox.spec.runtime.envs
|
|
@@ -29,6 +29,7 @@ from ...client.models import (
|
|
|
29
29
|
Preview,
|
|
30
30
|
PreviewSpec,
|
|
31
31
|
PreviewToken,
|
|
32
|
+
PreviewTokenMetadata,
|
|
32
33
|
PreviewTokenSpec,
|
|
33
34
|
Sandbox,
|
|
34
35
|
)
|
|
@@ -69,9 +70,10 @@ class SandboxPreviewTokens:
|
|
|
69
70
|
self.resource_name,
|
|
70
71
|
self.preview_name,
|
|
71
72
|
body=PreviewToken(
|
|
73
|
+
metadata=PreviewTokenMetadata(name=""),
|
|
72
74
|
spec=PreviewTokenSpec(
|
|
73
75
|
expires_at=to_utc_z(expires_at),
|
|
74
|
-
)
|
|
76
|
+
),
|
|
75
77
|
),
|
|
76
78
|
client=client,
|
|
77
79
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import uuid
|
|
3
|
-
from typing import Any, Dict, List, Union
|
|
3
|
+
from typing import Any, Callable, Dict, List, Union
|
|
4
4
|
|
|
5
5
|
from ...client.api.compute.create_sandbox import asyncio as create_sandbox
|
|
6
6
|
from ...client.api.compute.delete_sandbox import asyncio as delete_sandbox
|
|
@@ -8,7 +8,9 @@ from ...client.api.compute.get_sandbox import asyncio as get_sandbox
|
|
|
8
8
|
from ...client.api.compute.list_sandboxes import asyncio as list_sandboxes
|
|
9
9
|
from ...client.api.compute.update_sandbox import asyncio as update_sandbox
|
|
10
10
|
from ...client.client import client
|
|
11
|
-
from ...client.models import Metadata,
|
|
11
|
+
from ...client.models import Metadata, Sandbox, SandboxRuntime, SandboxSpec
|
|
12
|
+
from ...client.models.error import Error
|
|
13
|
+
from ...client.models.sandbox_error import SandboxError
|
|
12
14
|
from ...client.types import UNSET
|
|
13
15
|
from ..types import (
|
|
14
16
|
SandboxConfiguration,
|
|
@@ -23,10 +25,40 @@ from .preview import SandboxPreviews
|
|
|
23
25
|
from .process import SandboxProcess
|
|
24
26
|
from .session import SandboxSessions
|
|
25
27
|
|
|
28
|
+
|
|
29
|
+
class SandboxAPIError(Exception):
|
|
30
|
+
"""Exception raised when sandbox API returns an error."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, message: str, status_code: int | None = None, code: str | None = None):
|
|
33
|
+
super().__init__(message)
|
|
34
|
+
self.status_code = status_code
|
|
35
|
+
self.code = code
|
|
36
|
+
|
|
37
|
+
|
|
26
38
|
logger = logging.getLogger(__name__)
|
|
27
39
|
|
|
28
40
|
|
|
41
|
+
class _AsyncDeleteDescriptor:
|
|
42
|
+
"""Descriptor that provides both class-level and instance-level delete functionality."""
|
|
43
|
+
|
|
44
|
+
def __init__(self, delete_func: Callable):
|
|
45
|
+
self._delete_func = delete_func
|
|
46
|
+
|
|
47
|
+
def __get__(self, instance, owner):
|
|
48
|
+
if instance is None:
|
|
49
|
+
# Called on the class: SandboxInstance.delete("name")
|
|
50
|
+
return self._delete_func
|
|
51
|
+
else:
|
|
52
|
+
# Called on an instance: instance.delete()
|
|
53
|
+
async def instance_delete() -> Sandbox:
|
|
54
|
+
return await self._delete_func(instance.metadata.name)
|
|
55
|
+
|
|
56
|
+
return instance_delete
|
|
57
|
+
|
|
58
|
+
|
|
29
59
|
class SandboxInstance:
|
|
60
|
+
delete: "_AsyncDeleteDescriptor"
|
|
61
|
+
|
|
30
62
|
def __init__(
|
|
31
63
|
self,
|
|
32
64
|
sandbox: Union[Sandbox, SandboxConfiguration],
|
|
@@ -107,6 +139,7 @@ class SandboxInstance:
|
|
|
107
139
|
or "lifecycle" in (sandbox if isinstance(sandbox, dict) else sandbox.__dict__)
|
|
108
140
|
or "snapshot_enabled"
|
|
109
141
|
in (sandbox if isinstance(sandbox, dict) else sandbox.__dict__)
|
|
142
|
+
or "labels" in (sandbox if isinstance(sandbox, dict) else sandbox.__dict__)
|
|
110
143
|
)
|
|
111
144
|
)
|
|
112
145
|
):
|
|
@@ -135,14 +168,13 @@ class SandboxInstance:
|
|
|
135
168
|
|
|
136
169
|
# Create full Sandbox object
|
|
137
170
|
sandbox = Sandbox(
|
|
138
|
-
metadata=Metadata(name=name),
|
|
171
|
+
metadata=Metadata(name=name, labels=config.labels),
|
|
139
172
|
spec=SandboxSpec(
|
|
140
|
-
runtime=
|
|
173
|
+
runtime=SandboxRuntime(
|
|
141
174
|
image=image,
|
|
142
175
|
memory=memory,
|
|
143
176
|
ports=ports,
|
|
144
177
|
envs=envs,
|
|
145
|
-
generation="mk3",
|
|
146
178
|
),
|
|
147
179
|
volumes=volumes,
|
|
148
180
|
),
|
|
@@ -167,19 +199,26 @@ class SandboxInstance:
|
|
|
167
199
|
sandbox.metadata = Metadata(name=default_name)
|
|
168
200
|
if not sandbox.spec:
|
|
169
201
|
sandbox.spec = SandboxSpec(
|
|
170
|
-
runtime=
|
|
202
|
+
runtime=SandboxRuntime(image=default_image, memory=default_memory)
|
|
171
203
|
)
|
|
172
204
|
if not sandbox.spec.runtime:
|
|
173
|
-
sandbox.spec.runtime =
|
|
205
|
+
sandbox.spec.runtime = SandboxRuntime(image=default_image, memory=default_memory)
|
|
174
206
|
|
|
175
207
|
sandbox.spec.runtime.image = sandbox.spec.runtime.image or default_image
|
|
176
208
|
sandbox.spec.runtime.memory = sandbox.spec.runtime.memory or default_memory
|
|
177
|
-
sandbox.spec.runtime.generation = sandbox.spec.runtime.generation or "mk3"
|
|
178
209
|
|
|
179
210
|
response = await create_sandbox(
|
|
180
211
|
client=client,
|
|
181
212
|
body=sandbox,
|
|
182
213
|
)
|
|
214
|
+
|
|
215
|
+
# Check if response is an error
|
|
216
|
+
if isinstance(response, SandboxError):
|
|
217
|
+
status_code = response.status_code if response.status_code is not UNSET else None
|
|
218
|
+
code = response.code if response.code else None
|
|
219
|
+
message = response.message if response.message else str(response)
|
|
220
|
+
raise SandboxAPIError(message, status_code=status_code, code=code)
|
|
221
|
+
|
|
183
222
|
instance = cls(response)
|
|
184
223
|
# TODO remove this part once we have a better way to handle this
|
|
185
224
|
if safe:
|
|
@@ -195,21 +234,23 @@ class SandboxInstance:
|
|
|
195
234
|
sandbox_name,
|
|
196
235
|
client=client,
|
|
197
236
|
)
|
|
237
|
+
|
|
238
|
+
# Check if response is an error
|
|
239
|
+
if isinstance(response, Error):
|
|
240
|
+
status_code = response.code if response.code is not UNSET else None
|
|
241
|
+
message = response.message if response.message is not UNSET else response.error
|
|
242
|
+
raise SandboxAPIError(message, status_code=status_code, code=response.error)
|
|
243
|
+
|
|
244
|
+
if response is None:
|
|
245
|
+
raise SandboxAPIError(f"Sandbox '{sandbox_name}' not found", status_code=404)
|
|
246
|
+
|
|
198
247
|
return cls(response)
|
|
199
248
|
|
|
200
249
|
@classmethod
|
|
201
250
|
async def list(cls) -> List["SandboxInstance"]:
|
|
202
|
-
response = await list_sandboxes()
|
|
251
|
+
response = await list_sandboxes(client=client)
|
|
203
252
|
return [cls(sandbox) for sandbox in response]
|
|
204
253
|
|
|
205
|
-
@classmethod
|
|
206
|
-
async def delete(cls, sandbox_name: str) -> Sandbox:
|
|
207
|
-
response = await delete_sandbox(
|
|
208
|
-
sandbox_name,
|
|
209
|
-
client=client,
|
|
210
|
-
)
|
|
211
|
-
return response
|
|
212
|
-
|
|
213
254
|
@classmethod
|
|
214
255
|
async def update_metadata(
|
|
215
256
|
cls, sandbox_name: str, metadata: SandboxUpdateMetadata
|
|
@@ -229,10 +270,12 @@ class SandboxInstance:
|
|
|
229
270
|
|
|
230
271
|
# Prepare the updated sandbox object
|
|
231
272
|
updated_sandbox = Sandbox.from_dict(sandbox.to_dict())
|
|
273
|
+
if updated_sandbox is None:
|
|
274
|
+
raise ValueError(f"Sandbox {sandbox_name} not found")
|
|
232
275
|
|
|
233
276
|
# Merge metadata
|
|
234
277
|
if updated_sandbox.metadata is None:
|
|
235
|
-
updated_sandbox.metadata = Metadata()
|
|
278
|
+
updated_sandbox.metadata = Metadata(name=sandbox_name)
|
|
236
279
|
|
|
237
280
|
# Update labels if provided
|
|
238
281
|
if metadata.labels is not None:
|
|
@@ -240,8 +283,11 @@ class SandboxInstance:
|
|
|
240
283
|
if updated_sandbox.metadata.labels is None or updated_sandbox.metadata.labels is UNSET:
|
|
241
284
|
updated_sandbox.metadata.labels = {}
|
|
242
285
|
else:
|
|
243
|
-
# If labels exist,
|
|
244
|
-
|
|
286
|
+
# If labels exist, convert to dict (MetadataLabels stores in additional_properties)
|
|
287
|
+
if hasattr(updated_sandbox.metadata.labels, "to_dict"):
|
|
288
|
+
updated_sandbox.metadata.labels = updated_sandbox.metadata.labels.to_dict()
|
|
289
|
+
else:
|
|
290
|
+
updated_sandbox.metadata.labels = dict(updated_sandbox.metadata.labels)
|
|
245
291
|
updated_sandbox.metadata.labels.update(metadata.labels)
|
|
246
292
|
|
|
247
293
|
# Update display_name if provided
|
|
@@ -265,11 +311,9 @@ class SandboxInstance:
|
|
|
265
311
|
"""Create a sandbox if it doesn't exist, otherwise return existing."""
|
|
266
312
|
try:
|
|
267
313
|
return await cls.create(sandbox)
|
|
268
|
-
except
|
|
314
|
+
except SandboxAPIError as e:
|
|
269
315
|
# Check if it's a 409 conflict error (sandbox already exists)
|
|
270
|
-
if
|
|
271
|
-
hasattr(e, "code") and e.code in [409, "SANDBOX_ALREADY_EXISTS"]
|
|
272
|
-
):
|
|
316
|
+
if e.status_code == 409 or e.code in [409, "SANDBOX_ALREADY_EXISTS"]:
|
|
273
317
|
# Extract name from different configuration types
|
|
274
318
|
if isinstance(sandbox, SandboxCreateConfiguration):
|
|
275
319
|
name = sandbox.name
|
|
@@ -298,7 +342,7 @@ class SandboxInstance:
|
|
|
298
342
|
|
|
299
343
|
# Otherwise return the existing active sandbox
|
|
300
344
|
return sandbox_instance
|
|
301
|
-
raise
|
|
345
|
+
raise
|
|
302
346
|
|
|
303
347
|
@classmethod
|
|
304
348
|
async def from_session(
|
|
@@ -310,7 +354,7 @@ class SandboxInstance:
|
|
|
310
354
|
|
|
311
355
|
# Create a minimal sandbox configuration for session-based access
|
|
312
356
|
sandbox_name = session.name.split("-")[0] if "-" in session.name else session.name
|
|
313
|
-
sandbox = Sandbox(metadata=Metadata(name=sandbox_name))
|
|
357
|
+
sandbox = Sandbox(metadata=Metadata(name=sandbox_name), spec=SandboxSpec())
|
|
314
358
|
|
|
315
359
|
# Use the constructor with force_url, headers, and params
|
|
316
360
|
return cls(
|
|
@@ -319,3 +363,18 @@ class SandboxInstance:
|
|
|
319
363
|
headers={"X-Blaxel-Preview-Token": session.token},
|
|
320
364
|
params={"bl_preview_token": session.token},
|
|
321
365
|
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
async def _delete_sandbox_by_name(sandbox_name: str) -> Sandbox:
|
|
369
|
+
"""Delete a sandbox by name."""
|
|
370
|
+
response = await delete_sandbox(
|
|
371
|
+
sandbox_name,
|
|
372
|
+
client=client,
|
|
373
|
+
)
|
|
374
|
+
if response is None:
|
|
375
|
+
raise ValueError(f"Sandbox {sandbox_name} not found")
|
|
376
|
+
return response
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
# Assign the delete descriptor to support both class-level and instance-level calls
|
|
380
|
+
SandboxInstance.delete = _AsyncDeleteDescriptor(_delete_sandbox_by_name)
|
|
@@ -186,7 +186,8 @@ class SyncSandboxProcess(SyncSandboxAction):
|
|
|
186
186
|
|
|
187
187
|
if on_log or on_stdout or on_stderr:
|
|
188
188
|
stream_control = self._stream_logs(
|
|
189
|
-
result.pid,
|
|
189
|
+
result.pid,
|
|
190
|
+
{"on_log": on_log, "on_stdout": on_stdout, "on_stderr": on_stderr},
|
|
190
191
|
)
|
|
191
192
|
return ProcessResponseWithLog(
|
|
192
193
|
result,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import uuid
|
|
3
|
-
from typing import Any, Dict, List, Union
|
|
3
|
+
from typing import Any, Callable, Dict, List, Union
|
|
4
4
|
|
|
5
5
|
from ...client.api.compute.create_sandbox import sync as create_sandbox
|
|
6
6
|
from ...client.api.compute.delete_sandbox import sync as delete_sandbox
|
|
@@ -8,8 +8,11 @@ from ...client.api.compute.get_sandbox import sync as get_sandbox
|
|
|
8
8
|
from ...client.api.compute.list_sandboxes import sync as list_sandboxes
|
|
9
9
|
from ...client.api.compute.update_sandbox import sync as update_sandbox
|
|
10
10
|
from ...client.client import client
|
|
11
|
-
from ...client.models import Metadata,
|
|
11
|
+
from ...client.models import Metadata, Sandbox, SandboxRuntime, SandboxSpec
|
|
12
|
+
from ...client.models.error import Error
|
|
13
|
+
from ...client.models.sandbox_error import SandboxError
|
|
12
14
|
from ...client.types import UNSET
|
|
15
|
+
from ..default.sandbox import SandboxAPIError
|
|
13
16
|
from ..types import (
|
|
14
17
|
SandboxConfiguration,
|
|
15
18
|
SandboxCreateConfiguration,
|
|
@@ -26,6 +29,24 @@ from .session import SyncSandboxSessions
|
|
|
26
29
|
logger = logging.getLogger(__name__)
|
|
27
30
|
|
|
28
31
|
|
|
32
|
+
class _SyncDeleteDescriptor:
|
|
33
|
+
"""Descriptor that provides both class-level and instance-level delete functionality."""
|
|
34
|
+
|
|
35
|
+
def __init__(self, delete_func: Callable):
|
|
36
|
+
self._delete_func = delete_func
|
|
37
|
+
|
|
38
|
+
def __get__(self, instance, owner):
|
|
39
|
+
if instance is None:
|
|
40
|
+
# Called on the class: SyncSandboxInstance.delete("name")
|
|
41
|
+
return self._delete_func
|
|
42
|
+
else:
|
|
43
|
+
# Called on an instance: instance.delete()
|
|
44
|
+
def instance_delete() -> Sandbox:
|
|
45
|
+
return self._delete_func(instance.metadata.name)
|
|
46
|
+
|
|
47
|
+
return instance_delete
|
|
48
|
+
|
|
49
|
+
|
|
29
50
|
class SyncSandboxInstance:
|
|
30
51
|
def __init__(
|
|
31
52
|
self,
|
|
@@ -102,6 +123,7 @@ class SyncSandboxInstance:
|
|
|
102
123
|
or "lifecycle" in (sandbox if isinstance(sandbox, dict) else sandbox.__dict__)
|
|
103
124
|
or "snapshot_enabled"
|
|
104
125
|
in (sandbox if isinstance(sandbox, dict) else sandbox.__dict__)
|
|
126
|
+
or "labels" in (sandbox if isinstance(sandbox, dict) else sandbox.__dict__)
|
|
105
127
|
)
|
|
106
128
|
)
|
|
107
129
|
):
|
|
@@ -125,14 +147,13 @@ class SyncSandboxInstance:
|
|
|
125
147
|
region = config.region
|
|
126
148
|
lifecycle = config.lifecycle
|
|
127
149
|
sandbox = Sandbox(
|
|
128
|
-
metadata=Metadata(name=name),
|
|
150
|
+
metadata=Metadata(name=name, labels=config.labels),
|
|
129
151
|
spec=SandboxSpec(
|
|
130
|
-
runtime=
|
|
152
|
+
runtime=SandboxRuntime(
|
|
131
153
|
image=image,
|
|
132
154
|
memory=memory,
|
|
133
155
|
ports=ports,
|
|
134
156
|
envs=envs,
|
|
135
|
-
generation="mk3",
|
|
136
157
|
),
|
|
137
158
|
volumes=volumes,
|
|
138
159
|
),
|
|
@@ -152,17 +173,24 @@ class SyncSandboxInstance:
|
|
|
152
173
|
sandbox.metadata = Metadata(name=default_name)
|
|
153
174
|
if not sandbox.spec:
|
|
154
175
|
sandbox.spec = SandboxSpec(
|
|
155
|
-
runtime=
|
|
176
|
+
runtime=SandboxRuntime(image=default_image, memory=default_memory)
|
|
156
177
|
)
|
|
157
178
|
if not sandbox.spec.runtime:
|
|
158
|
-
sandbox.spec.runtime =
|
|
179
|
+
sandbox.spec.runtime = SandboxRuntime(image=default_image, memory=default_memory)
|
|
159
180
|
sandbox.spec.runtime.image = sandbox.spec.runtime.image or default_image
|
|
160
181
|
sandbox.spec.runtime.memory = sandbox.spec.runtime.memory or default_memory
|
|
161
|
-
sandbox.spec.runtime.generation = sandbox.spec.runtime.generation or "mk3"
|
|
162
182
|
response = create_sandbox(
|
|
163
183
|
client=client,
|
|
164
184
|
body=sandbox,
|
|
165
185
|
)
|
|
186
|
+
|
|
187
|
+
# Check if response is an error
|
|
188
|
+
if isinstance(response, SandboxError):
|
|
189
|
+
status_code = response.status_code if response.status_code is not UNSET else None
|
|
190
|
+
code = response.code if response.code else None
|
|
191
|
+
message = response.message if response.message else str(response)
|
|
192
|
+
raise SandboxAPIError(message, status_code=status_code, code=code)
|
|
193
|
+
|
|
166
194
|
instance = cls(response)
|
|
167
195
|
if safe:
|
|
168
196
|
try:
|
|
@@ -177,6 +205,16 @@ class SyncSandboxInstance:
|
|
|
177
205
|
sandbox_name,
|
|
178
206
|
client=client,
|
|
179
207
|
)
|
|
208
|
+
|
|
209
|
+
# Check if response is an error
|
|
210
|
+
if isinstance(response, Error):
|
|
211
|
+
status_code = response.code if response.code is not UNSET else None
|
|
212
|
+
message = response.message if response.message is not UNSET else response.error
|
|
213
|
+
raise SandboxAPIError(message, status_code=status_code, code=response.error)
|
|
214
|
+
|
|
215
|
+
if response is None:
|
|
216
|
+
raise SandboxAPIError(f"Sandbox '{sandbox_name}' not found", status_code=404)
|
|
217
|
+
|
|
180
218
|
return cls(response)
|
|
181
219
|
|
|
182
220
|
@classmethod
|
|
@@ -184,14 +222,6 @@ class SyncSandboxInstance:
|
|
|
184
222
|
response = list_sandboxes(client=client)
|
|
185
223
|
return [cls(sandbox) for sandbox in response]
|
|
186
224
|
|
|
187
|
-
@classmethod
|
|
188
|
-
def delete(cls, sandbox_name: str) -> Sandbox:
|
|
189
|
-
response = delete_sandbox(
|
|
190
|
-
sandbox_name,
|
|
191
|
-
client=client,
|
|
192
|
-
)
|
|
193
|
-
return response
|
|
194
|
-
|
|
195
225
|
@classmethod
|
|
196
226
|
def update_metadata(
|
|
197
227
|
cls, sandbox_name: str, metadata: SandboxUpdateMetadata
|
|
@@ -205,7 +235,11 @@ class SyncSandboxInstance:
|
|
|
205
235
|
if updated_sandbox.metadata.labels is None or updated_sandbox.metadata.labels is UNSET:
|
|
206
236
|
updated_sandbox.metadata.labels = {}
|
|
207
237
|
else:
|
|
208
|
-
|
|
238
|
+
# MetadataLabels stores in additional_properties, use to_dict()
|
|
239
|
+
if hasattr(updated_sandbox.metadata.labels, "to_dict"):
|
|
240
|
+
updated_sandbox.metadata.labels = updated_sandbox.metadata.labels.to_dict()
|
|
241
|
+
else:
|
|
242
|
+
updated_sandbox.metadata.labels = dict(updated_sandbox.metadata.labels)
|
|
209
243
|
updated_sandbox.metadata.labels.update(metadata.labels)
|
|
210
244
|
if metadata.display_name is not None:
|
|
211
245
|
updated_sandbox.metadata.display_name = metadata.display_name
|
|
@@ -222,10 +256,8 @@ class SyncSandboxInstance:
|
|
|
222
256
|
) -> "SyncSandboxInstance":
|
|
223
257
|
try:
|
|
224
258
|
return cls.create(sandbox)
|
|
225
|
-
except
|
|
226
|
-
if
|
|
227
|
-
hasattr(e, "code") and e.code in [409, "SANDBOX_ALREADY_EXISTS"]
|
|
228
|
-
):
|
|
259
|
+
except SandboxAPIError as e:
|
|
260
|
+
if e.status_code == 409 or e.code in [409, "SANDBOX_ALREADY_EXISTS"]:
|
|
229
261
|
if isinstance(sandbox, SandboxCreateConfiguration):
|
|
230
262
|
name = sandbox.name
|
|
231
263
|
elif isinstance(sandbox, dict):
|
|
@@ -245,7 +277,7 @@ class SyncSandboxInstance:
|
|
|
245
277
|
if sandbox_instance.status == "TERMINATED":
|
|
246
278
|
return cls.create(sandbox)
|
|
247
279
|
return sandbox_instance
|
|
248
|
-
raise
|
|
280
|
+
raise
|
|
249
281
|
|
|
250
282
|
@classmethod
|
|
251
283
|
def from_session(
|
|
@@ -254,10 +286,23 @@ class SyncSandboxInstance:
|
|
|
254
286
|
if isinstance(session, dict):
|
|
255
287
|
session = SessionWithToken.from_dict(session)
|
|
256
288
|
sandbox_name = session.name.split("-")[0] if "-" in session.name else session.name
|
|
257
|
-
sandbox = Sandbox(metadata=Metadata(name=sandbox_name))
|
|
289
|
+
sandbox = Sandbox(metadata=Metadata(name=sandbox_name), spec=SandboxSpec())
|
|
258
290
|
return cls(
|
|
259
291
|
sandbox=sandbox,
|
|
260
292
|
force_url=session.url,
|
|
261
293
|
headers={"X-Blaxel-Preview-Token": session.token},
|
|
262
294
|
params={"bl_preview_token": session.token},
|
|
263
295
|
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _delete_sandbox_by_name(sandbox_name: str) -> Sandbox:
|
|
299
|
+
"""Delete a sandbox by name."""
|
|
300
|
+
response = delete_sandbox(
|
|
301
|
+
sandbox_name,
|
|
302
|
+
client=client,
|
|
303
|
+
)
|
|
304
|
+
return response
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# Assign the delete descriptor to support both class-level and instance-level calls
|
|
308
|
+
SyncSandboxInstance.delete = _SyncDeleteDescriptor(_delete_sandbox_by_name)
|