66
77from pydantic import BaseModel , Field , GetCoreSchemaHandler
88from pydantic_core import CoreSchema , core_schema
9+ from rich .console import Console
10+ from rich .text import Text
911
1012from crewai .agents .agent_builder .base_agent_executor_mixin import CrewAgentExecutorMixin
1113from crewai .agents .parser import (
@@ -146,6 +148,7 @@ def __init__(
146148 self .request_within_rpm_limit = request_within_rpm_limit
147149 self .response_model = response_model
148150 self .log_error_after = 3
151+ self ._console : Console = Console ()
149152
150153 # Error context storage for recovery
151154 self ._last_parser_error : OutputParserError | None = None
@@ -220,7 +223,7 @@ def iterations(self) -> int:
220223 return self ._state .iterations
221224
222225 @start ()
223- def initialize_reasoning (self ) -> str :
226+ def initialize_reasoning (self ) -> Literal [ "initialized" ] :
224227 """Initialize the reasoning flow and emit agent start logs."""
225228 self ._show_start_logs ()
226229 return "initialized"
@@ -248,10 +251,6 @@ def call_llm_and_parse(self) -> str:
248251
249252 Returns routing decision based on parsing result.
250253 """
251- self ._printer .print (
252- content = f"🤖 call_llm_and_parse: About to call LLM (iteration { self .state .iterations } )" ,
253- color = "blue" ,
254- )
255254 try :
256255 enforce_rpm_limit (self .request_within_rpm_limit )
257256
@@ -270,16 +269,18 @@ def call_llm_and_parse(self) -> str:
270269 formatted_answer = process_llm_response (answer , self .use_stop_words )
271270 self .state .current_answer = formatted_answer
272271
273- # Debug: Check what we parsed
274- if "Final Answer:" in answer :
275- self ._printer .print (
276- content = f"⚠️ LLM returned Final Answer but parsed as: { type (formatted_answer ).__name__ } " ,
277- color = "yellow" ,
272+ if "Final Answer:" in answer and isinstance (formatted_answer , AgentAction ):
273+ warning_text = Text ()
274+ warning_text .append ("⚠️ " , style = "yellow bold" )
275+ warning_text .append (
276+ f"LLM returned 'Final Answer:' but parsed as AgentAction (tool: { formatted_answer .tool } )" ,
277+ style = "yellow" ,
278278 )
279- if isinstance (formatted_answer , AgentAction ):
280- self ._printer .print (
281- content = f"Answer preview: { answer [:200 ]} ..." , color = "yellow"
282- )
279+ self ._console .print (warning_text )
280+ preview_text = Text ()
281+ preview_text .append ("Answer preview: " , style = "yellow" )
282+ preview_text .append (f"{ answer [:200 ]} ..." , style = "yellow dim" )
283+ self ._console .print (preview_text )
283284
284285 return "parsed"
285286
@@ -300,11 +301,6 @@ def call_llm_and_parse(self) -> str:
300301 @router (call_llm_and_parse )
301302 def route_by_answer_type (self ) -> str :
302303 """Route based on whether answer is AgentAction or AgentFinish."""
303- answer_type = type (self .state .current_answer ).__name__
304- self ._printer .print (
305- content = f"🚦 route_by_answer_type: Got { answer_type } " ,
306- color = "yellow" ,
307- )
308304 if isinstance (self .state .current_answer , AgentAction ):
309305 return "execute_tool"
310306 return "agent_finished"
@@ -360,7 +356,10 @@ def execute_tool_action(self) -> str:
360356 return "tool_completed"
361357
362358 except Exception as e :
363- self ._printer .print (content = f"Error in tool execution: { e } " , color = "red" )
359+ error_text = Text ()
360+ error_text .append ("❌ Error in tool execution: " , style = "red bold" )
361+ error_text .append (str (e ), style = "red" )
362+ self ._console .print (error_text )
364363 raise
365364
366365 @listen ("initialized" )
@@ -371,10 +370,6 @@ def continue_iteration(self) -> str:
371370 @router (or_ (initialize_reasoning , continue_iteration ))
372371 def check_max_iterations (self ) -> str :
373372 """Check if max iterations reached before proceeding with reasoning."""
374- self ._printer .print (
375- content = f"🔄 check_max_iterations: iteration { self .state .iterations } /{ self .max_iter } " ,
376- color = "cyan" ,
377- )
378373 if has_reached_max_iterations (self .state .iterations , self .max_iter ):
379374 return "force_final_answer"
380375 return "continue_reasoning"
@@ -383,27 +378,35 @@ def check_max_iterations(self) -> str:
383378 def increment_and_continue (self ) -> str :
384379 """Increment iteration counter and loop back for next iteration."""
385380 self .state .iterations += 1
386- self ._printer .print (
387- content = f"+ increment_and_continue: Incremented to iteration { self .state .iterations } , looping back" ,
388- color = "magenta" ,
381+ inc_text = Text ()
382+ inc_text .append ("+ increment_and_continue: " , style = "magenta bold" )
383+ inc_text .append (
384+ f"Incremented to iteration { self .state .iterations } , looping back" ,
385+ style = "magenta" ,
389386 )
387+ self ._console .print (inc_text )
390388 return "initialized"
391389
392390 @listen (or_ ("agent_finished" , "tool_result_is_final" ))
393391 def finalize (self ) -> str :
394392 """Finalize execution and emit completion logs."""
395393 if self .state .current_answer is None :
396- self ._printer .print (
397- content = "⚠️ Finalize called but no answer in state - skipping" ,
398- color = "yellow" ,
394+ skip_text = Text ()
395+ skip_text .append ("⚠️ " , style = "yellow bold" )
396+ skip_text .append (
397+ "Finalize called but no answer in state - skipping" , style = "yellow"
399398 )
399+ self ._console .print (skip_text )
400400 return "skipped"
401401
402402 if not isinstance (self .state .current_answer , AgentFinish ):
403- self ._printer .print (
404- content = f"⚠️ Finalize called with { type (self .state .current_answer ).__name__ } instead of AgentFinish - skipping" ,
405- color = "yellow" ,
403+ skip_text = Text ()
404+ skip_text .append ("⚠️ " , style = "yellow bold" )
405+ skip_text .append (
406+ f"Finalize called with { type (self .state .current_answer ).__name__ } instead of AgentFinish - skipping" ,
407+ style = "yellow" ,
406408 )
409+ self ._console .print (skip_text )
407410 return "skipped"
408411
409412 self .state .is_finished = True
@@ -511,10 +514,13 @@ def invoke(self, inputs: dict[str, Any]) -> dict[str, Any]:
511514 return {"output" : formatted_answer .output }
512515
513516 except AssertionError :
514- self ._printer .print (
515- content = "Agent failed to reach a final answer. This is likely a bug - please report it." ,
516- color = "red" ,
517+ fail_text = Text ()
518+ fail_text .append ("❌ " , style = "red bold" )
519+ fail_text .append (
520+ "Agent failed to reach a final answer. This is likely a bug - please report it." ,
521+ style = "red" ,
517522 )
523+ self ._console .print (fail_text )
518524 raise
519525 except Exception as e :
520526 handle_unknown_error (self ._printer , e )
@@ -624,10 +630,13 @@ def _handle_crew_training_output(
624630 )
625631
626632 if train_iteration is None or not isinstance (train_iteration , int ):
627- self ._printer .print (
628- content = "Invalid or missing train iteration. Cannot save training data." ,
629- color = "red" ,
633+ train_error = Text ()
634+ train_error .append ("❌ " , style = "red bold" )
635+ train_error .append (
636+ "Invalid or missing train iteration. Cannot save training data." ,
637+ style = "red" ,
630638 )
639+ self ._console .print (train_error )
631640 return
632641
633642 training_handler = CrewTrainingHandler (TRAINING_DATA_FILE )
@@ -647,13 +656,14 @@ def _handle_crew_training_output(
647656 if train_iteration in agent_training_data :
648657 agent_training_data [train_iteration ]["improved_output" ] = result .output
649658 else :
650- self . _printer . print (
651- content = (
652- f"No existing training data for agent { agent_id } and iteration "
653- f" { train_iteration } . Cannot save improved output. "
654- ) ,
655- color = "red" ,
659+ train_error = Text ()
660+ train_error . append ( "❌ " , style = "red bold" )
661+ train_error . append (
662+ f"No existing training data for agent { agent_id } and iteration "
663+ f" { train_iteration } . Cannot save improved output." ,
664+ style = "red" ,
656665 )
666+ self ._console .print (train_error )
657667 return
658668
659669 # Update the training data and save
0 commit comments