blaxel 0.2.33__py3-none-any.whl → 0.2.35__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. blaxel/__init__.py +2 -2
  2. blaxel/core/__init__.py +2 -1
  3. blaxel/core/client/api/agents/create_agent.py +64 -19
  4. blaxel/core/client/api/agents/delete_agent.py +44 -15
  5. blaxel/core/client/api/agents/get_agent.py +43 -14
  6. blaxel/core/client/api/agents/list_agents.py +40 -11
  7. blaxel/core/client/api/agents/update_agent.py +60 -19
  8. blaxel/core/client/api/compute/create_sandbox.py +60 -23
  9. blaxel/core/client/api/compute/delete_sandbox.py +40 -19
  10. blaxel/core/client/api/compute/get_sandbox.py +39 -18
  11. blaxel/core/client/api/compute/list_sandboxes.py +40 -19
  12. blaxel/core/client/api/compute/update_sandbox.py +56 -23
  13. blaxel/core/client/api/configurations/get_configuration.py +16 -4
  14. blaxel/core/client/api/customdomains/create_custom_domain.py +12 -0
  15. blaxel/core/client/api/customdomains/list_custom_domains.py +16 -4
  16. blaxel/core/client/api/default/get_template.py +8 -4
  17. blaxel/core/client/api/functions/create_function.py +62 -19
  18. blaxel/core/client/api/functions/delete_function.py +46 -15
  19. blaxel/core/client/api/functions/get_function.py +45 -14
  20. blaxel/core/client/api/functions/list_functions.py +44 -15
  21. blaxel/core/client/api/functions/update_function.py +62 -19
  22. blaxel/core/client/api/images/cleanup_images.py +12 -12
  23. blaxel/core/client/api/images/delete_image.py +12 -8
  24. blaxel/core/client/api/images/delete_image_tag.py +12 -8
  25. blaxel/core/client/api/images/get_image.py +12 -8
  26. blaxel/core/client/api/images/list_images.py +12 -8
  27. blaxel/core/client/api/integrations/create_integration_connection.py +56 -23
  28. blaxel/core/client/api/integrations/delete_integration_connection.py +48 -19
  29. blaxel/core/client/api/integrations/get_integration.py +12 -8
  30. blaxel/core/client/api/integrations/get_integration_connection.py +44 -19
  31. blaxel/core/client/api/integrations/get_integration_connection_model.py +2 -2
  32. blaxel/core/client/api/integrations/list_integration_connections.py +36 -19
  33. blaxel/core/client/api/integrations/update_integration_connection.py +52 -19
  34. blaxel/core/client/api/jobs/create_job.py +20 -12
  35. blaxel/core/client/api/jobs/create_job_execution.py +22 -16
  36. blaxel/core/client/api/jobs/delete_job.py +12 -8
  37. blaxel/core/client/api/jobs/delete_job_execution.py +12 -8
  38. blaxel/core/client/api/jobs/get_job.py +24 -20
  39. blaxel/core/client/api/jobs/get_job_execution.py +8 -4
  40. blaxel/core/client/api/jobs/list_job_executions.py +8 -4
  41. blaxel/core/client/api/jobs/list_jobs.py +12 -8
  42. blaxel/core/client/api/jobs/update_job.py +20 -12
  43. blaxel/core/client/api/locations/list_locations.py +12 -8
  44. blaxel/core/client/api/{default → mcphub}/list_mcp_hub_definitions.py +20 -4
  45. blaxel/core/client/api/models/create_model.py +52 -23
  46. blaxel/core/client/api/models/delete_model.py +40 -19
  47. blaxel/core/client/api/models/get_model.py +40 -19
  48. blaxel/core/client/api/models/list_models.py +40 -19
  49. blaxel/core/client/api/models/update_model.py +52 -23
  50. blaxel/core/client/api/policies/create_policy.py +12 -8
  51. blaxel/core/client/api/policies/delete_policy.py +12 -8
  52. blaxel/core/client/api/policies/get_policy.py +12 -8
  53. blaxel/core/client/api/policies/list_policies.py +12 -8
  54. blaxel/core/client/api/policies/update_policy.py +12 -8
  55. blaxel/core/client/api/public_ipslist/list_public_ips.py +37 -5
  56. blaxel/core/client/api/sandboxhub/__init__.py +0 -0
  57. blaxel/core/client/api/{default → sandboxhub}/list_sandbox_hub_definitions.py +20 -4
  58. blaxel/core/client/api/service_accounts/create_api_key_for_service_account.py +12 -8
  59. blaxel/core/client/api/service_accounts/create_workspace_service_account.py +12 -8
  60. blaxel/core/client/api/service_accounts/delete_api_key_for_service_account.py +6 -4
  61. blaxel/core/client/api/service_accounts/delete_workspace_service_account.py +12 -8
  62. blaxel/core/client/api/service_accounts/get_workspace_service_accounts.py +12 -8
  63. blaxel/core/client/api/service_accounts/list_api_keys_for_service_account.py +12 -8
  64. blaxel/core/client/api/service_accounts/update_workspace_service_account.py +8 -8
  65. blaxel/core/client/api/templates/list_templates.py +12 -8
  66. blaxel/core/client/api/volume_templates/create_volume_template.py +8 -4
  67. blaxel/core/client/api/volume_templates/list_volume_templates.py +8 -4
  68. blaxel/core/client/api/volumes/create_volume.py +56 -23
  69. blaxel/core/client/api/volumes/delete_volume.py +44 -19
  70. blaxel/core/client/api/volumes/get_volume.py +40 -19
  71. blaxel/core/client/api/volumes/list_volumes.py +40 -19
  72. blaxel/core/client/api/workspaces/create_workspace.py +54 -23
  73. blaxel/core/client/api/workspaces/delete_workspace.py +42 -19
  74. blaxel/core/client/api/workspaces/get_workspace.py +42 -19
  75. blaxel/core/client/api/workspaces/invite_workspace_user.py +8 -4
  76. blaxel/core/client/api/workspaces/list_workspace_users.py +12 -8
  77. blaxel/core/client/api/workspaces/list_workspaces.py +36 -19
  78. blaxel/core/client/api/workspaces/update_workspace.py +50 -19
  79. blaxel/core/client/models/__init__.py +76 -146
  80. blaxel/core/client/models/agent.py +43 -47
  81. blaxel/core/client/models/agent_runtime.py +139 -0
  82. blaxel/core/client/models/agent_runtime_generation.py +18 -0
  83. blaxel/core/client/models/agent_spec.py +33 -110
  84. blaxel/core/client/models/api_key.py +5 -4
  85. blaxel/core/client/models/core_event.py +5 -5
  86. blaxel/core/client/models/create_api_key_for_service_account_body.py +2 -1
  87. blaxel/core/client/models/create_job_execution_request.py +1 -1
  88. blaxel/core/client/models/create_job_execution_response.py +13 -9
  89. blaxel/core/client/models/custom_domain.py +19 -36
  90. blaxel/core/client/models/custom_domain_metadata.py +4 -3
  91. blaxel/core/client/models/custom_domain_spec.py +14 -5
  92. blaxel/core/client/models/custom_domain_spec_status.py +19 -0
  93. blaxel/core/client/models/entrypoint.py +39 -13
  94. blaxel/core/client/models/{workspace_labels.py → entrypoint_args_item.py} +6 -6
  95. blaxel/core/client/models/entrypoint_env.py +3 -3
  96. blaxel/core/client/models/{job_metrics_executions_total.py → entrypoint_super_gateway_args_item.py} +6 -6
  97. blaxel/core/client/models/{spec_configuration.py → env.py} +17 -8
  98. blaxel/core/{sandbox/client/models/welcome_response.py → client/models/error.py} +26 -23
  99. blaxel/core/client/models/expiration_policy.py +30 -11
  100. blaxel/core/client/models/expiration_policy_action.py +17 -0
  101. blaxel/core/client/models/expiration_policy_type.py +19 -0
  102. blaxel/core/client/models/flavor.py +13 -5
  103. blaxel/core/client/models/flavor_type.py +18 -0
  104. blaxel/core/client/models/form.py +6 -6
  105. blaxel/core/client/models/function.py +43 -47
  106. blaxel/core/client/models/function_runtime.py +138 -0
  107. blaxel/core/client/models/function_runtime_generation.py +18 -0
  108. blaxel/core/client/models/function_spec.py +27 -73
  109. blaxel/core/client/models/function_spec_transport.py +18 -0
  110. blaxel/core/client/models/image.py +19 -36
  111. blaxel/core/client/models/integration_connection.py +25 -39
  112. blaxel/core/client/models/integration_connection_spec.py +8 -5
  113. blaxel/core/client/models/integration_connection_spec_config.py +1 -1
  114. blaxel/core/client/models/integration_connection_spec_secret.py +1 -1
  115. blaxel/core/client/models/integration_endpoint.py +41 -11
  116. blaxel/core/client/models/integration_endpoint_ignore_models_item.py +45 -0
  117. blaxel/core/client/models/{mcp_definition_entrypoint.py → integration_endpoint_models_item.py} +6 -6
  118. blaxel/core/client/models/job.py +43 -47
  119. blaxel/core/client/models/job_execution.py +30 -37
  120. blaxel/core/client/models/job_execution_metadata.py +3 -3
  121. blaxel/core/client/models/job_execution_spec.py +2 -2
  122. blaxel/core/client/models/job_execution_stats.py +5 -5
  123. blaxel/core/client/models/job_execution_status.py +24 -0
  124. blaxel/core/client/models/job_execution_task.py +12 -4
  125. blaxel/core/client/models/job_execution_task_metadata.py +1 -1
  126. blaxel/core/client/models/job_execution_task_spec.py +2 -2
  127. blaxel/core/client/models/job_execution_task_status.py +23 -0
  128. blaxel/core/client/models/job_runtime.py +172 -0
  129. blaxel/core/client/models/job_runtime_generation.py +18 -0
  130. blaxel/core/client/models/job_spec.py +20 -88
  131. blaxel/core/client/models/location_response.py +5 -5
  132. blaxel/core/client/models/mcp_definition.py +30 -17
  133. blaxel/core/client/models/{job_metrics_tasks_total.py → mcp_definition_categories_item.py} +6 -6
  134. blaxel/core/client/models/metadata.py +23 -17
  135. blaxel/core/client/models/metadata_labels.py +4 -1
  136. blaxel/core/client/models/model.py +43 -47
  137. blaxel/core/client/models/model_runtime.py +99 -0
  138. blaxel/core/client/models/model_runtime_type.py +34 -0
  139. blaxel/core/client/models/model_spec.py +12 -58
  140. blaxel/core/client/models/o_auth.py +23 -6
  141. blaxel/core/client/models/{form_oauth.py → o_auth_scope_item.py} +6 -6
  142. blaxel/core/client/models/pending_invitation_accept.py +2 -1
  143. blaxel/core/client/models/pending_invitation_workspace_details.py +27 -6
  144. blaxel/core/client/models/{metrics_request_total_per_code.py → pending_invitation_workspace_details_emails_item.py} +6 -6
  145. blaxel/core/client/models/policy.py +20 -36
  146. blaxel/core/client/models/policy_location.py +13 -5
  147. blaxel/core/client/models/policy_location_type.py +19 -0
  148. blaxel/core/client/models/policy_max_tokens.py +6 -6
  149. blaxel/core/client/models/policy_resource_type.py +20 -0
  150. blaxel/core/client/models/policy_spec.py +31 -10
  151. blaxel/core/client/models/policy_spec_type.py +19 -0
  152. blaxel/core/client/models/port.py +25 -15
  153. blaxel/core/client/models/port_protocol.py +19 -0
  154. blaxel/core/client/models/preview.py +19 -36
  155. blaxel/core/client/models/preview_metadata.py +12 -10
  156. blaxel/core/client/models/preview_token.py +19 -36
  157. blaxel/core/client/models/preview_token_metadata.py +8 -6
  158. blaxel/core/client/models/repository.py +2 -2
  159. blaxel/core/client/models/revision_configuration.py +3 -3
  160. blaxel/core/client/models/sandbox.py +45 -58
  161. blaxel/core/client/models/sandbox_definition.py +37 -22
  162. blaxel/core/client/models/sandbox_definition_categories_item.py +45 -0
  163. blaxel/core/client/models/sandbox_error.py +148 -0
  164. blaxel/core/client/models/sandbox_error_details.py +45 -0
  165. blaxel/core/client/models/sandbox_lifecycle.py +3 -2
  166. blaxel/core/client/models/sandbox_runtime.py +145 -0
  167. blaxel/core/client/models/sandbox_spec.py +33 -134
  168. blaxel/core/client/models/status.py +25 -0
  169. blaxel/core/client/models/template.py +8 -7
  170. blaxel/core/client/models/template_variable.py +5 -5
  171. blaxel/core/client/models/trigger.py +14 -6
  172. blaxel/core/client/models/trigger_configuration.py +7 -6
  173. blaxel/core/client/models/trigger_type.py +19 -0
  174. blaxel/core/client/models/volume.py +35 -47
  175. blaxel/core/client/models/volume_attachment.py +6 -4
  176. blaxel/core/client/models/volume_spec.py +7 -4
  177. blaxel/core/client/models/volume_state.py +3 -3
  178. blaxel/core/client/models/volume_template.py +19 -33
  179. blaxel/core/client/models/volume_template_state.py +12 -4
  180. blaxel/core/client/models/volume_template_state_status.py +19 -0
  181. blaxel/core/client/models/volume_template_version.py +12 -4
  182. blaxel/core/client/models/volume_template_version_status.py +19 -0
  183. blaxel/core/client/models/workspace.py +35 -25
  184. blaxel/core/client/models/workspace_runtime.py +3 -2
  185. blaxel/core/client/models/workspace_status.py +22 -0
  186. blaxel/core/common/__init__.py +1 -1
  187. blaxel/core/jobs/__init__.py +0 -1
  188. blaxel/core/sandbox/__init__.py +2 -0
  189. blaxel/core/sandbox/client/api/process/post_process.py +8 -4
  190. blaxel/core/sandbox/client/models/__init__.py +0 -2
  191. blaxel/core/sandbox/client/models/process_response.py +16 -0
  192. blaxel/core/sandbox/client/models/process_response_status.py +9 -0
  193. blaxel/core/sandbox/default/__init__.py +2 -0
  194. blaxel/core/sandbox/default/interpreter.py +5 -1
  195. blaxel/core/sandbox/default/preview.py +3 -1
  196. blaxel/core/sandbox/default/sandbox.py +85 -26
  197. blaxel/core/sandbox/sync/process.py +2 -1
  198. blaxel/core/sandbox/sync/sandbox.py +68 -23
  199. blaxel/core/sandbox/types.py +3 -0
  200. blaxel/core/tools/common.py +16 -2
  201. blaxel/core/volume/__init__.py +2 -2
  202. blaxel/core/volume/volume.py +227 -11
  203. blaxel/langgraph/tools.py +34 -2
  204. blaxel/openai/tools.py +33 -1
  205. {blaxel-0.2.33.dist-info → blaxel-0.2.35.dist-info}/METADATA +3 -3
  206. {blaxel-0.2.33.dist-info → blaxel-0.2.35.dist-info}/RECORD +209 -248
  207. blaxel/core/client/models/acl.py +0 -133
  208. blaxel/core/client/models/billable_time_metric.py +0 -89
  209. blaxel/core/client/models/core_spec.py +0 -194
  210. blaxel/core/client/models/core_spec_configurations.py +0 -77
  211. blaxel/core/client/models/histogram_bucket.py +0 -79
  212. blaxel/core/client/models/histogram_stats.py +0 -88
  213. blaxel/core/client/models/integration_model.py +0 -162
  214. blaxel/core/client/models/job_execution_config.py +0 -79
  215. blaxel/core/client/models/job_metrics.py +0 -262
  216. blaxel/core/client/models/jobs_chart_value.py +0 -70
  217. blaxel/core/client/models/jobs_network_chart.py +0 -102
  218. blaxel/core/client/models/jobs_success_failed_chart.py +0 -147
  219. blaxel/core/client/models/jobs_total.py +0 -88
  220. blaxel/core/client/models/last_n_requests_metric.py +0 -97
  221. blaxel/core/client/models/latency_metric.py +0 -148
  222. blaxel/core/client/models/logs_response.py +0 -63
  223. blaxel/core/client/models/logs_response_data.py +0 -99
  224. blaxel/core/client/models/mcp_definition_form.py +0 -45
  225. blaxel/core/client/models/memory_allocation_by_name.py +0 -70
  226. blaxel/core/client/models/memory_allocation_metric.py +0 -61
  227. blaxel/core/client/models/metric.py +0 -79
  228. blaxel/core/client/models/metrics.py +0 -273
  229. blaxel/core/client/models/metrics_models.py +0 -45
  230. blaxel/core/client/models/metrics_rps_per_code.py +0 -45
  231. blaxel/core/client/models/pod_template_spec.py +0 -45
  232. blaxel/core/client/models/request_duration_over_time_metric.py +0 -97
  233. blaxel/core/client/models/request_duration_over_time_metrics.py +0 -84
  234. blaxel/core/client/models/request_total_by_origin_metric.py +0 -129
  235. blaxel/core/client/models/request_total_by_origin_metric_request_total_by_origin.py +0 -45
  236. blaxel/core/client/models/request_total_by_origin_metric_request_total_by_origin_and_code.py +0 -45
  237. blaxel/core/client/models/request_total_metric.py +0 -155
  238. blaxel/core/client/models/request_total_metric_request_total_per_code.py +0 -45
  239. blaxel/core/client/models/request_total_metric_rps_per_code.py +0 -45
  240. blaxel/core/client/models/request_total_response_data.py +0 -97
  241. blaxel/core/client/models/resource.py +0 -99
  242. blaxel/core/client/models/resource_log.py +0 -88
  243. blaxel/core/client/models/resource_log_chart.py +0 -133
  244. blaxel/core/client/models/resource_log_response.py +0 -83
  245. blaxel/core/client/models/resource_metrics.py +0 -618
  246. blaxel/core/client/models/resource_metrics_request_total_per_code.py +0 -45
  247. blaxel/core/client/models/resource_metrics_request_total_per_code_previous.py +0 -45
  248. blaxel/core/client/models/resource_metrics_rps_per_code.py +0 -45
  249. blaxel/core/client/models/resource_metrics_rps_per_code_previous.py +0 -45
  250. blaxel/core/client/models/resource_trace.py +0 -97
  251. blaxel/core/client/models/runtime.py +0 -317
  252. blaxel/core/client/models/runtime_configuration.py +0 -45
  253. blaxel/core/client/models/runtime_startup_probe.py +0 -45
  254. blaxel/core/client/models/sandbox_metrics.py +0 -88
  255. blaxel/core/client/models/serverless_config.py +0 -117
  256. blaxel/core/client/models/serverless_config_configuration.py +0 -45
  257. blaxel/core/client/models/start_sandbox.py +0 -98
  258. blaxel/core/client/models/stop_sandbox.py +0 -98
  259. blaxel/core/client/models/store_agent.py +0 -181
  260. blaxel/core/client/models/store_agent_labels.py +0 -45
  261. blaxel/core/client/models/store_configuration.py +0 -156
  262. blaxel/core/client/models/store_configuration_option.py +0 -79
  263. blaxel/core/client/models/time_to_first_token_over_time_metrics.py +0 -87
  264. blaxel/core/client/models/token_rate_metric.py +0 -106
  265. blaxel/core/client/models/token_rate_metrics.py +0 -124
  266. blaxel/core/client/models/token_total_metric.py +0 -112
  267. blaxel/core/client/models/trace_ids_response.py +0 -45
  268. blaxel/core/client/models/websocket_channel.py +0 -97
  269. blaxel/core/client/models/websocket_message.py +0 -106
  270. blaxel/core/sandbox/client/api/root/delete.py +0 -130
  271. blaxel/core/sandbox/client/api/root/get.py +0 -130
  272. blaxel/core/sandbox/client/api/root/options.py +0 -130
  273. blaxel/core/sandbox/client/api/root/patch.py +0 -130
  274. blaxel/core/sandbox/client/api/root/post.py +0 -130
  275. blaxel/core/sandbox/client/api/root/put.py +0 -130
  276. /blaxel/core/{sandbox/client/api/root → client/api/mcphub}/__init__.py +0 -0
  277. {blaxel-0.2.33.dist-info → blaxel-0.2.35.dist-info}/WHEEL +0 -0
  278. {blaxel-0.2.33.dist-info → blaxel-0.2.35.dist-info}/licenses/LICENSE +0 -0
