akseljoonas HF Staff commited on
Commit
2464c2e
Β·
1 Parent(s): 66a2425

add create sandbox tool

Browse files
agent/core/agent_loop.py CHANGED
@@ -49,6 +49,11 @@ def _needs_approval(tool_name: str, tool_args: dict, config: Config | None = Non
49
  if not args_valid:
50
  return False
51
 
 
 
 
 
 
52
  if tool_name == "hf_jobs":
53
  operation = tool_args.get("operation", "")
54
  if operation not in ["run", "uv", "scheduled run", "scheduled uv"]:
 
49
  if not args_valid:
50
  return False
51
 
52
+ # Sandbox tools: only sandbox_create requires approval
53
+ SANDBOX_TOOLS = {"sandbox_create", "bash", "read", "write", "edit", "glob", "grep"}
54
+ if tool_name in SANDBOX_TOOLS:
55
+ return tool_name == "sandbox_create"
56
+
57
  if tool_name == "hf_jobs":
58
  operation = tool_args.get("operation", "")
59
  if operation not in ["run", "uv", "scheduled run", "scheduled uv"]:
agent/tools/sandbox_tool.py CHANGED
@@ -2,9 +2,10 @@
2
  Sandbox tools β€” expose the Sandbox client as agent tools.
3
 
4
  7 tools total:
5
- bash, read, write, edit, glob, grep, upload β€” operations on the sandbox
 
6
 
7
- Auto-creation: if any operation tool is called without an active sandbox,
8
  a cpu-basic sandbox is auto-created (no approval needed).
9
  """
10
 
@@ -14,7 +15,7 @@ import asyncio
14
  import os
15
  from typing import Any
16
 
17
- from huggingface_hub import HfApi
18
 
19
  from agent.core.session import Event
20
  from agent.tools.sandbox_client import Sandbox
@@ -71,6 +72,80 @@ async def _ensure_sandbox(
71
  return sb, None
72
 
73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  def _make_tool_handler(sandbox_tool_name: str):
75
  """Factory: create a handler for a sandbox operation tool."""
76
 
@@ -101,11 +176,22 @@ def _make_tool_handler(sandbox_tool_name: str):
101
 
102
 
103
  def get_sandbox_tools():
104
- """Return all 8 sandbox ToolSpecs."""
105
  from agent.core.tools import ToolSpec
106
 
107
  tools = []
108
 
 
 
 
 
 
 
 
 
 
 
 
109
  for name in Sandbox.TOOLS.keys():
110
  spec = Sandbox.TOOLS[name]
111
  tools.append(
 
2
  Sandbox tools β€” expose the Sandbox client as agent tools.
3
 
4
  7 tools total:
5
+ sandbox_create β€” explicit sandbox creation (requires approval)
6
+ bash, read, write, edit, glob, grep β€” operations on the sandbox
7
 
8
+ If any operation tool is called without an active sandbox,
9
  a cpu-basic sandbox is auto-created (no approval needed).
10
  """
11
 
 
15
  import os
16
  from typing import Any
17
 
18
+ from huggingface_hub import HfApi, SpaceHardware
19
 
20
  from agent.core.session import Event
21
  from agent.tools.sandbox_client import Sandbox
 
72
  return sb, None
73
 
74
 
75
+ # ── sandbox_create tool ──────────────────────────────────────────────
76
+
77
+ SANDBOX_CREATE_TOOL_SPEC = {
78
+ "name": "sandbox_create",
79
+ "description": (
80
+ "Create a persistent remote Linux sandbox on HF Spaces for interactive development.\n"
81
+ "YOU MUST DO THIS BEFORE USING bash/read/write/edit/glob/grep tools.\n"
82
+ "\n"
83
+ "Spins up a new sandbox environment where you can run commands, read/write/edit files, "
84
+ "install packages, and debug iteratively. The sandbox persists across tool calls within "
85
+ "the session.\n"
86
+ "You can choose from the following hardware tiers: " + ", ".join([e.value for e in SpaceHardware]) + ".\n"
87
+ "Use sandbox for: iterative development, debugging, multi-step workflows, testing code.\n"
88
+ "Use hf_jobs instead for: one-shot batch runs, scheduled tasks, fire-and-forget training.\n"
89
+ ),
90
+ "parameters": {
91
+ "type": "object",
92
+ "required": [],
93
+ "additionalProperties": False,
94
+ "properties": {
95
+ "hardware": {
96
+ "type": "string",
97
+ "enum": [e.value for e in SpaceHardware],
98
+ "description": "Hardware tier for the sandbox (default: cpu-basic)",
99
+ },
100
+ "private": {
101
+ "type": "boolean",
102
+ "description": "If true, create a private Space",
103
+ },
104
+ "sleep_time": {
105
+ "type": "integer",
106
+ "description": "Auto-sleep after N seconds of inactivity",
107
+ },
108
+ },
109
+ },
110
+ }
111
+
112
+
113
+ async def sandbox_create_handler(
114
+ args: dict[str, Any], session: Any = None
115
+ ) -> tuple[str, bool]:
116
+ """Handle sandbox_create tool calls."""
117
+ # If sandbox already exists, return its info
118
+ if session and getattr(session, "sandbox", None):
119
+ sb = session.sandbox
120
+ return (
121
+ f"Sandbox already active: {sb.space_id}\n"
122
+ f"URL: {sb.url}\n"
123
+ f"Use bash/read/write/edit/glob/grep to interact with it."
124
+ ), True
125
+
126
+ hardware = args.get("hardware", "cpu-basic")
127
+ create_kwargs = {}
128
+ if "private" in args:
129
+ create_kwargs["private"] = args["private"]
130
+ if "sleep_time" in args:
131
+ create_kwargs["sleep_time"] = args["sleep_time"]
132
+
133
+ try:
134
+ sb, error = await _ensure_sandbox(session, hardware=hardware, **create_kwargs)
135
+ except Exception as e:
136
+ return f"Failed to create sandbox: {e}", False
137
+
138
+ if error:
139
+ return error, False
140
+
141
+ return (
142
+ f"Sandbox created: {sb.space_id}\n"
143
+ f"URL: {sb.url}\n"
144
+ f"Hardware: {hardware}\n"
145
+ f"Use bash/read/write/edit/glob/grep to interact with it."
146
+ ), True
147
+
148
+
149
  def _make_tool_handler(sandbox_tool_name: str):
150
  """Factory: create a handler for a sandbox operation tool."""
151
 
 
176
 
177
 
178
  def get_sandbox_tools():
179
+ """Return all 7 sandbox ToolSpecs (sandbox_create + 6 operation tools)."""
180
  from agent.core.tools import ToolSpec
181
 
182
  tools = []
183
 
184
+ # sandbox_create (explicit creation, requires approval)
185
+ tools.append(
186
+ ToolSpec(
187
+ name=SANDBOX_CREATE_TOOL_SPEC["name"],
188
+ description=SANDBOX_CREATE_TOOL_SPEC["description"],
189
+ parameters=SANDBOX_CREATE_TOOL_SPEC["parameters"],
190
+ handler=sandbox_create_handler,
191
+ )
192
+ )
193
+
194
+ # Operation tools (auto-execute, no approval needed)
195
  for name in Sandbox.TOOLS.keys():
196
  spec = Sandbox.TOOLS[name]
197
  tools.append(