mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-10 00:28:31 +00:00
Address PR feedback: improve type hints, documentation and test coverage
Co-Authored-By: Joe Moura <joao@crewai.com>
This commit is contained in:
@@ -493,8 +493,18 @@ 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, result: Any = None
|
self, tool: Any, tool_calling: ToolCalling, from_cache: bool, started_at: float,
|
||||||
|
result: Union[str, dict, None] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
|
"""Handle tool usage completion event.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool: The tool that was used
|
||||||
|
tool_calling: The tool calling information
|
||||||
|
from_cache: Whether the result was retrieved from cache
|
||||||
|
started_at: Timestamp when the tool execution started
|
||||||
|
result: The execution result of the tool
|
||||||
|
"""
|
||||||
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)
|
||||||
event_data.update(
|
event_data.update(
|
||||||
@@ -502,7 +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,
|
||||||
"result": result, # Add the result to the event data
|
"result": result, # Tool execution result
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
crewai_event_bus.emit(self, ToolUsageFinishedEvent(**event_data))
|
crewai_event_bus.emit(self, ToolUsageFinishedEvent(**event_data))
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Callable, Dict
|
from typing import Any, Callable, Dict, Optional, Union
|
||||||
|
|
||||||
from .base_events import CrewEvent
|
from .base_events import CrewEvent
|
||||||
|
|
||||||
@@ -25,12 +25,16 @@ class ToolUsageStartedEvent(ToolUsageEvent):
|
|||||||
|
|
||||||
|
|
||||||
class ToolUsageFinishedEvent(ToolUsageEvent):
|
class ToolUsageFinishedEvent(ToolUsageEvent):
|
||||||
"""Event emitted when a tool execution is completed"""
|
"""Event emitted when a tool execution is completed
|
||||||
|
|
||||||
|
This event contains the result of the tool execution, allowing listeners
|
||||||
|
to access the output directly without implementing workarounds.
|
||||||
|
"""
|
||||||
|
|
||||||
started_at: datetime
|
started_at: datetime
|
||||||
finished_at: datetime
|
finished_at: datetime
|
||||||
from_cache: bool = False
|
from_cache: bool = False
|
||||||
result: Any = None # Add this field
|
result: Union[str, dict, None] = None # Tool execution result
|
||||||
type: str = "tool_usage_finished"
|
type: str = "tool_usage_finished"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from crewai.utilities.events.crew_events import (
|
|||||||
from crewai.utilities.events.crewai_event_bus import crewai_event_bus
|
from crewai.utilities.events.crewai_event_bus import crewai_event_bus
|
||||||
from crewai.utilities.events.event_listener import EventListener
|
from crewai.utilities.events.event_listener import EventListener
|
||||||
from crewai.utilities.events.event_types import ToolUsageFinishedEvent
|
from crewai.utilities.events.event_types import ToolUsageFinishedEvent
|
||||||
|
from crewai.tools.tool_calling import ToolCalling
|
||||||
from crewai.utilities.events.flow_events import (
|
from crewai.utilities.events.flow_events import (
|
||||||
FlowCreatedEvent,
|
FlowCreatedEvent,
|
||||||
FlowFinishedEvent,
|
FlowFinishedEvent,
|
||||||
@@ -329,36 +330,137 @@ class SayHiTool(BaseTool):
|
|||||||
return "hi"
|
return "hi"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.vcr(filter_headers=["authorization"])
|
class DictResultTool(BaseTool):
|
||||||
def test_tools_emits_finished_events():
|
name: str = Field(default="dict_result", description="The name of the tool")
|
||||||
|
description: str = Field(
|
||||||
|
default="Return a dictionary result",
|
||||||
|
description="The description of the tool"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run(self) -> dict:
|
||||||
|
return {"message": "success", "data": {"value": 42}}
|
||||||
|
|
||||||
|
|
||||||
|
class NoneResultTool(BaseTool):
|
||||||
|
name: str = Field(default="none_result", description="The name of the tool")
|
||||||
|
description: str = Field(
|
||||||
|
default="Return None as result",
|
||||||
|
description="The description of the tool"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run(self) -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def test_tools_emits_finished_events_with_string_result():
|
||||||
received_events = []
|
received_events = []
|
||||||
|
|
||||||
@crewai_event_bus.on(ToolUsageFinishedEvent)
|
@crewai_event_bus.on(ToolUsageFinishedEvent)
|
||||||
def handle_tool_end(source, event):
|
def handle_tool_end(source, event):
|
||||||
received_events.append(event)
|
received_events.append(event)
|
||||||
|
|
||||||
agent = Agent(
|
# Create a mock event with string result
|
||||||
role="base_agent",
|
tool = SayHiTool()
|
||||||
goal="Just say hi",
|
tool_calling = ToolCalling(tool_name=tool.name, arguments={}, log="")
|
||||||
backstory="You are a helpful assistant that just says hi",
|
event_data = {
|
||||||
tools=[SayHiTool()],
|
"agent_key": "test_agent_key",
|
||||||
)
|
"agent_role": "test_agent_role",
|
||||||
|
"tool_name": tool.name,
|
||||||
task = Task(
|
"tool_args": {},
|
||||||
description="Just say hi",
|
"tool_class": tool.__class__.__name__,
|
||||||
expected_output="hi",
|
"started_at": datetime.now(),
|
||||||
agent=agent,
|
"finished_at": datetime.now(),
|
||||||
)
|
"from_cache": False,
|
||||||
crew = Crew(agents=[agent], tasks=[task], name="TestCrew")
|
"result": "hi"
|
||||||
crew.kickoff()
|
}
|
||||||
|
|
||||||
|
# Emit the event
|
||||||
|
crewai_event_bus.emit(None, ToolUsageFinishedEvent(**event_data))
|
||||||
|
|
||||||
|
# Verify the event was received with the correct result
|
||||||
assert len(received_events) == 1
|
assert len(received_events) == 1
|
||||||
assert received_events[0].agent_key == agent.key
|
assert received_events[0].agent_key == "test_agent_key"
|
||||||
assert received_events[0].agent_role == agent.role
|
assert received_events[0].agent_role == "test_agent_role"
|
||||||
assert received_events[0].tool_name == SayHiTool().name
|
assert received_events[0].tool_name == tool.name
|
||||||
assert received_events[0].tool_args == {}
|
assert received_events[0].tool_args == {}
|
||||||
assert received_events[0].type == "tool_usage_finished"
|
assert received_events[0].type == "tool_usage_finished"
|
||||||
assert isinstance(received_events[0].timestamp, datetime)
|
assert isinstance(received_events[0].timestamp, datetime)
|
||||||
assert received_events[0].result == "hi" # The SayHiTool returns "hi"
|
assert received_events[0].result == "hi"
|
||||||
|
|
||||||
|
|
||||||
|
def test_tools_emits_finished_events_with_dict_result():
|
||||||
|
received_events = []
|
||||||
|
|
||||||
|
@crewai_event_bus.on(ToolUsageFinishedEvent)
|
||||||
|
def handle_tool_end(source, event):
|
||||||
|
received_events.append(event)
|
||||||
|
|
||||||
|
# Create a mock event with dictionary result
|
||||||
|
tool = DictResultTool()
|
||||||
|
tool_calling = ToolCalling(tool_name=tool.name, arguments={}, log="")
|
||||||
|
dict_result = {"message": "success", "data": {"value": 42}}
|
||||||
|
event_data = {
|
||||||
|
"agent_key": "test_agent_key",
|
||||||
|
"agent_role": "test_agent_role",
|
||||||
|
"tool_name": tool.name,
|
||||||
|
"tool_args": {},
|
||||||
|
"tool_class": tool.__class__.__name__,
|
||||||
|
"started_at": datetime.now(),
|
||||||
|
"finished_at": datetime.now(),
|
||||||
|
"from_cache": False,
|
||||||
|
"result": dict_result
|
||||||
|
}
|
||||||
|
|
||||||
|
# Emit the event
|
||||||
|
crewai_event_bus.emit(None, ToolUsageFinishedEvent(**event_data))
|
||||||
|
|
||||||
|
# Verify the event was received with the correct result
|
||||||
|
assert len(received_events) == 1
|
||||||
|
assert received_events[0].agent_key == "test_agent_key"
|
||||||
|
assert received_events[0].agent_role == "test_agent_role"
|
||||||
|
assert received_events[0].tool_name == tool.name
|
||||||
|
assert received_events[0].tool_args == {}
|
||||||
|
assert received_events[0].type == "tool_usage_finished"
|
||||||
|
assert isinstance(received_events[0].timestamp, datetime)
|
||||||
|
assert isinstance(received_events[0].result, dict)
|
||||||
|
assert received_events[0].result["message"] == "success"
|
||||||
|
assert received_events[0].result["data"]["value"] == 42
|
||||||
|
|
||||||
|
|
||||||
|
def test_tools_emits_finished_events_with_none_result():
|
||||||
|
received_events = []
|
||||||
|
|
||||||
|
@crewai_event_bus.on(ToolUsageFinishedEvent)
|
||||||
|
def handle_tool_end(source, event):
|
||||||
|
received_events.append(event)
|
||||||
|
|
||||||
|
# Create a mock event with None result
|
||||||
|
tool = NoneResultTool()
|
||||||
|
tool_calling = ToolCalling(tool_name=tool.name, arguments={}, log="")
|
||||||
|
event_data = {
|
||||||
|
"agent_key": "test_agent_key",
|
||||||
|
"agent_role": "test_agent_role",
|
||||||
|
"tool_name": tool.name,
|
||||||
|
"tool_args": {},
|
||||||
|
"tool_class": tool.__class__.__name__,
|
||||||
|
"started_at": datetime.now(),
|
||||||
|
"finished_at": datetime.now(),
|
||||||
|
"from_cache": False,
|
||||||
|
"result": None
|
||||||
|
}
|
||||||
|
|
||||||
|
# Emit the event
|
||||||
|
crewai_event_bus.emit(None, ToolUsageFinishedEvent(**event_data))
|
||||||
|
|
||||||
|
# Verify the event was received with the correct result
|
||||||
|
assert len(received_events) == 1
|
||||||
|
assert received_events[0].agent_key == "test_agent_key"
|
||||||
|
assert received_events[0].agent_role == "test_agent_role"
|
||||||
|
assert received_events[0].tool_name == tool.name
|
||||||
|
assert received_events[0].tool_args == {}
|
||||||
|
assert received_events[0].type == "tool_usage_finished"
|
||||||
|
assert isinstance(received_events[0].timestamp, datetime)
|
||||||
|
assert received_events[0].result is None
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.vcr(filter_headers=["authorization"])
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||||
|
|||||||
Reference in New Issue
Block a user