"""Tracers that print to the console."""importjsonfromtypingimportAny,Callablefromlangchain_core.tracers.baseimportBaseTracerfromlangchain_core.tracers.schemasimportRunfromlangchain_core.utils.inputimportget_bolded_text,get_colored_textMILLISECONDS_IN_SECOND=1000
[docs]deftry_json_stringify(obj:Any,fallback:str)->str:"""Try to stringify an object to JSON. Args: obj: Object to stringify. fallback: Fallback string to return if the object cannot be stringified. Returns: A JSON string if the object can be stringified, otherwise the fallback string. """try:returnjson.dumps(obj,indent=2,ensure_ascii=False)exceptException:returnfallback
[docs]defelapsed(run:Any)->str:"""Get the elapsed time of a run. Args: run: any object with a start_time and end_time attribute. Returns: A string with the elapsed time in seconds or milliseconds if time is less than a second. """elapsed_time=run.end_time-run.start_timeseconds=elapsed_time.total_seconds()ifseconds<1:returnf"{seconds*MILLISECONDS_IN_SECOND:.0f}ms"returnf"{seconds:.2f}s"
[docs]classFunctionCallbackHandler(BaseTracer):"""Tracer that calls a function with a single str parameter."""name:str="function_callback_handler""""The name of the tracer. This is used to identify the tracer in the logs. Default is "function_callback_handler"."""
[docs]def__init__(self,function:Callable[[str],None],**kwargs:Any)->None:"""Create a FunctionCallbackHandler. Args: function: The callback function to call. """super().__init__(**kwargs)self.function_callback=function
def_persist_run(self,run:Run)->None:pass
[docs]defget_parents(self,run:Run)->list[Run]:"""Get the parents of a run. Args: run: The run to get the parents of. Returns: A list of parent runs. """parents=[]current_run=runwhilecurrent_run.parent_run_id:parent=self.run_map.get(str(current_run.parent_run_id))ifparent:parents.append(parent)current_run=parentelse:breakreturnparents
[docs]defget_breadcrumbs(self,run:Run)->str:"""Get the breadcrumbs of a run. Args: run: The run to get the breadcrumbs of. Returns: A string with the breadcrumbs of the run. """parents=self.get_parents(run)[::-1]return" > ".join(f"{parent.run_type}:{parent.name}"fori,parentinenumerate([*parents,run]))
# logging methodsdef_on_chain_start(self,run:Run)->None:crumbs=self.get_breadcrumbs(run)run_type=run.run_type.capitalize()self.function_callback(f"{get_colored_text('[chain/start]',color='green')} "+get_bolded_text(f"[{crumbs}] Entering {run_type} run with input:\n")+f"{try_json_stringify(run.inputs,'[inputs]')}")def_on_chain_end(self,run:Run)->None:crumbs=self.get_breadcrumbs(run)run_type=run.run_type.capitalize()self.function_callback(f"{get_colored_text('[chain/end]',color='blue')} "+get_bolded_text(f"[{crumbs}] [{elapsed(run)}] Exiting {run_type} run with output:\n")+f"{try_json_stringify(run.outputs,'[outputs]')}")def_on_chain_error(self,run:Run)->None:crumbs=self.get_breadcrumbs(run)run_type=run.run_type.capitalize()self.function_callback(f"{get_colored_text('[chain/error]',color='red')} "+get_bolded_text(f"[{crumbs}] [{elapsed(run)}] {run_type} run errored with error:\n")+f"{try_json_stringify(run.error,'[error]')}")def_on_llm_start(self,run:Run)->None:crumbs=self.get_breadcrumbs(run)inputs=({"prompts":[p.strip()forpinrun.inputs["prompts"]]}if"prompts"inrun.inputselserun.inputs)self.function_callback(f"{get_colored_text('[llm/start]',color='green')} "+get_bolded_text(f"[{crumbs}] Entering LLM run with input:\n")+f"{try_json_stringify(inputs,'[inputs]')}")def_on_llm_end(self,run:Run)->None:crumbs=self.get_breadcrumbs(run)self.function_callback(f"{get_colored_text('[llm/end]',color='blue')} "+get_bolded_text(f"[{crumbs}] [{elapsed(run)}] Exiting LLM run with output:\n")+f"{try_json_stringify(run.outputs,'[response]')}")def_on_llm_error(self,run:Run)->None:crumbs=self.get_breadcrumbs(run)self.function_callback(f"{get_colored_text('[llm/error]',color='red')} "+get_bolded_text(f"[{crumbs}] [{elapsed(run)}] LLM run errored with error:\n")+f"{try_json_stringify(run.error,'[error]')}")def_on_tool_start(self,run:Run)->None:crumbs=self.get_breadcrumbs(run)self.function_callback(f"{get_colored_text('[tool/start]',color='green')} "+get_bolded_text(f"[{crumbs}] Entering Tool run with input:\n")+f'"{run.inputs["input"].strip()}"')def_on_tool_end(self,run:Run)->None:crumbs=self.get_breadcrumbs(run)ifrun.outputs:self.function_callback(f"{get_colored_text('[tool/end]',color='blue')} "+get_bolded_text(f"[{crumbs}] [{elapsed(run)}] Exiting Tool run with output:\n")+f'"{str(run.outputs["output"]).strip()}"')def_on_tool_error(self,run:Run)->None:crumbs=self.get_breadcrumbs(run)self.function_callback(f"{get_colored_text('[tool/error]',color='red')} "+get_bolded_text(f"[{crumbs}] [{elapsed(run)}] ")+f"Tool run errored with error:\n"f"{run.error}")
[docs]classConsoleCallbackHandler(FunctionCallbackHandler):"""Tracer that prints to the console."""name:str="console_callback_handler"
[docs]def__init__(self,**kwargs:Any)->None:"""Create a ConsoleCallbackHandler."""super().__init__(function=print,**kwargs)