blaxel 0.2.32__py3-none-any.whl → 0.2.34__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.
@@ -1,15 +1,55 @@
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
10
14
  from ..client.types import UNSET
11
15
 
12
16
 
17
+ class _AsyncDeleteDescriptor:
18
+ """Descriptor that provides both class-level and instance-level delete functionality."""
19
+
20
+ def __init__(self, delete_func: Callable):
21
+ self._delete_func = delete_func
22
+
23
+ def __get__(self, instance, owner):
24
+ if instance is None:
25
+ # Called on the class: VolumeInstance.delete("name")
26
+ return self._delete_func
27
+ else:
28
+ # Called on an instance: instance.delete()
29
+ async def instance_delete() -> Volume:
30
+ return await self._delete_func(instance.metadata.name or "")
31
+
32
+ return instance_delete
33
+
34
+
35
+ class _SyncDeleteDescriptor:
36
+ """Descriptor that provides both class-level and instance-level delete functionality (sync)."""
37
+
38
+ def __init__(self, delete_func: Callable):
39
+ self._delete_func = delete_func
40
+
41
+ def __get__(self, instance, owner):
42
+ if instance is None:
43
+ # Called on the class: SyncVolumeInstance.delete("name")
44
+ return self._delete_func
45
+ else:
46
+ # Called on an instance: instance.delete()
47
+ def instance_delete() -> Volume:
48
+ return self._delete_func(instance.metadata.name or "")
49
+
50
+ return instance_delete
51
+
52
+
13
53
  class VolumeCreateConfiguration:
14
54
  """Simplified configuration for creating volumes with default values."""
15
55
 
@@ -17,12 +57,14 @@ class VolumeCreateConfiguration:
17
57
  self,
18
58
  name: str | None = None,
19
59
  display_name: str | None = None,
60
+ labels: Dict[str, str] | None = None,
20
61
  size: int | None = None, # Size in MB
21
62
  region: str | None = None, # AWS region
22
63
  template: str | None = None, # Template
23
64
  ):
24
65
  self.name = name
25
66
  self.display_name = display_name
67
+ self.labels = labels
26
68
  self.size = size
27
69
  self.region = region
28
70
  self.template = template