@@ -155,6 +155,7 @@ class SandboxCreateConfiguration:
155
155
  region: str | None = None,
156
156
  lifecycle: Union[SandboxLifecycle, Dict[str, Any]] | None = None,
157
157
  snapshot_enabled: bool | None = None,
158
+ labels: Dict[str, str] | None = None,
158
159
  ):
159
160
  self.name = name
160
161
  self.image = image
@@ -167,6 +168,7 @@ class SandboxCreateConfiguration:
167
168
  self.region = region
168
169
  self.lifecycle = lifecycle
169
170
  self.snapshot_enabled = snapshot_enabled
171
+ self.labels = labels
170
172
 
171
173
  @classmethod
172
174
  def from_dict(cls, data: Dict[str, Any]) -> "SandboxCreateConfiguration":
@@ -190,6 +192,7 @@ class SandboxCreateConfiguration:
190
192
  region=data.get("region"),
191
193
  lifecycle=lifecycle,
192
194
  snapshot_enabled=data.get("snapshot_enabled"),
195
+ labels=data.get("labels"),
193
196
  )
194
197
 
195
198
  def _normalize_ports(self) -> List[Port] | None:
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict, Type
1
+ from typing import Any, Dict, List, Type, Union
2
2
 
3
3
  from pydantic import BaseModel, Field, create_model
