Updating Logging (#2874)

This commit is contained in:
João Moura
2025-05-21 09:44:56 -07:00
committed by GitHub
parent b3484c1d0e
commit 169d3233e8
3 changed files with 147 additions and 80 deletions

View File

@@ -64,7 +64,7 @@ class Telemetry:
attribute in the Crew class. attribute in the Crew class.
""" """
def __init__(self): def __init__(self) -> None:
self.ready: bool = False self.ready: bool = False
self.trace_set: bool = False self.trace_set: bool = False

View File

@@ -4,6 +4,7 @@ from rich.console import Console
from rich.panel import Panel 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
class ConsoleFormatter: class ConsoleFormatter:
@@ -20,6 +21,12 @@ class ConsoleFormatter:
def __init__(self, verbose: bool = False): def __init__(self, verbose: bool = False):
self.console = Console(width=None) self.console = Console(width=None)
self.verbose = verbose self.verbose = verbose
# Live instance to dynamically update a Tree renderable (e.g. the Crew tree)
# When multiple Tree objects are printed sequentially we reuse this Live
# instance so the previous render is replaced instead of writing a new one.
# Once any non-Tree renderable is printed we stop the Live session so the
# final Tree persists on the terminal.
self._live: Optional[Live] = None
def create_panel(self, content: Text, title: str, style: str = "blue") -> Panel: def create_panel(self, content: Text, title: str, style: str = "blue") -> Panel:
"""Create a standardized panel with consistent styling.""" """Create a standardized panel with consistent styling."""
@@ -60,7 +67,7 @@ class ConsoleFormatter:
label.append(f"{prefix} ", style=f"{style} bold") label.append(f"{prefix} ", style=f"{style} bold")
label.append(name, style=style) label.append(name, style=style)
if status: if status:
label.append("\n Status: ", style="white") label.append("\nStatus: ", style="white")
label.append(status, style=f"{style} bold") label.append(status, style=f"{style} bold")
tree.label = label tree.label = label
@@ -69,7 +76,46 @@ class ConsoleFormatter:
return parent.add(Text(text, style=style)) return parent.add(Text(text, style=style))
def print(self, *args, **kwargs) -> None: def print(self, *args, **kwargs) -> None:
"""Print to console with consistent formatting if verbose is enabled.""" """Custom print that replaces consecutive Tree renders.
* If the argument is a single ``Tree`` instance, we either start a
``Live`` session (first tree) or update the existing one (subsequent
trees). This results in the tree being rendered in-place instead of
being appended repeatedly to the log.
* A blank call (no positional arguments) is ignored while a Live
session is active so it does not prematurely terminate the tree
rendering.
* Any other renderable will terminate the Live session (if one is
active) so the last tree stays on screen and the new content is
printed normally.
"""
# Case 1: updating / starting live Tree rendering
if len(args) == 1 and isinstance(args[0], Tree):
tree = args[0]
if not self._live:
# Start a new Live session for the first tree
self._live = Live(tree, console=self.console, refresh_per_second=4)
self._live.start()
else:
# Update existing Live session
self._live.update(tree, refresh=True)
return # Nothing else to do
# Case 2: blank line while a live session is running ignore so we
# don't break the in-place rendering behaviour
if len(args) == 0 and self._live:
return
# Case 3: printing something other than a Tree → terminate live session
if self._live:
self._live.stop()
self._live = None
# Finally, pass through to the regular Console.print implementation
self.console.print(*args, **kwargs) self.console.print(*args, **kwargs)
def print_panel( def print_panel(
@@ -157,7 +203,7 @@ class ConsoleFormatter:
task_content = Text() task_content = Text()
task_content.append(f"📋 Task: {task_id}", style="yellow bold") task_content.append(f"📋 Task: {task_id}", style="yellow bold")
task_content.append("\n Status: ", style="white") task_content.append("\nStatus: ", style="white")
task_content.append("Executing Task...", style="yellow dim") task_content.append("Executing Task...", style="yellow dim")
task_branch = None task_branch = None
@@ -197,11 +243,17 @@ class ConsoleFormatter:
# Update tree label # Update tree label
for branch in crew_tree.children: for branch in crew_tree.children:
if str(task_id) in str(branch.label): if str(task_id) in str(branch.label):
# Build label without introducing stray blank lines
task_content = Text() task_content = Text()
# First line: Task ID
task_content.append(f"📋 Task: {task_id}", style=f"{style} bold") task_content.append(f"📋 Task: {task_id}", style=f"{style} bold")
task_content.append("\n Assigned to: ", style="white")
# Second line: Assigned to
task_content.append("\nAssigned to: ", style="white")
task_content.append(agent_role, style=style) task_content.append(agent_role, style=style)
task_content.append("\n Status: ", style="white")
# Third line: Status
task_content.append("Status: ", style="white")
task_content.append(status_text, style=f"{style} bold") task_content.append(status_text, style=f"{style} bold")
branch.label = task_content branch.label = task_content
self.print(crew_tree) self.print(crew_tree)
@@ -220,18 +272,16 @@ class ConsoleFormatter:
if not self.verbose or not task_branch or not crew_tree: if not self.verbose or not task_branch or not crew_tree:
return None return None
agent_branch = task_branch.add("") # Instead of creating a separate Agent node, we treat the task branch
self.update_tree_label( # itself as the logical agent branch so that Reasoning/Tool nodes are
agent_branch, "🤖 Agent:", agent_role, "green", "In Progress" # nested under the task without an extra visual level.
)
self.print(crew_tree) # Store the task branch as the current_agent_branch for future nesting.
self.print() self.current_agent_branch = task_branch
# Set the current_agent_branch attribute directly # No additional tree modification needed; return the task branch so
self.current_agent_branch = agent_branch # caller logic remains unchanged.
return task_branch
return agent_branch
def update_agent_status( def update_agent_status(
self, self,
@@ -241,19 +291,10 @@ class ConsoleFormatter:
status: str = "completed", status: str = "completed",
) -> None: ) -> None:
"""Update agent status in the tree.""" """Update agent status in the tree."""
if not self.verbose or agent_branch is None or crew_tree is None: # We no longer render a separate agent branch, so this method simply
return # updates the stored branch reference (already the task branch) without
# altering the tree. Keeping it a no-op avoids duplicate status lines.
self.update_tree_label( return
agent_branch,
"🤖 Agent:",
agent_role,
"green",
"✅ Completed" if status == "completed" else "❌ Failed",
)
self.print(crew_tree)
self.print()
def create_flow_tree(self, flow_name: str, flow_id: str) -> Optional[Tree]: def create_flow_tree(self, flow_name: str, flow_id: str) -> Optional[Tree]:
"""Create and initialize a flow tree.""" """Create and initialize a flow tree."""
@@ -266,7 +307,7 @@ class ConsoleFormatter:
flow_label = Text() flow_label = Text()
flow_label.append("🌊 Flow: ", style="blue bold") flow_label.append("🌊 Flow: ", style="blue bold")
flow_label.append(flow_name, style="blue") flow_label.append(flow_name, style="blue")
flow_label.append("\n ID: ", style="white") flow_label.append("\nID: ", style="white")
flow_label.append(flow_id, style="blue") flow_label.append(flow_id, style="blue")
flow_tree = Tree(flow_label) flow_tree = Tree(flow_label)
@@ -281,7 +322,7 @@ class ConsoleFormatter:
flow_label = Text() flow_label = Text()
flow_label.append("🌊 Flow: ", style="blue bold") flow_label.append("🌊 Flow: ", style="blue bold")
flow_label.append(flow_name, style="blue") flow_label.append(flow_name, style="blue")
flow_label.append("\n ID: ", style="white") flow_label.append("\nID: ", style="white")
flow_label.append(flow_id, style="blue") flow_label.append(flow_id, style="blue")
flow_tree.label = flow_label flow_tree.label = flow_label
@@ -395,9 +436,15 @@ class ConsoleFormatter:
if not self.verbose: if not self.verbose:
return None return None
# Use LiteAgent branch if available, otherwise use regular agent branch # Parent for tool usage: LiteAgent > Agent > Task
branch_to_use = self.current_lite_agent_branch or agent_branch branch_to_use = (
tree_to_use = branch_to_use or crew_tree self.current_lite_agent_branch
or agent_branch
or self.current_task_branch
)
# Render full crew tree when available for consistent live updates
tree_to_use = self.current_crew_tree or crew_tree or branch_to_use
if branch_to_use is None or tree_to_use is None: if branch_to_use is None or tree_to_use is None:
# If we don't have a valid branch, default to crew_tree if provided # If we don't have a valid branch, default to crew_tree if provided
@@ -423,10 +470,9 @@ class ConsoleFormatter:
"yellow", "yellow",
) )
# Only print if this is a new tool usage # Print updated tree immediately
if tool_branch not in branch_to_use.children: self.print(tree_to_use)
self.print(tree_to_use) self.print()
self.print()
return tool_branch return tool_branch
@@ -440,8 +486,8 @@ class ConsoleFormatter:
if not self.verbose or tool_branch is None: if not self.verbose or tool_branch is None:
return return
# Use LiteAgent branch if available, otherwise use crew tree # Decide which tree to render: prefer full crew tree, else parent branch
tree_to_use = self.current_lite_agent_branch or crew_tree tree_to_use = self.current_crew_tree or crew_tree or self.current_task_branch
if tree_to_use is None: if tree_to_use is None:
return return
@@ -472,8 +518,8 @@ class ConsoleFormatter:
if not self.verbose: if not self.verbose:
return return
# Use LiteAgent branch if available, otherwise use crew tree # Decide which tree to render: prefer full crew tree, else parent branch
tree_to_use = self.current_lite_agent_branch or crew_tree tree_to_use = self.current_crew_tree or crew_tree or self.current_task_branch
if tool_branch: if tool_branch:
self.update_tree_label( self.update_tree_label(
@@ -501,9 +547,15 @@ class ConsoleFormatter:
if not self.verbose: if not self.verbose:
return None return None
# Use LiteAgent branch if available, otherwise use regular agent branch # Parent for tool usage: LiteAgent > Agent > Task
branch_to_use = self.current_lite_agent_branch or agent_branch branch_to_use = (
tree_to_use = branch_to_use or crew_tree self.current_lite_agent_branch
or agent_branch
or self.current_task_branch
)
# Render full crew tree when available for consistent live updates
tree_to_use = self.current_crew_tree or crew_tree or branch_to_use
if branch_to_use is None or tree_to_use is None: if branch_to_use is None or tree_to_use is None:
# If we don't have a valid branch, default to crew_tree if provided # If we don't have a valid branch, default to crew_tree if provided
@@ -532,24 +584,33 @@ class ConsoleFormatter:
if not self.verbose or tool_branch is None: if not self.verbose or tool_branch is None:
return return
# Use LiteAgent branch if available, otherwise use regular agent branch # Decide which tree to render: prefer full crew tree, else parent branch
branch_to_use = self.current_lite_agent_branch or agent_branch tree_to_use = self.current_crew_tree or crew_tree or self.current_task_branch
tree_to_use = branch_to_use or crew_tree if tree_to_use is None:
if branch_to_use is None or tree_to_use is None:
return return
# Remove the thinking status node when complete, but only if it exists # Remove the thinking status node when complete
if "Thinking" in str(tool_branch.label): if "Thinking" in str(tool_branch.label):
try: parents = [
# Check if the node is actually in the children list self.current_lite_agent_branch,
if tool_branch in branch_to_use.children: self.current_agent_branch,
branch_to_use.children.remove(tool_branch) self.current_task_branch,
self.print(tree_to_use) tree_to_use,
self.print() ]
except Exception: removed = False
# If any error occurs during removal, just continue without removing for parent in parents:
pass if isinstance(parent, Tree) and tool_branch in parent.children:
parent.children.remove(tool_branch)
removed = True
break
# Clear pointer if we just removed the current_tool_branch
if self.current_tool_branch is tool_branch:
self.current_tool_branch = None
if removed:
self.print(tree_to_use)
self.print()
def handle_llm_call_failed( def handle_llm_call_failed(
self, tool_branch: Optional[Tree], error: str, crew_tree: Optional[Tree] self, tool_branch: Optional[Tree], error: str, crew_tree: Optional[Tree]
@@ -558,8 +619,8 @@ class ConsoleFormatter:
if not self.verbose: if not self.verbose:
return return
# Use LiteAgent branch if available, otherwise use crew tree # Decide which tree to render: prefer full crew tree, else parent branch
tree_to_use = self.current_lite_agent_branch or crew_tree tree_to_use = self.current_crew_tree or crew_tree or self.current_task_branch
# Update tool branch if it exists # Update tool branch if it exists
if tool_branch: if tool_branch:
@@ -601,7 +662,7 @@ class ConsoleFormatter:
test_label = Text() test_label = Text()
test_label.append("🧪 Test: ", style="blue bold") test_label.append("🧪 Test: ", style="blue bold")
test_label.append(crew_name or "Crew", style="blue") test_label.append(crew_name or "Crew", style="blue")
test_label.append("\n Status: ", style="white") test_label.append("\nStatus: ", style="white")
test_label.append("In Progress", style="yellow") test_label.append("In Progress", style="yellow")
test_tree = Tree(test_label) test_tree = Tree(test_label)
@@ -623,7 +684,7 @@ class ConsoleFormatter:
test_label = Text() test_label = Text()
test_label.append("✅ Test: ", style="green bold") test_label.append("✅ Test: ", style="green bold")
test_label.append(crew_name or "Crew", style="green") test_label.append(crew_name or "Crew", style="green")
test_label.append("\n Status: ", style="white") test_label.append("\nStatus: ", style="white")
test_label.append("Completed", style="green bold") test_label.append("Completed", style="green bold")
flow_tree.label = test_label flow_tree.label = test_label
@@ -712,7 +773,7 @@ class ConsoleFormatter:
lite_agent_label = Text() lite_agent_label = Text()
lite_agent_label.append("🤖 LiteAgent: ", style="cyan bold") lite_agent_label.append("🤖 LiteAgent: ", style="cyan bold")
lite_agent_label.append(lite_agent_role, style="cyan") lite_agent_label.append(lite_agent_role, style="cyan")
lite_agent_label.append("\n Status: ", style="white") lite_agent_label.append("\nStatus: ", style="white")
lite_agent_label.append("In Progress", style="yellow") lite_agent_label.append("In Progress", style="yellow")
lite_agent_tree = Tree(lite_agent_label) lite_agent_tree = Tree(lite_agent_label)
@@ -751,7 +812,7 @@ class ConsoleFormatter:
lite_agent_label = Text() lite_agent_label = Text()
lite_agent_label.append(f"{prefix} ", style=f"{style} bold") lite_agent_label.append(f"{prefix} ", style=f"{style} bold")
lite_agent_label.append(lite_agent_role, style=style) lite_agent_label.append(lite_agent_role, style=style)
lite_agent_label.append("\n Status: ", style="white") lite_agent_label.append("Status: ", style="white")
lite_agent_label.append(status_text, style=f"{style} bold") lite_agent_label.append(status_text, style=f"{style} bold")
lite_agent_branch.label = lite_agent_label lite_agent_branch.label = lite_agent_label
@@ -1004,16 +1065,20 @@ class ConsoleFormatter:
if not self.verbose: if not self.verbose:
return None return None
# Prefer LiteAgent branch if we are within a LiteAgent context # Prefer LiteAgent > Agent > Task branch as the parent for reasoning
branch_to_use = self.current_lite_agent_branch or agent_branch branch_to_use = (
tree_to_use = branch_to_use or crew_tree self.current_lite_agent_branch
or agent_branch
or self.current_task_branch
)
if branch_to_use is None or tree_to_use is None: # We always want to render the full crew tree when possible so the
# If we don't have a valid branch, default to crew_tree if provided # Live view updates coherently. Fallbacks: crew tree → branch itself.
if crew_tree is not None: tree_to_use = self.current_crew_tree or crew_tree or branch_to_use
branch_to_use = tree_to_use = crew_tree
else: if branch_to_use is None:
return None # Nothing to attach to, abort
return None
# Reuse existing reasoning branch if present # Reuse existing reasoning branch if present
reasoning_branch = self.current_reasoning_branch reasoning_branch = self.current_reasoning_branch
@@ -1044,8 +1109,9 @@ class ConsoleFormatter:
reasoning_branch = self.current_reasoning_branch reasoning_branch = self.current_reasoning_branch
tree_to_use = ( tree_to_use = (
self.current_lite_agent_branch self.current_crew_tree
or self.current_agent_branch or self.current_lite_agent_branch
or self.current_task_branch
or crew_tree or crew_tree
) )
@@ -1084,8 +1150,9 @@ class ConsoleFormatter:
reasoning_branch = self.current_reasoning_branch reasoning_branch = self.current_reasoning_branch
tree_to_use = ( tree_to_use = (
self.current_lite_agent_branch self.current_crew_tree
or self.current_agent_branch or self.current_lite_agent_branch
or self.current_task_branch
or crew_tree or crew_tree
) )