mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-04-10 05:03:41 +00:00
fix(tool-hint): fold paths in exec commands instead of blind truncation
exec tool hints previously used val[:40] which cut paths mid-segment (e.g. "D:\Documents\GitHub\nanobot.worktree…"). Now uses regex to detect file paths in commands and abbreviates them properly, with smart truncation at chain separators (&&, |, ;) as fallback.
This commit is contained in:
parent
473637ceff
commit
4962867112
@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
from nanobot.utils.path import abbreviate_path
|
||||
|
||||
# Registry: tool_name -> (key_args, template, is_path, is_command)
|
||||
@ -17,6 +19,11 @@ _TOOL_FORMATS: dict[str, tuple[list[str], str, bool, bool]] = {
|
||||
"list_dir": (["path"], "ls {}", True, False),
|
||||
}
|
||||
|
||||
# Matches file paths embedded in shell commands (Windows drive, ~/, or absolute after space)
|
||||
_PATH_IN_CMD_RE = re.compile(
|
||||
r"(?:[A-Za-z]:[/\\]|~/|(?<=\s)/)[^\s;&|<>\"']+"
|
||||
)
|
||||
|
||||
|
||||
def format_tool_hints(tool_calls: list) -> str:
|
||||
"""Format tool calls as concise hints with smart abbreviation."""
|
||||
@ -85,10 +92,20 @@ def _fmt_known(tc, fmt: tuple) -> str:
|
||||
if fmt[2]: # is_path
|
||||
val = abbreviate_path(val)
|
||||
elif fmt[3]: # is_command
|
||||
val = val[:40] + "\u2026" if len(val) > 40 else val
|
||||
val = _abbreviate_command(val)
|
||||
return fmt[1].format(val)
|
||||
|
||||
|
||||
def _abbreviate_command(cmd: str, max_len: int = 40) -> str:
|
||||
"""Abbreviate paths in a command string, then truncate."""
|
||||
abbreviated = _PATH_IN_CMD_RE.sub(
|
||||
lambda m: abbreviate_path(m.group(), max_len=25), cmd
|
||||
)
|
||||
if len(abbreviated) <= max_len:
|
||||
return abbreviated
|
||||
return abbreviated[:max_len - 1] + "\u2026"
|
||||
|
||||
|
||||
def _fmt_mcp(tc) -> str:
|
||||
"""Format MCP tool as server::tool."""
|
||||
name = tc.name
|
||||
|
||||
@ -52,6 +52,37 @@ class TestToolHintKnownTools:
|
||||
assert result.startswith("$ ")
|
||||
assert len(result) <= 50 # reasonable limit
|
||||
|
||||
def test_exec_abbreviates_paths_in_command(self):
|
||||
"""Windows paths in exec commands should be folded, not blindly truncated."""
|
||||
cmd = "cd D:\\Documents\\GitHub\\nanobot\\.worktree\\tomain\\nanobot && git diff origin/main...pr-2706 --name-only 2>&1"
|
||||
result = _hint([_tc("exec", {"command": cmd})])
|
||||
assert "\u2026/" in result # path should be folded with …/
|
||||
assert "worktree" not in result # middle segments should be collapsed
|
||||
|
||||
def test_exec_abbreviates_linux_paths(self):
|
||||
"""Unix absolute paths in exec commands should be folded."""
|
||||
cmd = "cd /home/user/projects/nanobot/.worktree/tomain && make build"
|
||||
result = _hint([_tc("exec", {"command": cmd})])
|
||||
assert "\u2026/" in result
|
||||
assert "projects" not in result
|
||||
|
||||
def test_exec_abbreviates_home_paths(self):
|
||||
"""~/ paths in exec commands should be folded."""
|
||||
cmd = "cd ~/projects/nanobot/workspace && pytest tests/"
|
||||
result = _hint([_tc("exec", {"command": cmd})])
|
||||
assert "\u2026/" in result
|
||||
|
||||
def test_exec_short_command_unchanged(self):
|
||||
result = _hint([_tc("exec", {"command": "npm install typescript"})])
|
||||
assert result == "$ npm install typescript"
|
||||
|
||||
def test_exec_chained_commands_truncated_not_mid_path(self):
|
||||
"""Long chained commands should truncate preserving abbreviated paths."""
|
||||
cmd = "cd D:\\Documents\\GitHub\\project && npm run build && npm test"
|
||||
result = _hint([_tc("exec", {"command": cmd})])
|
||||
assert "\u2026/" in result # path folded
|
||||
assert "npm" in result # chained command still visible
|
||||
|
||||
def test_web_search(self):
|
||||
result = _hint([_tc("web_search", {"query": "Claude 4 vs GPT-4"})])
|
||||
assert result == 'search "Claude 4 vs GPT-4"'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user