4
4
 
@@ -13,6 +13,20 @@ json_type_mapping: Dict[str, Type] = {
13
13
  }
14
14
 
15
15
 
16
+ def _get_python_type_from_json_type(json_type: Union[str, List[str]]) -> Type:
17
+ """Convert JSON Schema type to Python type.
18
+
19
+ Handles both simple types ("string") and nullable types (["string", "null"]).
20
+ """
21
+ if isinstance(json_type, list):
22
+ # Filter out "null" and get the primary type
23
+ non_null_types = [t for t in json_type if t != "null"]
24
+ if non_null_types:
25
+ return json_type_mapping.get(non_null_types[0], str)
26
+ return type(None)
27
+ return json_type_mapping.get(json_type, str)
28
+
29
+
16
30
  def create_model_from_json_schema(
17
31
  schema: Dict[str, Any], model_name: str = "DynamicModel"
18
32
  ) -> Type[BaseModel]:
@@ -32,7 +46,7 @@ def create_model_from_json_schema(
32
46
 
33
47
  for field_name, field_schema in properties.items():
34
48
  json_type = field_schema.get("type", "string")
35
- field_type = json_type_mapping.get(json_type, str)
49
+ field_type = _get_python_type_from_json_type(json_type)
36
50
  if field_name in required_fields:
37
51
  default_value = ...
38
52
  else:
@@ -1,5 +1,5 @@
1
1
  """Volume module for persistent storage management."""
2
2
 
3
- from .volume import VolumeCreateConfiguration, VolumeInstance
3
+ from .volume import SyncVolumeInstance, VolumeAPIError, VolumeCreateConfiguration, VolumeInstance
4
4
 
5
- __all__ = ["VolumeInstance", "VolumeCreateConfiguration"]
5
+ __all__ = ["VolumeInstance", "SyncVolumeInstance", "VolumeCreateConfiguration", "VolumeAPIError"]
@@ -1,15 +1,65 @@
1
1
  import uuid
2
- from typing import Dict, Union
2
+ from typing import Callable, Dict, List, Union
3
3
 
4
4
  from ..client.api.volumes.create_volume import asyncio as create_volume
5
+ from ..client.api.volumes.create_volume import sync as create_volume_sync
5
6
  from ..client.api.volumes.delete_volume import asyncio as delete_volume
7
+ from ..client.api.volumes.delete_volume import sync as delete_volume_sync
6
8
  from ..client.api.volumes.get_volume import asyncio as get_volume
9
+ from ..client.api.volumes.get_volume import sync as get_volume_sync
7
10
  from ..client.api.volumes.list_volumes import asyncio as list_volumes
11
+ from ..client.api.volumes.list_volumes import sync as list_volumes_sync
8
12
  from ..client.client import client
9
13
  from ..client.models import Metadata, Volume, VolumeSpec
14
+ from ..client.models.error import Error
10
15
  from ..client.types import UNSET
11
16
 
12
17
 
18
+ class VolumeAPIError(Exception):
19
+ """Exception raised when volume API returns an error."""
20
+
21
+ def __init__(self, message: str, status_code: int | None = None, code: str | None = None):
22
+ super().__init__(message)
23
+ self.status_code = status_code
24
+ self.code = code
25
+
26
+
27
+ class _AsyncDeleteDescriptor:
28
+ """Descriptor that provides both class-level and instance-level delete functionality."""
29
+
30
+ def __init__(self, delete_func: Callable):
31
+ self._delete_func = delete_func
32
+
33
+ def __get__(self, instance, owner):
34
+ if instance is None:
35
+ # Called on the class: VolumeInstance.delete("name")
36
+ return self._delete_func
37
+ else:
38
+ # Called on an instance: instance.delete()
39
+ async def instance_delete() -> Volume:
40
+ return await self._delete_func(instance.metadata.name or "")
41
+
42
+ return instance_delete
43
+
44
+
45
+ class _SyncDeleteDescriptor:
46
+ """Descriptor that provides both class-level and instance-level delete functionality (sync)."""
47
+
48
+ def __init__(self, delete_func: Callable):
49
+ self._delete_func = delete_func
50
+
51
+ def __get__(self, instance, owner):
52
+ if instance is None:
53
+ # Called on the class: SyncVolumeInstance.delete("name")
54
+ return self._delete_func
55
+ else:
56
+ # Called on an instance: instance.delete()
57
+ def instance_delete() -> Volume:
58
+ return self._delete_func(instance.metadata.name or "")
59
+
60
+ return instance_delete
61
+
62
+
13
63
  class VolumeCreateConfiguration:
14
64
  """Simplified configuration for creating volumes with default values."""
15
65
 
@@ -17,12 +67,14 @@ class VolumeCreateConfiguration:
17
67
  self,
18
68
  name: str | None = None,
19
69
  display_name: str | None = None,
70
+ labels: Dict[str, str] | None = None,
20
71
  size: int | None = None, # Size in MB
21
72
  region: str | None = None, # AWS region
22
73
  template: str | None = None, # Template
23
74
  ):
