Spaces:
Running
Running
Commit Β·
555a204
1
Parent(s): 829ae5c
feat: resolve sandbox file paths for hf_jobs script display
Browse files- Extract resolve_sandbox_script utility in sandbox_tool.py to detect
file paths and read content from sandbox via cat
- Resolve script paths before sending approval_required event so the
frontend receives actual file content for display and editing
- Show sandbox bash tool description arg instead of tool name in UI
agent/core/agent_loop.py
CHANGED
|
@@ -491,6 +491,16 @@ class Handlers:
|
|
| 491 |
tool_args = json.loads(tc.function.arguments)
|
| 492 |
except (json.JSONDecodeError, TypeError):
|
| 493 |
tool_args = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 494 |
tools_data.append(
|
| 495 |
{
|
| 496 |
"tool": tool_name,
|
|
|
|
| 491 |
tool_args = json.loads(tc.function.arguments)
|
| 492 |
except (json.JSONDecodeError, TypeError):
|
| 493 |
tool_args = {}
|
| 494 |
+
|
| 495 |
+
# Resolve sandbox file paths for hf_jobs scripts so the
|
| 496 |
+
# frontend can display & edit the actual file content.
|
| 497 |
+
if tool_name == "hf_jobs" and isinstance(tool_args.get("script"), str):
|
| 498 |
+
from agent.tools.sandbox_tool import resolve_sandbox_script
|
| 499 |
+
sandbox = getattr(session, "sandbox", None)
|
| 500 |
+
content, _ = await resolve_sandbox_script(sandbox, tool_args["script"])
|
| 501 |
+
if content:
|
| 502 |
+
tool_args = {**tool_args, "script": content}
|
| 503 |
+
|
| 504 |
tools_data.append(
|
| 505 |
{
|
| 506 |
"tool": tool_name,
|
agent/tools/sandbox_tool.py
CHANGED
|
@@ -12,6 +12,7 @@ a cpu-basic sandbox is auto-created (no approval needed).
|
|
| 12 |
from __future__ import annotations
|
| 13 |
|
| 14 |
import asyncio
|
|
|
|
| 15 |
from typing import Any
|
| 16 |
|
| 17 |
from huggingface_hub import HfApi, SpaceHardware
|
|
@@ -19,6 +20,37 @@ from huggingface_hub import HfApi, SpaceHardware
|
|
| 19 |
from agent.core.session import Event
|
| 20 |
from agent.tools.sandbox_client import Sandbox
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
# ββ Tool name mapping (short agent names β Sandbox client names) ββββββ
|
| 23 |
|
| 24 |
|
|
|
|
| 12 |
from __future__ import annotations
|
| 13 |
|
| 14 |
import asyncio
|
| 15 |
+
import shlex
|
| 16 |
from typing import Any
|
| 17 |
|
| 18 |
from huggingface_hub import HfApi, SpaceHardware
|
|
|
|
| 20 |
from agent.core.session import Event
|
| 21 |
from agent.tools.sandbox_client import Sandbox
|
| 22 |
|
| 23 |
+
|
| 24 |
+
def _looks_like_path(script: str) -> bool:
|
| 25 |
+
"""Return True if the script string looks like a file path (not inline code)."""
|
| 26 |
+
return (
|
| 27 |
+
isinstance(script, str)
|
| 28 |
+
and script.strip() == script
|
| 29 |
+
and not any(c in script for c in "\r\n\0")
|
| 30 |
+
and (script.startswith("/") or script.startswith("./") or script.startswith("../"))
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
async def resolve_sandbox_script(sandbox: Any, script: str) -> tuple[str | None, str | None]:
|
| 35 |
+
"""Read a file from the sandbox if *script* looks like a path.
|
| 36 |
+
|
| 37 |
+
Returns:
|
| 38 |
+
(content, error) β content is the file text on success,
|
| 39 |
+
error is a message on failure. Both None means *script*
|
| 40 |
+
is not a path (caller should use it as-is).
|
| 41 |
+
"""
|
| 42 |
+
if not sandbox or not _looks_like_path(script):
|
| 43 |
+
return None, None
|
| 44 |
+
try:
|
| 45 |
+
result = await asyncio.to_thread(
|
| 46 |
+
sandbox.bash, f"cat {shlex.quote(script)}"
|
| 47 |
+
)
|
| 48 |
+
if result.success and result.output:
|
| 49 |
+
return result.output, None
|
| 50 |
+
return None, f"Failed to read {script} from sandbox: {result.error}"
|
| 51 |
+
except Exception as e:
|
| 52 |
+
return None, f"Failed to read {script} from sandbox: {e}"
|
| 53 |
+
|
| 54 |
# ββ Tool name mapping (short agent names β Sandbox client names) ββββββ
|
| 55 |
|
| 56 |
|
frontend/src/components/Chat/ToolCallGroup.tsx
CHANGED
|
@@ -546,7 +546,7 @@ export default function ToolCallGroup({ tools, approveTools }: ToolCallGroupProp
|
|
| 546 |
whiteSpace: 'nowrap',
|
| 547 |
}}
|
| 548 |
>
|
| 549 |
-
{toolDisplayMap[tool.toolCallId] || tool.toolName}
|
| 550 |
</Typography>
|
| 551 |
|
| 552 |
{/* Status chip (non hf_jobs, or hf_jobs without final status) */}
|
|
|
|
| 546 |
whiteSpace: 'nowrap',
|
| 547 |
}}
|
| 548 |
>
|
| 549 |
+
{toolDisplayMap[tool.toolCallId] || String((tool.input as Record<string, unknown>)?.description || '') || tool.toolName}
|
| 550 |
</Typography>
|
| 551 |
|
| 552 |
{/* Status chip (non hf_jobs, or hf_jobs without final status) */}
|