@@ -32,6 +74,7 @@ class VolumeCreateConfiguration:
32
74
  return cls(
33
75
  name=data.get("name"),
34
76
  display_name=data.get("display_name"),
77
+ labels=data.get("labels"),
35
78
  size=data.get("size"),
36
79
  region=data.get("region"),
37
80
  template=data.get("template"),
@@ -90,6 +133,7 @@ class VolumeInstance:
90
133
  metadata=Metadata(
91
134
  name=config.name or default_name,
92
135
  display_name=config.display_name or config.name or default_name,
136
+ labels=config.labels,
93
137
  ),
94
138
  spec=VolumeSpec(
95
139
  size=config.size or default_size,
@@ -103,6 +147,7 @@ class VolumeInstance:
103
147
  metadata=Metadata(
104
148
  name=volume_config.name or default_name,
105
149
  display_name=volume_config.display_name or volume_config.name or default_name,
150
+ labels=volume_config.labels,
106
151
  ),
107
152
  spec=VolumeSpec(
108
153
  size=volume_config.size or default_size,
@@ -138,11 +183,6 @@ class VolumeInstance:
138
183
  response = await list_volumes(client=client)
139
184
  return [cls(volume) for volume in response or []]
140
185
 
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
186
  @classmethod
147
187
  async def create_if_not_exists(
148
188
  cls, config: Union[VolumeCreateConfiguration, Volume, Dict[str, any]]
@@ -171,3 +211,157 @@ class VolumeInstance:
171
211
  volume_instance = await cls.get(name)
172
212
  return volume_instance
173
213
  raise e
214
+
215
+
216
+ class SyncVolumeInstance:
217
+ """Synchronous volume instance for managing persistent storage."""
218
+
219
+ def __init__(self, volume: Volume):
220
+ self.volume = volume
221
+
222
+ @property
223
+ def metadata(self):
224
+ return self.volume.metadata
225
+
226
+ @property
227
+ def spec(self):
228
+ return self.volume.spec
229
+
230
+ @property
231
+ def status(self):
232
+ return self.volume.status
233
+
234
+ @property
235
+ def name(self):
236
+ return self.volume.metadata.name if self.volume.metadata else None
237
+
238
+ @property
239
+ def display_name(self):
240
+ return self.volume.metadata.display_name if self.volume.metadata else None
241
+
242
+ @property
243
+ def size(self):
244
+ return self.volume.spec.size if self.volume.spec else None
245
+
246
+ @property
247
+ def region(self):
248
+ return self.volume.spec.region if self.volume.spec else None
249
+
250
+ @property
251
+ def template(self):
252
+ return self.volume.spec.template if self.volume.spec else None
253
+
254
+ @classmethod
255
+ def create(
256
+ cls, config: Union[VolumeCreateConfiguration, Volume, Dict[str, any]]
257
+ ) -> "SyncVolumeInstance":
258
+ """Create a new volume synchronously."""
259
+ # Generate default values
260
+ default_name = f"volume-{uuid.uuid4().hex[:8]}"
261
+ default_size = 1024 # 1GB in MB
262
+
263
+ # Handle different configuration types
264
+ if isinstance(config, Volume):
265
+ volume = config
266
+ elif isinstance(config, VolumeCreateConfiguration):
267
+ volume = Volume(
268
+ metadata=Metadata(
269
+ name=config.name or default_name,
270
+ display_name=config.display_name or config.name or default_name,
271
+ labels=config.labels,
272
+ ),
273
+ spec=VolumeSpec(
274
+ size=config.size or default_size,
275
+ region=config.region or UNSET,
276
+ template=config.template or UNSET,
277
+ ),
278
+ )
279
+ elif isinstance(config, dict):
280
+ volume_config = VolumeCreateConfiguration.from_dict(config)
281
+ volume = Volume(
282
+ metadata=Metadata(
283
+ name=volume_config.name or default_name,
284
+ display_name=volume_config.display_name or volume_config.name or default_name,
285
+ labels=volume_config.labels,
286
+ ),
287
+ spec=VolumeSpec(
288
+ size=volume_config.size or default_size,
289
+ region=volume_config.region or UNSET,
290
+ template=volume_config.template or UNSET,
291
+ ),
292
+ )
293
+ else:
294
+ raise ValueError(
295
+ f"Invalid config type: {type(config)}. Expected VolumeCreateConfiguration, Volume, or dict."
296
+ )
297
+
298
+ # Ensure required fields have defaults
299
+ if not volume.metadata:
300
+ volume.metadata = Metadata(name=default_name)
301
+ if not volume.metadata.name:
302
+ volume.metadata.name = default_name
303
+ if not volume.spec:
304
+ volume.spec = VolumeSpec(size=default_size)
305
+ if not volume.spec.size:
306
+ volume.spec.size = default_size
307
+
308
+ response = create_volume_sync(client=client, body=volume)
309
+ return cls(response)
310
+
311
+ @classmethod
312
+ def get(cls, volume_name: str) -> "SyncVolumeInstance":
313
+ """Get a volume by name synchronously."""
314
+ response = get_volume_sync(volume_name=volume_name, client=client)
315
+ return cls(response)
316
+
317
+ @classmethod
318
+ def list(cls) -> List["SyncVolumeInstance"]:
319
+ """List all volumes synchronously."""
320
+ response = list_volumes_sync(client=client)
321
+ return [cls(volume) for volume in response or []]
322
+
323
+ @classmethod
324
+ def create_if_not_exists(
325
+ cls, config: Union[VolumeCreateConfiguration, Volume, Dict[str, any]]
326
+ ) -> "SyncVolumeInstance":
327
+ """Create a volume if it doesn't exist, otherwise return existing."""
328
+ try:
329
+ return cls.create(config)
330
+ except Exception as e:
331
+ # Check if it's a 409 conflict error (volume already exists)
332
+ if (hasattr(e, "status_code") and e.status_code == 409) or (
333
+ hasattr(e, "code") and e.code in [409, "VOLUME_ALREADY_EXISTS"]
334
+ ):
335
+ # Extract name from different configuration types
336
+ if isinstance(config, VolumeCreateConfiguration):
337
+ name = config.name
338
+ elif isinstance(config, dict):
339
+ name = config.get("name")
340
+ elif isinstance(config, Volume):
341
+ name = config.metadata.name if config.metadata else None
342
+ else:
343
+ name = None
344
+
345
+ if not name:
346
+ raise ValueError("Volume name is required")
347
+
348
+ volume_instance = cls.get(name)
349
+ return volume_instance
350
+ raise e
351
+
352
+
353
+ async def _delete_volume_by_name(volume_name: str) -> Volume:
354
+ """Delete a volume by name (async)."""
355
+ response = await delete_volume(volume_name=volume_name, client=client)
356
+ return response
357
+
358
+
359
+ def _delete_volume_by_name_sync(volume_name: str) -> Volume:
360
+ """Delete a volume by name (sync)."""
361
+ response = delete_volume_sync(volume_name=volume_name, client=client)
362
+ return response
363
+
364
+
365
+ # Assign the delete descriptors to support both class-level and instance-level calls
366
+ VolumeInstance.delete = _AsyncDeleteDescriptor(_delete_volume_by_name)
367
+ SyncVolumeInstance.delete = _SyncDeleteDescriptor(_delete_volume_by_name_sync)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blaxel
3
- Version: 0.2.32
3
+ Version: 0.2.34
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
@@ -17,7 +17,6 @@ Requires-Dist: pyjwt>=2.0.0
17
17
  Requires-Dist: python-dateutil>=2.8.0
18
18
  Requires-Dist: pyyaml>=6.0.0
19
19
  Requires-Dist: requests>=2.32.3
20
- Requires-Dist: sentry-sdk>=2.46.0
21
20
  Requires-Dist: tomli>=2.2.1
22
21
  Requires-Dist: websockets<16.0.0
23
22
  Provides-Extra: all
@@ -1,5 +1,5 @@
1
- blaxel/__init__.py,sha256=DodOCaAPXa8pstbUXBPzRjID-7e_I2akWFDmtctRxkc,413
2
- blaxel/core/__init__.py,sha256=CKMC7TaCYdOdnwqcJCN9VjBbNg366coZUGTxI1mgFQQ,1710
1
+ blaxel/__init__.py,sha256=JFHDlTbbIPz2qG5ETm0mcQ1NX7AXph_tw6L5PVKLXzg,413
2
+ blaxel/core/__init__.py,sha256=CU0gXpVRbuQZNWoCJuuhZS0ZhXPEu0cg-3XzoYMrBm4,1756
3
3
  blaxel/core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  blaxel/core/agents/__init__.py,sha256=MJZga99lU8JWUUPHd4rmUfdo7ALwWgF7CQq95SfT2OI,4456
5
5
  blaxel/core/authentication/__init__.py,sha256=5uHv73xZ2aZRXzGHc1sP-XWqZUryfXxfC9dMX7-gd6o,3241
@@ -330,11 +330,12 @@ blaxel/core/client/models/workspace.py,sha256=3Z-bpTYukkbqW46B39Nljr4XZtAV41AqqK
330
330
  blaxel/core/client/models/workspace_labels.py,sha256=M_ZbpQeVNdgqWv2RmTYO7znBczUJ6zi5_XS1EDin3Gk,1253
331
331
  blaxel/core/client/models/workspace_runtime.py,sha256=Pdr_Q-ugs_5famziqGSyBzUkKQ6e-pAFRGza6GhppSA,1672
332
332
  blaxel/core/client/models/workspace_user.py,sha256=f28XZuP3V46y2YYbfiIiNbnIMpy3JINLwpy_mX9N1iU,3430
333
- blaxel/core/common/__init__.py,sha256=K5uIZe98NIjYkjMPdKvP5RuwzIfv1Obm6g3aWh2qjZ0,599
334
- blaxel/core/common/autoload.py,sha256=1v6-MwjifmPLnfFA11deNYxydSPTKIawc3nQGOb4l50,6375
333
+ blaxel/core/common/__init__.py,sha256=A69U94NslPOYjKvja0bN5z98eguJPC6UaGaVN2-FdRY,736
334
+ blaxel/core/common/autoload.py,sha256=Tq6cmmNcOwUy77ACWM-4Ap8DgRY6GWkAOJmIwugRAPU,1481
335
335
  blaxel/core/common/env.py,sha256=05Jm2mw0-KIgR7QaNVyvZPP91B9OlxlJ6mcx6Mqfji0,1234
336
336
  blaxel/core/common/internal.py,sha256=NDTFh9Duj84os8GkMXjGzn-UVS9zBDyLAcfPxIpoQGA,3218
337
337
  blaxel/core/common/logger.py,sha256=Jt0MCJgYDPq36rl7UyKRDJH76a-AwYdfggNeNYJt6N0,4779
338
+ blaxel/core/common/sentry.py,sha256=P_v1vWivlh-usXV1_JeJ603r1OHoWaR5jaYV2JJ6vDM,9759
338
339
  blaxel/core/common/settings.py,sha256=f7ZP8VpB8gKESptyb6S82gTHQsbmx6RcaAkDkxy2JpE,4599
339
340
  blaxel/core/common/webhook.py,sha256=N1f2bamP7wRyPyCfmAZKMdjeB3aQ6d6pcafHyVZKtPk,5330
340
341
  blaxel/core/jobs/__init__.py,sha256=LZTtkOwqUyMjRTdeLv4EXLbVhgebwvY95jd56IGO4MQ,17082
@@ -343,7 +344,7 @@ blaxel/core/mcp/client.py,sha256=EZ7l5w3bTXaD41nalHzM-byxfQK-JdcmQqxg3zGpVO4,550
343
344
  blaxel/core/mcp/server.py,sha256=edAztWBlukERw9-dzS2Sk96TP8R3-CSofY1CZDu19ZA,5967
344
345
  blaxel/core/models/__init__.py,sha256=ydz1txqIVyOhehItut-AOnLMnGp7AtCD2zku9gkvAsE,1722
345
346
  blaxel/core/sandbox/__init__.py,sha256=SQ9YH0I9ruAYscY_W75jqIhNbejbY_92xL7VqM083Mc,1345
346
- blaxel/core/sandbox/types.py,sha256=h_e0Zk_BNRjix1RRtomqVwdG64ViA34KwM4ZQIM5Bp0,12601
347
+ blaxel/core/sandbox/types.py,sha256=mi2fbD5vvPzgyDbmN1YA-LkFS6Iaq0dLtI4OxQDb16s,12817
347
348
  blaxel/core/sandbox/client/__init__.py,sha256=N26bD5o1jsTb48oExow6Rgivd8ylaU9jaWZfZsVilP8,128
348
349
  blaxel/core/sandbox/client/client.py,sha256=EGCYliUHCk4RyIBlydEZyVpc_VUiIIGPuu2E-xYeKFY,7074
349
350
  blaxel/core/sandbox/client/errors.py,sha256=gO8GBmKqmSNgAg-E5oT-oOyxztvp7V_6XG7OUTT15q0,546
@@ -438,8 +439,8 @@ blaxel/core/sandbox/default/filesystem.py,sha256=Gn1G3DpMmDXplUEpXVENXkjUDAEkWAK
438
439
  blaxel/core/sandbox/default/interpreter.py,sha256=TJSryQvq2rWedyhMU69tOlXVOF1iIpgIbV2z3_mF72E,11316
439
440
  blaxel/core/sandbox/default/network.py,sha256=3ZvrJB_9JdZrclNkwifZOIciz2OqzV0LQfbebjZXLIY,358
440
441
  blaxel/core/sandbox/default/preview.py,sha256=dV_xuu9Efop5TnzuFJPeLUZ7CEepuYkJedx01fDVMX4,6132
441
- blaxel/core/sandbox/default/process.py,sha256=hUdFxFD5w8Md827gVwRijhY55D4BQEbmxuLrqFrCC6I,11965
442
- blaxel/core/sandbox/default/sandbox.py,sha256=b641_MHILDdsz9ZkbSIoRAmbB_0grbd3C_SG5JOwe18,12576
442
+ blaxel/core/sandbox/default/process.py,sha256=7nI1wJXeZWoveBesC13wur-ghIjP5POZ38G8wqCdJTw,16983
443
+ blaxel/core/sandbox/default/sandbox.py,sha256=B7MGnVnelV4iOh1PSBWbMwO6Vq6KNNxpQQ42mMNgShU,13485
443
444
  blaxel/core/sandbox/default/session.py,sha256=XzVpPOH_az6T38Opp4Hmj3RIg7QCzA1l5wh1YDh7czc,5313
444
445
  blaxel/core/sandbox/sync/__init__.py,sha256=iqTRxQYbJyHTXoA4MHaigeXFxi9wtJ3o9XygZuFe3bM,372
445
446
  blaxel/core/sandbox/sync/action.py,sha256=9t8nCS9KX6SBflJmzK8dsen_bDim3zElBTebY6N6JQc,2136
@@ -448,14 +449,14 @@ blaxel/core/sandbox/sync/filesystem.py,sha256=FoxM9EJ5sXGysf-x22tbt9yrcbbpaunTD3
448
449
  blaxel/core/sandbox/sync/interpreter.py,sha256=5cAzwnt5BgnByGimagMBotjGW2vMAz4vutBBrrFV9-A,11062
449
450
  blaxel/core/sandbox/sync/network.py,sha256=QkCFKfFayvwL1J4JYwOuXPGlYQuX4J9Jj55Kf_kD-ig,283
450
451
  blaxel/core/sandbox/sync/preview.py,sha256=w3bC8iA3QecHiLkRvITmQ6LTT9Co_93G24QpZFgEQSE,6379
451
- blaxel/core/sandbox/sync/process.py,sha256=U4aYv-uRXDOKScz6Ets-QkwZZ1kdgR9ve04CkUaUkEU,9624
452
- blaxel/core/sandbox/sync/sandbox.py,sha256=jNqwMJXIpjC8Fs9nn-ujfSpFK2PgCvEePISKQKaMhH0,10515
452
+ blaxel/core/sandbox/sync/process.py,sha256=W-ZUM6VyFDxTmexHTQn9PI6iRc0QiB9JMOEq__r2bBA,14913
453
+ blaxel/core/sandbox/sync/sandbox.py,sha256=mz1_AJkoIBIgrAtX5pGn5h3lhmKI1jtKB75fMmoJ3UM,11418
453
454
  blaxel/core/sandbox/sync/session.py,sha256=e0CVbW2LBRYTwm4RL52S0UdNvhNfuFLo6AYE5hk9DH0,4931
454
455
  blaxel/core/tools/__init__.py,sha256=OK2TFqeXAIi6CC7xtL8fFl-4DvCB7jjihkhx6RTld_c,13147
455
456
  blaxel/core/tools/common.py,sha256=fjapOcJwFCS1Do9FCwqmdDTXpo1pZ-9vbfSjadJ9l8g,1321
456
457
  blaxel/core/tools/types.py,sha256=EXa-10iOOXvd8zB2IsTS0gWrgoC2hqbIv6iK5m6E5t8,693
457
- blaxel/core/volume/__init__.py,sha256=W7MLNeA5AvpvE1NwyTzWrkwypNaj6xTIbRmILtoaItE,177
458
- blaxel/core/volume/volume.py,sha256=10j4fnhsX9IDBSEI-KG5RrAZc2Q5bLnedy9-E_ToUyU,6068
458
+ blaxel/core/volume/__init__.py,sha256=YgEquO_GVxtcymSZoIJTEPvL8KeXtHIobNjGecEKtwM,219
459
+ blaxel/core/volume/volume.py,sha256=24ihurOCGfvadeWNvJCEI3OAK1UpLUWpYacALS8u0MI,13153
459
460
  blaxel/crewai/__init__.py,sha256=HdAZxRDgC2zW-O5xYzb_Xuysb66_fMykNJVPxlTmFnY,123
460
461
  blaxel/crewai/model.py,sha256=iR-40o8pGBFrktYNKcTaMdqbF_F1SFVavNOhhi2fW3w,1957
461
462
  blaxel/crewai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -500,7 +501,7 @@ blaxel/telemetry/instrumentation/map.py,sha256=PCzZJj39yiYVYJrxLBNP-NW-tjjYyTijw
500
501
  blaxel/telemetry/instrumentation/utils.py,sha256=FGyMY5ZE4f-0JdZpm_R_BCoKLJ18hftz8vsh7ftDwMk,1889
501
502
  blaxel/telemetry/log/log.py,sha256=vtzUIFIIj4MTTKUigILDYXN8NHHPOo44OaKukpyIjQg,2407
502
503
  blaxel/telemetry/log/logger.py,sha256=IcFWCd1yyWWGAjAd2i0pDYqpZHQ61pmcaQ7Kf4bC8lg,4150
503
- blaxel-0.2.32.dist-info/METADATA,sha256=yQQO_JdQpH1Ka7_HxoTU2he4DXnWLWh8--eD8LWzdZc,10108
504
- blaxel-0.2.32.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
505
- blaxel-0.2.32.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
506
- blaxel-0.2.32.dist-info/RECORD,,
504
+ blaxel-0.2.34.dist-info/METADATA,sha256=L6AhAF7TXWMZCy4FRqjnkHYUEOl-WBxbDO09pVzZdv0,10074
505
+ blaxel-0.2.34.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
506
+ blaxel-0.2.34.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
507
+ blaxel-0.2.34.dist-info/RECORD,,