blaxel 0.1.22rc70__py3-none-any.whl → 0.2.0__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 +26 -15
  63. blaxel/{mcp → core/mcp}/server.py +12 -37
  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 +125 -0
  111. blaxel/telemetry/instrumentation/blaxel_langgraph.py +99 -0
  112. blaxel/telemetry/instrumentation/blaxel_langgraph_gemini.py +346 -0
  113. blaxel/telemetry/instrumentation/blaxel_llamaindex.py +85 -0
  114. blaxel/telemetry/instrumentation/map.py +62 -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.0.dist-info/METADATA +224 -0
  121. blaxel-0.2.0.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.0.dist-info}/WHEEL +0 -0
  419. {blaxel-0.1.22rc70.dist-info → blaxel-0.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -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
@@ -1,25 +1,38 @@
1
1
  from dataclasses import dataclass
2
2
  from datetime import datetime
3
- from typing import List, Optional
3
+ from typing import Any, Dict, List, Optional, Union
4
4
 
5
- from ..client.api.compute.create_sandbox_preview import asyncio as create_sandbox_preview
5
+ from ..client.api.compute.create_sandbox_preview import (
6
+ asyncio as create_sandbox_preview,
7
+ )
6
8
  from ..client.api.compute.create_sandbox_preview_token import (
7
9
  asyncio as create_sandbox_preview_token,
8
10
  )
9
- from ..client.api.compute.delete_sandbox_preview import asyncio as delete_sandbox_preview
11
+ from ..client.api.compute.delete_sandbox_preview import (
12
+ asyncio as delete_sandbox_preview,
13
+ )
10
14
  from ..client.api.compute.delete_sandbox_preview_token import (
11
15
  asyncio as delete_sandbox_preview_token,
12
16
  )
13
17
  from ..client.api.compute.get_sandbox_preview import asyncio as get_sandbox_preview
14
- from ..client.api.compute.list_sandbox_preview_tokens import asyncio as list_sandbox_preview_tokens
18
+ from ..client.api.compute.list_sandbox_preview_tokens import (
19
+ asyncio as list_sandbox_preview_tokens,
20
+ )
15
21
  from ..client.api.compute.list_sandbox_previews import asyncio as list_sandbox_previews
16
22
  from ..client.client import client
17
- from ..client.models import Preview, PreviewSpec, PreviewToken, PreviewTokenSpec, Sandbox
23
+ from ..client.models import (
24
+ Preview,
25
+ PreviewSpec,
26
+ PreviewToken,
27
+ PreviewTokenSpec,
28
+ Sandbox,
29
+ )
18
30
 
19
31
 
20
32
  @dataclass
21
33
  class SandboxPreviewToken:
22
34
  """Represents a preview token with its value and expiration."""
35
+
23
36
  preview_token: PreviewToken
24
37
 
25
38
  @property
@@ -30,23 +43,22 @@ class SandboxPreviewToken:
30
43
  def expires_at(self) -> datetime:
31
44
  return self.preview_token.spec.expires_at if self.preview_token.spec else datetime.now()
32
45
 
46
+
33
47
  class SandboxPreviewTokens:
34
48
  """Manages preview tokens for a sandbox preview."""
35
- def __init__(self, preview: Preview):
49
+
50
+ def __init__(self, preview: Preview, sandbox_name: str):
36
51
  self.preview = preview
52
+ self.sandbox_name = sandbox_name
37
53
 
38
54
  @property
39
55
  def preview_name(self) -> str:
40
56
  return self.preview.metadata.name if self.preview.metadata else ""
41
57
 
42
- @property
43
- def resource_name(self) -> str:
44
- return self.preview.metadata.resource_name if self.preview.metadata else ""
45
-
46
58
  async def create(self, expires_at: datetime) -> SandboxPreviewToken:
47
59
  """Create a new preview token."""
48
60
  response: PreviewToken = await create_sandbox_preview_token(
49
- self.resource_name,
61
+ self.sandbox_name,
50
62
  self.preview_name,
51
63
  body=PreviewToken(
52
64
  spec=PreviewTokenSpec(
@@ -60,7 +72,7 @@ class SandboxPreviewTokens:
60
72
  async def list(self) -> List[SandboxPreviewToken]:
61
73
  """List all preview tokens."""
62
74
  response: List[PreviewToken] = await list_sandbox_preview_tokens(
63
- self.resource_name,
75
+ self.sandbox_name,
64
76
  self.preview_name,
65
77
  client=client,
66
78
  )
@@ -69,18 +81,21 @@ class SandboxPreviewTokens:
69
81
  async def delete(self, token_name: str) -> dict:
70
82
  """Delete a preview token."""
71
83
  response: PreviewToken = await delete_sandbox_preview_token(
72
- self.resource_name,
84
+ self.sandbox_name,
73
85
  self.preview_name,
74
86
  token_name,
75
87
  client=client,
76
88
  )
77
89
  return response
78
90
 
91
+
79
92
  class SandboxPreview:
80
93
  """Represents a sandbox preview with its metadata and tokens."""
81
- def __init__(self, preview: Preview):
94
+
95
+ def __init__(self, preview: Preview, sandbox_name: str = ""):
82
96
  self.preview = preview
83
- self.tokens = SandboxPreviewTokens(self)
97
+ self.sandbox_name = sandbox_name
98
+ self.tokens = SandboxPreviewTokens(preview, sandbox_name)
84
99
 
85
100
  @property
86
101
  def name(self) -> str:
@@ -94,8 +109,10 @@ class SandboxPreview:
94
109
  def spec(self) -> Optional[PreviewSpec]:
95
110
  return self.preview.spec
96
111
 
112
+
97
113
  class SandboxPreviews:
98
114
  """Manages sandbox previews."""
115
+
99
116
  def __init__(self, sandbox: Sandbox):
100
117
  self.sandbox = sandbox
101
118
 
@@ -111,8 +128,11 @@ class SandboxPreviews:
111
128
  )
112
129
  return [SandboxPreview(preview) for preview in response]
113
130
 
114
- async def create(self, preview: Preview) -> SandboxPreview:
131
+ async def create(self, preview: Union[Preview, Dict[str, Any]]) -> SandboxPreview:
115
132
  """Create a new preview."""
133
+ if isinstance(preview, dict):
134
+ preview = Preview.from_dict(preview)
135
+
116
136
  response: Preview = await create_sandbox_preview(
117
137
  self.sandbox_name,
118
138
  body=preview,
@@ -138,5 +158,13 @@ class SandboxPreviews:
138
158
  )
139
159
  return response
140
160
 
161
+
141
162
  def to_utc_z(dt: datetime) -> str:
142
- return dt.isoformat(timespec="milliseconds").replace("+00:00", "Z")
163
+ """Convert datetime to UTC Z format string."""
164
+ # Simple approach: format as ISO string and add Z
165
+ iso_string = dt.isoformat()
166
+ if iso_string.endswith("+00:00"):
167
+ return iso_string.replace("+00:00", "Z")
168
+ elif "T" in iso_string and not iso_string.endswith("Z"):
169
+ return iso_string + "Z"
170
+ return iso_string
@@ -0,0 +1,159 @@
1
+ import asyncio
2
+ from typing import Any, Callable, Dict, Literal, Optional, Union
3
+
4
+ import httpx
5
+
6
+ from ..common.settings import settings
7
+ from .action import SandboxAction
8
+ from .client.models import ProcessResponse, SuccessResponse
9
+ from .client.models.process_request import ProcessRequest
10
+ from .types import SandboxConfiguration
11
+
12
+
13
+ class SandboxProcess(SandboxAction):
14
+ def __init__(self, sandbox_config: SandboxConfiguration):
15
+ super().__init__(sandbox_config)
16
+
17
+ def stream_logs(
18
+ self, identifier: str, options: Optional[Dict[str, Callable[[str], None]]] = None
19
+ ) -> Dict[str, Callable[[], None]]:
20
+ """Stream logs from a process with callbacks for different output types."""
21
+ if options is None:
22
+ options = {}
23
+
24
+ closed = False
25
+
26
+ async def start_streaming():
27
+ nonlocal closed
28
+
29
+ url = f"{self.url}/process/{identifier}/logs/stream"
30
+ headers = {**settings.headers, **self.sandbox_config.headers}
31
+
32
+ try:
33
+ async with httpx.AsyncClient() as client_instance:
34
+ async with client_instance.stream("GET", url, headers=headers) as response:
35
+ if response.status_code != 200:
36
+ raise Exception(f"Failed to stream logs: {await response.aread()}")
37
+
38
+ buffer = ""
39
+ async for chunk in response.aiter_text():
40
+ if closed:
41
+ break
42
+
43
+ buffer += chunk
44
+ lines = buffer.split("\n")
45
+ buffer = lines.pop() # Keep incomplete line in buffer
46
+
47
+ for line in lines:
48
+ if line.startswith("stdout:"):
49
+ content = line[7:] # Remove 'stdout:' prefix
50
+ if options.get("on_stdout"):
51
+ options["on_stdout"](content)
52
+ if options.get("on_log"):
53
+ options["on_log"](content)
54
+ elif line.startswith("stderr:"):
55
+ content = line[7:] # Remove 'stderr:' prefix
56
+ if options.get("on_stderr"):
57
+ options["on_stderr"](content)
58
+ if options.get("on_log"):
59
+ options["on_log"](content)
60
+ else:
61
+ if options.get("on_log"):
62
+ options["on_log"](line)
63
+ except Exception as e:
64
+ # Suppress AbortError when closing
65
+ if not (hasattr(e, "name") and e.name == "AbortError"):
66
+ raise e
67
+
68
+ # Start streaming in the background
69
+ task = asyncio.create_task(start_streaming())
70
+
71
+ def close():
72
+ nonlocal closed
73
+ closed = True
74
+ task.cancel()
75
+
76
+ return {"close": close}
77
+
78
+ async def exec(self, process: Union[ProcessRequest, Dict[str, Any]]) -> ProcessResponse:
79
+ if isinstance(process, dict):
80
+ process = ProcessRequest.from_dict(process)
81
+
82
+ async with self.get_client() as client_instance:
83
+ response = await client_instance.post("/process", json=process.to_dict())
84
+ self.handle_response_error(
85
+ response, response.json() if response.content else None, None
86
+ )
87
+ return ProcessResponse.from_dict(response.json())
88
+
89
+ async def wait(
90
+ self, identifier: str, max_wait: int = 60000, interval: int = 1000
91
+ ) -> ProcessResponse:
92
+ """Wait for a process to complete."""
93
+ start_time = asyncio.get_event_loop().time() * 1000 # Convert to milliseconds
94
+ status = "running"
95
+ data = await self.get(identifier)
96
+
97
+ while status == "running":
98
+ await asyncio.sleep(interval / 1000) # Convert to seconds
99
+ try:
100
+ data = await self.get(identifier)
101
+ status = data.status or "running"
102
+ except:
103
+ break
104
+
105
+ if (asyncio.get_event_loop().time() * 1000) - start_time > max_wait:
106
+ raise Exception("Process did not finish in time")
107
+
108
+ return data
109
+
110
+ async def get(self, identifier: str) -> ProcessResponse:
111
+ async with self.get_client() as client_instance:
112
+ response = await client_instance.get(f"/process/{identifier}")
113
+ self.handle_response_error(
114
+ response, response.json() if response.content else None, None
115
+ )
116
+ return ProcessResponse.from_dict(response.json())
117
+
118
+ async def list(self) -> list[ProcessResponse]:
119
+ async with self.get_client() as client_instance:
120
+ response = await client_instance.get("/process")
121
+ self.handle_response_error(
122
+ response, response.json() if response.content else None, None
123
+ )
124
+ return [ProcessResponse.from_dict(item) for item in response.json()]
125
+
126
+ async def stop(self, identifier: str) -> SuccessResponse:
127
+ async with self.get_client() as client_instance:
128
+ response = await client_instance.delete(f"/process/{identifier}")
129
+ self.handle_response_error(
130
+ response, response.json() if response.content else None, None
131
+ )
132
+ return SuccessResponse.from_dict(response.json())
133
+
134
+ async def kill(self, identifier: str) -> SuccessResponse:
135
+ async with self.get_client() as client_instance:
136
+ response = await client_instance.delete(f"/process/{identifier}/kill")
137
+ self.handle_response_error(
138
+ response, response.json() if response.content else None, None
139
+ )
140
+ return SuccessResponse.from_dict(response.json())
141
+
142
+ async def logs(
143
+ self, identifier: str, log_type: Literal["stdout", "stderr", "all"] = "all"
144
+ ) -> str:
145
+ async with self.get_client() as client_instance:
146
+ response = await client_instance.get(f"/process/{identifier}/logs")
147
+ self.handle_response_error(
148
+ response, response.json() if response.content else None, None
149
+ )
150
+
151
+ data = response.json()
152
+ if log_type == "all":
153
+ return data.get("logs", "")
154
+ elif log_type == "stdout":
155
+ return data.get("stdout", "")
156
+ elif log_type == "stderr":
157
+ return data.get("stderr", "")
158
+
159
+ raise Exception("Unsupported log type")