mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-07 07:08:31 +00:00
Compare commits
3 Commits
gl/chore/a
...
devin/1741
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf35f5af75 | ||
|
|
c2245c7024 | ||
|
|
cf2a1346fd |
@@ -432,7 +432,13 @@ class ToolUsage:
|
|||||||
# Attempt 1: Parse as JSON
|
# Attempt 1: Parse as JSON
|
||||||
try:
|
try:
|
||||||
arguments = json.loads(tool_input)
|
arguments = json.loads(tool_input)
|
||||||
if isinstance(arguments, dict):
|
# Handle case where arguments is a list
|
||||||
|
if isinstance(arguments, list) and len(arguments) > 0 and isinstance(arguments[0], dict):
|
||||||
|
self._printer.print(
|
||||||
|
content=f"Tool input is a list, extracting first element: {arguments[0]}", color="blue"
|
||||||
|
)
|
||||||
|
return arguments[0]
|
||||||
|
elif isinstance(arguments, dict):
|
||||||
return arguments
|
return arguments
|
||||||
except (JSONDecodeError, TypeError):
|
except (JSONDecodeError, TypeError):
|
||||||
pass # Continue to the next parsing attempt
|
pass # Continue to the next parsing attempt
|
||||||
@@ -440,7 +446,13 @@ class ToolUsage:
|
|||||||
# Attempt 2: Parse as Python literal
|
# Attempt 2: Parse as Python literal
|
||||||
try:
|
try:
|
||||||
arguments = ast.literal_eval(tool_input)
|
arguments = ast.literal_eval(tool_input)
|
||||||
if isinstance(arguments, dict):
|
# Handle case where arguments is a list
|
||||||
|
if isinstance(arguments, list) and len(arguments) > 0 and isinstance(arguments[0], dict):
|
||||||
|
self._printer.print(
|
||||||
|
content=f"Tool input is a list, extracting first element: {arguments[0]}", color="blue"
|
||||||
|
)
|
||||||
|
return arguments[0]
|
||||||
|
elif isinstance(arguments, dict):
|
||||||
return arguments
|
return arguments
|
||||||
except (ValueError, SyntaxError):
|
except (ValueError, SyntaxError):
|
||||||
pass # Continue to the next parsing attempt
|
pass # Continue to the next parsing attempt
|
||||||
@@ -448,7 +460,13 @@ class ToolUsage:
|
|||||||
# Attempt 3: Parse as JSON5
|
# Attempt 3: Parse as JSON5
|
||||||
try:
|
try:
|
||||||
arguments = json5.loads(tool_input)
|
arguments = json5.loads(tool_input)
|
||||||
if isinstance(arguments, dict):
|
# Handle case where arguments is a list
|
||||||
|
if isinstance(arguments, list) and len(arguments) > 0 and isinstance(arguments[0], dict):
|
||||||
|
self._printer.print(
|
||||||
|
content=f"Tool input is a list, extracting first element: {arguments[0]}", color="blue"
|
||||||
|
)
|
||||||
|
return arguments[0]
|
||||||
|
elif isinstance(arguments, dict):
|
||||||
return arguments
|
return arguments
|
||||||
except (JSONDecodeError, ValueError, TypeError):
|
except (JSONDecodeError, ValueError, TypeError):
|
||||||
pass # Continue to the next parsing attempt
|
pass # Continue to the next parsing attempt
|
||||||
@@ -460,7 +478,13 @@ class ToolUsage:
|
|||||||
content=f"Repaired JSON: {repaired_input}", color="blue"
|
content=f"Repaired JSON: {repaired_input}", color="blue"
|
||||||
)
|
)
|
||||||
arguments = json.loads(repaired_input)
|
arguments = json.loads(repaired_input)
|
||||||
if isinstance(arguments, dict):
|
# Handle case where arguments is a list
|
||||||
|
if isinstance(arguments, list) and len(arguments) > 0 and isinstance(arguments[0], dict):
|
||||||
|
self._printer.print(
|
||||||
|
content=f"Tool input is a list, extracting first element: {arguments[0]}", color="blue"
|
||||||
|
)
|
||||||
|
return arguments[0]
|
||||||
|
elif isinstance(arguments, dict):
|
||||||
return arguments
|
return arguments
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error = f"Failed to repair JSON: {e}"
|
error = f"Failed to repair JSON: {e}"
|
||||||
|
|||||||
76
tests/tools/test_tool_input_validation.py
Normal file
76
tests/tools/test_tool_input_validation.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from crewai.tools.tool_usage import ToolUsage
|
||||||
|
|
||||||
|
|
||||||
|
class TestToolInputValidation:
|
||||||
|
def setup_method(self):
|
||||||
|
# Create mock objects for testing
|
||||||
|
self.mock_tools_handler = MagicMock()
|
||||||
|
self.mock_tools = [MagicMock()]
|
||||||
|
self.mock_original_tools = [MagicMock()]
|
||||||
|
self.mock_tools_description = "Mock tools description"
|
||||||
|
self.mock_tools_names = "Mock tools names"
|
||||||
|
self.mock_task = MagicMock()
|
||||||
|
self.mock_function_calling_llm = MagicMock()
|
||||||
|
|
||||||
|
# Create mock agent with required string attributes
|
||||||
|
self.mock_agent = MagicMock()
|
||||||
|
self.mock_agent.key = "mock_agent_key"
|
||||||
|
self.mock_agent.role = "mock_agent_role"
|
||||||
|
self.mock_agent._original_role = "mock_original_role"
|
||||||
|
|
||||||
|
# Create mock action with required string attributes
|
||||||
|
self.mock_action = MagicMock()
|
||||||
|
self.mock_action.tool = "mock_tool_name"
|
||||||
|
self.mock_action.tool_input = "mock_tool_input"
|
||||||
|
|
||||||
|
# Create ToolUsage instance
|
||||||
|
self.tool_usage = ToolUsage(
|
||||||
|
tools_handler=self.mock_tools_handler,
|
||||||
|
tools=self.mock_tools,
|
||||||
|
original_tools=self.mock_original_tools,
|
||||||
|
tools_description=self.mock_tools_description,
|
||||||
|
tools_names=self.mock_tools_names,
|
||||||
|
task=self.mock_task,
|
||||||
|
function_calling_llm=self.mock_function_calling_llm,
|
||||||
|
agent=self.mock_agent,
|
||||||
|
action=self.mock_action,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Patch the _emit_validate_input_error method to avoid event emission
|
||||||
|
self.original_emit_validate_input_error = self.tool_usage._emit_validate_input_error
|
||||||
|
self.tool_usage._emit_validate_input_error = MagicMock()
|
||||||
|
|
||||||
|
def teardown_method(self):
|
||||||
|
# Restore the original method
|
||||||
|
if hasattr(self, 'original_emit_validate_input_error'):
|
||||||
|
self.tool_usage._emit_validate_input_error = self.original_emit_validate_input_error
|
||||||
|
|
||||||
|
def test_validate_tool_input_with_dict(self):
|
||||||
|
# Test with a valid dictionary input
|
||||||
|
tool_input = '{"ticker": "VST"}'
|
||||||
|
result = self.tool_usage._validate_tool_input(tool_input)
|
||||||
|
assert result == {"ticker": "VST"}
|
||||||
|
|
||||||
|
def test_validate_tool_input_with_list(self):
|
||||||
|
# Test with a list input containing a dictionary as the first element
|
||||||
|
tool_input = '[{"ticker": "VST"}, {"tool_code": "Stock Info", "tool_input": {"ticker": "VST"}}]'
|
||||||
|
result = self.tool_usage._validate_tool_input(tool_input)
|
||||||
|
assert result == {"ticker": "VST"}
|
||||||
|
|
||||||
|
def test_validate_tool_input_with_empty_list(self):
|
||||||
|
# Test with an empty list input
|
||||||
|
tool_input = '[]'
|
||||||
|
with pytest.raises(Exception) as excinfo:
|
||||||
|
self.tool_usage._validate_tool_input(tool_input)
|
||||||
|
assert "Tool input must be a valid dictionary in JSON or Python literal format" in str(excinfo.value)
|
||||||
|
|
||||||
|
def test_validate_tool_input_with_list_of_non_dicts(self):
|
||||||
|
# Test with a list input containing non-dictionary elements
|
||||||
|
tool_input = '["not a dict", 123]'
|
||||||
|
with pytest.raises(Exception) as excinfo:
|
||||||
|
self.tool_usage._validate_tool_input(tool_input)
|
||||||
|
assert "Tool input must be a valid dictionary in JSON or Python literal format" in str(excinfo.value)
|
||||||
Reference in New Issue
Block a user