11"""
2- Integration tests for OpenAIAgentGraphRunner tracking pipeline.
2+ Integration tests for OpenAIAgentGraphRunner + ManagedAgentGraph tracking pipeline.
33
44Uses real AIGraphTracker and LDAIConfigTracker backed by a mock LD client,
55and a crafted RunResult to verify that the correct LD events are emitted
66with the correct payloads — without making real API calls.
7+
8+ Tracking events are now emitted by ManagedAgentGraph._flush_graph_tracking()
9+ from the GraphMetrics returned by the runner, rather than directly inside the
10+ runner. These tests exercise the full pipeline through ManagedAgentGraph.run().
711"""
812
913import pytest
1014from collections import defaultdict
1115from unittest .mock import AsyncMock , MagicMock , patch
1216
1317from ldai .agent_graph import AgentGraphDefinition
18+ from ldai .managed_agent_graph import ManagedAgentGraph
1419from ldai .models import AIAgentGraphConfig , AIAgentConfig , Edge , ModelConfig , ProviderConfig
1520from ldai .tracker import AIGraphTracker , LDAIConfigTracker
1621from ldai_openai .openai_agent_graph_runner import OpenAIAgentGraphRunner
@@ -171,7 +176,7 @@ def _make_agents_modules(run_result: MagicMock) -> dict:
171176
172177
173178def _make_two_node_graph (mock_ld_client : MagicMock ) -> AgentGraphDefinition :
174- """Build a two-node AgentGraphDefinition (root-agent → child-agent)."""
179+ """Build a two-node AgentGraphDefinition (root-agent -> child-agent)."""
175180 context = MagicMock ()
176181
177182 root_tracker = LDAIConfigTracker (
@@ -253,6 +258,12 @@ def _events(mock_ld_client: MagicMock) -> dict:
253258 return dict (result )
254259
255260
261+ async def _run_through_managed (graph : AgentGraphDefinition , runner : OpenAIAgentGraphRunner , input : str ):
262+ """Run through the full ManagedAgentGraph pipeline so tracking events are emitted."""
263+ managed = ManagedAgentGraph (runner , graph = graph )
264+ return await managed .run (input )
265+
266+
256267# ---------------------------------------------------------------------------
257268# Tests
258269# ---------------------------------------------------------------------------
@@ -266,10 +277,10 @@ async def test_tracks_graph_invocation_success_and_latency():
266277
267278 with patch .dict ('sys.modules' , _make_agents_modules (run_result )):
268279 runner = OpenAIAgentGraphRunner (graph , {})
269- result = await runner . run ( 'hello' )
280+ result = await _run_through_managed ( graph , runner , 'hello' )
270281
271282 assert result .metrics .success is True
272- assert result .output == 'done'
283+ assert result .content == 'done'
273284
274285 ev = _events (mock_ld_client )
275286 assert ev ['$ld:ai:graph:invocation_success' ][0 ][1 ] == 1
@@ -279,7 +290,7 @@ async def test_tracks_graph_invocation_success_and_latency():
279290
280291@pytest .mark .asyncio
281292async def test_tracks_per_node_tokens_and_success ():
282- """Node-level token and success events fire with correct values."""
293+ """Node-level token and success events fire with correct values via managed layer ."""
283294 mock_ld_client = MagicMock ()
284295 graph = _make_graph (mock_ld_client , node_key = 'root-agent' , graph_key = 'test-graph' )
285296 run_result = _make_run_result (
@@ -291,11 +302,11 @@ async def test_tracks_per_node_tokens_and_success():
291302
292303 with patch .dict ('sys.modules' , _make_agents_modules (run_result )):
293304 runner = OpenAIAgentGraphRunner (graph , {})
294- await runner . run ( 'hello' )
305+ await _run_through_managed ( graph , runner , 'hello' )
295306
296307 ev = _events (mock_ld_client )
297308
298- # Node-level events
309+ # Node-level events (emitted by ManagedAgentGraph from node_metrics)
299310 assert ev ['$ld:ai:tokens:total' ][0 ][1 ] == 30
300311 assert ev ['$ld:ai:tokens:input' ][0 ][1 ] == 20
301312 assert ev ['$ld:ai:tokens:output' ][0 ][1 ] == 10
@@ -314,7 +325,7 @@ async def test_tracks_graph_key_on_node_events():
314325
315326 with patch .dict ('sys.modules' , _make_agents_modules (run_result )):
316327 runner = OpenAIAgentGraphRunner (graph , {})
317- await runner . run ( 'hello' )
328+ await _run_through_managed ( graph , runner , 'hello' )
318329
319330 ev = _events (mock_ld_client )
320331 token_data = ev ['$ld:ai:tokens:total' ][0 ][0 ]
@@ -332,7 +343,7 @@ async def test_tracks_tool_calls_from_run_items():
332343
333344 with patch .dict ('sys.modules' , _make_agents_modules (run_result )):
334345 runner = OpenAIAgentGraphRunner (graph , _tool_registry ('get_weather' ))
335- await runner . run ( 'What is the weather?' )
346+ await _run_through_managed ( graph , runner , 'What is the weather?' )
336347
337348 ev = _events (mock_ld_client )
338349 tool_events = ev .get ('$ld:ai:tool_call' , [])
@@ -356,7 +367,7 @@ async def test_tracks_multiple_tool_calls():
356367
357368 with patch .dict ('sys.modules' , _make_agents_modules (run_result )):
358369 runner = OpenAIAgentGraphRunner (graph , _tool_registry ('search' , 'summarize' ))
359- await runner . run ( 'Search and summarize.' )
370+ await _run_through_managed ( graph , runner , 'Search and summarize.' )
360371
361372 ev = _events (mock_ld_client )
362373 tool_keys = [data ['toolKey' ] for data , _ in ev .get ('$ld:ai:tool_call' , [])]
@@ -379,7 +390,7 @@ async def test_same_run_id_across_token_success_and_tool_call_events():
379390
380391 with patch .dict ('sys.modules' , _make_agents_modules (run_result )):
381392 runner = OpenAIAgentGraphRunner (graph , _tool_registry ('search' ))
382- await runner . run ( 'go' )
393+ await _run_through_managed ( graph , runner , 'go' )
383394
384395 ev = _events (mock_ld_client )
385396
@@ -408,7 +419,7 @@ async def test_does_not_track_tool_calls_without_graph_and_registry_config():
408419
409420 with patch .dict ('sys.modules' , _make_agents_modules (run_result )):
410421 runner = OpenAIAgentGraphRunner (graph , {})
411- await runner . run ( 'prompt' )
422+ await _run_through_managed ( graph , runner , 'prompt' )
412423
413424 ev = _events (mock_ld_client )
414425 assert ev .get ('$ld:ai:tool_call' , []) == []
@@ -420,10 +431,10 @@ async def test_tracks_failure_and_latency_on_runner_error():
420431 mock_ld_client = MagicMock ()
421432 graph = _make_graph (mock_ld_client )
422433
423- mock_runner = MagicMock ()
424- mock_runner .run = AsyncMock (side_effect = RuntimeError ('runner error' ))
434+ mock_runner_module = MagicMock ()
435+ mock_runner_module .run = AsyncMock (side_effect = RuntimeError ('runner error' ))
425436 mock_agents = MagicMock ()
426- mock_agents .Runner = mock_runner
437+ mock_agents .Runner = mock_runner_module
427438 mock_agents .Agent = MagicMock (return_value = MagicMock ())
428439 mock_agents .Handoff = MagicMock ()
429440 mock_agents .Tool = MagicMock ()
@@ -439,7 +450,7 @@ async def test_tracks_failure_and_latency_on_runner_error():
439450 'agents.tool_context' : MagicMock (),
440451 }):
441452 runner = OpenAIAgentGraphRunner (graph , {})
442- result = await runner . run ( 'fail' )
453+ result = await _run_through_managed ( graph , runner , 'fail' )
443454
444455 assert result .metrics .success is False
445456
@@ -450,8 +461,8 @@ async def test_tracks_failure_and_latency_on_runner_error():
450461
451462
452463@pytest .mark .asyncio
453- async def test_multi_node_tracks_per_node_tokens_and_handoff ():
454- """Each node emits its own token events; handoff event fires between them ."""
464+ async def test_multi_node_tracks_per_node_tokens ():
465+ """Each node emits its own token events via the managed layer ."""
455466 mock_ld_client = MagicMock ()
456467 graph = _make_two_node_graph (mock_ld_client )
457468
@@ -483,7 +494,7 @@ def capture_handoff(**kwargs):
483494 return MagicMock ()
484495
485496 async def mock_run (agent , input_str , ** kwargs ):
486- # Simulate the root→ child handoff before returning
497+ # Simulate the root-> child handoff before returning
487498 if on_handoff_callbacks :
488499 run_ctx = MagicMock ()
489500 run_ctx .usage .request_usage_entries = [root_entry ]
@@ -511,7 +522,7 @@ async def mock_run(agent, input_str, **kwargs):
511522 'agents.tool_context' : MagicMock (),
512523 }):
513524 runner = OpenAIAgentGraphRunner (graph , {})
514- result = await runner . run ( 'hello' )
525+ result = await _run_through_managed ( graph , runner , 'hello' )
515526
516527 assert result .metrics .success is True
517528
@@ -527,9 +538,3 @@ async def mock_run(agent, input_str, **kwargs):
527538 path_data = ev ['$ld:ai:graph:path' ][0 ][0 ]
528539 assert 'root-agent' in path_data ['path' ]
529540 assert 'child-agent' in path_data ['path' ]
530-
531- # Handoff event fires with correct source and target
532- handoff_events = ev .get ('$ld:ai:graph:handoff_success' , [])
533- assert len (handoff_events ) == 1
534- assert handoff_events [0 ][0 ]['sourceKey' ] == 'root-agent'
535- assert handoff_events [0 ][0 ]['targetKey' ] == 'child-agent'
0 commit comments