Deploy 2026-05-04
Browse filesCo-authored-by: OpenAI Codex <codex@openai.com>
agent/tools/sandbox_client.py
CHANGED
|
@@ -66,6 +66,7 @@ WAIT_TIMEOUT = 600
|
|
| 66 |
WAIT_INTERVAL = 5
|
| 67 |
API_WAIT_TIMEOUT = 180
|
| 68 |
HARDWARE_REQUEST_TIMEOUT = 60
|
|
|
|
| 69 |
|
| 70 |
|
| 71 |
def _is_transient_space_visibility_error(error: Exception) -> bool:
|
|
@@ -612,7 +613,7 @@ class Sandbox:
|
|
| 612 |
*,
|
| 613 |
name: str | None = None,
|
| 614 |
template: str = TEMPLATE_SPACE,
|
| 615 |
-
hardware: str =
|
| 616 |
private: bool = True,
|
| 617 |
sleep_time: int | None = None,
|
| 618 |
token: str | None = None,
|
|
@@ -678,18 +679,24 @@ class Sandbox:
|
|
| 678 |
|
| 679 |
_check_cancel()
|
| 680 |
|
| 681 |
-
#
|
| 682 |
-
#
|
| 683 |
-
#
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
|
| 687 |
-
|
| 688 |
-
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
|
| 692 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 693 |
|
| 694 |
# Inject secrets BEFORE uploading server files (which triggers rebuild).
|
| 695 |
# Secrets added after a Space is running aren't available until restart,
|
|
|
|
| 66 |
WAIT_INTERVAL = 5
|
| 67 |
API_WAIT_TIMEOUT = 180
|
| 68 |
HARDWARE_REQUEST_TIMEOUT = 60
|
| 69 |
+
CPU_BASIC_HARDWARE = "cpu-basic"
|
| 70 |
|
| 71 |
|
| 72 |
def _is_transient_space_visibility_error(error: Exception) -> bool:
|
|
|
|
| 613 |
*,
|
| 614 |
name: str | None = None,
|
| 615 |
template: str = TEMPLATE_SPACE,
|
| 616 |
+
hardware: str = CPU_BASIC_HARDWARE,
|
| 617 |
private: bool = True,
|
| 618 |
sleep_time: int | None = None,
|
| 619 |
token: str | None = None,
|
|
|
|
| 679 |
|
| 680 |
_check_cancel()
|
| 681 |
|
| 682 |
+
# ``duplicate_space`` already receives the target hardware. The extra
|
| 683 |
+
# /hardware call is useful for paid tiers, but hosted OAuth tokens can
|
| 684 |
+
# 401 on that endpoint for a fresh private Space even after duplication
|
| 685 |
+
# succeeds. Avoid the redundant call for default CPU sandboxes when no
|
| 686 |
+
# auto-sleep timer is requested; with sleep_time set, the hardware
|
| 687 |
+
# endpoint is still needed to configure auto-sleep.
|
| 688 |
+
if hardware == CPU_BASIC_HARDWARE and sleep_time is None:
|
| 689 |
+
_log(f"Using duplicated Space hardware: {hardware}")
|
| 690 |
+
else:
|
| 691 |
+
_request_space_hardware_with_retry(
|
| 692 |
+
api,
|
| 693 |
+
space_id,
|
| 694 |
+
hardware=hardware,
|
| 695 |
+
sleep_time=sleep_time,
|
| 696 |
+
log=_log,
|
| 697 |
+
check_cancel=_check_cancel,
|
| 698 |
+
)
|
| 699 |
+
_log(f"Requested hardware: {hardware}")
|
| 700 |
|
| 701 |
# Inject secrets BEFORE uploading server files (which triggers rebuild).
|
| 702 |
# Secrets added after a Space is running aren't available until restart,
|
tests/unit/test_sandbox_private_spaces.py
CHANGED
|
@@ -43,7 +43,8 @@ def test_sandbox_client_defaults_to_private_spaces(monkeypatch):
|
|
| 43 |
Sandbox.create(owner="alice", token="hf-token", log=lambda msg: None)
|
| 44 |
|
| 45 |
assert duplicate_kwargs["private"] is True
|
| 46 |
-
assert
|
|
|
|
| 47 |
|
| 48 |
|
| 49 |
def test_sandbox_client_retries_transient_runtime_404(monkeypatch):
|
|
@@ -124,7 +125,7 @@ def test_sandbox_client_retries_transient_hardware_401(monkeypatch):
|
|
| 124 |
pass
|
| 125 |
|
| 126 |
def get_space_runtime(self, space_id):
|
| 127 |
-
return SimpleNamespace(stage="RUNNING", hardware="
|
| 128 |
|
| 129 |
monkeypatch.setattr(sandbox_client, "HfApi", FakeApi)
|
| 130 |
monkeypatch.setattr(sandbox_client.time, "sleep", lambda seconds: None)
|
|
@@ -135,7 +136,12 @@ def test_sandbox_client_retries_transient_hardware_401(monkeypatch):
|
|
| 135 |
)
|
| 136 |
monkeypatch.setattr(Sandbox, "_wait_for_api", lambda self, *args, **kwargs: None)
|
| 137 |
|
| 138 |
-
sandbox = Sandbox.create(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
|
| 140 |
assert sandbox.space_id.startswith("alice/sandbox-")
|
| 141 |
assert hardware_calls == 2
|
|
|
|
| 43 |
Sandbox.create(owner="alice", token="hf-token", log=lambda msg: None)
|
| 44 |
|
| 45 |
assert duplicate_kwargs["private"] is True
|
| 46 |
+
assert duplicate_kwargs["hardware"] == "cpu-basic"
|
| 47 |
+
assert requested_hardware == []
|
| 48 |
|
| 49 |
|
| 50 |
def test_sandbox_client_retries_transient_runtime_404(monkeypatch):
|
|
|
|
| 125 |
pass
|
| 126 |
|
| 127 |
def get_space_runtime(self, space_id):
|
| 128 |
+
return SimpleNamespace(stage="RUNNING", hardware="t4-small")
|
| 129 |
|
| 130 |
monkeypatch.setattr(sandbox_client, "HfApi", FakeApi)
|
| 131 |
monkeypatch.setattr(sandbox_client.time, "sleep", lambda seconds: None)
|
|
|
|
| 136 |
)
|
| 137 |
monkeypatch.setattr(Sandbox, "_wait_for_api", lambda self, *args, **kwargs: None)
|
| 138 |
|
| 139 |
+
sandbox = Sandbox.create(
|
| 140 |
+
owner="alice",
|
| 141 |
+
token="hf-token",
|
| 142 |
+
hardware="t4-small",
|
| 143 |
+
log=logs.append,
|
| 144 |
+
)
|
| 145 |
|
| 146 |
assert sandbox.space_id.startswith("alice/sandbox-")
|
| 147 |
assert hardware_calls == 2
|