akseljoonas HF Staff commited on
Commit
9bb2fc3
·
2 Parent(s): e96ab7e 5ede083

Merge configurable-permissions: add confirm_cpu_jobs, auto_file_upload, keep yolo mode

Browse files
agent/config.py CHANGED
@@ -24,6 +24,10 @@ class Config(BaseModel):
24
  auto_save_interval: int = 3 # Save every N user turns (0 = disabled)
25
  yolo_mode: bool = False # Auto-approve all tool calls without confirmation
26
 
 
 
 
 
27
 
28
  def substitute_env_vars(obj: Any) -> Any:
29
  """
 
24
  auto_save_interval: int = 3 # Save every N user turns (0 = disabled)
25
  yolo_mode: bool = False # Auto-approve all tool calls without confirmation
26
 
27
+ # Permission control parameters
28
+ confirm_cpu_jobs: bool = True
29
+ auto_file_upload: bool = False
30
+
31
 
32
  def substitute_env_vars(obj: Any) -> Any:
33
  """
agent/core/agent_loop.py CHANGED
@@ -11,6 +11,7 @@ from lmnr import observe
11
  from agent.config import Config
12
  from agent.core.session import Event, OpType, Session
13
  from agent.core.tools import ToolRouter
 
14
 
15
  ToolCall = ChatCompletionMessageToolCall
16
 
@@ -37,10 +38,10 @@ def _validate_tool_args(tool_args: dict) -> tuple[bool, str | None]:
37
  return True, None
38
 
39
 
40
- def _needs_approval(tool_name: str, tool_args: dict, yolo_mode: bool = False) -> bool:
41
- """Check if a tool call requires user approval before execution"""
42
  # Yolo mode: skip all approvals
43
- if yolo_mode:
44
  return False
45
 
46
  # If args are malformed, skip approval (validation error will be shown later)
@@ -49,15 +50,33 @@ def _needs_approval(tool_name: str, tool_args: dict, yolo_mode: bool = False) ->
49
  return False
50
 
51
  if tool_name == "hf_jobs":
52
- # Check if it's a run or uv operation
53
  operation = tool_args.get("operation", "")
54
- return operation in ["run", "uv"]
55
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  if tool_name == "hf_private_repos":
57
- # Repo creation and file uploads require approval
58
  operation = tool_args.get("operation", "")
59
- return operation in ["create_repo", "upload_file"]
60
-
 
 
 
 
 
 
61
  return False
62
 
63
 
@@ -147,7 +166,7 @@ class Handlers:
147
  tool_name = tc.function.name
148
  tool_args = json.loads(tc.function.arguments)
149
 
150
- if _needs_approval(tool_name, tool_args, session.config.yolo_mode):
151
  approval_required_tools.append(tc)
152
  else:
153
  non_approval_tools.append(tc)
 
11
  from agent.config import Config
12
  from agent.core.session import Event, OpType, Session
13
  from agent.core.tools import ToolRouter
14
+ from agent.tools.jobs_tool import CPU_FLAVORS
15
 
16
  ToolCall = ChatCompletionMessageToolCall
17
 
 
38
  return True, None
39
 
40
 
41
+ def _needs_approval(tool_name: str, tool_args: dict, config: Config | None = None) -> bool:
42
+ """Check if a tool call requires user approval before execution."""
43
  # Yolo mode: skip all approvals
44
+ if config and config.yolo_mode:
45
  return False
46
 
47
  # If args are malformed, skip approval (validation error will be shown later)
 
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"]:
55
+ return False
56
+
57
+ # Check if this is a CPU-only job
58
+ args = tool_args.get("args", {})
59
+ hardware_flavor = args.get("flavor") or args.get("hardware") or args.get("hardware_flavor") or "cpu-basic"
60
+ is_cpu_job = hardware_flavor in CPU_FLAVORS
61
+
62
+ if is_cpu_job:
63
+ if config and not config.confirm_cpu_jobs:
64
+ return False
65
+ return True
66
+
67
+ return True
68
+
69
+ # Check for file upload operations (hf_private_repos or other tools)
70
  if tool_name == "hf_private_repos":
 
71
  operation = tool_args.get("operation", "")
72
+ if operation == "upload_file":
73
+ if config and config.auto_file_upload:
74
+ return False
75
+ return True
76
+ # Other operations (create_repo, etc.) always require approval
77
+ if operation in ["create_repo"]:
78
+ return True
79
+
80
  return False
