Переглянути джерело

fix: tool input parsing — extract nested ReAct format {action, input}

Tools received full ReAct JSON {"action":"Bash","input":{"command":"ls"}}
but schemas expected flat params {"command":"ls"}. Now _parse_input()
and ValidatedTool._validate() extract the nested "input" field.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kenny67nju 5 місяців тому
батько
коміт
e8e24a4dbb

+ 13 - 1
lambdagent/builtin_tools/file_tools.py

@@ -457,11 +457,23 @@ def _parse_input(input_val: Any, schema_cls) -> dict:
         try:
             data = json.loads(input_val)
         except (json.JSONDecodeError, ValueError):
-            # Single string → treat as primary param
             data = {"file_path": input_val} if "file" in schema_cls.__name__.lower() else {"pattern": input_val}
     else:
         data = {"file_path": str(input_val)}
 
+    # Extract nested "input" from ReAct format: {"action":"Tool","input":{...}}
+    if isinstance(data, dict) and "input" in data and ("action" in data or "tool" in data):
+        inner = data["input"]
+        if isinstance(inner, str):
+            try:
+                data = json.loads(inner)
+            except (json.JSONDecodeError, ValueError):
+                data = {"file_path": inner} if "file" in schema_cls.__name__.lower() else {"pattern": inner}
+        elif isinstance(inner, dict):
+            data = inner
+        else:
+            data = {"file_path": str(inner)}
+
     try:
         validated = schema_cls(**data)
         return validated.dict()

+ 8 - 1
lambdagent/builtin_tools/shell_tools.py

@@ -228,10 +228,17 @@ def git_branch(input_val: Any) -> str:
 
 def _parse_input(input_val: Any) -> dict:
     if isinstance(input_val, dict):
+        # Extract nested "input" from ReAct format: {"action":"Bash","input":{...}}
+        if "input" in input_val and ("action" in input_val or "tool" in input_val):
+            inner = input_val["input"]
+            return json.loads(inner) if isinstance(inner, str) else inner if isinstance(inner, dict) else {"command": str(inner)}
         return input_val
     if isinstance(input_val, str):
         try:
-            return json.loads(input_val)
+            parsed = json.loads(input_val)
+            if isinstance(parsed, dict):
+                return _parse_input(parsed)  # recurse to handle nested format
+            return {"command": input_val}
         except (json.JSONDecodeError, ValueError):
             return {"command": input_val}
     return {}

+ 9 - 1
lambdagent/validated_tool.py

@@ -55,7 +55,15 @@ class ValidatedTool(Term):
                     input = {"input": input}
 
             if isinstance(input, dict):
-                validated = self.schema(**input)
+                # Extract nested "input" field if present (from ReAct JSON format)
+                if "input" in input and ("action" in input or "tool" in input):
+                    input = input["input"]
+                    if isinstance(input, str):
+                        try:
+                            input = json.loads(input)
+                        except (json.JSONDecodeError, ValueError):
+                            input = {"command": input} if "Bash" in self._name or "Shell" in self._name else {"input": input}
+                validated = self.schema(**input) if isinstance(input, dict) else self.schema(input)
             else:
                 validated = self.schema(input=input)