blaxel 0.2.8__py3-none-any.whl → 0.2.9__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.
@@ -8,6 +8,7 @@ from .sandbox import (
8
8
  from .types import (
9
9
  CopyResponse,
10
10
  ProcessRequestWithLog,
11
+ ProcessResponseWithLog,
11
12
  SandboxConfiguration,
12
13
  SandboxCreateConfiguration,
13
14
  SandboxFilesystemFile,
@@ -30,4 +31,5 @@ __all__ = [
30
31
  "SandboxPreviews",
31
32
  "SandboxProcess",
32
33
  "ProcessRequestWithLog",
34
+ "ProcessResponseWithLog",
33
35
  ]
@@ -7,7 +7,7 @@ from ..common.settings import settings
7
7
  from .action import SandboxAction
8
8
  from .client.models import ProcessResponse, SuccessResponse
9
9
  from .client.models.process_request import ProcessRequest
10
- from .types import ProcessRequestWithLog, SandboxConfiguration
10
+ from .types import ProcessRequestWithLog, ProcessResponseWithLog, SandboxConfiguration
11
11
 
12
12
 
13
13
  class SandboxProcess(SandboxAction):
@@ -77,7 +77,8 @@ class SandboxProcess(SandboxAction):
77
77
 
78
78
  async def exec(
79
79
  self, process: Union[ProcessRequest, ProcessRequestWithLog, Dict[str, Any]]
80
- ) -> ProcessResponse:
80
+ ) -> Union[ProcessResponse, ProcessResponseWithLog]:
81
+ """Execute a process in the sandbox."""
81
82
  on_log = None
82
83
  if isinstance(process, ProcessRequestWithLog):
83
84
  on_log = process.on_log
@@ -95,7 +96,6 @@ class SandboxProcess(SandboxAction):
95
96
  # Always start process without wait_for_completion to avoid server-side blocking
96
97
  if should_wait_for_completion and on_log is not None:
97
98
  process.wait_for_completion = False
98
-
99
99
  async with self.get_client() as client_instance:
100
100
  response = await client_instance.post("/process", json=process.to_dict())
101
101
  # Parse JSON response only once, with better error handling
@@ -118,7 +118,7 @@ class SandboxProcess(SandboxAction):
118
118
  stream_control = self.stream_logs(result.pid, {"on_log": on_log})
119
119
  try:
120
120
  # Wait for process completion
121
- result = await self.wait(result.pid, interval=50)
121
+ result = await self.wait(result.pid, interval=500, max_wait=1000 * 60 * 60)
122
122
  finally:
123
123
  # Clean up log streaming
124
124
  if stream_control:
@@ -126,8 +126,11 @@ class SandboxProcess(SandboxAction):
126
126
  else:
127
127
  # For non-blocking execution, set up log streaming immediately if requested
128
128
  if on_log is not None:
129
- stream = self.stream_logs(result.pid, {"on_log": on_log})
130
- result.additional_properties["close"] = stream["close"]
129
+ stream_control = self.stream_logs(result.pid, {"on_log": on_log})
130
+ return ProcessResponseWithLog(
131
+ result, lambda: stream_control["close"]() if stream_control else None
132
+ )
133
+
131
134
  return result
132
135
 
133
136
  async def wait(
@@ -47,7 +47,7 @@ class SandboxInstance:
47
47
  def spec(self):
48
48
  return self.sandbox.spec
49
49
 
50
- async def wait(self, max_wait: int = 60000, interval: int = 1000) -> None:
50
+ async def wait(self, max_wait: int = 60000, interval: int = 1000) -> "SandboxInstance":
51
51
  start_time = time.time() * 1000 # Convert to milliseconds
52
52
  while self.sandbox.status != "DEPLOYED":
53
53
  await asyncio.sleep(interval / 1000) # Convert to seconds
@@ -68,6 +68,17 @@ class SandboxInstance:
68
68
  if (time.time() * 1000) - start_time > max_wait:
69
69
  raise Exception("Sandbox did not deploy in time")
70
70
 
71
+ if self.sandbox.status == "DEPLOYED":
72
+ try:
73
+ # This is a hack for sometime receiving a 502,
74
+ # need to remove this once we have a better way to handle this
75
+ await self.fs.ls("/")
76
+ except:
77
+ # pass
78
+ pass
79
+
80
+ return self
81
+
71
82
  @classmethod
72
83
  async def create(
73
84
  cls, sandbox: Union[Sandbox, SandboxCreateConfiguration, Dict[str, Any], None] = None
@@ -109,7 +120,9 @@ class SandboxInstance:
109
120
  sandbox = Sandbox(
110
121
  metadata=Metadata(name=name),
111
122
  spec=SandboxSpec(
112
- runtime=Runtime(image=image, memory=memory, ports=ports, envs=envs, generation="mk3")
123
+ runtime=Runtime(
124
+ image=image, memory=memory, ports=ports, envs=envs, generation="mk3"
125
+ )
113
126
  ),
114
127
  )
115
128
  else:
@@ -6,6 +6,7 @@ from attrs import define as _attrs_define
6
6
  from ..client.models import Port, Sandbox
7
7
  from ..client.types import UNSET
8
8
  from .client.models.process_request import ProcessRequest
9
+ from .client.models.process_response import ProcessResponse
9
10
 
10
11
 
11
12
  class SessionCreateOptions:
@@ -182,3 +183,26 @@ class SandboxCreateConfiguration:
182
183
  @_attrs_define
183
184
  class ProcessRequestWithLog(ProcessRequest):
184
185
  on_log: Callable[[str], None] = None
186
+
187
+
188
+ class ProcessResponseWithLog:
189
+ """A process response with additional close functionality for stream management."""
190
+
191
+ def __init__(self, process_response: ProcessResponse, close_func: Callable[[], None]):
192
+ self._process_response = process_response
193
+ self._close_func = close_func
194
+
195
+ def close(self) -> None:
196
+ """Close the log stream without terminating the process."""
197
+ self._close_func()
198
+
199
+ def __getattr__(self, name: str) -> Any:
200
+ """Delegate attribute access to the underlying ProcessResponse."""
201
+ return getattr(self._process_response, name)
202
+
203
+ def __setattr__(self, name: str, value: Any) -> None:
204
+ """Handle setting attributes, preserving special attributes."""
205
+ if name.startswith("_") or name == "close":
206
+ super().__setattr__(name, value)
207
+ else:
208
+ setattr(self._process_response, name, value)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blaxel
3
- Version: 0.2.8
3
+ Version: 0.2.9
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
@@ -304,15 +304,15 @@ blaxel/core/mcp/__init__.py,sha256=5VjkiQFb1QWW5QKRgwPHARlxZJ9Xqaz0diJTpM8LLF0,1
304
304
  blaxel/core/mcp/client.py,sha256=aK3wSnsO8DmT1BZqw4eiCMF71Jwvni6Qga0DhPP806Y,5437
305
305
  blaxel/core/mcp/server.py,sha256=tXySGZKgK3IllYOzYOecp58BixKBkmAIvQp_4nSM_Ww,5919
306
306
  blaxel/core/models/__init__.py,sha256=HbRDsMnUFHkPC-MMkzPXh4mUqkVjqO6p3j7m00N_XSo,1722
307
- blaxel/core/sandbox/__init__.py,sha256=fyxxdPhVIkI1FwSI4tHG1DH0W4sYuNaWvVxO7_due2U,673
307
+ blaxel/core/sandbox/__init__.py,sha256=sDsT9d6_bQvB-7dR6ZxODYrkzqQdQznhtXTfdqcSRLI,731
308
308
  blaxel/core/sandbox/action.py,sha256=9Zjkco7YkLzBThD3N2Hr5SpeEiqU_-Ktk8HlKpkpiAg,2802
309
309
  blaxel/core/sandbox/filesystem.py,sha256=dyIvDdlPZO0ijD6mXXX8Yl0t75VijQ6_uMz_9rJd-_4,11317
310
310
  blaxel/core/sandbox/network.py,sha256=P5jLd4AAg1zgyIK4qGWvZaDZ5BzIcxRx2ffz_JLsLMI,357
311
311
  blaxel/core/sandbox/preview.py,sha256=g0uVbMsIi8gRXmmyOfSyqm1qO4Cv6rsq92fs_k884dY,6120
312
- blaxel/core/sandbox/process.py,sha256=g13yig8pvIwHtABFXwxWKSd5K4KYhEQQcGumME9I-GA,8609
313
- blaxel/core/sandbox/sandbox.py,sha256=y6mV2-i6TsSZGQsvBBGptR514OfgvhV0hXBMEsOBcsY,8648
312
+ blaxel/core/sandbox/process.py,sha256=95fu9WU-CCdJcnHV8tYErkKTv5YoRcOqO68BH9NopvQ,8837
313
+ blaxel/core/sandbox/sandbox.py,sha256=J7wGqtelXZFk5XV99qTWSWS0TMHjo2C5qqZ7EYrD8j8,9036
314
314
  blaxel/core/sandbox/session.py,sha256=3PfoekfdVzLYttsmKeK3MePhuprjqv_FDyVQTQME0OE,5277
315
- blaxel/core/sandbox/types.py,sha256=E-IjEetAMhFDz-mDizFlifCrXoIeJnkbxolQS3p8TKQ,5936
315
+ blaxel/core/sandbox/types.py,sha256=hf3WCk8P2e87jT4cJGuz3aZCihffun8bVb2k91YfVBU,6894
316
316
  blaxel/core/sandbox/client/__init__.py,sha256=N26bD5o1jsTb48oExow6Rgivd8ylaU9jaWZfZsVilP8,128
317
317
  blaxel/core/sandbox/client/client.py,sha256=tcP8cJ4Q3dV9aB3yQ01dDXO-ekfsa3WGGFz4DQAEf8I,7079
318
318
  blaxel/core/sandbox/client/errors.py,sha256=gO8GBmKqmSNgAg-E5oT-oOyxztvp7V_6XG7OUTT15q0,546
@@ -402,7 +402,7 @@ blaxel/telemetry/instrumentation/map.py,sha256=PCzZJj39yiYVYJrxLBNP-NW-tjjYyTijw
402
402
  blaxel/telemetry/instrumentation/utils.py,sha256=KInMYZH-mu9_wvetmf0EmgrfN3Sw8IWk2Y95v2u90_U,1901
403
403
  blaxel/telemetry/log/log.py,sha256=RvQByRjZMoP_dRaAZu8oK6DTegsHs-xV4W-UIqis6CA,2461
404
404
  blaxel/telemetry/log/logger.py,sha256=NPAS3g82ryROjvc_DEZaTIfrcehoLEZoP-JkLxADxc0,4113
405
- blaxel-0.2.8.dist-info/METADATA,sha256=rrITjLt4KlYbQ4-wpWGd1hhxIvwkYjLyusPvuqflfgA,9875
406
- blaxel-0.2.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
407
- blaxel-0.2.8.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
408
- blaxel-0.2.8.dist-info/RECORD,,
405
+ blaxel-0.2.9.dist-info/METADATA,sha256=9bQ896L9Q_2VbaSu0vqkvYf_vJ8KAtnn4RKyOFFGlm4,9875
406
+ blaxel-0.2.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
407
+ blaxel-0.2.9.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
408
+ blaxel-0.2.9.dist-info/RECORD,,
File without changes