@@ -92,11 +92,15 @@ async def run(self, input: Any) -> AgentGraphResult:
9292 path .append (root_node .get_key ())
9393
9494 start_ns = time .perf_counter_ns ()
95+ # Mutable cell so handoff callbacks can update time-between-handoffs without globals.
96+ last_handoff_ns : List [int ] = [start_ns ]
9597 try :
9698 from agents import Runner
97- root_agent = self ._build_agents (path )
99+ root_agent = self ._build_agents (path , last_handoff_ns )
98100 result = await Runner .run (root_agent , str (input ))
99101 # _log_run_result_shape(result)
102+ self ._flush_final_segment (path , last_handoff_ns , tracker , result )
103+
100104 duration = (time .perf_counter_ns () - start_ns ) // 1_000_000
101105
102106 if tracker :
@@ -135,6 +139,44 @@ async def run(self, input: Any) -> AgentGraphResult:
135139 metrics = LDAIMetrics (success = False ),
136140 )
137141
142+ def _flush_final_segment (
143+ self ,
144+ path : List [str ],
145+ last_handoff_ns : List [int ],
146+ tracker : Any ,
147+ result : Any ,
148+ ) -> None :
149+ """Record duration/tokens for the last active agent (no handoff after it)."""
150+ if not path :
151+ return
152+ last_key = path [- 1 ]
153+ node = self ._graph .get_node (last_key )
154+ if node is None :
155+ return
156+ config_tracker = node .get_config ().tracker
157+ if config_tracker is None :
158+ return
159+
160+ now_ns = time .perf_counter_ns ()
161+ duration_ms = (now_ns - last_handoff_ns [0 ]) // 1_000_000
162+
163+ usage : Optional [TokenUsage ] = None
164+ try :
165+ usage_entry = result .context_wrapper .usage .request_usage_entries [- 1 ]
166+ usage = TokenUsage (
167+ total = usage_entry .total_tokens ,
168+ input = usage_entry .input_tokens ,
169+ output = usage_entry .output_tokens ,
170+ )
171+ except Exception :
172+ pass
173+
174+ gk = tracker .graph_key if tracker is not None else None
175+ if usage is not None :
176+ config_tracker .track_tokens (usage , graph_key = gk )
177+ config_tracker .track_duration (int (duration_ms ), graph_key = gk )
178+ config_tracker .track_success (graph_key = gk )
179+
138180 def _handle_handoff (
139181 self ,
140182 run_ctx : Any ,
@@ -143,23 +185,23 @@ def _handle_handoff(
143185 path : List [str ],
144186 tracker : Any ,
145187 config_tracker : Any ,
188+ last_handoff_ns : List [int ],
146189 ) -> None :
147190 path .append (tgt )
148191 if tracker :
149192 tracker .track_handoff_success (src , tgt )
150193
151194 usage : Optional [TokenUsage ] = None
152- duration_ms : Optional [int ] = None
195+ now_ns = time .perf_counter_ns ()
196+ duration_ms = (now_ns - last_handoff_ns [0 ]) // 1_000_000
197+ last_handoff_ns [0 ] = now_ns
153198 try :
154199 usage_entry = run_ctx .usage .request_usage_entries [- 1 ]
155200 usage = TokenUsage (
156201 total = usage_entry .total_tokens ,
157202 input = usage_entry .input_tokens ,
158203 output = usage_entry .output_tokens ,
159204 )
160- duration_ms = getattr (usage_entry , 'duration_ms' , None )
161- if duration_ms is None :
162- duration_ms = getattr (usage_entry , 'latency_ms' , None )
163205 except Exception :
164206 pass
165207
@@ -178,12 +220,15 @@ def _make_on_handoff(
178220 path : List [str ],
179221 tracker : Any ,
180222 config_tracker : Any ,
223+ last_handoff_ns : List [int ],
181224 ):
182225 def on_handoff (run_ctx : Any ) -> None :
183- self ._handle_handoff (run_ctx , src , tgt , path , tracker , config_tracker )
226+ self ._handle_handoff (
227+ run_ctx , src , tgt , path , tracker , config_tracker , last_handoff_ns
228+ )
184229 return on_handoff
185230
186- def _build_agents (self , path : List [str ]) -> Any :
231+ def _build_agents (self , path : List [str ], last_handoff_ns : List [ int ] ) -> Any :
187232 """
188233 Build the agent tree from the graph definition via reverse_traverse.
189234
@@ -235,6 +280,7 @@ def build_node(node: AgentGraphNode, ctx: dict) -> Any:
235280 path ,
236281 tracker ,
237282 config_tracker ,
283+ last_handoff_ns ,
238284 ),
239285 )
240286 )
0 commit comments