mirror of
https://github.com/crewAIInc/crewAI.git
synced 2025-12-17 12:58:31 +00:00
Compare commits
5 Commits
fix/unsafe
...
feat/trace
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c37a75797a | ||
|
|
f71aae97e0 | ||
|
|
161f552c77 | ||
|
|
7c5160bc92 | ||
|
|
fbd9d832ef |
@@ -117,7 +117,10 @@ class ToolUsage:
|
|||||||
self._printer.print(content=f"\n\n{error}\n", color="red")
|
self._printer.print(content=f"\n\n{error}\n", color="red")
|
||||||
return error
|
return error
|
||||||
|
|
||||||
if isinstance(tool, CrewStructuredTool) and tool.name == self._i18n.tools("add_image")["name"]: # type: ignore
|
if (
|
||||||
|
isinstance(tool, CrewStructuredTool)
|
||||||
|
and tool.name == self._i18n.tools("add_image")["name"] # type: ignore
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
result = self._use(tool_string=tool_string, tool=tool, calling=calling)
|
result = self._use(tool_string=tool_string, tool=tool, calling=calling)
|
||||||
return result
|
return result
|
||||||
@@ -181,7 +184,9 @@ class ToolUsage:
|
|||||||
|
|
||||||
if calling.arguments:
|
if calling.arguments:
|
||||||
try:
|
try:
|
||||||
acceptable_args = tool.args_schema.model_json_schema()["properties"].keys() # type: ignore
|
acceptable_args = tool.args_schema.model_json_schema()[
|
||||||
|
"properties"
|
||||||
|
].keys() # type: ignore
|
||||||
arguments = {
|
arguments = {
|
||||||
k: v
|
k: v
|
||||||
for k, v in calling.arguments.items()
|
for k, v in calling.arguments.items()
|
||||||
@@ -202,7 +207,7 @@ class ToolUsage:
|
|||||||
error=e, tool=tool.name, tool_inputs=tool.description
|
error=e, tool=tool.name, tool_inputs=tool.description
|
||||||
)
|
)
|
||||||
error = ToolUsageErrorException(
|
error = ToolUsageErrorException(
|
||||||
f'\n{error_message}.\nMoving on then. {self._i18n.slice("format").format(tool_names=self.tools_names)}'
|
f"\n{error_message}.\nMoving on then. {self._i18n.slice('format').format(tool_names=self.tools_names)}"
|
||||||
).message
|
).message
|
||||||
self.task.increment_tools_errors()
|
self.task.increment_tools_errors()
|
||||||
if self.agent.verbose:
|
if self.agent.verbose:
|
||||||
@@ -244,6 +249,7 @@ class ToolUsage:
|
|||||||
tool_calling=calling,
|
tool_calling=calling,
|
||||||
from_cache=from_cache,
|
from_cache=from_cache,
|
||||||
started_at=started_at,
|
started_at=started_at,
|
||||||
|
result=result,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -380,7 +386,7 @@ class ToolUsage:
|
|||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
return ToolUsageErrorException(
|
return ToolUsageErrorException(
|
||||||
f'{self._i18n.errors("tool_arguments_error")}'
|
f"{self._i18n.errors('tool_arguments_error')}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not isinstance(arguments, dict):
|
if not isinstance(arguments, dict):
|
||||||
@@ -388,7 +394,7 @@ class ToolUsage:
|
|||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
return ToolUsageErrorException(
|
return ToolUsageErrorException(
|
||||||
f'{self._i18n.errors("tool_arguments_error")}'
|
f"{self._i18n.errors('tool_arguments_error')}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return ToolCalling(
|
return ToolCalling(
|
||||||
@@ -416,7 +422,7 @@ class ToolUsage:
|
|||||||
if self.agent.verbose:
|
if self.agent.verbose:
|
||||||
self._printer.print(content=f"\n\n{e}\n", color="red")
|
self._printer.print(content=f"\n\n{e}\n", color="red")
|
||||||
return ToolUsageErrorException( # type: ignore # Incompatible return value type (got "ToolUsageErrorException", expected "ToolCalling | InstructorToolCalling")
|
return ToolUsageErrorException( # type: ignore # Incompatible return value type (got "ToolUsageErrorException", expected "ToolCalling | InstructorToolCalling")
|
||||||
f'{self._i18n.errors("tool_usage_error").format(error=e)}\nMoving on then. {self._i18n.slice("format").format(tool_names=self.tools_names)}'
|
f"{self._i18n.errors('tool_usage_error').format(error=e)}\nMoving on then. {self._i18n.slice('format').format(tool_names=self.tools_names)}"
|
||||||
)
|
)
|
||||||
return self._tool_calling(tool_string)
|
return self._tool_calling(tool_string)
|
||||||
|
|
||||||
@@ -492,7 +498,12 @@ class ToolUsage:
|
|||||||
crewai_event_bus.emit(self, ToolUsageErrorEvent(**{**event_data, "error": e}))
|
crewai_event_bus.emit(self, ToolUsageErrorEvent(**{**event_data, "error": e}))
|
||||||
|
|
||||||
def on_tool_use_finished(
|
def on_tool_use_finished(
|
||||||
self, tool: Any, tool_calling: ToolCalling, from_cache: bool, started_at: float
|
self,
|
||||||
|
tool: Any,
|
||||||
|
tool_calling: ToolCalling,
|
||||||
|
from_cache: bool,
|
||||||
|
started_at: float,
|
||||||
|
result: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
finished_at = time.time()
|
finished_at = time.time()
|
||||||
event_data = self._prepare_event_data(tool, tool_calling)
|
event_data = self._prepare_event_data(tool, tool_calling)
|
||||||
@@ -501,6 +512,7 @@ class ToolUsage:
|
|||||||
"started_at": datetime.datetime.fromtimestamp(started_at),
|
"started_at": datetime.datetime.fromtimestamp(started_at),
|
||||||
"finished_at": datetime.datetime.fromtimestamp(finished_at),
|
"finished_at": datetime.datetime.fromtimestamp(finished_at),
|
||||||
"from_cache": from_cache,
|
"from_cache": from_cache,
|
||||||
|
"output": result,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
crewai_event_bus.emit(self, ToolUsageFinishedEvent(**event_data))
|
crewai_event_bus.emit(self, ToolUsageFinishedEvent(**event_data))
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class ToolUsageFinishedEvent(ToolUsageEvent):
|
|||||||
started_at: datetime
|
started_at: datetime
|
||||||
finished_at: datetime
|
finished_at: datetime
|
||||||
from_cache: bool = False
|
from_cache: bool = False
|
||||||
|
output: Any
|
||||||
type: str = "tool_usage_finished"
|
type: str = "tool_usage_finished"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import datetime
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
import time
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -11,6 +13,7 @@ from crewai.tools.tool_usage import ToolUsage
|
|||||||
from crewai.utilities.events import crewai_event_bus
|
from crewai.utilities.events import crewai_event_bus
|
||||||
from crewai.utilities.events.tool_usage_events import (
|
from crewai.utilities.events.tool_usage_events import (
|
||||||
ToolSelectionErrorEvent,
|
ToolSelectionErrorEvent,
|
||||||
|
ToolUsageFinishedEvent,
|
||||||
ToolValidateInputErrorEvent,
|
ToolValidateInputErrorEvent,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -624,3 +627,161 @@ def test_tool_validate_input_error_event():
|
|||||||
assert event.agent_role == "test_role"
|
assert event.agent_role == "test_role"
|
||||||
assert event.tool_name == "test_tool"
|
assert event.tool_name == "test_tool"
|
||||||
assert "must be a valid dictionary" in event.error
|
assert "must be a valid dictionary" in event.error
|
||||||
|
|
||||||
|
|
||||||
|
def test_tool_usage_finished_event_with_result():
|
||||||
|
"""Test that ToolUsageFinishedEvent is emitted with correct result attributes."""
|
||||||
|
# Create mock agent with proper string values
|
||||||
|
mock_agent = MagicMock()
|
||||||
|
mock_agent.key = "test_agent_key"
|
||||||
|
mock_agent.role = "test_agent_role"
|
||||||
|
mock_agent._original_role = "test_agent_role"
|
||||||
|
mock_agent.i18n = MagicMock()
|
||||||
|
mock_agent.verbose = False
|
||||||
|
|
||||||
|
# Create mock task
|
||||||
|
mock_task = MagicMock()
|
||||||
|
mock_task.delegations = 0
|
||||||
|
|
||||||
|
# Create mock tool
|
||||||
|
class TestTool(BaseTool):
|
||||||
|
name: str = "Test Tool"
|
||||||
|
description: str = "A test tool"
|
||||||
|
|
||||||
|
def _run(self, input: dict) -> str:
|
||||||
|
return "test result"
|
||||||
|
|
||||||
|
test_tool = TestTool()
|
||||||
|
|
||||||
|
# Create mock tool calling
|
||||||
|
mock_tool_calling = MagicMock()
|
||||||
|
mock_tool_calling.arguments = {"arg1": "value1"}
|
||||||
|
|
||||||
|
# Create ToolUsage instance
|
||||||
|
tool_usage = ToolUsage(
|
||||||
|
tools_handler=MagicMock(),
|
||||||
|
tools=[test_tool],
|
||||||
|
original_tools=[test_tool],
|
||||||
|
tools_description="Test Tool Description",
|
||||||
|
tools_names="Test Tool",
|
||||||
|
task=mock_task,
|
||||||
|
function_calling_llm=None,
|
||||||
|
agent=mock_agent,
|
||||||
|
action=MagicMock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Track received events
|
||||||
|
received_events = []
|
||||||
|
|
||||||
|
@crewai_event_bus.on(ToolUsageFinishedEvent)
|
||||||
|
def event_handler(source, event):
|
||||||
|
received_events.append(event)
|
||||||
|
|
||||||
|
# Call on_tool_use_finished with test data
|
||||||
|
started_at = time.time()
|
||||||
|
result = "test output result"
|
||||||
|
tool_usage.on_tool_use_finished(
|
||||||
|
tool=test_tool,
|
||||||
|
tool_calling=mock_tool_calling,
|
||||||
|
from_cache=False,
|
||||||
|
started_at=started_at,
|
||||||
|
result=result,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify event was emitted
|
||||||
|
assert len(received_events) == 1, "Expected one event to be emitted"
|
||||||
|
event = received_events[0]
|
||||||
|
assert isinstance(event, ToolUsageFinishedEvent)
|
||||||
|
|
||||||
|
# Verify event attributes
|
||||||
|
assert event.agent_key == "test_agent_key"
|
||||||
|
assert event.agent_role == "test_agent_role"
|
||||||
|
assert event.tool_name == "Test Tool"
|
||||||
|
assert event.tool_args == {"arg1": "value1"}
|
||||||
|
assert event.tool_class == "TestTool"
|
||||||
|
assert event.run_attempts == 1 # Default value from ToolUsage
|
||||||
|
assert event.delegations == 0
|
||||||
|
assert event.from_cache is False
|
||||||
|
assert event.output == "test output result"
|
||||||
|
assert isinstance(event.started_at, datetime.datetime)
|
||||||
|
assert isinstance(event.finished_at, datetime.datetime)
|
||||||
|
assert event.type == "tool_usage_finished"
|
||||||
|
|
||||||
|
|
||||||
|
def test_tool_usage_finished_event_with_cached_result():
|
||||||
|
"""Test that ToolUsageFinishedEvent is emitted with correct result attributes when using cached result."""
|
||||||
|
# Create mock agent with proper string values
|
||||||
|
mock_agent = MagicMock()
|
||||||
|
mock_agent.key = "test_agent_key"
|
||||||
|
mock_agent.role = "test_agent_role"
|
||||||
|
mock_agent._original_role = "test_agent_role"
|
||||||
|
mock_agent.i18n = MagicMock()
|
||||||
|
mock_agent.verbose = False
|
||||||
|
|
||||||
|
# Create mock task
|
||||||
|
mock_task = MagicMock()
|
||||||
|
mock_task.delegations = 0
|
||||||
|
|
||||||
|
# Create mock tool
|
||||||
|
class TestTool(BaseTool):
|
||||||
|
name: str = "Test Tool"
|
||||||
|
description: str = "A test tool"
|
||||||
|
|
||||||
|
def _run(self, input: dict) -> str:
|
||||||
|
return "test result"
|
||||||
|
|
||||||
|
test_tool = TestTool()
|
||||||
|
|
||||||
|
# Create mock tool calling
|
||||||
|
mock_tool_calling = MagicMock()
|
||||||
|
mock_tool_calling.arguments = {"arg1": "value1"}
|
||||||
|
|
||||||
|
# Create ToolUsage instance
|
||||||
|
tool_usage = ToolUsage(
|
||||||
|
tools_handler=MagicMock(),
|
||||||
|
tools=[test_tool],
|
||||||
|
original_tools=[test_tool],
|
||||||
|
tools_description="Test Tool Description",
|
||||||
|
tools_names="Test Tool",
|
||||||
|
task=mock_task,
|
||||||
|
function_calling_llm=None,
|
||||||
|
agent=mock_agent,
|
||||||
|
action=MagicMock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Track received events
|
||||||
|
received_events = []
|
||||||
|
|
||||||
|
@crewai_event_bus.on(ToolUsageFinishedEvent)
|
||||||
|
def event_handler(source, event):
|
||||||
|
received_events.append(event)
|
||||||
|
|
||||||
|
# Call on_tool_use_finished with test data and from_cache=True
|
||||||
|
started_at = time.time()
|
||||||
|
result = "cached test output result"
|
||||||
|
tool_usage.on_tool_use_finished(
|
||||||
|
tool=test_tool,
|
||||||
|
tool_calling=mock_tool_calling,
|
||||||
|
from_cache=True,
|
||||||
|
started_at=started_at,
|
||||||
|
result=result,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify event was emitted
|
||||||
|
assert len(received_events) == 1, "Expected one event to be emitted"
|
||||||
|
event = received_events[0]
|
||||||
|
assert isinstance(event, ToolUsageFinishedEvent)
|
||||||
|
|
||||||
|
# Verify event attributes
|
||||||
|
assert event.agent_key == "test_agent_key"
|
||||||
|
assert event.agent_role == "test_agent_role"
|
||||||
|
assert event.tool_name == "Test Tool"
|
||||||
|
assert event.tool_args == {"arg1": "value1"}
|
||||||
|
assert event.tool_class == "TestTool"
|
||||||
|
assert event.run_attempts == 1 # Default value from ToolUsage
|
||||||
|
assert event.delegations == 0
|
||||||
|
assert event.from_cache is True
|
||||||
|
assert event.output == "cached test output result"
|
||||||
|
assert isinstance(event.started_at, datetime.datetime)
|
||||||
|
assert isinstance(event.finished_at, datetime.datetime)
|
||||||
|
assert event.type == "tool_usage_finished"
|
||||||
|
|||||||
Reference in New Issue
Block a user