24
75
  self.name = name
25
76
  self.display_name = display_name
77
+ self.labels = labels
26
78
  self.size = size
27
79
  self.region = region
28
80
  self.template = template
@@ -32,6 +84,7 @@ class VolumeCreateConfiguration:
32
84
  return cls(
33
85
  name=data.get("name"),
34
86
  display_name=data.get("display_name"),
87
+ labels=data.get("labels"),
35
88
  size=data.get("size"),
36
89
  region=data.get("region"),
37
90
  template=data.get("template"),
@@ -90,6 +143,7 @@ class VolumeInstance:
90
143
  metadata=Metadata(
91
144
  name=config.name or default_name,
92
145
  display_name=config.display_name or config.name or default_name,
146
+ labels=config.labels,
93
147
  ),
94
148
  spec=VolumeSpec(
95
149
  size=config.size or default_size,
@@ -103,6 +157,7 @@ class VolumeInstance:
103
157
  metadata=Metadata(
104
158
  name=volume_config.name or default_name,
105
159
  display_name=volume_config.display_name or volume_config.name or default_name,
160
+ labels=volume_config.labels,
106
161
  ),
107
162
  spec=VolumeSpec(
108
163
  size=volume_config.size or default_size,
@@ -126,11 +181,19 @@ class VolumeInstance:
126
181
  volume.spec.size = default_size
127
182
 
128
183
  response = await create_volume(client=client, body=volume)
184
+ if isinstance(response, Error):
185
+ status_code = int(response.code) if response.code is not UNSET else None
186
+ message = response.message if response.message is not UNSET else response.error
187
+ raise VolumeAPIError(message, status_code=status_code, code=response.error)
129
188
  return cls(response)
130
189
 
131
190
  @classmethod
132
191
  async def get(cls, volume_name: str) -> "VolumeInstance":
133
192
  response = await get_volume(volume_name=volume_name, client=client)
193
+ if isinstance(response, Error):
194
+ status_code = int(response.code) if response.code is not UNSET else None
195
+ message = response.message if response.message is not UNSET else response.error
196
+ raise VolumeAPIError(message, status_code=status_code, code=response.error)
134
197
  return cls(response)
135
198
 
136
199
  @classmethod
@@ -138,11 +201,6 @@ class VolumeInstance:
138
201
  response = await list_volumes(client=client)
139
202
  return [cls(volume) for volume in response or []]
140
203
 
141
- @classmethod
142
- async def delete(cls, volume_name: str) -> Volume:
143
- response = await delete_volume(volume_name=volume_name, client=client)
144
- return response
145
-
146
204
  @classmethod
147
205
  async def create_if_not_exists(
148
206
  cls, config: Union[VolumeCreateConfiguration, Volume, Dict[str, any]]
@@ -150,11 +208,9 @@ class VolumeInstance:
150
208
  """Create a volume if it doesn't exist, otherwise return existing."""
151
209
  try:
152
210
  return await cls.create(config)
153
- except Exception as e:
211
+ except VolumeAPIError as e:
154
212
  # Check if it's a 409 conflict error (volume already exists)
155
- if (hasattr(e, "status_code") and e.status_code == 409) or (
156
- hasattr(e, "code") and e.code in [409, "VOLUME_ALREADY_EXISTS"]
157
- ):
213
+ if e.status_code == 409 or e.code in ["409", "VOLUME_ALREADY_EXISTS"]:
158
214
  # Extract name from different configuration types
159
215
  if isinstance(config, VolumeCreateConfiguration):
160
216
  name = config.name
@@ -170,4 +226,164 @@ class VolumeInstance:
170
226
 
171
227
  volume_instance = await cls.get(name)
172
228
  return volume_instance
173
- raise e
229
+ raise
230
+
231
+
232
+ class SyncVolumeInstance:
233
+ """Synchronous volume instance for managing persistent storage."""
234
+
235
+ def __init__(self, volume: Volume):
236
+ self.volume = volume
237
+
238
+ @property
239
+ def metadata(self):
240
+ return self.volume.metadata
241
+
242
+ @property
243
+ def spec(self):
244
+ return self.volume.spec
245
+
246
+ @property
247
+ def status(self):
248
+ return self.volume.status
249
+
250
+ @property
251
+ def name(self):
252
+ return self.volume.metadata.name if self.volume.metadata else None
253
+
254
+ @property
255
+ def display_name(self):
256
+ return self.volume.metadata.display_name if self.volume.metadata else None
257
+
258
+ @property
259
+ def size(self):
260
+ return self.volume.spec.size if self.volume.spec else None
261
+
262
+ @property
263
+ def region(self):
264
+ return self.volume.spec.region if self.volume.spec else None
265
+
266
+ @property
267
+ def template(self):
268
+ return self.volume.spec.template if self.volume.spec else None
269
+
270
+ @classmethod
271
+ def create(
272
+ cls, config: Union[VolumeCreateConfiguration, Volume, Dict[str, any]]
273
+ ) -> "SyncVolumeInstance":
274
+ """Create a new volume synchronously."""
275
+ # Generate default values
276
+ default_name = f"volume-{uuid.uuid4().hex[:8]}"
277
+ default_size = 1024 # 1GB in MB
278
+
279
+ # Handle different configuration types
280
+ if isinstance(config, Volume):
281
+ volume = config
282
+ elif isinstance(config, VolumeCreateConfiguration):
283
+ volume = Volume(
284
+ metadata=Metadata(
285
+ name=config.name or default_name,
286
+ display_name=config.display_name or config.name or default_name,
287
+ labels=config.labels,
288
+ ),
289
+ spec=VolumeSpec(
290
+ size=config.size or default_size,
291
+ region=config.region or UNSET,
292
+ template=config.template or UNSET,
293
+ ),
294
+ )
295
+ elif isinstance(config, dict):
296
+ volume_config = VolumeCreateConfiguration.from_dict(config)
297
+ volume = Volume(
298
+ metadata=Metadata(
299
+ name=volume_config.name or default_name,
300
+ display_name=volume_config.display_name or volume_config.name or default_name,
301
+ labels=volume_config.labels,
302
+ ),
303
+ spec=VolumeSpec(
304
+ size=volume_config.size or default_size,
305
+ region=volume_config.region or UNSET,
306
+ template=volume_config.template or UNSET,
307
+ ),
308
+ )
309
+ else:
310
+ raise ValueError(
311
+ f"Invalid config type: {type(config)}. Expected VolumeCreateConfiguration, Volume, or dict."
312
+ )
313
+
314
+ # Ensure required fields have defaults
315
+ if not volume.metadata:
316
+ volume.metadata = Metadata(name=default_name)
317
+ if not volume.metadata.name:
318
+ volume.metadata.name = default_name
319
+ if not volume.spec:
320
+ volume.spec = VolumeSpec(size=default_size)
321
+ if not volume.spec.size:
322
+ volume.spec.size = default_size
323
+
324
+ response = create_volume_sync(client=client, body=volume)
325
+ if isinstance(response, Error):
326
+ status_code = int(response.code) if response.code is not UNSET else None
327
+ message = response.message if response.message is not UNSET else response.error
328
+ raise VolumeAPIError(message, status_code=status_code, code=response.error)
329
+ return cls(response)
330
+
331
+ @classmethod
332
+ def get(cls, volume_name: str) -> "SyncVolumeInstance":
333
+ """Get a volume by name synchronously."""
334
+ response = get_volume_sync(volume_name=volume_name, client=client)
335
+ if isinstance(response, Error):
336
+ status_code = int(response.code) if response.code is not UNSET else None
337
+ message = response.message if response.message is not UNSET else response.error
338
+ raise VolumeAPIError(message, status_code=status_code, code=response.error)
339
+ return cls(response)
340
+
341
+ @classmethod
342
+ def list(cls) -> List["SyncVolumeInstance"]:
343
+ """List all volumes synchronously."""
344
+ response = list_volumes_sync(client=client)
345
+ return [cls(volume) for volume in response or []]
346
+
347
+ @classmethod
348
+ def create_if_not_exists(
349
+ cls, config: Union[VolumeCreateConfiguration, Volume, Dict[str, any]]
350
+ ) -> "SyncVolumeInstance":
351
+ """Create a volume if it doesn't exist, otherwise return existing."""
352
+ try:
353
+ return cls.create(config)
354
+ except VolumeAPIError as e:
355
+ # Check if it's a 409 conflict error (volume already exists)
356
+ if e.status_code == 409 or e.code in ["409", "VOLUME_ALREADY_EXISTS"]:
357
+ # Extract name from different configuration types
358
+ if isinstance(config, VolumeCreateConfiguration):
359
+ name = config.name
360
+ elif isinstance(config, dict):
361
+ name = config.get("name")
362
+ elif isinstance(config, Volume):
363
+ name = config.metadata.name if config.metadata else None
364
+ else:
365
+ name = None
366
+
367
+ if not name:
368
+ raise ValueError("Volume name is required")
369
+
370
+ volume_instance = cls.get(name)
371
+ return volume_instance
372
+ raise
373
+
374
+
375
+ async def _delete_volume_by_name(volume_name: str) -> Volume:
376
+ """Delete a volume by name (async)."""
377
+ response = await delete_volume(volume_name=volume_name, client=client)
378
+ return response
379
+
380
+
381
+ def _delete_volume_by_name_sync(volume_name: str) -> Volume:
382
+ """Delete a volume by name (sync)."""
383
+ response = delete_volume_sync(volume_name=volume_name, client=client)
384
+ return response
385
+
386
+
387
+ # Assign the delete descriptors to support both class-level and instance-level calls
388
+ VolumeInstance.delete = _AsyncDeleteDescriptor(_delete_volume_by_name)
389
+ SyncVolumeInstance.delete = _SyncDeleteDescriptor(_delete_volume_by_name_sync)
blaxel/langgraph/tools.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Any
1
+ from typing import Any, Dict
2
2
 
3
3
  from langchain_core.tools import StructuredTool
4
4
  from mcp.types import (
@@ -14,6 +14,35 @@ from blaxel.core.tools.types import Tool, ToolException
14
14
  NonTextContent = ImageContent | EmbeddedResource
15
15
 
16
16
 
17
+ def _clean_schema_for_openai(schema: Dict[str, Any]) -> Dict[str, Any]:
18
+ """Clean JSON schema to be compatible with OpenAI function calling.
19
+
20
+ OpenAI requires object schemas to have a 'properties' field, even if empty.
21
+ This function ensures the schema is properly formatted.
22
+ """
23
+ if not isinstance(schema, dict):
24
+ return schema
25
+
26
+ cleaned = schema.copy()
27
+
28
+ # Ensure object type schemas have properties
29
+ if cleaned.get("type") == "object":
30
+ if "properties" not in cleaned:
31
+ cleaned["properties"] = {}
32
+ if "required" not in cleaned:
33
+ cleaned["required"] = []
34
+
35
+ # Recursively clean nested schemas
36
+ if "properties" in cleaned:
37
+ cleaned["properties"] = {
38
+ k: _clean_schema_for_openai(v) for k, v in cleaned["properties"].items()
39
+ }
40
+ if "items" in cleaned and isinstance(cleaned["items"], dict):
41
+ cleaned["items"] = _clean_schema_for_openai(cleaned["items"])
42
+
43
+ return cleaned
44
+
45
+
17
46
  def get_langchain_tool(tool: Tool) -> StructuredTool:
18
47
  async def langchain_coroutine(
19
48
  **arguments: dict[str, Any],
@@ -36,10 +65,13 @@ def get_langchain_tool(tool: Tool) -> StructuredTool:
36
65
 
37
66
  return tool_content, non_text_contents or None
38
67
 
68
+ # Clean the schema to ensure OpenAI compatibility
69
+ cleaned_schema = _clean_schema_for_openai(tool.input_schema)
70
+
39
71
  return StructuredTool(
40
72
  name=tool.name,
41
73
  description=tool.description,
42
- args_schema=tool.input_schema,
74
+ args_schema=cleaned_schema,
43
75
  coroutine=langchain_coroutine,
44
76
  sync_coroutine=tool.sync_coroutine,
45
77
  )
blaxel/openai/tools.py CHANGED
@@ -7,6 +7,36 @@ from blaxel.core.tools import bl_tools as bl_tools_core
7
7
  from blaxel.core.tools.types import Tool
8
8
 
9
9
 
10
+ def _clean_schema_for_openai(schema: dict) -> dict:
11
+ """Clean JSON schema to be compatible with OpenAI agents SDK.
12
+
13
+ OpenAI agents SDK doesn't allow additionalProperties on object types.
14
+ This function recursively removes additionalProperties and $schema fields.
15
+ """
16
+ if not isinstance(schema, dict):
17
+ return schema
18
+
19
+ cleaned_schema = schema.copy()
20
+
21
+ # Remove $schema and additionalProperties at current level
22
+ if "$schema" in cleaned_schema:
23
+ del cleaned_schema["$schema"]
24
+ if "additionalProperties" in cleaned_schema:
25
+ del cleaned_schema["additionalProperties"]
26
+
27
+ # Recursively clean properties if they exist
28
+ if "properties" in cleaned_schema:
29
+ cleaned_schema["properties"] = {
30
+ k: _clean_schema_for_openai(v) for k, v in cleaned_schema["properties"].items()
31
+ }
32
+
33
+ # Recursively clean items for array types
34
+ if "items" in cleaned_schema:
35
+ cleaned_schema["items"] = _clean_schema_for_openai(cleaned_schema["items"])
36
+
37
+ return cleaned_schema
38
+
39
+
10
40
  def get_openai_tool(tool: Tool) -> FunctionTool:
11
41
  async def openai_coroutine(
12
42
  _: RunContextWrapper,
@@ -15,10 +45,12 @@ def get_openai_tool(tool: Tool) -> FunctionTool:
15
45
  result = await tool.coroutine(**json.loads(arguments))
16
46
  return result
17
47
 
48
+ cleaned_schema = _clean_schema_for_openai(tool.input_schema)
49
+
18
50
  return FunctionTool(
19
51
  name=tool.name,
20
52
  description=tool.description,
21
- params_json_schema=tool.input_schema,
53
+ params_json_schema=cleaned_schema,
22
54
  on_invoke_tool=openai_coroutine,
23
55
  )
24
56
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blaxel
3
- Version: 0.2.33
3
+ Version: 0.2.35
4
4
  Summary: Blaxel - AI development platform SDK
5
5
  Project-URL: Homepage, https://blaxel.ai
6
6
  Project-URL: Documentation, https://docs.blaxel.ai
@@ -23,7 +23,7 @@ Provides-Extra: all
23
23
  Requires-Dist: crewai==0.159.0; extra == 'all'
24
24
  Requires-Dist: google-adk>=1.4.0; extra == 'all'
25
25
  Requires-Dist: langchain-anthropic>=0.3.10; extra == 'all'
26
- Requires-Dist: langchain-cerebras>=0.5.0; extra == 'all'
26
+ Requires-Dist: langchain-cerebras<0.6.0,>=0.5.0; extra == 'all'
27
27
  Requires-Dist: langchain-cohere>=0.4.3; extra == 'all'
28
28
  Requires-Dist: langchain-community<0.4.0,>=0.3.3; extra == 'all'
29
29
  Requires-Dist: langchain-core<0.4.0,>=0.3.13; extra == 'all'
@@ -74,7 +74,7 @@ Requires-Dist: google-adk>=1.4.0; extra == 'googleadk'
74
74
  Requires-Dist: litellm>=1.63.11; extra == 'googleadk'
75
75
  Provides-Extra: langgraph
76
76
  Requires-Dist: langchain-anthropic>=0.3.10; extra == 'langgraph'
77
- Requires-Dist: langchain-cerebras>=0.5.0; extra == 'langgraph'
77
+ Requires-Dist: langchain-cerebras<0.6.0,>=0.5.0; extra == 'langgraph'
78
78
  Requires-Dist: langchain-cohere>=0.4.3; extra == 'langgraph'
79
79
  Requires-Dist: langchain-community<0.4.0,>=0.3.3; extra == 'langgraph'
80
80
  Requires-Dist: langchain-core<0.4.0,>=0.3.13; extra == 'langgraph'