1717from ldai .agent_graph import AgentGraphDefinition
1818from ldai .models import AIAgentConfig , AIAgentGraphConfig , ModelConfig , ProviderConfig
1919from ldai .tracker import AIGraphTracker , LDAIConfigTracker , TokenUsage
20+ from ldai .evaluator import Evaluator
2021from ldai_langchain .langgraph_callback_handler import LDMetricsCallbackHandler
2122
2223
@@ -48,6 +49,7 @@ def _make_graph(mock_ld_client: MagicMock, node_key: str = 'root-agent', graph_k
4849 node_config = AIAgentConfig (
4950 key = node_key ,
5051 enabled = True ,
52+ evaluator = Evaluator .noop (),
5153 model = ModelConfig (name = 'gpt-4' , parameters = {}),
5254 provider = ProviderConfig (name = 'openai' ),
5355 instructions = 'Be helpful.' ,
@@ -317,7 +319,8 @@ def test_on_tool_end_none_name_ignored():
317319# flush() tests
318320# ---------------------------------------------------------------------------
319321
320- def test_flush_emits_token_events_to_ld_tracker ():
322+ @pytest .mark .asyncio
323+ async def test_flush_emits_token_events_to_ld_tracker ():
321324 """flush() calls track_tokens on the node's config tracker."""
322325 mock_ld_client = MagicMock ()
323326 graph = _make_graph (mock_ld_client , node_key = 'root-agent' , graph_key = 'g1' )
@@ -327,7 +330,7 @@ def test_flush_emits_token_events_to_ld_tracker():
327330 node_run_id = uuid4 ()
328331 handler .on_chain_start ({}, {}, run_id = node_run_id , name = 'root-agent' )
329332 handler .on_llm_end (_llm_result (15 , 10 , 5 ), run_id = uuid4 (), parent_run_id = node_run_id )
330- handler .flush (graph )
333+ await handler .flush (graph )
331334
332335 ev = _events (mock_ld_client )
333336 assert ev ['$ld:ai:tokens:total' ][0 ][1 ] == 15
@@ -336,7 +339,8 @@ def test_flush_emits_token_events_to_ld_tracker():
336339 assert ev ['$ld:ai:generation:success' ][0 ][1 ] == 1
337340
338341
339- def test_flush_emits_duration ():
342+ @pytest .mark .asyncio
343+ async def test_flush_emits_duration ():
340344 """flush() calls track_duration when duration was recorded."""
341345 mock_ld_client = MagicMock ()
342346 graph = _make_graph (mock_ld_client )
@@ -346,13 +350,14 @@ def test_flush_emits_duration():
346350 run_id = uuid4 ()
347351 handler .on_chain_start ({}, {}, run_id = run_id , name = 'root-agent' )
348352 handler .on_chain_end ({}, run_id = run_id )
349- handler .flush (graph )
353+ await handler .flush (graph )
350354
351355 ev = _events (mock_ld_client )
352356 assert '$ld:ai:duration:total' in ev
353357
354358
355- def test_flush_emits_tool_calls ():
359+ @pytest .mark .asyncio
360+ async def test_flush_emits_tool_calls ():
356361 """flush() calls track_tool_call for each recorded tool invocation."""
357362 mock_ld_client = MagicMock ()
358363 graph = _make_graph (mock_ld_client )
@@ -366,15 +371,16 @@ def test_flush_emits_tool_calls():
366371 tools_run_id = uuid4 ()
367372 handler .on_chain_start ({}, {}, run_id = tools_run_id , name = 'root-agent__tools' )
368373 handler .on_tool_end ('r' , run_id = uuid4 (), parent_run_id = tools_run_id , name = 'fn_search' )
369- handler .flush (graph )
374+ await handler .flush (graph )
370375
371376 ev = _events (mock_ld_client )
372377 tool_events = ev .get ('$ld:ai:tool_call' , [])
373378 assert len (tool_events ) == 1
374379 assert tool_events [0 ][0 ]['toolKey' ] == 'search'
375380
376381
377- def test_flush_includes_graph_key_in_node_events ():
382+ @pytest .mark .asyncio
383+ async def test_flush_includes_graph_key_in_node_events ():
378384 """flush() passes graph_key to the node tracker so graphKey appears in events."""
379385 mock_ld_client = MagicMock ()
380386 graph = _make_graph (mock_ld_client , graph_key = 'my-graph' )
@@ -384,14 +390,15 @@ def test_flush_includes_graph_key_in_node_events():
384390 node_run_id = uuid4 ()
385391 handler .on_chain_start ({}, {}, run_id = node_run_id , name = 'root-agent' )
386392 handler .on_llm_end (_llm_result (5 , 3 , 2 ), run_id = uuid4 (), parent_run_id = node_run_id )
387- handler .flush (graph )
393+ await handler .flush (graph )
388394
389395 ev = _events (mock_ld_client )
390396 token_data = ev ['$ld:ai:tokens:total' ][0 ][0 ]
391397 assert token_data .get ('graphKey' ) == 'my-graph'
392398
393399
394- def test_flush_with_no_graph_key_on_node_tracker ():
400+ @pytest .mark .asyncio
401+ async def test_flush_with_no_graph_key_on_node_tracker ():
395402 """When node tracker has no graph_key, events omit graphKey."""
396403 mock_ld_client = MagicMock ()
397404 context = MagicMock ()
@@ -408,6 +415,7 @@ def test_flush_with_no_graph_key_on_node_tracker():
408415 node_config = AIAgentConfig (
409416 key = 'root-agent' ,
410417 enabled = True ,
418+ evaluator = Evaluator .noop (),
411419 model = ModelConfig (name = 'gpt-4' , parameters = {}),
412420 provider = ProviderConfig (name = 'openai' ),
413421 instructions = 'Be helpful.' ,
@@ -425,36 +433,38 @@ def test_flush_with_no_graph_key_on_node_tracker():
425433 nodes = nodes ,
426434 context = context ,
427435 enabled = True ,
428- create_tracker = lambda : None ,
436+ create_tracker = lambda : AIGraphTracker ( mock_ld_client , 'v1' , 'test-graph' , 1 , context ) ,
429437 )
430438
431439 handler = LDMetricsCallbackHandler ({'root-agent' }, {})
432440 node_run_id = uuid4 ()
433441 handler .on_chain_start ({}, {}, run_id = node_run_id , name = 'root-agent' )
434442 handler .on_llm_end (_llm_result (5 , 3 , 2 ), run_id = uuid4 (), parent_run_id = node_run_id )
435- handler .flush (graph )
443+ await handler .flush (graph )
436444
437445 ev = _events (mock_ld_client )
438446 token_data = ev ['$ld:ai:tokens:total' ][0 ][0 ]
439447 assert 'graphKey' not in token_data
440448
441449
442- def test_flush_skips_nodes_not_in_path ():
450+ @pytest .mark .asyncio
451+ async def test_flush_skips_nodes_not_in_path ():
443452 """flush() only emits events for nodes that were actually executed."""
444453 mock_ld_client = MagicMock ()
445454 graph = _make_graph (mock_ld_client )
446455 tracker = graph .create_tracker ()
447456
448457 # Handler with 'root-agent' in node_keys but never started
449458 handler = LDMetricsCallbackHandler ({'root-agent' }, {})
450- handler .flush (graph )
459+ await handler .flush (graph )
451460
452461 ev = _events (mock_ld_client )
453462 assert '$ld:ai:tokens:total' not in ev
454463 assert '$ld:ai:generation:success' not in ev
455464
456465
457- def test_flush_skips_node_without_tracker ():
466+ @pytest .mark .asyncio
467+ async def test_flush_skips_node_without_tracker ():
458468 """flush() silently skips nodes whose config has no tracker."""
459469 mock_ld_client = MagicMock ()
460470 context = MagicMock ()
@@ -463,6 +473,7 @@ def test_flush_skips_node_without_tracker():
463473 key = 'no-track' ,
464474 enabled = True ,
465475 create_tracker = lambda : None ,
476+ evaluator = Evaluator .noop (),
466477 model = ModelConfig (name = 'gpt-4' , parameters = {}),
467478 provider = ProviderConfig (name = 'openai' ),
468479 instructions = '' ,
@@ -483,7 +494,7 @@ def test_flush_skips_node_without_tracker():
483494 node_run_id = uuid4 ()
484495 handler .on_chain_start ({}, {}, run_id = node_run_id , name = 'no-track' )
485496 handler .on_llm_end (_llm_result (5 , 3 , 2 ), run_id = uuid4 (), parent_run_id = node_run_id )
486- handler .flush (graph ) # should not raise
497+ await handler .flush (graph ) # should not raise
487498
488499 mock_ld_client .track .assert_not_called ()
489500
0 commit comments