81
 
82
 
 
166
  tool_name = tc.function.name
167
  tool_args = json.loads(tc.function.arguments)
168
 
169
+ if _needs_approval(tool_name, tool_args, session.config):
170
  approval_required_tools.append(tc)
171
  else:
172
  non_approval_tools.append(tc)
configs/main_agent_config.json CHANGED
@@ -2,6 +2,8 @@
2
  "model_name": "anthropic/claude-opus-4-5-20251101",
3
  "save_sessions": true,
4
  "session_dataset_repo": "akseljoonas/hf-agent-sessions",
 
 
5
  "mcpServers": {
6
  "hf-mcp-server": {
7
  "transport": "http",
@@ -11,4 +13,4 @@
11
  }
12
  }
13
  }
14
- }
 
2
  "model_name": "anthropic/claude-opus-4-5-20251101",
3
  "save_sessions": true,
4
  "session_dataset_repo": "akseljoonas/hf-agent-sessions",
5
+ "confirm_cpu_jobs": false,
6
+ "auto_file_upload": false,
7
  "mcpServers": {
8
  "hf-mcp-server": {
9
  "transport": "http",
 
13
  }
14
  }
15
  }
16
+ }
skills ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit 9a355bbd756d4c61a717323fd855eb9b615eff4d
test_dataset_tools.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Test script for unified dataset inspection tool
3
+ """
4
+
5
+ import asyncio
6
+ import sys
7
+ from typing import TypedDict
8
+ from unittest.mock import MagicMock
9
+
10
+
11
+ # Mock the types module before importing dataset_tools
12
+ class ToolResult(TypedDict, total=False):
13
+ formatted: str
14
+ totalResults: int
15
+ resultsShared: int
16
+ isError: bool
17
+
18
+
19
+ mock_types = MagicMock()
20
+ mock_types.ToolResult = ToolResult
21
+ sys.modules["agent.tools.types"] = mock_types
22
+
23
+ # Now import directly from the file
24
+ sys.path.insert(0, "/Users/akseljoonas/Documents/hf-agent/agent/tools")
25
+ from dataset_tools import hf_inspect_dataset_handler, inspect_dataset
26
+
27
+
28
+ async def test_inspect_dataset():
29
+ """Test the unified inspect_dataset function"""
30
+ print("=" * 70)
31
+ print("Testing inspect_dataset()")
32
+ print("=" * 70)
33
+
34
+ # Test with akseljoonas/hf-agent-sessions as specified
35
+ print("\n→ inspect_dataset('akseljoonas/hf-agent-sessions'):")
36
+ result = await inspect_dataset("akseljoonas/hf-agent-sessions")
37
+ print(f" isError: {result['isError']}")
38
+ print(f" Output:\n{result['formatted']}")
39
+
40
+ print("\n" + "=" * 70)
41
+
42
+ # # Test with stanfordnlp/imdb
43
+ # print("\n→ inspect_dataset('stanfordnlp/imdb'):")
44
+ # result = await inspect_dataset("stanfordnlp/imdb")
45
+ # print(f" isError: {result['isError']}")
46
+ # print(f" Output:\n{result['formatted']}")
47
+
48
+ # print("\n" + "=" * 70)
49
+
50
+ # # Test with multi-config dataset
51
+ # print("\n→ inspect_dataset('nyu-mll/glue', config='mrpc'):")
52
+ # result = await inspect_dataset("nyu-mll/glue", config="mrpc")
53
+ # print(f" isError: {result['isError']}")
54
+ # print(f" Output:\n{result['formatted']}")
55
+
56
+
57
+ async def test_handler():
58
+ """Test the handler (what the agent calls)"""
59
+ print("\n" + "=" * 70)
60
+ print("Testing hf_inspect_dataset_handler()")
61
+ print("=" * 70)
62
+
63
+ result, success = await hf_inspect_dataset_handler(
64
+ {
65
+ "dataset": "stanfordnlp/imdb",
66
+ "sample_rows": 2,
67
+ }
68
+ )
69
+ print("\n→ Handler result:")
70
+ print(f" success: {success}")
71
+ print(f" output:\n{result}")
72
+
73
+
74
+ if __name__ == "__main__":
75
+ print("\nUnified Dataset Inspection Tool Test\n")
76
+ asyncio.run(test_inspect_dataset())
77
+ # asyncio.run(test_handler())
78
+ print("\n" + "=" * 70)
79
+ print("Done!")