blaxel 0.1.22rc70__py3-none-any.whl → 0.2.0rc2__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 +6 -3
- blaxel/core/__init__.py +44 -0
- blaxel/{agents → core/agents}/__init__.py +30 -50
- blaxel/{authentication → core/authentication}/apikey.py +1 -0
- blaxel/{authentication → core/authentication}/clientcredentials.py +6 -2
- blaxel/{client → core/client}/api/agents/create_agent.py +1 -1
- blaxel/{client → core/client}/api/agents/update_agent.py +1 -1
- blaxel/{client → core/client}/api/compute/create_sandbox.py +1 -1
- blaxel/{client → core/client}/api/compute/create_sandbox_preview.py +1 -1
- blaxel/{client → core/client}/api/compute/create_sandbox_preview_token.py +1 -1
- blaxel/{client → core/client}/api/compute/update_sandbox.py +1 -1
- blaxel/{client → core/client}/api/compute/update_sandbox_preview.py +1 -1
- blaxel/{client → core/client}/api/functions/create_function.py +1 -1
- blaxel/{client → core/client}/api/functions/update_function.py +1 -1
- blaxel/{client → core/client}/api/integrations/create_integration_connection.py +1 -1
- blaxel/{client → core/client}/api/integrations/update_integration_connection.py +1 -1
- blaxel/{client → core/client}/api/jobs/create_job.py +1 -1
- blaxel/{client → core/client}/api/jobs/update_job.py +1 -1
- blaxel/{client → core/client}/api/knowledgebases/create_knowledgebase.py +1 -1
- blaxel/{client → core/client}/api/knowledgebases/update_knowledgebase.py +1 -1
- blaxel/{client → core/client}/api/models/create_model.py +1 -1
- blaxel/{client → core/client}/api/models/update_model.py +1 -1
- blaxel/{client → core/client}/api/policies/create_policy.py +1 -1
- blaxel/{client → core/client}/api/policies/update_policy.py +1 -1
- blaxel/{client → core/client}/api/service_accounts/create_api_key_for_service_account.py +1 -1
- blaxel/{client → core/client}/api/service_accounts/create_workspace_service_account.py +1 -1
- blaxel/{client → core/client}/api/service_accounts/update_workspace_service_account.py +1 -1
- blaxel/{client → core/client}/api/workspaces/check_workspace_availability.py +1 -1
- blaxel/{client → core/client}/api/workspaces/create_worspace.py +1 -1
- blaxel/{client → core/client}/api/workspaces/invite_workspace_user.py +1 -1
- blaxel/{client → core/client}/api/workspaces/update_workspace.py +1 -1
- blaxel/{client → core/client}/api/workspaces/update_workspace_user_role.py +1 -1
- blaxel/{client → core/client}/models/agent.py +1 -1
- blaxel/{client → core/client}/models/agent_spec.py +2 -2
- blaxel/{client → core/client}/models/core_spec.py +1 -1
- blaxel/{client → core/client}/models/function.py +1 -1
- blaxel/{client → core/client}/models/function_schema_properties.py +1 -1
- blaxel/{client → core/client}/models/function_spec.py +2 -2
- blaxel/{client → core/client}/models/integration.py +2 -2
- blaxel/{client → core/client}/models/integration_endpoints.py +1 -1
- blaxel/{client → core/client}/models/job.py +1 -1
- blaxel/{client → core/client}/models/job_spec.py +1 -1
- blaxel/{client → core/client}/models/knowledgebase.py +1 -1
- blaxel/{client → core/client}/models/location_response.py +1 -1
- blaxel/{client → core/client}/models/model.py +1 -1
- blaxel/{client → core/client}/models/model_spec.py +1 -1
- blaxel/{client → core/client}/models/policy_spec.py +2 -2
- blaxel/{client → core/client}/models/resource_metrics.py +3 -3
- blaxel/{client → core/client}/models/runtime.py +1 -1
- blaxel/{client → core/client}/models/sandbox.py +1 -1
- blaxel/{client → core/client}/models/sandbox_definition.py +1 -1
- blaxel/{client → core/client}/models/sandbox_spec.py +1 -1
- blaxel/{client → core/client}/models/store_agent.py +1 -1
- blaxel/{client → core/client}/models/store_configuration.py +1 -1
- blaxel/{client → core/client}/models/template.py +1 -1
- blaxel/core/common/__init__.py +6 -0
- blaxel/core/common/autoload.py +21 -0
- blaxel/{common → core/common}/internal.py +33 -62
- blaxel/core/common/logger.py +131 -0
- blaxel/{jobs → core/jobs}/__init__.py +40 -60
- blaxel/core/mcp/__init__.py +4 -0
- blaxel/{mcp → core/mcp}/client.py +13 -7
- blaxel/{mcp → core/mcp}/server.py +4 -30
- blaxel/core/models/__init__.py +52 -0
- blaxel/core/sandbox/__init__.py +29 -0
- blaxel/core/sandbox/action.py +79 -0
- blaxel/{sandbox → core/sandbox}/client/api/filesystem/put_filesystem_path.py +1 -1
- blaxel/{sandbox → core/sandbox}/client/api/network/post_network_process_pid_monitor.py +1 -1
- blaxel/core/sandbox/client/api/process/__init__.py +0 -0
- blaxel/{sandbox → core/sandbox}/client/api/process/post_process.py +1 -1
- blaxel/{sandbox → core/sandbox}/client/models/directory.py +2 -2
- blaxel/core/sandbox/filesystem.py +280 -0
- blaxel/core/sandbox/network.py +10 -0
- blaxel/{sandbox → core/sandbox}/preview.py +45 -17
- blaxel/core/sandbox/process.py +159 -0
- blaxel/{sandbox → core/sandbox}/sandbox.py +62 -5
- blaxel/core/sandbox/session.py +124 -0
- blaxel/core/sandbox/types.py +103 -0
- blaxel/{tools → core/tools}/__init__.py +62 -90
- blaxel/crewai/__init__.py +4 -0
- blaxel/{models/crewai.py → crewai/model.py} +4 -2
- blaxel/crewai/py.typed +0 -0
- blaxel/crewai/tools.py +26 -0
- blaxel/googleadk/__init__.py +4 -0
- blaxel/{models/googleadk.py → googleadk/model.py} +8 -2
- blaxel/googleadk/py.typed +0 -0
- blaxel/googleadk/tools.py +72 -0
- blaxel/langgraph/__init__.py +4 -0
- blaxel/{models/langchain.py → langgraph/model.py} +8 -4
- blaxel/langgraph/py.typed +0 -0
- blaxel/{tools/langchain.py → langgraph/tools.py} +7 -3
- blaxel/livekit/__init__.py +4 -0
- blaxel/{models/livekit.py → livekit/model.py} +7 -1
- blaxel/livekit/py.typed +0 -0
- blaxel/{tools/livekit.py → livekit/tools.py} +8 -1
- blaxel/llamaindex/__init__.py +4 -0
- blaxel/{models/llamaindex.py → llamaindex/model.py} +6 -3
- blaxel/llamaindex/py.typed +0 -0
- blaxel/{tools/llamaindex.py → llamaindex/tools.py} +7 -4
- blaxel/openai/__init__.py +4 -0
- blaxel/{models/openai.py → openai/model.py} +4 -2
- blaxel/openai/py.typed +0 -0
- blaxel/{tools/openai.py → openai/tools.py} +7 -3
- blaxel/pydantic/__init__.py +4 -0
- blaxel/{models/custom/pydantic → pydantic/custom}/gemini.py +0 -1
- blaxel/{models/pydantic.py → pydantic/model.py} +6 -4
- blaxel/pydantic/py.typed +0 -0
- blaxel/{tools/pydantic.py → pydantic/tools.py} +6 -3
- blaxel/telemetry/__init__.py +6 -0
- blaxel/telemetry/instrumentation/blaxel_core.py +124 -0
- blaxel/telemetry/instrumentation/blaxel_langgraph.py +74 -0
- blaxel/telemetry/instrumentation/blaxel_langgraph_gemini.py +360 -0
- blaxel/telemetry/instrumentation/blaxel_llamaindex.py +89 -0
- blaxel/telemetry/instrumentation/map.py +61 -0
- blaxel/telemetry/instrumentation/utils.py +74 -0
- blaxel/{common → telemetry/log}/logger.py +6 -12
- blaxel/{instrumentation → telemetry}/manager.py +20 -12
- blaxel/telemetry/py.typed +0 -0
- blaxel/{instrumentation → telemetry}/span.py +12 -1
- blaxel-0.2.0rc2.dist-info/METADATA +224 -0
- blaxel-0.2.0rc2.dist-info/RECORD +408 -0
- blaxel/common/autoload.py +0 -9
- blaxel/instrumentation/map.py +0 -49
- blaxel/mcp/__init__.py +0 -3
- blaxel/models/__init__.py +0 -104
- blaxel/sandbox/base.py +0 -67
- blaxel/sandbox/filesystem.py +0 -104
- blaxel/sandbox/process.py +0 -56
- blaxel/tools/crewai.py +0 -22
- blaxel/tools/googleadk.py +0 -66
- blaxel-0.1.22rc70.dist-info/METADATA +0 -169
- blaxel-0.1.22rc70.dist-info/RECORD +0 -379
- /blaxel/{authentication → core/authentication}/__init__.py +0 -0
- /blaxel/{authentication → core/authentication}/devicemode.py +0 -0
- /blaxel/{authentication → core/authentication}/oauth.py +0 -0
- /blaxel/{authentication → core/authentication}/types.py +0 -0
- /blaxel/{cache → core/cache}/__init__.py +0 -0
- /blaxel/{cache → core/cache}/cache.py +0 -0
- /blaxel/{client → core/client}/__init__.py +0 -0
- /blaxel/{client → core/client}/api/__init__.py +0 -0
- /blaxel/{client → core/client}/api/agents/__init__.py +0 -0
- /blaxel/{client → core/client}/api/agents/delete_agent.py +0 -0
- /blaxel/{client → core/client}/api/agents/get_agent.py +0 -0
- /blaxel/{client → core/client}/api/agents/list_agent_revisions.py +0 -0
- /blaxel/{client → core/client}/api/agents/list_agents.py +0 -0
- /blaxel/{client → core/client}/api/compute/__init__.py +0 -0
- /blaxel/{client → core/client}/api/compute/delete_sandbox.py +0 -0
- /blaxel/{client → core/client}/api/compute/delete_sandbox_preview.py +0 -0
- /blaxel/{client → core/client}/api/compute/delete_sandbox_preview_token.py +0 -0
- /blaxel/{client → core/client}/api/compute/get_sandbox.py +0 -0
- /blaxel/{client → core/client}/api/compute/get_sandbox_preview.py +0 -0
- /blaxel/{client → core/client}/api/compute/list_sandbox_preview_tokens.py +0 -0
- /blaxel/{client → core/client}/api/compute/list_sandbox_previews.py +0 -0
- /blaxel/{client → core/client}/api/compute/list_sandboxes.py +0 -0
- /blaxel/{client → core/client}/api/compute/start_sandbox.py +0 -0
- /blaxel/{client → core/client}/api/compute/stop_sandbox.py +0 -0
- /blaxel/{client → core/client}/api/configurations/__init__.py +0 -0
- /blaxel/{client → core/client}/api/configurations/get_configuration.py +0 -0
- /blaxel/{client → core/client}/api/default/__init__.py +0 -0
- /blaxel/{client → core/client}/api/default/get_template.py +0 -0
- /blaxel/{client → core/client}/api/default/list_mcp_hub_definitions.py +0 -0
- /blaxel/{client → core/client}/api/default/list_sandbox_hub_definitions.py +0 -0
- /blaxel/{client → core/client}/api/functions/__init__.py +0 -0
- /blaxel/{client → core/client}/api/functions/delete_function.py +0 -0
- /blaxel/{client → core/client}/api/functions/get_function.py +0 -0
- /blaxel/{client → core/client}/api/functions/list_function_revisions.py +0 -0
- /blaxel/{client → core/client}/api/functions/list_functions.py +0 -0
- /blaxel/{client → core/client}/api/integrations/__init__.py +0 -0
- /blaxel/{client → core/client}/api/integrations/delete_integration_connection.py +0 -0
- /blaxel/{client → core/client}/api/integrations/get_integration.py +0 -0
- /blaxel/{client → core/client}/api/integrations/get_integration_connection.py +0 -0
- /blaxel/{client → core/client}/api/integrations/get_integration_connection_model.py +0 -0
- /blaxel/{client → core/client}/api/integrations/get_integration_connection_model_endpoint_configurations.py +0 -0
- /blaxel/{client → core/client}/api/integrations/list_integration_connection_models.py +0 -0
- /blaxel/{client → core/client}/api/integrations/list_integration_connections.py +0 -0
- /blaxel/{client → core/client}/api/invitations/__init__.py +0 -0
- /blaxel/{client → core/client}/api/invitations/list_all_pending_invitations.py +0 -0
- /blaxel/{client → core/client}/api/jobs/__init__.py +0 -0
- /blaxel/{client → core/client}/api/jobs/delete_job.py +0 -0
- /blaxel/{client → core/client}/api/jobs/get_job.py +0 -0
- /blaxel/{client → core/client}/api/jobs/list_job_revisions.py +0 -0
- /blaxel/{client → core/client}/api/jobs/list_jobs.py +0 -0
- /blaxel/{client → core/client}/api/knowledgebases/__init__.py +0 -0
- /blaxel/{client → core/client}/api/knowledgebases/delete_knowledgebase.py +0 -0
- /blaxel/{client → core/client}/api/knowledgebases/get_knowledgebase.py +0 -0
- /blaxel/{client → core/client}/api/knowledgebases/list_knowledgebase_revisions.py +0 -0
- /blaxel/{client → core/client}/api/knowledgebases/list_knowledgebases.py +0 -0
- /blaxel/{client → core/client}/api/locations/__init__.py +0 -0
- /blaxel/{client → core/client}/api/locations/list_locations.py +0 -0
- /blaxel/{client → core/client}/api/models/__init__.py +0 -0
- /blaxel/{client → core/client}/api/models/delete_model.py +0 -0
- /blaxel/{client → core/client}/api/models/get_model.py +0 -0
- /blaxel/{client → core/client}/api/models/list_model_revisions.py +0 -0
- /blaxel/{client → core/client}/api/models/list_models.py +0 -0
- /blaxel/{client → core/client}/api/policies/__init__.py +0 -0
- /blaxel/{client → core/client}/api/policies/delete_policy.py +0 -0
- /blaxel/{client → core/client}/api/policies/get_policy.py +0 -0
- /blaxel/{client → core/client}/api/policies/list_policies.py +0 -0
- /blaxel/{client → core/client}/api/privateclusters/__init__.py +0 -0
- /blaxel/{client → core/client}/api/privateclusters/create_private_cluster.py +0 -0
- /blaxel/{client → core/client}/api/privateclusters/delete_private_cluster.py +0 -0
- /blaxel/{client → core/client}/api/privateclusters/get_private_cluster.py +0 -0
- /blaxel/{client → core/client}/api/privateclusters/get_private_cluster_health.py +0 -0
- /blaxel/{client → core/client}/api/privateclusters/list_private_clusters.py +0 -0
- /blaxel/{client → core/client}/api/privateclusters/update_private_cluster.py +0 -0
- /blaxel/{client → core/client}/api/privateclusters/update_private_cluster_health.py +0 -0
- /blaxel/{client → core/client}/api/service_accounts/__init__.py +0 -0
- /blaxel/{client → core/client}/api/service_accounts/delete_api_key_for_service_account.py +0 -0
- /blaxel/{client → core/client}/api/service_accounts/delete_workspace_service_account.py +0 -0
- /blaxel/{client → core/client}/api/service_accounts/get_workspace_service_accounts.py +0 -0
- /blaxel/{client → core/client}/api/service_accounts/list_api_keys_for_service_account.py +0 -0
- /blaxel/{client → core/client}/api/templates/__init__.py +0 -0
- /blaxel/{client → core/client}/api/templates/list_templates.py +0 -0
- /blaxel/{client → core/client}/api/workspaces/__init__.py +0 -0
- /blaxel/{client → core/client}/api/workspaces/accept_workspace_invitation.py +0 -0
- /blaxel/{client → core/client}/api/workspaces/decline_workspace_invitation.py +0 -0
- /blaxel/{client → core/client}/api/workspaces/delete_workspace.py +0 -0
- /blaxel/{client → core/client}/api/workspaces/get_workspace.py +0 -0
- /blaxel/{client → core/client}/api/workspaces/leave_workspace.py +0 -0
- /blaxel/{client → core/client}/api/workspaces/list_workspace_users.py +0 -0
- /blaxel/{client → core/client}/api/workspaces/list_workspaces.py +0 -0
- /blaxel/{client → core/client}/api/workspaces/remove_workspace_user.py +0 -0
- /blaxel/{client → core/client}/client.py +0 -0
- /blaxel/{client → core/client}/errors.py +0 -0
- /blaxel/{client → core/client}/models/__init__.py +0 -0
- /blaxel/{client → core/client}/models/acl.py +0 -0
- /blaxel/{client → core/client}/models/api_key.py +0 -0
- /blaxel/{client → core/client}/models/billable_time_metric.py +0 -0
- /blaxel/{client → core/client}/models/check_workspace_availability_body.py +0 -0
- /blaxel/{client → core/client}/models/configuration.py +0 -0
- /blaxel/{client → core/client}/models/continent.py +0 -0
- /blaxel/{client → core/client}/models/core_event.py +0 -0
- /blaxel/{client → core/client}/models/core_spec_configurations.py +0 -0
- /blaxel/{client → core/client}/models/country.py +0 -0
- /blaxel/{client → core/client}/models/create_api_key_for_service_account_body.py +0 -0
- /blaxel/{client → core/client}/models/create_workspace_service_account_body.py +0 -0
- /blaxel/{client → core/client}/models/create_workspace_service_account_response_200.py +0 -0
- /blaxel/{client → core/client}/models/delete_sandbox_preview_token_response_200.py +0 -0
- /blaxel/{client → core/client}/models/delete_workspace_service_account_response_200.py +0 -0
- /blaxel/{client → core/client}/models/entrypoint.py +0 -0
- /blaxel/{client → core/client}/models/entrypoint_env.py +0 -0
- /blaxel/{client → core/client}/models/flavor.py +0 -0
- /blaxel/{client → core/client}/models/form.py +0 -0
- /blaxel/{client → core/client}/models/form_config.py +0 -0
- /blaxel/{client → core/client}/models/form_oauth.py +0 -0
- /blaxel/{client → core/client}/models/form_secrets.py +0 -0
- /blaxel/{client → core/client}/models/function_kit.py +0 -0
- /blaxel/{client → core/client}/models/function_schema.py +0 -0
- /blaxel/{client → core/client}/models/function_schema_not.py +0 -0
- /blaxel/{client → core/client}/models/function_schema_or_bool.py +0 -0
- /blaxel/{client → core/client}/models/get_workspace_service_accounts_response_200_item.py +0 -0
- /blaxel/{client → core/client}/models/histogram_bucket.py +0 -0
- /blaxel/{client → core/client}/models/histogram_stats.py +0 -0
- /blaxel/{client → core/client}/models/integration_additional_infos.py +0 -0
- /blaxel/{client → core/client}/models/integration_connection.py +0 -0
- /blaxel/{client → core/client}/models/integration_connection_spec.py +0 -0
- /blaxel/{client → core/client}/models/integration_connection_spec_config.py +0 -0
- /blaxel/{client → core/client}/models/integration_connection_spec_secret.py +0 -0
- /blaxel/{client → core/client}/models/integration_endpoint.py +0 -0
- /blaxel/{client → core/client}/models/integration_endpoint_token.py +0 -0
- /blaxel/{client → core/client}/models/integration_headers.py +0 -0
- /blaxel/{client → core/client}/models/integration_model.py +0 -0
- /blaxel/{client → core/client}/models/integration_organization.py +0 -0
- /blaxel/{client → core/client}/models/integration_query_params.py +0 -0
- /blaxel/{client → core/client}/models/integration_repository.py +0 -0
- /blaxel/{client → core/client}/models/invite_workspace_user_body.py +0 -0
- /blaxel/{client → core/client}/models/job_execution_config.py +0 -0
- /blaxel/{client → core/client}/models/job_metrics.py +0 -0
- /blaxel/{client → core/client}/models/job_metrics_executions_chart.py +0 -0
- /blaxel/{client → core/client}/models/job_metrics_executions_total.py +0 -0
- /blaxel/{client → core/client}/models/job_metrics_tasks_chart.py +0 -0
- /blaxel/{client → core/client}/models/job_metrics_tasks_total.py +0 -0
- /blaxel/{client → core/client}/models/jobs_chart.py +0 -0
- /blaxel/{client → core/client}/models/jobs_chart_value.py +0 -0
- /blaxel/{client → core/client}/models/jobs_executions.py +0 -0
- /blaxel/{client → core/client}/models/jobs_network_chart.py +0 -0
- /blaxel/{client → core/client}/models/jobs_success_failed_chart.py +0 -0
- /blaxel/{client → core/client}/models/jobs_tasks.py +0 -0
- /blaxel/{client → core/client}/models/jobs_total.py +0 -0
- /blaxel/{client → core/client}/models/knowledgebase_spec.py +0 -0
- /blaxel/{client → core/client}/models/knowledgebase_spec_options.py +0 -0
- /blaxel/{client → core/client}/models/last_n_requests_metric.py +0 -0
- /blaxel/{client → core/client}/models/latency_metric.py +0 -0
- /blaxel/{client → core/client}/models/logs_response.py +0 -0
- /blaxel/{client → core/client}/models/logs_response_data.py +0 -0
- /blaxel/{client → core/client}/models/mcp_definition.py +0 -0
- /blaxel/{client → core/client}/models/mcp_definition_entrypoint.py +0 -0
- /blaxel/{client → core/client}/models/mcp_definition_form.py +0 -0
- /blaxel/{client → core/client}/models/memory_allocation_by_name.py +0 -0
- /blaxel/{client → core/client}/models/memory_allocation_metric.py +0 -0
- /blaxel/{client → core/client}/models/metadata.py +0 -0
- /blaxel/{client → core/client}/models/metadata_labels.py +0 -0
- /blaxel/{client → core/client}/models/metric.py +0 -0
- /blaxel/{client → core/client}/models/metrics.py +0 -0
- /blaxel/{client → core/client}/models/metrics_models.py +0 -0
- /blaxel/{client → core/client}/models/metrics_request_total_per_code.py +0 -0
- /blaxel/{client → core/client}/models/metrics_rps_per_code.py +0 -0
- /blaxel/{client → core/client}/models/model_private_cluster.py +0 -0
- /blaxel/{client → core/client}/models/o_auth.py +0 -0
- /blaxel/{client → core/client}/models/owner_fields.py +0 -0
- /blaxel/{client → core/client}/models/pending_invitation.py +0 -0
- /blaxel/{client → core/client}/models/pending_invitation_accept.py +0 -0
- /blaxel/{client → core/client}/models/pending_invitation_render.py +0 -0
- /blaxel/{client → core/client}/models/pending_invitation_render_invited_by.py +0 -0
- /blaxel/{client → core/client}/models/pending_invitation_render_workspace.py +0 -0
- /blaxel/{client → core/client}/models/pending_invitation_workspace_details.py +0 -0
- /blaxel/{client → core/client}/models/pod_template_spec.py +0 -0
- /blaxel/{client → core/client}/models/policy.py +0 -0
- /blaxel/{client → core/client}/models/policy_location.py +0 -0
- /blaxel/{client → core/client}/models/policy_max_tokens.py +0 -0
- /blaxel/{client → core/client}/models/port.py +0 -0
- /blaxel/{client → core/client}/models/preview.py +0 -0
- /blaxel/{client → core/client}/models/preview_metadata.py +0 -0
- /blaxel/{client → core/client}/models/preview_spec.py +0 -0
- /blaxel/{client → core/client}/models/preview_spec_request_headers.py +0 -0
- /blaxel/{client → core/client}/models/preview_spec_response_headers.py +0 -0
- /blaxel/{client → core/client}/models/preview_token.py +0 -0
- /blaxel/{client → core/client}/models/preview_token_metadata.py +0 -0
- /blaxel/{client → core/client}/models/preview_token_spec.py +0 -0
- /blaxel/{client → core/client}/models/private_cluster.py +0 -0
- /blaxel/{client → core/client}/models/private_location.py +0 -0
- /blaxel/{client → core/client}/models/repository.py +0 -0
- /blaxel/{client → core/client}/models/request_duration_over_time_metric.py +0 -0
- /blaxel/{client → core/client}/models/request_duration_over_time_metrics.py +0 -0
- /blaxel/{client → core/client}/models/request_total_by_origin_metric.py +0 -0
- /blaxel/{client → core/client}/models/request_total_by_origin_metric_request_total_by_origin.py +0 -0
- /blaxel/{client → core/client}/models/request_total_by_origin_metric_request_total_by_origin_and_code.py +0 -0
- /blaxel/{client → core/client}/models/request_total_metric.py +0 -0
- /blaxel/{client → core/client}/models/request_total_metric_request_total_per_code.py +0 -0
- /blaxel/{client → core/client}/models/request_total_metric_rps_per_code.py +0 -0
- /blaxel/{client → core/client}/models/request_total_response_data.py +0 -0
- /blaxel/{client → core/client}/models/resource.py +0 -0
- /blaxel/{client → core/client}/models/resource_log.py +0 -0
- /blaxel/{client → core/client}/models/resource_log_chart.py +0 -0
- /blaxel/{client → core/client}/models/resource_log_response.py +0 -0
- /blaxel/{client → core/client}/models/resource_metrics_request_total_per_code.py +0 -0
- /blaxel/{client → core/client}/models/resource_metrics_request_total_per_code_previous.py +0 -0
- /blaxel/{client → core/client}/models/resource_metrics_rps_per_code.py +0 -0
- /blaxel/{client → core/client}/models/resource_metrics_rps_per_code_previous.py +0 -0
- /blaxel/{client → core/client}/models/resource_trace.py +0 -0
- /blaxel/{client → core/client}/models/revision_configuration.py +0 -0
- /blaxel/{client → core/client}/models/revision_metadata.py +0 -0
- /blaxel/{client → core/client}/models/runtime_configuration.py +0 -0
- /blaxel/{client → core/client}/models/runtime_startup_probe.py +0 -0
- /blaxel/{client → core/client}/models/serverless_config.py +0 -0
- /blaxel/{client → core/client}/models/serverless_config_configuration.py +0 -0
- /blaxel/{client → core/client}/models/spec_configuration.py +0 -0
- /blaxel/{client → core/client}/models/start_sandbox.py +0 -0
- /blaxel/{client → core/client}/models/stop_sandbox.py +0 -0
- /blaxel/{client → core/client}/models/store_agent_labels.py +0 -0
- /blaxel/{client → core/client}/models/store_configuration_option.py +0 -0
- /blaxel/{client → core/client}/models/template_variable.py +0 -0
- /blaxel/{client → core/client}/models/time_fields.py +0 -0
- /blaxel/{client → core/client}/models/time_to_first_token_over_time_metrics.py +0 -0
- /blaxel/{client → core/client}/models/token_rate_metric.py +0 -0
- /blaxel/{client → core/client}/models/token_rate_metrics.py +0 -0
- /blaxel/{client → core/client}/models/token_total_metric.py +0 -0
- /blaxel/{client → core/client}/models/trace_ids_response.py +0 -0
- /blaxel/{client → core/client}/models/trigger.py +0 -0
- /blaxel/{client → core/client}/models/trigger_configuration.py +0 -0
- /blaxel/{client → core/client}/models/update_workspace_service_account_body.py +0 -0
- /blaxel/{client → core/client}/models/update_workspace_service_account_response_200.py +0 -0
- /blaxel/{client → core/client}/models/update_workspace_user_role_body.py +0 -0
- /blaxel/{client → core/client}/models/websocket_channel.py +0 -0
- /blaxel/{client → core/client}/models/websocket_message.py +0 -0
- /blaxel/{client → core/client}/models/workspace.py +0 -0
- /blaxel/{client → core/client}/models/workspace_labels.py +0 -0
- /blaxel/{client → core/client}/models/workspace_runtime.py +0 -0
- /blaxel/{client → core/client}/models/workspace_user.py +0 -0
- /blaxel/{client → core/client}/py.typed +0 -0
- /blaxel/{client → core/client}/types.py +0 -0
- /blaxel/{common → core/common}/env.py +0 -0
- /blaxel/{common → core/common}/settings.py +0 -0
- /blaxel/{sandbox/client/api/filesystem/__init__.py → core/py.typed} +0 -0
- /blaxel/{sandbox → core/sandbox}/client/__init__.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/__init__.py +0 -0
- /blaxel/{sandbox/client/api/network → core/sandbox/client/api/filesystem}/__init__.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/filesystem/delete_filesystem_path.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/filesystem/get_filesystem_path.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/filesystem/get_watch_filesystem_path.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/filesystem/get_ws_watch_filesystem_path.py +0 -0
- /blaxel/{sandbox/client/api/process → core/sandbox/client/api/network}/__init__.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/network/delete_network_process_pid_monitor.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/network/get_network_process_pid_ports.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/process/delete_process_identifier.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/process/delete_process_identifier_kill.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/process/get_process.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/process/get_process_identifier.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/process/get_process_identifier_logs.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/process/get_process_identifier_logs_stream.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/api/process/get_ws_process_identifier_logs_stream.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/client.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/errors.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/__init__.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/delete_network_process_pid_monitor_response_200.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/error_response.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/file.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/file_request.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/file_with_content.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/get_network_process_pid_ports_response_200.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/port_monitor_request.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/post_network_process_pid_monitor_response_200.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/process_logs.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/process_request.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/process_request_env.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/process_response.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/process_response_status.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/subdirectory.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/models/success_response.py +0 -0
- /blaxel/{sandbox → core/sandbox}/client/py.typed +0 -0
- /blaxel/{sandbox → core/sandbox}/client/types.py +0 -0
- /blaxel/{tools → core/tools}/common.py +0 -0
- /blaxel/{tools → core/tools}/types.py +0 -0
- /blaxel/{models/custom/langchain → langgraph/custom}/gemini.py +0 -0
- /blaxel/{models/custom/llamaindex → llamaindex/custom}/cohere.py +0 -0
- /blaxel/{instrumentation → telemetry}/exporters.py +0 -0
- /blaxel/{instrumentation → telemetry/log}/log.py +0 -0
- {blaxel-0.1.22rc70.dist-info → blaxel-0.2.0rc2.dist-info}/WHEEL +0 -0
- {blaxel-0.1.22rc70.dist-info → blaxel-0.2.0rc2.dist-info}/licenses/LICENSE +0 -0
@@ -7,11 +7,12 @@ import anyio
|
|
7
7
|
import mcp.types as types
|
8
8
|
from anyio.abc import TaskStatus
|
9
9
|
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
10
|
-
from websockets.client import
|
11
|
-
from websockets.client import connect as ws_connect
|
10
|
+
from websockets.asyncio.client import ClientConnection
|
11
|
+
from websockets.asyncio.client import connect as ws_connect
|
12
12
|
|
13
13
|
logger = logging.getLogger(__name__)
|
14
14
|
|
15
|
+
|
15
16
|
def remove_request_params(url: str) -> str:
|
16
17
|
return urljoin(url, urlparse(url).path)
|
17
18
|
|
@@ -44,11 +45,14 @@ async def websocket_client(
|
|
44
45
|
ws_url = url.replace("http://", "ws://").replace("https://", "wss://")
|
45
46
|
logger.debug(f"Connecting to WebSocket endpoint: {remove_request_params(ws_url)}")
|
46
47
|
|
47
|
-
|
48
|
+
# Use different parameters based on websockets version
|
49
|
+
connection_kwargs = {"additional_headers": headers, "open_timeout": timeout}
|
50
|
+
|
51
|
+
async with ws_connect(ws_url, **connection_kwargs) as websocket:
|
48
52
|
logger.debug("WebSocket connection established")
|
49
53
|
|
50
54
|
async def ws_reader(
|
51
|
-
websocket:
|
55
|
+
websocket: ClientConnection,
|
52
56
|
task_status: TaskStatus[None] = anyio.TASK_STATUS_IGNORED,
|
53
57
|
):
|
54
58
|
try:
|
@@ -56,7 +60,9 @@ async def websocket_client(
|
|
56
60
|
async for message in websocket:
|
57
61
|
logger.debug(f"Received WebSocket message: {message}")
|
58
62
|
try:
|
59
|
-
parsed_message = types.JSONRPCMessage.model_validate_json(
|
63
|
+
parsed_message = types.JSONRPCMessage.model_validate_json(
|
64
|
+
message
|
65
|
+
)
|
60
66
|
logger.debug(f"Received server message: {parsed_message}")
|
61
67
|
await read_stream_writer.send(parsed_message)
|
62
68
|
except Exception as exc:
|
@@ -68,7 +74,7 @@ async def websocket_client(
|
|
68
74
|
finally:
|
69
75
|
await read_stream_writer.aclose()
|
70
76
|
|
71
|
-
async def ws_writer(websocket:
|
77
|
+
async def ws_writer(websocket: ClientConnection):
|
72
78
|
try:
|
73
79
|
async with write_stream_reader:
|
74
80
|
async for message in write_stream_reader:
|
@@ -107,4 +113,4 @@ async def websocket_client(
|
|
107
113
|
finally:
|
108
114
|
# Ensure streams are closed even if task group setup fails or GeneratorExit is caught
|
109
115
|
await read_stream_writer.aclose()
|
110
|
-
await write_stream.aclose()
|
116
|
+
await write_stream.aclose()
|
@@ -2,24 +2,21 @@ import logging
|
|
2
2
|
import traceback
|
3
3
|
import uuid
|
4
4
|
from contextlib import asynccontextmanager
|
5
|
-
from typing import
|
5
|
+
from typing import Literal
|
6
6
|
|
7
7
|
import anyio
|
8
8
|
import mcp.types as types
|
9
9
|
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
10
10
|
from mcp.server.fastmcp import FastMCP as FastMCPBase
|
11
|
-
from
|
12
|
-
from websockets.server import WebSocketServerProtocol, serve
|
11
|
+
from websockets.asyncio.server import ServerConnection, serve
|
13
12
|
|
14
13
|
from ..common.env import env
|
15
|
-
from ..instrumentation.span import SpanManager
|
16
14
|
|
17
15
|
logger = logging.getLogger(__name__)
|
18
16
|
|
19
17
|
|
20
18
|
class BlaxelMcpServerTransport:
|
21
19
|
"""WebSocket server transport for MCP."""
|
22
|
-
spans: Dict[str, Span] = {}
|
23
20
|
|
24
21
|
def __init__(self, port: int = 8080):
|
25
22
|
"""Initialize the WebSocket server transport.
|
@@ -33,7 +30,6 @@ class BlaxelMcpServerTransport:
|
|
33
30
|
self.port = port
|
34
31
|
self.clients = {}
|
35
32
|
self.server = None
|
36
|
-
self.span_manager = SpanManager("blaxel-tracer")
|
37
33
|
|
38
34
|
@asynccontextmanager
|
39
35
|
async def websocket_server(self):
|
@@ -47,33 +43,21 @@ class BlaxelMcpServerTransport:
|
|
47
43
|
read_stream_writer, read_stream = anyio.create_memory_object_stream(0)
|
48
44
|
write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
|
49
45
|
|
50
|
-
async def handler(websocket:
|
46
|
+
async def handler(websocket: ServerConnection):
|
51
47
|
client_id = str(uuid.uuid4())
|
52
48
|
self.clients[client_id] = websocket
|
53
49
|
logger.info(f"Client connected: {client_id}")
|
54
50
|
|
55
51
|
try:
|
56
52
|
async for message in websocket:
|
57
|
-
span = self.span_manager.create_span("message", {"mcp.client.id": client_id})
|
58
53
|
try:
|
59
54
|
msg = types.JSONRPCMessage.model_validate_json(message)
|
60
55
|
# Modify message ID to include client ID
|
61
56
|
if hasattr(msg, "id") and msg.id is not None:
|
62
57
|
original_id = msg.id
|
63
58
|
msg.id = f"{client_id}:{original_id}"
|
64
|
-
span.set_attributes({
|
65
|
-
"mcp.message.parsed": True,
|
66
|
-
"mcp.method": getattr(msg, "method", None),
|
67
|
-
"mcp.messageId": getattr(msg, "id", None),
|
68
|
-
"mcp.toolName": getattr(getattr(msg, "params", None), "name", None),
|
69
|
-
"span.type": "mcp.message",
|
70
|
-
})
|
71
|
-
self.spans[client_id+":"+msg.id] = span
|
72
59
|
await read_stream_writer.send(msg)
|
73
60
|
except Exception as exc:
|
74
|
-
span.set_status(StatusCode.ERROR)
|
75
|
-
span.record_exception(exc)
|
76
|
-
span.end()
|
77
61
|
logger.error(f"Failed to parse message: {exc}\n{traceback.format_exc()}")
|
78
62
|
await read_stream_writer.send(exc)
|
79
63
|
except Exception as e:
|
@@ -102,23 +86,12 @@ class BlaxelMcpServerTransport:
|
|
102
86
|
if client_id and client_id in self.clients:
|
103
87
|
# Send to specific client
|
104
88
|
websocket = self.clients[client_id]
|
105
|
-
span = self.spans.get(client_id+":"+msg_id)
|
106
89
|
try:
|
107
90
|
await websocket.send(data)
|
108
|
-
if span:
|
109
|
-
span.set_attributes({
|
110
|
-
"mcp.message.response_sent": True,
|
111
|
-
})
|
112
91
|
except Exception as e:
|
113
|
-
if span:
|
114
|
-
span.set_status(StatusCode.ERROR)
|
115
|
-
span.record_exception(e)
|
116
92
|
logger.error(f"Failed to send message to client {client_id}: {e}")
|
117
93
|
if client_id in self.clients:
|
118
94
|
del self.clients[client_id]
|
119
|
-
finally:
|
120
|
-
if span:
|
121
|
-
span.end()
|
122
95
|
else:
|
123
96
|
# Broadcast to all clients
|
124
97
|
dead_clients = []
|
@@ -140,6 +113,7 @@ class BlaxelMcpServerTransport:
|
|
140
113
|
tg.start_soon(message_sender)
|
141
114
|
yield read_stream, write_stream
|
142
115
|
|
116
|
+
|
143
117
|
class FastMCP(FastMCPBase):
|
144
118
|
def run(self, transport: Literal["stdio", "sse", "ws"] = "stdio") -> None:
|
145
119
|
"""Run the FastMCP server. Note this is a synchronous function.
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
from ..cache import find_from_cache
|
3
|
+
from ..client import client
|
4
|
+
from ..client.api.models import get_model
|
5
|
+
from ..client.models import Model
|
6
|
+
from ..common import settings
|
7
|
+
|
8
|
+
|
9
|
+
class BLModel:
|
10
|
+
models = {}
|
11
|
+
|
12
|
+
def __init__(self, model_name, **kwargs):
|
13
|
+
self.model_name = model_name
|
14
|
+
self.kwargs = kwargs
|
15
|
+
|
16
|
+
async def get_parameters(self) -> tuple[str, str, str]:
|
17
|
+
if self.model_name in self.models:
|
18
|
+
# We get the headers in case we need to refresh the token
|
19
|
+
settings.auth.get_headers()
|
20
|
+
model = self.models[self.model_name]
|
21
|
+
return model["url"], model["type"], model["model"]
|
22
|
+
url = f"{settings.run_url}/{settings.auth.workspace_name}/models/{self.model_name}"
|
23
|
+
model_data = await self._get_model_metadata()
|
24
|
+
if not model_data:
|
25
|
+
raise Exception(f"Model {self.model_name} not found")
|
26
|
+
runtime = (model_data.spec and model_data.spec.runtime)
|
27
|
+
if not runtime:
|
28
|
+
raise Exception(f"Model {self.model_name} has no runtime")
|
29
|
+
|
30
|
+
type = runtime.type_ or 'openai'
|
31
|
+
model = runtime.model
|
32
|
+
self.models[self.model_name] = {
|
33
|
+
"url": url,
|
34
|
+
"type": type,
|
35
|
+
"model": model
|
36
|
+
}
|
37
|
+
return url, type, model
|
38
|
+
|
39
|
+
async def _get_model_metadata(self) -> Model | None:
|
40
|
+
cache_data = await find_from_cache('Model', self.model_name)
|
41
|
+
if cache_data:
|
42
|
+
return Model.from_dict(cache_data)
|
43
|
+
|
44
|
+
try:
|
45
|
+
return await get_model.asyncio(client=client, model_name=self.model_name)
|
46
|
+
except Exception:
|
47
|
+
return None
|
48
|
+
|
49
|
+
def bl_model(model_name, **kwargs):
|
50
|
+
return BLModel(model_name, **kwargs)
|
51
|
+
|
52
|
+
__all__ = ["bl_model"]
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from .sandbox import (
|
2
|
+
Sandbox,
|
3
|
+
SandboxFileSystem,
|
4
|
+
SandboxInstance,
|
5
|
+
SandboxPreviews,
|
6
|
+
SandboxProcess,
|
7
|
+
)
|
8
|
+
from .types import (
|
9
|
+
CopyResponse,
|
10
|
+
SandboxConfiguration,
|
11
|
+
SandboxFilesystemFile,
|
12
|
+
SessionCreateOptions,
|
13
|
+
SessionWithToken,
|
14
|
+
WatchEvent,
|
15
|
+
)
|
16
|
+
|
17
|
+
__all__ = [
|
18
|
+
"SandboxInstance",
|
19
|
+
"SessionCreateOptions",
|
20
|
+
"SessionWithToken",
|
21
|
+
"SandboxConfiguration",
|
22
|
+
"WatchEvent",
|
23
|
+
"SandboxFilesystemFile",
|
24
|
+
"CopyResponse",
|
25
|
+
"Sandbox",
|
26
|
+
"SandboxFileSystem",
|
27
|
+
"SandboxPreviews",
|
28
|
+
"SandboxProcess",
|
29
|
+
]
|
@@ -0,0 +1,79 @@
|
|
1
|
+
from typing import Any, Optional
|
2
|
+
|
3
|
+
import httpx
|
4
|
+
|
5
|
+
from ..common.internal import get_forced_url, get_global_unique_hash
|
6
|
+
from ..common.settings import settings
|
7
|
+
from .types import SandboxConfiguration
|
8
|
+
|
9
|
+
|
10
|
+
class ResponseError(Exception):
|
11
|
+
def __init__(self, response: httpx.Response, data: Any = None, error: Any = None):
|
12
|
+
data_error = {}
|
13
|
+
if isinstance(data, dict) and "error" in data:
|
14
|
+
data_error = data
|
15
|
+
if isinstance(error, dict) and "error" in error:
|
16
|
+
data_error["error"] = error["error"]
|
17
|
+
if response.status_code:
|
18
|
+
data_error["status"] = response.status_code
|
19
|
+
if response.reason_phrase:
|
20
|
+
data_error["statusText"] = response.reason_phrase
|
21
|
+
|
22
|
+
super().__init__(str(data_error))
|
23
|
+
self.response = response
|
24
|
+
self.data = data
|
25
|
+
self.error = error
|
26
|
+
|
27
|
+
|
28
|
+
class SandboxAction:
|
29
|
+
def __init__(self, sandbox_config: SandboxConfiguration):
|
30
|
+
self.sandbox_config = sandbox_config
|
31
|
+
|
32
|
+
@property
|
33
|
+
def name(self) -> str:
|
34
|
+
return self.sandbox_config.metadata.name if self.sandbox_config.metadata else ""
|
35
|
+
|
36
|
+
@property
|
37
|
+
def external_url(self) -> str:
|
38
|
+
return f"{settings.run_url}/{settings.workspace}/sandboxes/{self.name}"
|
39
|
+
|
40
|
+
@property
|
41
|
+
def internal_url(self) -> str:
|
42
|
+
hash_value = get_global_unique_hash(settings.workspace, "sandbox", self.name)
|
43
|
+
return f"{settings.run_internal_protocol}://bl-{settings.env}-{hash_value}.{settings.run_internal_hostname}"
|
44
|
+
|
45
|
+
@property
|
46
|
+
def forced_url(self) -> Optional[str]:
|
47
|
+
if self.sandbox_config.force_url:
|
48
|
+
return self.sandbox_config.force_url
|
49
|
+
return get_forced_url("sandbox", self.name)
|
50
|
+
|
51
|
+
@property
|
52
|
+
def url(self) -> str:
|
53
|
+
if self.forced_url:
|
54
|
+
return self.forced_url
|
55
|
+
# Uncomment when mk3 is fully available
|
56
|
+
# if settings.run_internal_hostname:
|
57
|
+
# return self.internal_url
|
58
|
+
return self.external_url
|
59
|
+
|
60
|
+
@property
|
61
|
+
def fallback_url(self) -> Optional[str]:
|
62
|
+
if self.external_url != self.url:
|
63
|
+
return self.external_url
|
64
|
+
return None
|
65
|
+
|
66
|
+
def get_client(self) -> httpx.AsyncClient:
|
67
|
+
if self.sandbox_config.force_url:
|
68
|
+
return httpx.AsyncClient(
|
69
|
+
base_url=self.sandbox_config.force_url, headers=self.sandbox_config.headers
|
70
|
+
)
|
71
|
+
# Create a new client instance each time to avoid "Cannot open a client instance more than once" error
|
72
|
+
return httpx.AsyncClient(
|
73
|
+
base_url=self.url,
|
74
|
+
headers={**settings.headers, **self.sandbox_config.headers},
|
75
|
+
)
|
76
|
+
|
77
|
+
def handle_response_error(self, response: httpx.Response, data: Any, error: Any):
|
78
|
+
if not response.is_success or not data:
|
79
|
+
raise ResponseError(response, data, error)
|
File without changes
|
@@ -34,7 +34,7 @@ class Directory:
|
|
34
34
|
if not isinstance(self.files, Unset):
|
35
35
|
files = []
|
36
36
|
for files_item_data in self.files:
|
37
|
-
if type(files_item_data)
|
37
|
+
if type(files_item_data) is dict:
|
38
38
|
files_item = files_item_data
|
39
39
|
else:
|
40
40
|
files_item = files_item_data.to_dict()
|
@@ -48,7 +48,7 @@ class Directory:
|
|
48
48
|
if not isinstance(self.subdirectories, Unset):
|
49
49
|
subdirectories = []
|
50
50
|
for subdirectories_item_data in self.subdirectories:
|
51
|
-
if type(subdirectories_item_data)
|
51
|
+
if type(subdirectories_item_data) is dict:
|
52
52
|
subdirectories_item = subdirectories_item_data
|
53
53
|
else:
|
54
54
|
subdirectories_item = subdirectories_item_data.to_dict()
|
@@ -0,0 +1,280 @@
|
|
1
|
+
import asyncio
|
2
|
+
import io
|
3
|
+
import json
|
4
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
5
|
+
|
6
|
+
import httpx
|
7
|
+
|
8
|
+
from ..common.settings import settings
|
9
|
+
from .action import SandboxAction
|
10
|
+
from .client.models import Directory, FileRequest, SuccessResponse
|
11
|
+
from .types import CopyResponse, SandboxConfiguration, SandboxFilesystemFile, WatchEvent
|
12
|
+
|
13
|
+
|
14
|
+
class SandboxFileSystem(SandboxAction):
|
15
|
+
def __init__(self, sandbox_config: SandboxConfiguration):
|
16
|
+
super().__init__(sandbox_config)
|
17
|
+
|
18
|
+
async def mkdir(self, path: str, permissions: str = "0755") -> SuccessResponse:
|
19
|
+
path = self.format_path(path)
|
20
|
+
body = FileRequest(is_directory=True, permissions=permissions)
|
21
|
+
|
22
|
+
async with self.get_client() as client_instance:
|
23
|
+
response = await client_instance.put(f"/filesystem/{path}", json=body.to_dict())
|
24
|
+
self.handle_response_error(
|
25
|
+
response, response.json() if response.content else None, None
|
26
|
+
)
|
27
|
+
return SuccessResponse.from_dict(response.json())
|
28
|
+
|
29
|
+
async def write(self, path: str, content: str) -> SuccessResponse:
|
30
|
+
path = self.format_path(path)
|
31
|
+
body = FileRequest(content=content)
|
32
|
+
|
33
|
+
async with self.get_client() as client_instance:
|
34
|
+
response = await client_instance.put(f"/filesystem/{path}", json=body.to_dict())
|
35
|
+
self.handle_response_error(
|
36
|
+
response, response.json() if response.content else None, None
|
37
|
+
)
|
38
|
+
return SuccessResponse.from_dict(response.json())
|
39
|
+
|
40
|
+
async def write_binary(self, path: str, content: Union[bytes, bytearray]) -> SuccessResponse:
|
41
|
+
"""Write binary content to a file."""
|
42
|
+
path = self.format_path(path)
|
43
|
+
|
44
|
+
# Convert bytearray to bytes if necessary
|
45
|
+
if isinstance(content, bytearray):
|
46
|
+
content = bytes(content)
|
47
|
+
|
48
|
+
# Wrap binary content in BytesIO to provide file-like interface
|
49
|
+
binary_file = io.BytesIO(content)
|
50
|
+
|
51
|
+
# Prepare multipart form data
|
52
|
+
files = {
|
53
|
+
"file": ("binary-file.bin", binary_file, "application/octet-stream"),
|
54
|
+
}
|
55
|
+
data = {"permissions": "0644", "path": path}
|
56
|
+
|
57
|
+
# Use the fixed get_client method
|
58
|
+
url = f"{self.url}/filesystem/{path}"
|
59
|
+
headers = {**settings.headers, **self.sandbox_config.headers}
|
60
|
+
|
61
|
+
async with self.get_client() as client_instance:
|
62
|
+
response = await client_instance.put(url, files=files, data=data, headers=headers)
|
63
|
+
|
64
|
+
if not response.is_success:
|
65
|
+
raise Exception(f"Failed to write binary: {response.status_code} {response.text}")
|
66
|
+
|
67
|
+
return SuccessResponse.from_dict(response.json())
|
68
|
+
|
69
|
+
async def write_tree(
|
70
|
+
self,
|
71
|
+
files: List[Union[SandboxFilesystemFile, Dict[str, Any]]],
|
72
|
+
destination_path: Optional[str] = None,
|
73
|
+
) -> Directory:
|
74
|
+
"""Write multiple files in a tree structure."""
|
75
|
+
files_dict = {}
|
76
|
+
for file in files:
|
77
|
+
if isinstance(file, dict):
|
78
|
+
file = SandboxFilesystemFile.from_dict(file)
|
79
|
+
files_dict[file.path] = file.content
|
80
|
+
|
81
|
+
path = destination_path or ""
|
82
|
+
|
83
|
+
async with self.get_client() as client_instance:
|
84
|
+
response = await client_instance.put(
|
85
|
+
f"/filesystem/tree/{path}",
|
86
|
+
json={"files": files_dict},
|
87
|
+
headers={"Content-Type": "application/json"},
|
88
|
+
)
|
89
|
+
self.handle_response_error(
|
90
|
+
response, response.json() if response.content else None, None
|
91
|
+
)
|
92
|
+
return Directory.from_dict(response.json())
|
93
|
+
|
94
|
+
async def read(self, path: str) -> str:
|
95
|
+
path = self.format_path(path)
|
96
|
+
|
97
|
+
async with self.get_client() as client_instance:
|
98
|
+
response = await client_instance.get(f"/filesystem/{path}")
|
99
|
+
self.handle_response_error(
|
100
|
+
response, response.json() if response.content else None, None
|
101
|
+
)
|
102
|
+
|
103
|
+
data = response.json()
|
104
|
+
if "content" in data:
|
105
|
+
return data["content"]
|
106
|
+
raise Exception("Unsupported file type")
|
107
|
+
|
108
|
+
async def rm(self, path: str, recursive: bool = False) -> SuccessResponse:
|
109
|
+
path = self.format_path(path)
|
110
|
+
|
111
|
+
async with self.get_client() as client_instance:
|
112
|
+
params = {"recursive": "true"} if recursive else {}
|
113
|
+
response = await client_instance.delete(f"/filesystem/{path}", params=params)
|
114
|
+
self.handle_response_error(
|
115
|
+
response, response.json() if response.content else None, None
|
116
|
+
)
|
117
|
+
return SuccessResponse.from_dict(response.json())
|
118
|
+
|
119
|
+
async def ls(self, path: str) -> Directory:
|
120
|
+
path = self.format_path(path)
|
121
|
+
|
122
|
+
async with self.get_client() as client_instance:
|
123
|
+
response = await client_instance.get(f"/filesystem/{path}")
|
124
|
+
self.handle_response_error(
|
125
|
+
response, response.json() if response.content else None, None
|
126
|
+
)
|
127
|
+
|
128
|
+
data = response.json()
|
129
|
+
if not ("files" in data or "subdirectories" in data):
|
130
|
+
raise Exception('{"error": "Directory not found"}')
|
131
|
+
return Directory.from_dict(data)
|
132
|
+
|
133
|
+
async def cp(self, source: str, destination: str) -> CopyResponse:
|
134
|
+
source = self.format_path(source)
|
135
|
+
destination = self.format_path(destination)
|
136
|
+
|
137
|
+
async with self.get_client() as client_instance:
|
138
|
+
response = await client_instance.get(f"/filesystem/{source}")
|
139
|
+
self.handle_response_error(
|
140
|
+
response, response.json() if response.content else None, None
|
141
|
+
)
|
142
|
+
|
143
|
+
data = response.json()
|
144
|
+
if "files" in data or "subdirectories" in data:
|
145
|
+
# Create destination directory
|
146
|
+
await self.mkdir(destination)
|
147
|
+
|
148
|
+
# Process subdirectories in batches of 5
|
149
|
+
subdirectories = data.get("subdirectories", [])
|
150
|
+
for i in range(0, len(subdirectories), 5):
|
151
|
+
batch = subdirectories[i : i + 5]
|
152
|
+
await asyncio.gather(
|
153
|
+
*[
|
154
|
+
self.cp(
|
155
|
+
subdir.get("path", f"{source}/{subdir.get('path', '')}"),
|
156
|
+
f"{destination}/{subdir.get('path', '')}",
|
157
|
+
)
|
158
|
+
for subdir in batch
|
159
|
+
]
|
160
|
+
)
|
161
|
+
|
162
|
+
# Process files in batches of 10
|
163
|
+
files = data.get("files", [])
|
164
|
+
for i in range(0, len(files), 10):
|
165
|
+
batch = files[i : i + 10]
|
166
|
+
tasks = []
|
167
|
+
for file in batch:
|
168
|
+
source_path = file.get("path", f"{source}/{file.get('path', '')}")
|
169
|
+
dest_path = f"{destination}/{file.get('path', '')}"
|
170
|
+
tasks.append(self._copy_file(source_path, dest_path))
|
171
|
+
await asyncio.gather(*tasks)
|
172
|
+
|
173
|
+
return CopyResponse(
|
174
|
+
message="Directory copied successfully", source=source, destination=destination
|
175
|
+
)
|
176
|
+
elif "content" in data:
|
177
|
+
await self.write(destination, data["content"])
|
178
|
+
return CopyResponse(
|
179
|
+
message="File copied successfully", source=source, destination=destination
|
180
|
+
)
|
181
|
+
|
182
|
+
raise Exception("Unsupported file type")
|
183
|
+
|
184
|
+
async def _copy_file(self, source_path: str, dest_path: str):
|
185
|
+
"""Helper method to copy a single file."""
|
186
|
+
content = await self.read(source_path)
|
187
|
+
await self.write(dest_path, content)
|
188
|
+
|
189
|
+
def watch(
|
190
|
+
self,
|
191
|
+
path: str,
|
192
|
+
callback: Callable[[WatchEvent], None],
|
193
|
+
options: Optional[Dict[str, Any]] = None,
|
194
|
+
) -> Dict[str, Callable]:
|
195
|
+
"""Watch for file system changes."""
|
196
|
+
path = self.format_path(path)
|
197
|
+
closed = False
|
198
|
+
|
199
|
+
if options is None:
|
200
|
+
options = {}
|
201
|
+
|
202
|
+
async def start_watching():
|
203
|
+
nonlocal closed
|
204
|
+
|
205
|
+
params = {}
|
206
|
+
if options.get("ignore"):
|
207
|
+
params["ignore"] = ",".join(options["ignore"])
|
208
|
+
|
209
|
+
url = f"{self.url}/filesystem/{path}/watch"
|
210
|
+
headers = {**settings.headers, **self.sandbox_config.headers}
|
211
|
+
|
212
|
+
async with httpx.AsyncClient() as client_instance:
|
213
|
+
async with client_instance.stream(
|
214
|
+
"GET", url, params=params, headers=headers
|
215
|
+
) as response:
|
216
|
+
if not response.is_success:
|
217
|
+
raise Exception(f"Failed to start watching: {response.status_code}")
|
218
|
+
|
219
|
+
buffer = ""
|
220
|
+
async for chunk in response.aiter_text():
|
221
|
+
if closed:
|
222
|
+
break
|
223
|
+
|
224
|
+
buffer += chunk
|
225
|
+
lines = buffer.split("\n")
|
226
|
+
buffer = lines.pop() # Keep incomplete line in buffer
|
227
|
+
|
228
|
+
for line in lines:
|
229
|
+
line = line.strip()
|
230
|
+
if not line:
|
231
|
+
continue
|
232
|
+
|
233
|
+
try:
|
234
|
+
file_event_data = json.loads(line)
|
235
|
+
file_event = WatchEvent(
|
236
|
+
op=file_event_data.get("op", ""),
|
237
|
+
path=file_event_data.get("path", ""),
|
238
|
+
name=file_event_data.get("name", ""),
|
239
|
+
content=file_event_data.get("content"),
|
240
|
+
)
|
241
|
+
|
242
|
+
if options.get("with_content") and file_event.op in [
|
243
|
+
"CREATE",
|
244
|
+
"WRITE",
|
245
|
+
]:
|
246
|
+
try:
|
247
|
+
file_path = file_event.path
|
248
|
+
if file_path.endswith("/"):
|
249
|
+
file_path = file_path + file_event.name
|
250
|
+
else:
|
251
|
+
file_path = file_path + "/" + file_event.name
|
252
|
+
|
253
|
+
content = await self.read(file_path)
|
254
|
+
file_event.content = content
|
255
|
+
except:
|
256
|
+
file_event.content = None
|
257
|
+
|
258
|
+
await asyncio.create_task(asyncio.coroutine(callback)(file_event))
|
259
|
+
except json.JSONDecodeError:
|
260
|
+
continue
|
261
|
+
except Exception as e:
|
262
|
+
if options.get("on_error"):
|
263
|
+
options["on_error"](e)
|
264
|
+
|
265
|
+
# Start watching in the background
|
266
|
+
task = asyncio.create_task(start_watching())
|
267
|
+
|
268
|
+
def close():
|
269
|
+
nonlocal closed
|
270
|
+
closed = True
|
271
|
+
task.cancel()
|
272
|
+
|
273
|
+
return {"close": close}
|
274
|
+
|
275
|
+
def format_path(self, path: str) -> str:
|
276
|
+
if path == "/":
|
277
|
+
return path
|
278
|
+
if path.startswith("/"):
|
279
|
+
path = path[1:]
|
280
|
+
return path
|
@@ -0,0 +1,10 @@
|
|
1
|
+
from .action import SandboxAction
|
2
|
+
from .types import SandboxConfiguration
|
3
|
+
|
4
|
+
|
5
|
+
class SandboxNetwork(SandboxAction):
|
6
|
+
def __init__(self, sandbox_config: SandboxConfiguration):
|
7
|
+
super().__init__(sandbox_config)
|
8
|
+
|
9
|
+
# Network functionality can be expanded here in the future
|
10
|
+
# Currently this is a placeholder matching the TypeScript implementation
|