44import time
55from typing import Annotated , Any , List
66
7+ from ldai import log
78from ldai .agent_graph import AgentGraphDefinition , AgentGraphNode
89from ldai .providers .types import LDAIMetrics
910from ldai .runners .agent_graph_runner import AgentGraphRunner
1011from ldai .runners .types import AgentGraphResult , ToolRegistry
1112
13+ from ldai_langchain .langchain_helper import LangChainHelper
14+
1215
1316class LangGraphAgentGraphRunner (AgentGraphRunner ):
1417 """
1518 AgentGraphRunner implementation for LangGraph.
1619
17- Builds a LangGraph StateGraph from an AgentGraphDefinition and
18- ToolRegistry via traverse(), compiles it, and executes it with
19- ainvoke(). Auto-tracks latency and invocation success/failure via
20- the graph's AIGraphTracker.
20+ Compiles and runs the agent graph with LangGraph and automatically records
21+ graph- and node-level AI metric data to the LaunchDarkly trackers on the
22+ graph definition and each node.
2123
2224 Requires ``langgraph`` to be installed.
2325 """
@@ -43,18 +45,12 @@ async def run(self, input: Any) -> AgentGraphResult:
4345 :return: AgentGraphResult with the final output and metrics
4446 """
4547 tracker = self ._graph .get_tracker ()
46- start_time = time .time ()
48+ start_ns = time .perf_counter_ns ()
4749 try :
48- try :
49- from langchain .chat_models import init_chat_model
50- from langchain_core .messages import AnyMessage , HumanMessage
51- from langgraph .graph import END , START , StateGraph
52- from typing_extensions import TypedDict
53- except ImportError as exc :
54- raise ImportError (
55- "langgraph is required for LangGraphAgentGraphRunner. "
56- "Install it with: pip install langgraph"
57- ) from exc
50+ from langchain .chat_models import init_chat_model
51+ from langchain_core .messages import AnyMessage , HumanMessage
52+ from langgraph .graph import END , START , StateGraph
53+ from typing_extensions import TypedDict
5854
5955 class WorkflowState (TypedDict ):
6056 messages : Annotated [List [AnyMessage ], operator .add ]
@@ -63,10 +59,12 @@ class WorkflowState(TypedDict):
6359 root_node = self ._graph .root ()
6460 root_key = root_node .get_key () if root_node else None
6561 tools_ref = self ._tools
62+ exec_path : List [str ] = []
6663
6764 def handle_traversal (node : AgentGraphNode , ctx : dict ) -> None :
6865 node_config = node .get_config ()
6966 node_key = node .get_key ()
67+ node_tracker = node_config .tracker
7068
7169 model = None
7270 if node_config .model :
@@ -82,10 +80,24 @@ def handle_traversal(node: AgentGraphNode, ctx: dict) -> None:
8280 model = lc_model
8381
8482 def invoke (state : WorkflowState ) -> WorkflowState :
85- if model :
83+ exec_path .append (node_key )
84+ if not model :
85+ return state
86+ gk = tracker .graph_key if tracker is not None else None
87+ if node_tracker :
88+ response = node_tracker .track_metrics_of (
89+ lambda : model .invoke (state ['messages' ]),
90+ LangChainHelper .get_ai_metrics_from_response ,
91+ graph_key = gk ,
92+ )
93+ node_tracker .track_tool_calls (
94+ LangChainHelper .get_tool_calls_from_response (response ),
95+ graph_key = tracker .graph_key if tracker is not None else None ,
96+ )
97+ else :
8698 response = model .invoke (state ['messages' ])
87- return { 'messages' : [ response ]}
88- return state
99+
100+ return { 'messages' : [ response ]}
89101
90102 invoke .__name__ = node_key
91103
@@ -108,7 +120,7 @@ def invoke(state: WorkflowState) -> WorkflowState:
108120 result = await compiled .ainvoke (
109121 {'messages' : [HumanMessage (content = str (input ))]}
110122 )
111- duration = int (( time .time () - start_time ) * 1000 )
123+ duration = ( time .perf_counter_ns () - start_ns ) // 1_000_000
112124
113125 output = ''
114126 messages = result .get ('messages' , [])
@@ -118,16 +130,27 @@ def invoke(state: WorkflowState) -> WorkflowState:
118130 output = str (last .content )
119131
120132 if tracker :
133+ tracker .track_path (exec_path )
121134 tracker .track_latency (duration )
122135 tracker .track_invocation_success ()
136+ tracker .track_total_tokens (
137+ LangChainHelper .sum_token_usage_from_messages (messages )
138+ )
123139
124140 return AgentGraphResult (
125141 output = output ,
126142 raw = result ,
127143 metrics = LDAIMetrics (success = True ),
128144 )
129- except Exception :
130- duration = int ((time .time () - start_time ) * 1000 )
145+ except Exception as exc :
146+ if isinstance (exc , ImportError ):
147+ log .warning (
148+ "langgraph is required for LangGraphAgentGraphRunner. "
149+ "Install it with: pip install langgraph"
150+ )
151+ else :
152+ log .warning (f'LangGraphAgentGraphRunner run failed: { exc } ' )
153+ duration = (time .perf_counter_ns () - start_ns ) // 1_000_000
131154 if tracker :
132155 tracker .track_latency (duration )
133156 tracker .track_invocation_failure ()
0 commit comments