1818 from uuid import UUID
1919
2020 from langchain .callbacks .base import BaseCallbackHandler
21+ from langsmith .run_trees import RunTree
2122
2223 from langflow .graph .vertex .base import Vertex
2324 from langflow .services .tracing .schema import Log
@@ -29,28 +30,51 @@ def __init__(self, trace_name: str, trace_type: str, project_name: str, trace_id
2930 self ._ready = self .setup_langsmith ()
3031 if not self ._ready :
3132 return
32- from langsmith .run_trees import RunTree
33-
3433 self .trace_name = trace_name
3534 self .trace_type = trace_type
3635 self .project_name = project_name
3736 self .trace_id = trace_id
38- self ._run_tree = RunTree (
39- project_name = self .project_name ,
40- name = self .trace_name ,
41- run_type = self .trace_type ,
42- id = self .trace_id ,
43- )
44- self ._run_tree .add_event ({"name" : "Start" , "time" : datetime .now (timezone .utc ).isoformat ()})
37+ from langsmith import get_current_run_tree
38+ from langsmith .run_helpers import trace
39+
40+ self ._run_tree : RunTree | None = None
4541 self ._children : dict [str , RunTree ] = {}
46- except Exception : # noqa: BLE001
47- logger .debug ("Error setting up LangSmith tracer" )
42+ self ._children_traces : dict [str , trace ] = {}
43+ self ._child_link : dict [str , str ] = {}
44+ parent = get_current_run_tree ()
45+ if parent is not None and (parent .id == trace_id or parent .name == trace_name ):
46+ # duplicate init of LangSmithTracer with same trace_id\\trace_name, using current run tree
47+ self ._run_tree = parent
48+ else :
49+ self ._trace = trace (
50+ project_name = self .project_name ,
51+ name = self .trace_name ,
52+ run_type = self .get_run_type (self .trace_type ),
53+ run_id = self .trace_id if parent is None else None ,
54+ parent = parent ,
55+ )
56+ self ._run_tree = self ._trace .__enter__ ()
57+ self ._run_tree .add_event ({"name" : "Start" , "time" : datetime .now (timezone .utc ).isoformat ()})
58+ self ._run_tree .post ()
59+ except Exception as ex : # noqa: BLE001
60+ logger .warning (f"Error setting up LangSmith tracer: { ex } " )
4861 self ._ready = False
4962
5063 @property
5164 def ready (self ):
5265 return self ._ready
5366
67+ def get_run_type (self , run_type : str ) -> str :
68+ from typing import get_args
69+
70+ from langsmith import client
71+
72+ valid_run_types = set (get_args (client .RUN_TYPE_T ))
73+ if run_type not in valid_run_types :
74+ logger .warning ("Run type %s is not valid. Using default run type 'chain'." , run_type )
75+ return "chain"
76+ return run_type
77+
5478 def setup_langsmith (self ) -> bool :
5579 if os .getenv ("LANGCHAIN_API_KEY" ) is None :
5680 return False
@@ -66,7 +90,7 @@ def setup_langsmith(self) -> bool:
6690
6791 def add_trace (
6892 self ,
69- trace_id : str , # noqa: ARG002
93+ trace_id : str ,
7094 trace_name : str ,
7195 trace_type : str ,
7296 inputs : dict [str , Any ],
@@ -78,15 +102,20 @@ def add_trace(
78102 processed_inputs = {}
79103 if inputs :
80104 processed_inputs = self ._convert_to_langchain_types (inputs )
81- child = self ._run_tree .create_child (
105+
106+ from langsmith .run_helpers import trace
107+
108+ child_trace = trace (
82109 name = trace_name ,
83- run_type = trace_type , # type: ignore[arg-type]
110+ run_type = self .get_run_type (trace_type ),
111+ parent = self ._run_tree ,
84112 inputs = processed_inputs ,
113+ metadata = self ._convert_to_langchain_types (metadata ) if metadata else None ,
85114 )
86- if metadata :
87- child .add_metadata ( self . _convert_to_langchain_types ( metadata ) )
88- self ._children [trace_name ] = child
89- self ._child_link : dict [ str , str ] = {}
115+ child = child_trace . __enter__ ()
116+ child .post ( )
117+ self ._children [trace_id ] = child
118+ self ._children_traces [ trace_id ] = child_trace
90119
91120 def _convert_to_langchain_types (self , io_dict : dict [str , Any ]):
92121 converted = {}
@@ -117,15 +146,18 @@ def _convert_to_langchain_type(self, value):
117146
118147 def end_trace (
119148 self ,
120- trace_id : str , # noqa: ARG002
121- trace_name : str ,
149+ trace_id : str ,
150+ trace_name : str , # noqa: ARG002
122151 outputs : dict [str , Any ] | None = None ,
123152 error : Exception | None = None ,
124153 logs : Sequence [Log | dict ] = (),
125154 ):
126- if not self ._ready or trace_name not in self ._children :
155+ if not self ._ready or not self ._run_tree :
156+ return
157+ if trace_id not in self ._children :
158+ logger .warning (f"Trace { trace_id } not found in children traces" )
127159 return
128- child = self ._children [trace_name ]
160+ child = self ._children [trace_id ]
129161 raw_outputs = {}
130162 processed_outputs = {}
131163 if outputs :
@@ -136,10 +168,8 @@ def end_trace(
136168 child .add_metadata (self ._convert_to_langchain_types ({"logs" : {log .get ("name" ): log for log in logs_dicts }}))
137169 child .add_metadata (self ._convert_to_langchain_types ({"outputs" : raw_outputs }))
138170 child .end (outputs = processed_outputs , error = self ._error_to_string (error ))
139- if error :
140- child .patch ()
141- else :
142- child .post ()
171+ self ._children_traces [trace_id ].__exit__ (None , None , None )
172+ self ._child_link [trace_id ] = child .get_url ()
143173
144174 @staticmethod
145175 def _error_to_string (error : Exception | None ):
@@ -162,7 +192,10 @@ def end(
162192 if metadata :
163193 self ._run_tree .add_metadata (serialize (metadata ))
164194 self ._run_tree .end (outputs = serialize (outputs ), error = self ._error_to_string (error ))
165- self ._run_tree .post ()
195+ self ._run_tree .patch ()
196+ self ._run_link = self ._run_tree .get_url ()
197+ if getattr (self , "_trace" , None ):
198+ self ._trace .__exit__ ()
166199
167200 @property
168201 def run_link (self ):
0 commit comments