mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-11 00:58:30 +00:00
feat: add agent logging events for execution tracking
- Introduced AgentLogsStartedEvent and AgentLogsExecutionEvent to enhance logging capabilities during agent execution. - Updated CrewAgentExecutor to emit these events at the start and during execution, respectively. - Modified EventListener to handle the new logging events and format output accordingly in the console. - Enhanced ConsoleFormatter to display agent logs in a structured format, improving visibility of agent actions and outputs.
This commit is contained in:
@@ -25,12 +25,16 @@ from crewai.utilities.agent_utils import (
|
|||||||
has_reached_max_iterations,
|
has_reached_max_iterations,
|
||||||
is_context_length_exceeded,
|
is_context_length_exceeded,
|
||||||
process_llm_response,
|
process_llm_response,
|
||||||
show_agent_logs,
|
|
||||||
)
|
)
|
||||||
from crewai.utilities.constants import MAX_LLM_RETRY, TRAINING_DATA_FILE
|
from crewai.utilities.constants import MAX_LLM_RETRY, TRAINING_DATA_FILE
|
||||||
from crewai.utilities.logger import Logger
|
from crewai.utilities.logger import Logger
|
||||||
from crewai.utilities.tool_utils import execute_tool_and_check_finality
|
from crewai.utilities.tool_utils import execute_tool_and_check_finality
|
||||||
from crewai.utilities.training_handler import CrewTrainingHandler
|
from crewai.utilities.training_handler import CrewTrainingHandler
|
||||||
|
from crewai.utilities.events.agent_events import (
|
||||||
|
AgentLogsStartedEvent,
|
||||||
|
AgentLogsExecutionEvent,
|
||||||
|
)
|
||||||
|
from crewai.utilities.events.crewai_event_bus import crewai_event_bus
|
||||||
|
|
||||||
|
|
||||||
class CrewAgentExecutor(CrewAgentExecutorMixin):
|
class CrewAgentExecutor(CrewAgentExecutorMixin):
|
||||||
@@ -263,26 +267,32 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
|||||||
"""Show logs for the start of agent execution."""
|
"""Show logs for the start of agent execution."""
|
||||||
if self.agent is None:
|
if self.agent is None:
|
||||||
raise ValueError("Agent cannot be None")
|
raise ValueError("Agent cannot be None")
|
||||||
show_agent_logs(
|
|
||||||
printer=self._printer,
|
crewai_event_bus.emit(
|
||||||
|
self.agent,
|
||||||
|
AgentLogsStartedEvent(
|
||||||
agent_role=self.agent.role,
|
agent_role=self.agent.role,
|
||||||
task_description=(
|
task_description=(
|
||||||
getattr(self.task, "description") if self.task else "Not Found"
|
getattr(self.task, "description") if self.task else "Not Found"
|
||||||
),
|
),
|
||||||
verbose=self.agent.verbose
|
verbose=self.agent.verbose
|
||||||
or (hasattr(self, "crew") and getattr(self.crew, "verbose", False)),
|
or (hasattr(self, "crew") and getattr(self.crew, "verbose", False)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _show_logs(self, formatted_answer: Union[AgentAction, AgentFinish]):
|
def _show_logs(self, formatted_answer: Union[AgentAction, AgentFinish]):
|
||||||
"""Show logs for the agent's execution."""
|
"""Show logs for the agent's execution."""
|
||||||
if self.agent is None:
|
if self.agent is None:
|
||||||
raise ValueError("Agent cannot be None")
|
raise ValueError("Agent cannot be None")
|
||||||
show_agent_logs(
|
|
||||||
printer=self._printer,
|
crewai_event_bus.emit(
|
||||||
|
self.agent,
|
||||||
|
AgentLogsExecutionEvent(
|
||||||
agent_role=self.agent.role,
|
agent_role=self.agent.role,
|
||||||
formatted_answer=formatted_answer,
|
formatted_answer=formatted_answer,
|
||||||
verbose=self.agent.verbose
|
verbose=self.agent.verbose
|
||||||
or (hasattr(self, "crew") and getattr(self.crew, "verbose", False)),
|
or (hasattr(self, "crew") and getattr(self.crew, "verbose", False)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _summarize_messages(self) -> None:
|
def _summarize_messages(self) -> None:
|
||||||
|
|||||||
@@ -102,3 +102,24 @@ class LiteAgentExecutionErrorEvent(BaseEvent):
|
|||||||
agent_info: Dict[str, Any]
|
agent_info: Dict[str, Any]
|
||||||
error: str
|
error: str
|
||||||
type: str = "lite_agent_execution_error"
|
type: str = "lite_agent_execution_error"
|
||||||
|
|
||||||
|
|
||||||
|
# New logging events
|
||||||
|
class AgentLogsStartedEvent(BaseEvent):
|
||||||
|
"""Event emitted when agent logs should be shown at start"""
|
||||||
|
|
||||||
|
agent_role: str
|
||||||
|
task_description: Optional[str] = None
|
||||||
|
verbose: bool = False
|
||||||
|
type: str = "agent_logs_started"
|
||||||
|
|
||||||
|
|
||||||
|
class AgentLogsExecutionEvent(BaseEvent):
|
||||||
|
"""Event emitted when agent logs should be shown during execution"""
|
||||||
|
|
||||||
|
agent_role: str
|
||||||
|
formatted_answer: Any
|
||||||
|
verbose: bool = False
|
||||||
|
type: str = "agent_logs_execution"
|
||||||
|
|
||||||
|
model_config = {"arbitrary_types_allowed": True}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ from crewai.utilities.events.utils.console_formatter import ConsoleFormatter
|
|||||||
from .agent_events import (
|
from .agent_events import (
|
||||||
AgentExecutionCompletedEvent,
|
AgentExecutionCompletedEvent,
|
||||||
AgentExecutionStartedEvent,
|
AgentExecutionStartedEvent,
|
||||||
|
AgentLogsStartedEvent,
|
||||||
|
AgentLogsExecutionEvent,
|
||||||
LiteAgentExecutionCompletedEvent,
|
LiteAgentExecutionCompletedEvent,
|
||||||
LiteAgentExecutionErrorEvent,
|
LiteAgentExecutionErrorEvent,
|
||||||
LiteAgentExecutionStartedEvent,
|
LiteAgentExecutionStartedEvent,
|
||||||
@@ -466,5 +468,23 @@ class EventListener(BaseEventListener):
|
|||||||
self.formatter.current_crew_tree,
|
self.formatter.current_crew_tree,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ----------- AGENT LOGGING EVENTS -----------
|
||||||
|
|
||||||
|
@crewai_event_bus.on(AgentLogsStartedEvent)
|
||||||
|
def on_agent_logs_started(source, event: AgentLogsStartedEvent):
|
||||||
|
self.formatter.handle_agent_logs_started(
|
||||||
|
event.agent_role,
|
||||||
|
event.task_description,
|
||||||
|
event.verbose,
|
||||||
|
)
|
||||||
|
|
||||||
|
@crewai_event_bus.on(AgentLogsExecutionEvent)
|
||||||
|
def on_agent_logs_execution(source, event: AgentLogsExecutionEvent):
|
||||||
|
self.formatter.handle_agent_logs_execution(
|
||||||
|
event.agent_role,
|
||||||
|
event.formatted_answer,
|
||||||
|
event.verbose,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
event_listener = EventListener()
|
event_listener = EventListener()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from rich.panel import Panel
|
|||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
from rich.tree import Tree
|
from rich.tree import Tree
|
||||||
from rich.live import Live
|
from rich.live import Live
|
||||||
|
from rich.syntax import Syntax
|
||||||
|
|
||||||
|
|
||||||
class ConsoleFormatter:
|
class ConsoleFormatter:
|
||||||
@@ -498,9 +499,7 @@ class ConsoleFormatter:
|
|||||||
|
|
||||||
# Parent for tool usage: LiteAgent > Agent > Task
|
# Parent for tool usage: LiteAgent > Agent > Task
|
||||||
branch_to_use = (
|
branch_to_use = (
|
||||||
self.current_lite_agent_branch
|
self.current_lite_agent_branch or agent_branch or self.current_task_branch
|
||||||
or agent_branch
|
|
||||||
or self.current_task_branch
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Render full crew tree when available for consistent live updates
|
# Render full crew tree when available for consistent live updates
|
||||||
@@ -609,9 +608,7 @@ class ConsoleFormatter:
|
|||||||
|
|
||||||
# Parent for tool usage: LiteAgent > Agent > Task
|
# Parent for tool usage: LiteAgent > Agent > Task
|
||||||
branch_to_use = (
|
branch_to_use = (
|
||||||
self.current_lite_agent_branch
|
self.current_lite_agent_branch or agent_branch or self.current_task_branch
|
||||||
or agent_branch
|
|
||||||
or self.current_task_branch
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Render full crew tree when available for consistent live updates
|
# Render full crew tree when available for consistent live updates
|
||||||
@@ -626,9 +623,8 @@ class ConsoleFormatter:
|
|||||||
|
|
||||||
# Only add thinking status if we don't have a current tool branch
|
# Only add thinking status if we don't have a current tool branch
|
||||||
# or if the current tool branch is not a thinking node
|
# or if the current tool branch is not a thinking node
|
||||||
should_add_thinking = (
|
should_add_thinking = self.current_tool_branch is None or "Thinking" not in str(
|
||||||
self.current_tool_branch is None or
|
self.current_tool_branch.label
|
||||||
"Thinking" not in str(self.current_tool_branch.label)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if should_add_thinking:
|
if should_add_thinking:
|
||||||
@@ -691,7 +687,10 @@ class ConsoleFormatter:
|
|||||||
tree_to_use,
|
tree_to_use,
|
||||||
]
|
]
|
||||||
for parent in parents:
|
for parent in parents:
|
||||||
if isinstance(parent, Tree) and thinking_branch_to_remove in parent.children:
|
if (
|
||||||
|
isinstance(parent, Tree)
|
||||||
|
and thinking_branch_to_remove in parent.children
|
||||||
|
):
|
||||||
parent.children.remove(thinking_branch_to_remove)
|
parent.children.remove(thinking_branch_to_remove)
|
||||||
removed = True
|
removed = True
|
||||||
break
|
break
|
||||||
@@ -1186,9 +1185,7 @@ class ConsoleFormatter:
|
|||||||
|
|
||||||
# Prefer LiteAgent > Agent > Task branch as the parent for reasoning
|
# Prefer LiteAgent > Agent > Task branch as the parent for reasoning
|
||||||
branch_to_use = (
|
branch_to_use = (
|
||||||
self.current_lite_agent_branch
|
self.current_lite_agent_branch or agent_branch or self.current_task_branch
|
||||||
or agent_branch
|
|
||||||
or self.current_task_branch
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# We always want to render the full crew tree when possible so the
|
# We always want to render the full crew tree when possible so the
|
||||||
@@ -1235,7 +1232,9 @@ class ConsoleFormatter:
|
|||||||
)
|
)
|
||||||
|
|
||||||
style = "green" if ready else "yellow"
|
style = "green" if ready else "yellow"
|
||||||
status_text = "Reasoning Completed" if ready else "Reasoning Completed (Not Ready)"
|
status_text = (
|
||||||
|
"Reasoning Completed" if ready else "Reasoning Completed (Not Ready)"
|
||||||
|
)
|
||||||
|
|
||||||
if reasoning_branch is not None:
|
if reasoning_branch is not None:
|
||||||
self.update_tree_label(reasoning_branch, "✅", status_text, style)
|
self.update_tree_label(reasoning_branch, "✅", status_text, style)
|
||||||
@@ -1292,3 +1291,149 @@ class ConsoleFormatter:
|
|||||||
|
|
||||||
# Clear stored branch after failure
|
# Clear stored branch after failure
|
||||||
self.current_reasoning_branch = None
|
self.current_reasoning_branch = None
|
||||||
|
|
||||||
|
# ----------- AGENT LOGGING EVENTS -----------
|
||||||
|
|
||||||
|
def handle_agent_logs_started(
|
||||||
|
self,
|
||||||
|
agent_role: str,
|
||||||
|
task_description: Optional[str] = None,
|
||||||
|
verbose: bool = False,
|
||||||
|
) -> None:
|
||||||
|
"""Handle agent logs started event."""
|
||||||
|
if not verbose:
|
||||||
|
return
|
||||||
|
|
||||||
|
agent_role = agent_role.split("\n")[0]
|
||||||
|
|
||||||
|
# Create panel content
|
||||||
|
content = Text()
|
||||||
|
content.append("Agent: ", style="white")
|
||||||
|
content.append(f"{agent_role}", style="bright_green bold")
|
||||||
|
|
||||||
|
if task_description:
|
||||||
|
content.append("\n\nTask: ", style="white")
|
||||||
|
content.append(f"{task_description}", style="bright_green")
|
||||||
|
|
||||||
|
# Create and display the panel
|
||||||
|
agent_panel = Panel(
|
||||||
|
content,
|
||||||
|
title="🤖 Agent Started",
|
||||||
|
border_style="magenta",
|
||||||
|
padding=(1, 2),
|
||||||
|
)
|
||||||
|
self.print(agent_panel)
|
||||||
|
self.print()
|
||||||
|
|
||||||
|
def handle_agent_logs_execution(
|
||||||
|
self,
|
||||||
|
agent_role: str,
|
||||||
|
formatted_answer: Any,
|
||||||
|
verbose: bool = False,
|
||||||
|
) -> None:
|
||||||
|
"""Handle agent logs execution event."""
|
||||||
|
if not verbose:
|
||||||
|
return
|
||||||
|
|
||||||
|
from crewai.agents.parser import AgentAction, AgentFinish
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
agent_role = agent_role.split("\n")[0]
|
||||||
|
|
||||||
|
if isinstance(formatted_answer, AgentAction):
|
||||||
|
thought = re.sub(r"\n+", "\n", formatted_answer.thought)
|
||||||
|
formatted_json = json.dumps(
|
||||||
|
formatted_answer.tool_input,
|
||||||
|
indent=2,
|
||||||
|
ensure_ascii=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create content for the action panel
|
||||||
|
content = Text()
|
||||||
|
content.append("Agent: ", style="white")
|
||||||
|
content.append(f"{agent_role}\n\n", style="bright_green bold")
|
||||||
|
|
||||||
|
if thought and thought != "":
|
||||||
|
content.append("Thought: ", style="white")
|
||||||
|
content.append(f"{thought}\n\n", style="bright_green")
|
||||||
|
|
||||||
|
content.append("Using Tool: ", style="white")
|
||||||
|
content.append(f"{formatted_answer.tool}\n\n", style="bright_green bold")
|
||||||
|
|
||||||
|
content.append("Tool Input:\n", style="white")
|
||||||
|
|
||||||
|
# Create a syntax-highlighted JSON code block
|
||||||
|
json_syntax = Syntax(
|
||||||
|
formatted_json,
|
||||||
|
"json",
|
||||||
|
theme="monokai",
|
||||||
|
line_numbers=False,
|
||||||
|
background_color="default",
|
||||||
|
)
|
||||||
|
|
||||||
|
content.append("\n")
|
||||||
|
|
||||||
|
# Create separate panels for better organization
|
||||||
|
main_content = Text()
|
||||||
|
main_content.append("Agent: ", style="white")
|
||||||
|
main_content.append(f"{agent_role}\n\n", style="bright_green bold")
|
||||||
|
|
||||||
|
if thought and thought != "":
|
||||||
|
main_content.append("Thought: ", style="white")
|
||||||
|
main_content.append(f"{thought}\n\n", style="bright_green")
|
||||||
|
|
||||||
|
main_content.append("Using Tool: ", style="white")
|
||||||
|
main_content.append(f"{formatted_answer.tool}", style="bright_green bold")
|
||||||
|
|
||||||
|
# Create the main action panel
|
||||||
|
action_panel = Panel(
|
||||||
|
main_content,
|
||||||
|
title="🔧 Agent Tool Execution",
|
||||||
|
border_style="magenta",
|
||||||
|
padding=(1, 2),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the JSON input panel
|
||||||
|
input_panel = Panel(
|
||||||
|
json_syntax,
|
||||||
|
title="📥 Tool Input",
|
||||||
|
border_style="blue",
|
||||||
|
padding=(1, 2),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create tool output content with better formatting
|
||||||
|
output_text = str(formatted_answer.result)
|
||||||
|
if len(output_text) > 1000:
|
||||||
|
output_text = output_text[:997] + "..."
|
||||||
|
|
||||||
|
output_panel = Panel(
|
||||||
|
Text(output_text, style="bright_green"),
|
||||||
|
title="📤 Tool Output",
|
||||||
|
border_style="green",
|
||||||
|
padding=(1, 2),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Print all panels
|
||||||
|
self.print(action_panel)
|
||||||
|
self.print(input_panel)
|
||||||
|
self.print(output_panel)
|
||||||
|
self.print()
|
||||||
|
|
||||||
|
elif isinstance(formatted_answer, AgentFinish):
|
||||||
|
# Create content for the finish panel
|
||||||
|
content = Text()
|
||||||
|
content.append("Agent: ", style="white")
|
||||||
|
content.append(f"{agent_role}\n\n", style="bright_green bold")
|
||||||
|
content.append("Final Answer:\n", style="white")
|
||||||
|
content.append(f"{formatted_answer.output}", style="bright_green")
|
||||||
|
|
||||||
|
# Create and display the finish panel
|
||||||
|
finish_panel = Panel(
|
||||||
|
content,
|
||||||
|
title="✅ Agent Final Answer",
|
||||||
|
border_style="green",
|
||||||
|
padding=(1, 2),
|
||||||
|
)
|
||||||
|
self.print(finish_panel)
|
||||||
|
self.print()
|
||||||
|
|||||||
Reference in New Issue
Block a user