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.
Files changed (419) hide show
  1. blaxel/__init__.py +6 -3
  2. blaxel/core/__init__.py +44 -0
  3. blaxel/{agents → core/agents}/__init__.py +30 -50
  4. blaxel/{authentication → core/authentication}/apikey.py +1 -0
  5. blaxel/{authentication → core/authentication}/clientcredentials.py +6 -2
  6. blaxel/{client → core/client}/api/agents/create_agent.py +1 -1
  7. blaxel/{client → core/client}/api/agents/update_agent.py +1 -1
  8. blaxel/{client → core/client}/api/compute/create_sandbox.py +1 -1
  9. blaxel/{client → core/client}/api/compute/create_sandbox_preview.py +1 -1
  10. blaxel/{client → core/client}/api/compute/create_sandbox_preview_token.py +1 -1
  11. blaxel/{client → core/client}/api/compute/update_sandbox.py +1 -1
  12. blaxel/{client → core/client}/api/compute/update_sandbox_preview.py +1 -1
  13. blaxel/{client → core/client}/api/functions/create_function.py +1 -1
  14. blaxel/{client → core/client}/api/functions/update_function.py +1 -1
  15. blaxel/{client → core/client}/api/integrations/create_integration_connection.py +1 -1
  16. blaxel/{client → core/client}/api/integrations/update_integration_connection.py +1 -1
  17. blaxel/{client → core/client}/api/jobs/create_job.py +1 -1
  18. blaxel/{client → core/client}/api/jobs/update_job.py +1 -1
  19. blaxel/{client → core/client}/api/knowledgebases/create_knowledgebase.py +1 -1
  20. blaxel/{client → core/client}/api/knowledgebases/update_knowledgebase.py +1 -1
  21. blaxel/{client → core/client}/api/models/create_model.py +1 -1
  22. blaxel/{client → core/client}/api/models/update_model.py +1 -1
  23. blaxel/{client → core/client}/api/policies/create_policy.py +1 -1
  24. blaxel/{client → core/client}/api/policies/update_policy.py +1 -1
  25. blaxel/{client → core/client}/api/service_accounts/create_api_key_for_service_account.py +1 -1
  26. blaxel/{client → core/client}/api/service_accounts/create_workspace_service_account.py +1 -1
  27. blaxel/{client → core/client}/api/service_accounts/update_workspace_service_account.py +1 -1
  28. blaxel/{client → core/client}/api/workspaces/check_workspace_availability.py +1 -1
  29. blaxel/{client → core/client}/api/workspaces/create_worspace.py +1 -1
  30. blaxel/{client → core/client}/api/workspaces/invite_workspace_user.py +1 -1
  31. blaxel/{client → core/client}/api/workspaces/update_workspace.py +1 -1
  32. blaxel/{client → core/client}/api/workspaces/update_workspace_user_role.py +1 -1
  33. blaxel/{client → core/client}/models/agent.py +1 -1
  34. blaxel/{client → core/client}/models/agent_spec.py +2 -2
  35. blaxel/{client → core/client}/models/core_spec.py +1 -1
  36. blaxel/{client → core/client}/models/function.py +1 -1
  37. blaxel/{client → core/client}/models/function_schema_properties.py +1 -1
  38. blaxel/{client → core/client}/models/function_spec.py +2 -2
  39. blaxel/{client → core/client}/models/integration.py +2 -2
  40. blaxel/{client → core/client}/models/integration_endpoints.py +1 -1
  41. blaxel/{client → core/client}/models/job.py +1 -1
  42. blaxel/{client → core/client}/models/job_spec.py +1 -1
  43. blaxel/{client → core/client}/models/knowledgebase.py +1 -1
  44. blaxel/{client → core/client}/models/location_response.py +1 -1
  45. blaxel/{client → core/client}/models/model.py +1 -1
  46. blaxel/{client → core/client}/models/model_spec.py +1 -1
  47. blaxel/{client → core/client}/models/policy_spec.py +2 -2
  48. blaxel/{client → core/client}/models/resource_metrics.py +3 -3
  49. blaxel/{client → core/client}/models/runtime.py +1 -1
  50. blaxel/{client → core/client}/models/sandbox.py +1 -1
  51. blaxel/{client → core/client}/models/sandbox_definition.py +1 -1
  52. blaxel/{client → core/client}/models/sandbox_spec.py +1 -1
  53. blaxel/{client → core/client}/models/store_agent.py +1 -1
  54. blaxel/{client → core/client}/models/store_configuration.py +1 -1
  55. blaxel/{client → core/client}/models/template.py +1 -1
  56. blaxel/core/common/__init__.py +6 -0
  57. blaxel/core/common/autoload.py +21 -0
  58. blaxel/{common → core/common}/internal.py +33 -62
  59. blaxel/core/common/logger.py +131 -0
  60. blaxel/{jobs → core/jobs}/__init__.py +40 -60
  61. blaxel/core/mcp/__init__.py +4 -0
  62. blaxel/{mcp → core/mcp}/client.py +13 -7
  63. blaxel/{mcp → core/mcp}/server.py +4 -30
  64. blaxel/core/models/__init__.py +52 -0
  65. blaxel/core/sandbox/__init__.py +29 -0
  66. blaxel/core/sandbox/action.py +79 -0
  67. blaxel/{sandbox → core/sandbox}/client/api/filesystem/put_filesystem_path.py +1 -1
  68. blaxel/{sandbox → core/sandbox}/client/api/network/post_network_process_pid_monitor.py +1 -1
  69. blaxel/core/sandbox/client/api/process/__init__.py +0 -0
  70. blaxel/{sandbox → core/sandbox}/client/api/process/post_process.py +1 -1
  71. blaxel/{sandbox → core/sandbox}/client/models/directory.py +2 -2
  72. blaxel/core/sandbox/filesystem.py +280 -0
  73. blaxel/core/sandbox/network.py +10 -0
  74. blaxel/{sandbox → core/sandbox}/preview.py +45 -17
  75. blaxel/core/sandbox/process.py +159 -0
  76. blaxel/{sandbox → core/sandbox}/sandbox.py +62 -5
  77. blaxel/core/sandbox/session.py +124 -0
  78. blaxel/core/sandbox/types.py +103 -0
  79. blaxel/{tools → core/tools}/__init__.py +62 -90
  80. blaxel/crewai/__init__.py +4 -0
  81. blaxel/{models/crewai.py → crewai/model.py} +4 -2
  82. blaxel/crewai/py.typed +0 -0
  83. blaxel/crewai/tools.py +26 -0
  84. blaxel/googleadk/__init__.py +4 -0
  85. blaxel/{models/googleadk.py → googleadk/model.py} +8 -2
  86. blaxel/googleadk/py.typed +0 -0
  87. blaxel/googleadk/tools.py +72 -0
  88. blaxel/langgraph/__init__.py +4 -0
  89. blaxel/{models/langchain.py → langgraph/model.py} +8 -4
  90. blaxel/langgraph/py.typed +0 -0
  91. blaxel/{tools/langchain.py → langgraph/tools.py} +7 -3
  92. blaxel/livekit/__init__.py +4 -0
  93. blaxel/{models/livekit.py → livekit/model.py} +7 -1
  94. blaxel/livekit/py.typed +0 -0
  95. blaxel/{tools/livekit.py → livekit/tools.py} +8 -1
  96. blaxel/llamaindex/__init__.py +4 -0
  97. blaxel/{models/llamaindex.py → llamaindex/model.py} +6 -3
  98. blaxel/llamaindex/py.typed +0 -0
  99. blaxel/{tools/llamaindex.py → llamaindex/tools.py} +7 -4
  100. blaxel/openai/__init__.py +4 -0
  101. blaxel/{models/openai.py → openai/model.py} +4 -2
  102. blaxel/openai/py.typed +0 -0
  103. blaxel/{tools/openai.py → openai/tools.py} +7 -3
  104. blaxel/pydantic/__init__.py +4 -0
  105. blaxel/{models/custom/pydantic → pydantic/custom}/gemini.py +0 -1
  106. blaxel/{models/pydantic.py → pydantic/model.py} +6 -4
  107. blaxel/pydantic/py.typed +0 -0
  108. blaxel/{tools/pydantic.py → pydantic/tools.py} +6 -3
  109. blaxel/telemetry/__init__.py +6 -0
  110. blaxel/telemetry/instrumentation/blaxel_core.py +124 -0
  111. blaxel/telemetry/instrumentation/blaxel_langgraph.py +74 -0
  112. blaxel/telemetry/instrumentation/blaxel_langgraph_gemini.py +360 -0
  113. blaxel/telemetry/instrumentation/blaxel_llamaindex.py +89 -0
  114. blaxel/telemetry/instrumentation/map.py +61 -0
  115. blaxel/telemetry/instrumentation/utils.py +74 -0
  116. blaxel/{common → telemetry/log}/logger.py +6 -12
  117. blaxel/{instrumentation → telemetry}/manager.py +20 -12
  118. blaxel/telemetry/py.typed +0 -0
  119. blaxel/{instrumentation → telemetry}/span.py +12 -1
  120. blaxel-0.2.0rc2.dist-info/METADATA +224 -0
  121. blaxel-0.2.0rc2.dist-info/RECORD +408 -0
  122. blaxel/common/autoload.py +0 -9
  123. blaxel/instrumentation/map.py +0 -49
  124. blaxel/mcp/__init__.py +0 -3
  125. blaxel/models/__init__.py +0 -104
  126. blaxel/sandbox/base.py +0 -67
  127. blaxel/sandbox/filesystem.py +0 -104
  128. blaxel/sandbox/process.py +0 -56
  129. blaxel/tools/crewai.py +0 -22
  130. blaxel/tools/googleadk.py +0 -66
  131. blaxel-0.1.22rc70.dist-info/METADATA +0 -169
  132. blaxel-0.1.22rc70.dist-info/RECORD +0 -379
  133. /blaxel/{authentication → core/authentication}/__init__.py +0 -0
  134. /blaxel/{authentication → core/authentication}/devicemode.py +0 -0
  135. /blaxel/{authentication → core/authentication}/oauth.py +0 -0
  136. /blaxel/{authentication → core/authentication}/types.py +0 -0
  137. /blaxel/{cache → core/cache}/__init__.py +0 -0
  138. /blaxel/{cache → core/cache}/cache.py +0 -0
  139. /blaxel/{client → core/client}/__init__.py +0 -0
  140. /blaxel/{client → core/client}/api/__init__.py +0 -0
  141. /blaxel/{client → core/client}/api/agents/__init__.py +0 -0
  142. /blaxel/{client → core/client}/api/agents/delete_agent.py +0 -0
  143. /blaxel/{client → core/client}/api/agents/get_agent.py +0 -0
  144. /blaxel/{client → core/client}/api/agents/list_agent_revisions.py +0 -0
  145. /blaxel/{client → core/client}/api/agents/list_agents.py +0 -0
  146. /blaxel/{client → core/client}/api/compute/__init__.py +0 -0
  147. /blaxel/{client → core/client}/api/compute/delete_sandbox.py +0 -0
  148. /blaxel/{client → core/client}/api/compute/delete_sandbox_preview.py +0 -0
  149. /blaxel/{client → core/client}/api/compute/delete_sandbox_preview_token.py +0 -0
  150. /blaxel/{client → core/client}/api/compute/get_sandbox.py +0 -0
  151. /blaxel/{client → core/client}/api/compute/get_sandbox_preview.py +0 -0
  152. /blaxel/{client → core/client}/api/compute/list_sandbox_preview_tokens.py +0 -0
  153. /blaxel/{client → core/client}/api/compute/list_sandbox_previews.py +0 -0
  154. /blaxel/{client → core/client}/api/compute/list_sandboxes.py +0 -0
  155. /blaxel/{client → core/client}/api/compute/start_sandbox.py +0 -0
  156. /blaxel/{client → core/client}/api/compute/stop_sandbox.py +0 -0
  157. /blaxel/{client → core/client}/api/configurations/__init__.py +0 -0
  158. /blaxel/{client → core/client}/api/configurations/get_configuration.py +0 -0
  159. /blaxel/{client → core/client}/api/default/__init__.py +0 -0
  160. /blaxel/{client → core/client}/api/default/get_template.py +0 -0
  161. /blaxel/{client → core/client}/api/default/list_mcp_hub_definitions.py +0 -0
  162. /blaxel/{client → core/client}/api/default/list_sandbox_hub_definitions.py +0 -0
  163. /blaxel/{client → core/client}/api/functions/__init__.py +0 -0
  164. /blaxel/{client → core/client}/api/functions/delete_function.py +0 -0
  165. /blaxel/{client → core/client}/api/functions/get_function.py +0 -0
  166. /blaxel/{client → core/client}/api/functions/list_function_revisions.py +0 -0
  167. /blaxel/{client → core/client}/api/functions/list_functions.py +0 -0
  168. /blaxel/{client → core/client}/api/integrations/__init__.py +0 -0
  169. /blaxel/{client → core/client}/api/integrations/delete_integration_connection.py +0 -0
  170. /blaxel/{client → core/client}/api/integrations/get_integration.py +0 -0
  171. /blaxel/{client → core/client}/api/integrations/get_integration_connection.py +0 -0
  172. /blaxel/{client → core/client}/api/integrations/get_integration_connection_model.py +0 -0
  173. /blaxel/{client → core/client}/api/integrations/get_integration_connection_model_endpoint_configurations.py +0 -0
  174. /blaxel/{client → core/client}/api/integrations/list_integration_connection_models.py +0 -0
  175. /blaxel/{client → core/client}/api/integrations/list_integration_connections.py +0 -0
  176. /blaxel/{client → core/client}/api/invitations/__init__.py +0 -0
  177. /blaxel/{client → core/client}/api/invitations/list_all_pending_invitations.py +0 -0
  178. /blaxel/{client → core/client}/api/jobs/__init__.py +0 -0
  179. /blaxel/{client → core/client}/api/jobs/delete_job.py +0 -0
  180. /blaxel/{client → core/client}/api/jobs/get_job.py +0 -0
  181. /blaxel/{client → core/client}/api/jobs/list_job_revisions.py +0 -0
  182. /blaxel/{client → core/client}/api/jobs/list_jobs.py +0 -0
  183. /blaxel/{client → core/client}/api/knowledgebases/__init__.py +0 -0
  184. /blaxel/{client → core/client}/api/knowledgebases/delete_knowledgebase.py +0 -0
  185. /blaxel/{client → core/client}/api/knowledgebases/get_knowledgebase.py +0 -0
  186. /blaxel/{client → core/client}/api/knowledgebases/list_knowledgebase_revisions.py +0 -0
  187. /blaxel/{client → core/client}/api/knowledgebases/list_knowledgebases.py +0 -0
  188. /blaxel/{client → core/client}/api/locations/__init__.py +0 -0
  189. /blaxel/{client → core/client}/api/locations/list_locations.py +0 -0
  190. /blaxel/{client → core/client}/api/models/__init__.py +0 -0
  191. /blaxel/{client → core/client}/api/models/delete_model.py +0 -0
  192. /blaxel/{client → core/client}/api/models/get_model.py +0 -0
  193. /blaxel/{client → core/client}/api/models/list_model_revisions.py +0 -0
  194. /blaxel/{client → core/client}/api/models/list_models.py +0 -0
  195. /blaxel/{client → core/client}/api/policies/__init__.py +0 -0
  196. /blaxel/{client → core/client}/api/policies/delete_policy.py +0 -0
  197. /blaxel/{client → core/client}/api/policies/get_policy.py +0 -0
  198. /blaxel/{client → core/client}/api/policies/list_policies.py +0 -0
  199. /blaxel/{client → core/client}/api/privateclusters/__init__.py +0 -0
  200. /blaxel/{client → core/client}/api/privateclusters/create_private_cluster.py +0 -0
  201. /blaxel/{client → core/client}/api/privateclusters/delete_private_cluster.py +0 -0
  202. /blaxel/{client → core/client}/api/privateclusters/get_private_cluster.py +0 -0
  203. /blaxel/{client → core/client}/api/privateclusters/get_private_cluster_health.py +0 -0
  204. /blaxel/{client → core/client}/api/privateclusters/list_private_clusters.py +0 -0
  205. /blaxel/{client → core/client}/api/privateclusters/update_private_cluster.py +0 -0
  206. /blaxel/{client → core/client}/api/privateclusters/update_private_cluster_health.py +0 -0
  207. /blaxel/{client → core/client}/api/service_accounts/__init__.py +0 -0
  208. /blaxel/{client → core/client}/api/service_accounts/delete_api_key_for_service_account.py +0 -0
  209. /blaxel/{client → core/client}/api/service_accounts/delete_workspace_service_account.py +0 -0
  210. /blaxel/{client → core/client}/api/service_accounts/get_workspace_service_accounts.py +0 -0
  211. /blaxel/{client → core/client}/api/service_accounts/list_api_keys_for_service_account.py +0 -0
  212. /blaxel/{client → core/client}/api/templates/__init__.py +0 -0
  213. /blaxel/{client → core/client}/api/templates/list_templates.py +0 -0
  214. /blaxel/{client → core/client}/api/workspaces/__init__.py +0 -0
  215. /blaxel/{client → core/client}/api/workspaces/accept_workspace_invitation.py +0 -0
  216. /blaxel/{client → core/client}/api/workspaces/decline_workspace_invitation.py +0 -0
  217. /blaxel/{client → core/client}/api/workspaces/delete_workspace.py +0 -0
  218. /blaxel/{client → core/client}/api/workspaces/get_workspace.py +0 -0
  219. /blaxel/{client → core/client}/api/workspaces/leave_workspace.py +0 -0
  220. /blaxel/{client → core/client}/api/workspaces/list_workspace_users.py +0 -0
  221. /blaxel/{client → core/client}/api/workspaces/list_workspaces.py +0 -0
  222. /blaxel/{client → core/client}/api/workspaces/remove_workspace_user.py +0 -0
  223. /blaxel/{client → core/client}/client.py +0 -0
  224. /blaxel/{client → core/client}/errors.py +0 -0
  225. /blaxel/{client → core/client}/models/__init__.py +0 -0
  226. /blaxel/{client → core/client}/models/acl.py +0 -0
  227. /blaxel/{client → core/client}/models/api_key.py +0 -0
  228. /blaxel/{client → core/client}/models/billable_time_metric.py +0 -0
  229. /blaxel/{client → core/client}/models/check_workspace_availability_body.py +0 -0
  230. /blaxel/{client → core/client}/models/configuration.py +0 -0
  231. /blaxel/{client → core/client}/models/continent.py +0 -0
  232. /blaxel/{client → core/client}/models/core_event.py +0 -0
  233. /blaxel/{client → core/client}/models/core_spec_configurations.py +0 -0
  234. /blaxel/{client → core/client}/models/country.py +0 -0
  235. /blaxel/{client → core/client}/models/create_api_key_for_service_account_body.py +0 -0
  236. /blaxel/{client → core/client}/models/create_workspace_service_account_body.py +0 -0
  237. /blaxel/{client → core/client}/models/create_workspace_service_account_response_200.py +0 -0
  238. /blaxel/{client → core/client}/models/delete_sandbox_preview_token_response_200.py +0 -0
  239. /blaxel/{client → core/client}/models/delete_workspace_service_account_response_200.py +0 -0
  240. /blaxel/{client → core/client}/models/entrypoint.py +0 -0
  241. /blaxel/{client → core/client}/models/entrypoint_env.py +0 -0
  242. /blaxel/{client → core/client}/models/flavor.py +0 -0
  243. /blaxel/{client → core/client}/models/form.py +0 -0
  244. /blaxel/{client → core/client}/models/form_config.py +0 -0
  245. /blaxel/{client → core/client}/models/form_oauth.py +0 -0
  246. /blaxel/{client → core/client}/models/form_secrets.py +0 -0
  247. /blaxel/{client → core/client}/models/function_kit.py +0 -0
  248. /blaxel/{client → core/client}/models/function_schema.py +0 -0
  249. /blaxel/{client → core/client}/models/function_schema_not.py +0 -0
  250. /blaxel/{client → core/client}/models/function_schema_or_bool.py +0 -0
  251. /blaxel/{client → core/client}/models/get_workspace_service_accounts_response_200_item.py +0 -0
  252. /blaxel/{client → core/client}/models/histogram_bucket.py +0 -0
  253. /blaxel/{client → core/client}/models/histogram_stats.py +0 -0
  254. /blaxel/{client → core/client}/models/integration_additional_infos.py +0 -0
  255. /blaxel/{client → core/client}/models/integration_connection.py +0 -0
  256. /blaxel/{client → core/client}/models/integration_connection_spec.py +0 -0
  257. /blaxel/{client → core/client}/models/integration_connection_spec_config.py +0 -0
  258. /blaxel/{client → core/client}/models/integration_connection_spec_secret.py +0 -0
  259. /blaxel/{client → core/client}/models/integration_endpoint.py +0 -0
  260. /blaxel/{client → core/client}/models/integration_endpoint_token.py +0 -0
  261. /blaxel/{client → core/client}/models/integration_headers.py +0 -0
  262. /blaxel/{client → core/client}/models/integration_model.py +0 -0
  263. /blaxel/{client → core/client}/models/integration_organization.py +0 -0
  264. /blaxel/{client → core/client}/models/integration_query_params.py +0 -0
  265. /blaxel/{client → core/client}/models/integration_repository.py +0 -0
  266. /blaxel/{client → core/client}/models/invite_workspace_user_body.py +0 -0
  267. /blaxel/{client → core/client}/models/job_execution_config.py +0 -0
  268. /blaxel/{client → core/client}/models/job_metrics.py +0 -0
  269. /blaxel/{client → core/client}/models/job_metrics_executions_chart.py +0 -0
  270. /blaxel/{client → core/client}/models/job_metrics_executions_total.py +0 -0
  271. /blaxel/{client → core/client}/models/job_metrics_tasks_chart.py +0 -0
  272. /blaxel/{client → core/client}/models/job_metrics_tasks_total.py +0 -0
  273. /blaxel/{client → core/client}/models/jobs_chart.py +0 -0
  274. /blaxel/{client → core/client}/models/jobs_chart_value.py +0 -0
  275. /blaxel/{client → core/client}/models/jobs_executions.py +0 -0
  276. /blaxel/{client → core/client}/models/jobs_network_chart.py +0 -0
  277. /blaxel/{client → core/client}/models/jobs_success_failed_chart.py +0 -0
  278. /blaxel/{client → core/client}/models/jobs_tasks.py +0 -0
  279. /blaxel/{client → core/client}/models/jobs_total.py +0 -0
  280. /blaxel/{client → core/client}/models/knowledgebase_spec.py +0 -0
  281. /blaxel/{client → core/client}/models/knowledgebase_spec_options.py +0 -0
  282. /blaxel/{client → core/client}/models/last_n_requests_metric.py +0 -0
  283. /blaxel/{client → core/client}/models/latency_metric.py +0 -0
  284. /blaxel/{client → core/client}/models/logs_response.py +0 -0
  285. /blaxel/{client → core/client}/models/logs_response_data.py +0 -0
  286. /blaxel/{client → core/client}/models/mcp_definition.py +0 -0
  287. /blaxel/{client → core/client}/models/mcp_definition_entrypoint.py +0 -0
  288. /blaxel/{client → core/client}/models/mcp_definition_form.py +0 -0
  289. /blaxel/{client → core/client}/models/memory_allocation_by_name.py +0 -0
  290. /blaxel/{client → core/client}/models/memory_allocation_metric.py +0 -0
  291. /blaxel/{client → core/client}/models/metadata.py +0 -0
  292. /blaxel/{client → core/client}/models/metadata_labels.py +0 -0
  293. /blaxel/{client → core/client}/models/metric.py +0 -0
  294. /blaxel/{client → core/client}/models/metrics.py +0 -0
  295. /blaxel/{client → core/client}/models/metrics_models.py +0 -0
  296. /blaxel/{client → core/client}/models/metrics_request_total_per_code.py +0 -0
  297. /blaxel/{client → core/client}/models/metrics_rps_per_code.py +0 -0
  298. /blaxel/{client → core/client}/models/model_private_cluster.py +0 -0
  299. /blaxel/{client → core/client}/models/o_auth.py +0 -0
  300. /blaxel/{client → core/client}/models/owner_fields.py +0 -0
  301. /blaxel/{client → core/client}/models/pending_invitation.py +0 -0
  302. /blaxel/{client → core/client}/models/pending_invitation_accept.py +0 -0
  303. /blaxel/{client → core/client}/models/pending_invitation_render.py +0 -0
  304. /blaxel/{client → core/client}/models/pending_invitation_render_invited_by.py +0 -0
  305. /blaxel/{client → core/client}/models/pending_invitation_render_workspace.py +0 -0
  306. /blaxel/{client → core/client}/models/pending_invitation_workspace_details.py +0 -0
  307. /blaxel/{client → core/client}/models/pod_template_spec.py +0 -0
  308. /blaxel/{client → core/client}/models/policy.py +0 -0
  309. /blaxel/{client → core/client}/models/policy_location.py +0 -0
  310. /blaxel/{client → core/client}/models/policy_max_tokens.py +0 -0
  311. /blaxel/{client → core/client}/models/port.py +0 -0
  312. /blaxel/{client → core/client}/models/preview.py +0 -0
  313. /blaxel/{client → core/client}/models/preview_metadata.py +0 -0
  314. /blaxel/{client → core/client}/models/preview_spec.py +0 -0
  315. /blaxel/{client → core/client}/models/preview_spec_request_headers.py +0 -0
  316. /blaxel/{client → core/client}/models/preview_spec_response_headers.py +0 -0
  317. /blaxel/{client → core/client}/models/preview_token.py +0 -0
  318. /blaxel/{client → core/client}/models/preview_token_metadata.py +0 -0
  319. /blaxel/{client → core/client}/models/preview_token_spec.py +0 -0
  320. /blaxel/{client → core/client}/models/private_cluster.py +0 -0
  321. /blaxel/{client → core/client}/models/private_location.py +0 -0
  322. /blaxel/{client → core/client}/models/repository.py +0 -0
  323. /blaxel/{client → core/client}/models/request_duration_over_time_metric.py +0 -0
  324. /blaxel/{client → core/client}/models/request_duration_over_time_metrics.py +0 -0
  325. /blaxel/{client → core/client}/models/request_total_by_origin_metric.py +0 -0
  326. /blaxel/{client → core/client}/models/request_total_by_origin_metric_request_total_by_origin.py +0 -0
  327. /blaxel/{client → core/client}/models/request_total_by_origin_metric_request_total_by_origin_and_code.py +0 -0
  328. /blaxel/{client → core/client}/models/request_total_metric.py +0 -0
  329. /blaxel/{client → core/client}/models/request_total_metric_request_total_per_code.py +0 -0
  330. /blaxel/{client → core/client}/models/request_total_metric_rps_per_code.py +0 -0
  331. /blaxel/{client → core/client}/models/request_total_response_data.py +0 -0
  332. /blaxel/{client → core/client}/models/resource.py +0 -0
  333. /blaxel/{client → core/client}/models/resource_log.py +0 -0
  334. /blaxel/{client → core/client}/models/resource_log_chart.py +0 -0
  335. /blaxel/{client → core/client}/models/resource_log_response.py +0 -0
  336. /blaxel/{client → core/client}/models/resource_metrics_request_total_per_code.py +0 -0
  337. /blaxel/{client → core/client}/models/resource_metrics_request_total_per_code_previous.py +0 -0
  338. /blaxel/{client → core/client}/models/resource_metrics_rps_per_code.py +0 -0
  339. /blaxel/{client → core/client}/models/resource_metrics_rps_per_code_previous.py +0 -0
  340. /blaxel/{client → core/client}/models/resource_trace.py +0 -0
  341. /blaxel/{client → core/client}/models/revision_configuration.py +0 -0
  342. /blaxel/{client → core/client}/models/revision_metadata.py +0 -0
  343. /blaxel/{client → core/client}/models/runtime_configuration.py +0 -0
  344. /blaxel/{client → core/client}/models/runtime_startup_probe.py +0 -0
  345. /blaxel/{client → core/client}/models/serverless_config.py +0 -0
  346. /blaxel/{client → core/client}/models/serverless_config_configuration.py +0 -0
  347. /blaxel/{client → core/client}/models/spec_configuration.py +0 -0
  348. /blaxel/{client → core/client}/models/start_sandbox.py +0 -0
  349. /blaxel/{client → core/client}/models/stop_sandbox.py +0 -0
  350. /blaxel/{client → core/client}/models/store_agent_labels.py +0 -0
  351. /blaxel/{client → core/client}/models/store_configuration_option.py +0 -0
  352. /blaxel/{client → core/client}/models/template_variable.py +0 -0
  353. /blaxel/{client → core/client}/models/time_fields.py +0 -0
  354. /blaxel/{client → core/client}/models/time_to_first_token_over_time_metrics.py +0 -0
  355. /blaxel/{client → core/client}/models/token_rate_metric.py +0 -0
  356. /blaxel/{client → core/client}/models/token_rate_metrics.py +0 -0
  357. /blaxel/{client → core/client}/models/token_total_metric.py +0 -0
  358. /blaxel/{client → core/client}/models/trace_ids_response.py +0 -0
  359. /blaxel/{client → core/client}/models/trigger.py +0 -0
  360. /blaxel/{client → core/client}/models/trigger_configuration.py +0 -0
  361. /blaxel/{client → core/client}/models/update_workspace_service_account_body.py +0 -0
  362. /blaxel/{client → core/client}/models/update_workspace_service_account_response_200.py +0 -0
  363. /blaxel/{client → core/client}/models/update_workspace_user_role_body.py +0 -0
  364. /blaxel/{client → core/client}/models/websocket_channel.py +0 -0
  365. /blaxel/{client → core/client}/models/websocket_message.py +0 -0
  366. /blaxel/{client → core/client}/models/workspace.py +0 -0
  367. /blaxel/{client → core/client}/models/workspace_labels.py +0 -0
  368. /blaxel/{client → core/client}/models/workspace_runtime.py +0 -0
  369. /blaxel/{client → core/client}/models/workspace_user.py +0 -0
  370. /blaxel/{client → core/client}/py.typed +0 -0
  371. /blaxel/{client → core/client}/types.py +0 -0
  372. /blaxel/{common → core/common}/env.py +0 -0
  373. /blaxel/{common → core/common}/settings.py +0 -0
  374. /blaxel/{sandbox/client/api/filesystem/__init__.py → core/py.typed} +0 -0
  375. /blaxel/{sandbox → core/sandbox}/client/__init__.py +0 -0
  376. /blaxel/{sandbox → core/sandbox}/client/api/__init__.py +0 -0
  377. /blaxel/{sandbox/client/api/network → core/sandbox/client/api/filesystem}/__init__.py +0 -0
  378. /blaxel/{sandbox → core/sandbox}/client/api/filesystem/delete_filesystem_path.py +0 -0
  379. /blaxel/{sandbox → core/sandbox}/client/api/filesystem/get_filesystem_path.py +0 -0
  380. /blaxel/{sandbox → core/sandbox}/client/api/filesystem/get_watch_filesystem_path.py +0 -0
  381. /blaxel/{sandbox → core/sandbox}/client/api/filesystem/get_ws_watch_filesystem_path.py +0 -0
  382. /blaxel/{sandbox/client/api/process → core/sandbox/client/api/network}/__init__.py +0 -0
  383. /blaxel/{sandbox → core/sandbox}/client/api/network/delete_network_process_pid_monitor.py +0 -0
  384. /blaxel/{sandbox → core/sandbox}/client/api/network/get_network_process_pid_ports.py +0 -0
  385. /blaxel/{sandbox → core/sandbox}/client/api/process/delete_process_identifier.py +0 -0
  386. /blaxel/{sandbox → core/sandbox}/client/api/process/delete_process_identifier_kill.py +0 -0
  387. /blaxel/{sandbox → core/sandbox}/client/api/process/get_process.py +0 -0
  388. /blaxel/{sandbox → core/sandbox}/client/api/process/get_process_identifier.py +0 -0
  389. /blaxel/{sandbox → core/sandbox}/client/api/process/get_process_identifier_logs.py +0 -0
  390. /blaxel/{sandbox → core/sandbox}/client/api/process/get_process_identifier_logs_stream.py +0 -0
  391. /blaxel/{sandbox → core/sandbox}/client/api/process/get_ws_process_identifier_logs_stream.py +0 -0
  392. /blaxel/{sandbox → core/sandbox}/client/client.py +0 -0
  393. /blaxel/{sandbox → core/sandbox}/client/errors.py +0 -0
  394. /blaxel/{sandbox → core/sandbox}/client/models/__init__.py +0 -0
  395. /blaxel/{sandbox → core/sandbox}/client/models/delete_network_process_pid_monitor_response_200.py +0 -0
  396. /blaxel/{sandbox → core/sandbox}/client/models/error_response.py +0 -0
  397. /blaxel/{sandbox → core/sandbox}/client/models/file.py +0 -0
  398. /blaxel/{sandbox → core/sandbox}/client/models/file_request.py +0 -0
  399. /blaxel/{sandbox → core/sandbox}/client/models/file_with_content.py +0 -0
  400. /blaxel/{sandbox → core/sandbox}/client/models/get_network_process_pid_ports_response_200.py +0 -0
  401. /blaxel/{sandbox → core/sandbox}/client/models/port_monitor_request.py +0 -0
  402. /blaxel/{sandbox → core/sandbox}/client/models/post_network_process_pid_monitor_response_200.py +0 -0
  403. /blaxel/{sandbox → core/sandbox}/client/models/process_logs.py +0 -0
  404. /blaxel/{sandbox → core/sandbox}/client/models/process_request.py +0 -0
  405. /blaxel/{sandbox → core/sandbox}/client/models/process_request_env.py +0 -0
  406. /blaxel/{sandbox → core/sandbox}/client/models/process_response.py +0 -0
  407. /blaxel/{sandbox → core/sandbox}/client/models/process_response_status.py +0 -0
  408. /blaxel/{sandbox → core/sandbox}/client/models/subdirectory.py +0 -0
  409. /blaxel/{sandbox → core/sandbox}/client/models/success_response.py +0 -0
  410. /blaxel/{sandbox → core/sandbox}/client/py.typed +0 -0
  411. /blaxel/{sandbox → core/sandbox}/client/types.py +0 -0
  412. /blaxel/{tools → core/tools}/common.py +0 -0
  413. /blaxel/{tools → core/tools}/types.py +0 -0
  414. /blaxel/{models/custom/langchain → langgraph/custom}/gemini.py +0 -0
  415. /blaxel/{models/custom/llamaindex → llamaindex/custom}/cohere.py +0 -0
  416. /blaxel/{instrumentation → telemetry}/exporters.py +0 -0
  417. /blaxel/{instrumentation → telemetry/log}/log.py +0 -0
  418. {blaxel-0.1.22rc70.dist-info → blaxel-0.2.0rc2.dist-info}/WHEEL +0 -0
  419. {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 WebSocketClientProtocol
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
- async with ws_connect(ws_url, extra_headers=headers, open_timeout=timeout) as websocket:
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: WebSocketClientProtocol,
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(message)
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: WebSocketClientProtocol):
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 Dict, Literal
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 opentelemetry.trace import Span, StatusCode
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: WebSocketServerProtocol):
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)
@@ -23,7 +23,7 @@ def _get_kwargs(
23
23
  "url": f"/filesystem/{path}",
24
24
  }
25
25
 
26
- if type(body) == dict:
26
+ if type(body) is dict:
27
27
  _body = body
28
28
  else:
29
29
  _body = body.to_dict()
@@ -25,7 +25,7 @@ def _get_kwargs(
25
25
  "url": f"/network/process/{pid}/monitor",
26
26
  }
27
27
 
28
- if type(body) == dict:
28
+ if type(body) is dict:
29
29
  _body = body
30
30
  else:
31
31
  _body = body.to_dict()
File without changes
@@ -22,7 +22,7 @@ def _get_kwargs(
22
22
  "url": "/process",
23
23
  }
24
24
 
25
- if type(body) == dict:
25
+ if type(body) is dict:
26
26
  _body = body
27
27
  else:
28
28
  _body = body.to_dict()
@@ -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) == dict:
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) == dict:
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