diff --git a/agent/tools/sandbox_client.py b/agent/tools/sandbox_client.py index 1871d8fc..d1644fc9 100644 --- a/agent/tools/sandbox_client.py +++ b/agent/tools/sandbox_client.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # /// script # requires-python = ">=3.10" -# dependencies = ["huggingface_hub>=0.20.0", "httpx>=0.27.0"] +# dependencies = ["huggingface_hub>=1.12.0", "httpx>=0.27.0"] # /// """ Sandbox Tools — Agent-native primitives for HF Space dev-mode sandboxes. @@ -615,18 +615,19 @@ def _check_cancel(): kwargs = { "from_id": template, "to_id": space_id, + "repo_type": "space", "private": private, - "hardware": hardware, + "space_hardware": hardware, } if sleep_time is not None: - kwargs["sleep_time"] = sleep_time + kwargs["space_sleep_time"] = sleep_time - api.duplicate_space(**kwargs) + api.duplicate_repo(**kwargs) _log(f"Space created: https://huggingface.co/spaces/{space_id}") _check_cancel() - # ``duplicate_space`` sends hardware and sleepTimeSeconds in the + # ``duplicate_repo`` sends hardware and sleepTimeSeconds in the # initial create request. Avoid a second /hardware call: deployed HF # OAuth tokens can 401 on that endpoint for a just-created private # Space even though duplication itself succeeded. We rely on the diff --git a/tests/unit/test_sandbox_private_spaces.py b/tests/unit/test_sandbox_private_spaces.py index 31332ee3..b05b0ab2 100644 --- a/tests/unit/test_sandbox_private_spaces.py +++ b/tests/unit/test_sandbox_private_spaces.py @@ -13,6 +13,28 @@ def _fail_metadata_update(*args, **kwargs): raise AssertionError("sandbox creation should not update Space metadata") +def _capture_duplicate_repo_call( + captured, + *, + from_id, + to_id, + repo_type, + private, + space_hardware, + space_sleep_time=None, +): + captured.update( + { + "from_id": from_id, + "to_id": to_id, + "repo_type": repo_type, + "private": private, + "space_hardware": space_hardware, + "space_sleep_time": space_sleep_time, + } + ) + + def test_sandbox_client_defaults_to_private_spaces(monkeypatch): duplicate_kwargs = {} logs: list[str] = [] @@ -22,8 +44,25 @@ class FakeApi: def __init__(self, token=None): self.token = token - def duplicate_space(self, **kwargs): - duplicate_kwargs.update(kwargs) + def duplicate_repo( + self, + *, + from_id, + to_id, + repo_type, + private, + space_hardware, + space_sleep_time=None, + ): + _capture_duplicate_repo_call( + duplicate_kwargs, + from_id=from_id, + to_id=to_id, + repo_type=repo_type, + private=private, + space_hardware=space_hardware, + space_sleep_time=space_sleep_time, + ) def request_space_hardware(self, space_id, hardware, sleep_time=None): requested_hardware.append((space_id, hardware, sleep_time)) @@ -45,8 +84,9 @@ def get_space_runtime(self, space_id): Sandbox.create(owner="alice", token="hf-token", log=logs.append) + assert duplicate_kwargs["repo_type"] == "space" assert duplicate_kwargs["private"] is True - assert duplicate_kwargs["hardware"] == "cpu-basic" + assert duplicate_kwargs["space_hardware"] == "cpu-basic" assert requested_hardware == [] assert not any("sleep time" in log for log in logs) @@ -67,7 +107,16 @@ class FakeApi: def __init__(self, token=None): self.token = token - def duplicate_space(self, **kwargs): + def duplicate_repo( + self, + *, + from_id, + to_id, + repo_type, + private, + space_hardware, + space_sleep_time=None, + ): pass def request_space_hardware(self, space_id, hardware, sleep_time=None): @@ -107,8 +156,25 @@ class FakeApi: def __init__(self, token=None): self.token = token - def duplicate_space(self, **kwargs): - duplicate_kwargs.update(kwargs) + def duplicate_repo( + self, + *, + from_id, + to_id, + repo_type, + private, + space_hardware, + space_sleep_time=None, + ): + _capture_duplicate_repo_call( + duplicate_kwargs, + from_id=from_id, + to_id=to_id, + repo_type=repo_type, + private=private, + space_hardware=space_hardware, + space_sleep_time=space_sleep_time, + ) def request_space_hardware(self, space_id, hardware, sleep_time=None): requested_hardware.append((space_id, hardware, sleep_time)) @@ -137,8 +203,9 @@ def get_space_runtime(self, space_id): ) assert sandbox.space_id.startswith("alice/sandbox-") - assert duplicate_kwargs["hardware"] == "t4-small" - assert duplicate_kwargs["sleep_time"] == 2700 + assert duplicate_kwargs["repo_type"] == "space" + assert duplicate_kwargs["space_hardware"] == "t4-small" + assert duplicate_kwargs["space_sleep_time"] == 2700 assert requested_hardware == [] assert "Using duplicated Space hardware: t4-small" in logs assert "Using duplicated Space sleep time: 2700s" in logs @@ -153,8 +220,25 @@ class FakeApi: def __init__(self, token=None): self.token = token - def duplicate_space(self, **kwargs): - duplicate_kwargs.update(kwargs) + def duplicate_repo( + self, + *, + from_id, + to_id, + repo_type, + private, + space_hardware, + space_sleep_time=None, + ): + _capture_duplicate_repo_call( + duplicate_kwargs, + from_id=from_id, + to_id=to_id, + repo_type=repo_type, + private=private, + space_hardware=space_hardware, + space_sleep_time=space_sleep_time, + ) def request_space_hardware(self, space_id, hardware, sleep_time=None): requested_hardware.append((space_id, hardware, sleep_time)) @@ -180,8 +264,9 @@ def get_space_runtime(self, space_id): log=logs.append, ) - assert duplicate_kwargs["hardware"] == "cpu-basic" - assert duplicate_kwargs["sleep_time"] == 2700 + assert duplicate_kwargs["repo_type"] == "space" + assert duplicate_kwargs["space_hardware"] == "cpu-basic" + assert duplicate_kwargs["space_sleep_time"] == 2700 assert requested_hardware == [] assert "Using duplicated Space hardware: cpu-basic" in logs assert (