tenseleyflow/loader / f3985be

Browse files

fix: improve bracket-format tool call extraction

- Avoid duplicate extraction by tracking match positions
- Expand ~ in all file paths using os.path.expanduser
- Better bash command extraction handling 'command=' prefix
- Handle model outputting 'cmd, command=cmd' format
- Use position-based IDs to prevent duplicates across patterns
Authored by espadonne
SHA
f3985be03423d42612dc197abfe9c68cbdfcb01b
Parents
aacef5f
Tree
359c9af

1 changed file

StatusFile+-
M src/loader/agent/loop.py 35 11
src/loader/agent/loop.pymodified
@@ -1425,6 +1425,7 @@ class Agent:
14251425
         """
14261426
         import re
14271427
         import json
1428
+        import os
14281429
 
14291430
         tool_calls = []
14301431
         tool_names = ["write", "read", "edit", "bash", "glob", "grep"]
@@ -1461,14 +1462,31 @@ class Agent:
14611462
                     debug(f"  skipping - tool_name '{tool_name}' not in tool_names")
14621463
                     continue
14631464
 
1465
+                # Skip if we already have a tool call at this position (avoid duplicates)
1466
+                match_start = match.start()
1467
+                if any(tc.id.endswith(f"_pos{match_start}") for tc in tool_calls):
1468
+                    debug(f"  skipping - already extracted at position {match_start}")
1469
+                    continue
1470
+
14641471
                 try:
14651472
                     # Parse the arguments based on tool type
14661473
                     if tool_name == "bash":
1467
-                        # bash tool: command is the whole string
1474
+                        # bash tool: extract command, handling various formats
1475
+                        # Model might output: "mkdir -p /foo" or "command='mkdir -p /foo'"
1476
+                        cmd = args_str
1477
+                        # If it has command= prefix, extract just the command value
1478
+                        cmd_match = re.search(r'command\s*[=:]\s*["\']?([^"\']+)["\']?', args_str)
1479
+                        if cmd_match:
1480
+                            cmd = cmd_match.group(1).strip()
1481
+                        # Also handle case where model outputs "cmd, command='cmd'" - take first part
1482
+                        elif ',' in args_str and 'command=' in args_str:
1483
+                            cmd = args_str.split(',')[0].strip()
1484
+                        # Expand ~ in command
1485
+                        cmd = os.path.expanduser(cmd)
14681486
                         tool_calls.append(ToolCall(
1469
-                            id=f"bracket_{tool_name}_{len(tool_calls)}",
1487
+                            id=f"bracket_{tool_name}_{len(tool_calls)}_pos{match_start}",
14701488
                             name=tool_name,
1471
-                            arguments={"command": args_str},
1489
+                            arguments={"command": cmd},
14721490
                         ))
14731491
                     elif tool_name == "write":
14741492
                         # write tool: file_path=..., content="..."
@@ -1504,41 +1522,47 @@ class Agent:
15041522
 
15051523
                         if file_path_match:
15061524
                             file_path = file_path_match.group(1).strip('"\'')
1525
+                            file_path = os.path.expanduser(file_path)  # Expand ~
15071526
                             tool_calls.append(ToolCall(
1508
-                                id=f"bracket_{tool_name}_{len(tool_calls)}",
1527
+                                id=f"bracket_{tool_name}_{len(tool_calls)}_pos{match_start}",
15091528
                                 name=tool_name,
15101529
                                 arguments={"file_path": file_path, "content": file_content},
15111530
                             ))
15121531
                     elif tool_name == "read":
15131532
                         # read tool: file_path
15141533
                         file_path = args_str.split(',')[0].split('=')[-1].strip().strip('"\'')
1534
+                        file_path = os.path.expanduser(file_path)
15151535
                         tool_calls.append(ToolCall(
1516
-                            id=f"bracket_{tool_name}_{len(tool_calls)}",
1536
+                            id=f"bracket_{tool_name}_{len(tool_calls)}_pos{match_start}",
15171537
                             name=tool_name,
15181538
                             arguments={"file_path": file_path},
15191539
                         ))
15201540
                     elif tool_name == "edit":
15211541
                         # edit tool: file_path=..., old_string="...", new_string="..."
1522
-                        file_path_match = re.search(r'file_path[=:]\s*([^,\s]+)', args_str)
1542
+                        file_path_match = re.search(r'file_path[=:]\s*["\']?([^"\'`,]+)["\']?', args_str)
15231543
                         old_match = re.search(r'old_string[=:]\s*["\'](.+?)["\']', args_str)
15241544
                         new_match = re.search(r'new_string[=:]\s*["\'](.+?)["\']', args_str)
15251545
 
15261546
                         if file_path_match and old_match and new_match:
1547
+                            file_path = os.path.expanduser(file_path_match.group(1).strip('"\''))
15271548
                             tool_calls.append(ToolCall(
1528
-                                id=f"bracket_{tool_name}_{len(tool_calls)}",
1549
+                                id=f"bracket_{tool_name}_{len(tool_calls)}_pos{match_start}",
15291550
                                 name=tool_name,
15301551
                                 arguments={
1531
-                                    "file_path": file_path_match.group(1).strip('"\''),
1552
+                                    "file_path": file_path,
15321553
                                     "old_string": old_match.group(1),
15331554
                                     "new_string": new_match.group(1),
15341555
                                 },
15351556
                             ))
15361557
                     elif tool_name in ("glob", "grep"):
1537
-                        # glob/grep: pattern
1558
+                        # glob/grep: pattern - expand ~ if it looks like a path
1559
+                        pattern = args_str
1560
+                        if '~' in pattern:
1561
+                            pattern = os.path.expanduser(pattern)
15381562
                         tool_calls.append(ToolCall(
1539
-                            id=f"bracket_{tool_name}_{len(tool_calls)}",
1563
+                            id=f"bracket_{tool_name}_{len(tool_calls)}_pos{match_start}",
15401564
                             name=tool_name,
1541
-                            arguments={"pattern": args_str},
1565
+                            arguments={"pattern": pattern},
15421566
                         ))
15431567
                 except Exception:
15441568
                     continue