@@ -402,7 +402,7 @@ class TestCreateAgent:
402402 """Tests for LangChainRunnerFactory.create_agent."""
403403
404404 def test_creates_agent_runner_with_instructions_and_tool_definitions (self ):
405- """Should create LangChainAgentRunner with instructions and tool definitions ."""
405+ """Should create LangChainAgentRunner wrapping a compiled graph ."""
406406 from unittest .mock import patch
407407 from ldai_langchain import LangChainAgentRunner
408408
@@ -420,15 +420,18 @@ def test_creates_agent_runner_with_instructions_and_tool_definitions(self):
420420 'provider' : {'name' : 'openai' },
421421 }
422422
423- with patch ('ldai_langchain.langchain_runner_factory.create_langchain_model' ) as mock_create :
424- mock_llm = MagicMock ()
425- mock_create .return_value = mock_llm
423+ mock_agent = MagicMock ()
424+ with patch ('ldai_langchain.langchain_runner_factory.create_langchain_model' ) as mock_create , \
425+ patch ('ldai_langchain.langchain_runner_factory.build_structured_tools' ) as mock_tools , \
426+ patch ('ldai_langchain.langchain_runner_factory.lc_create_agent' , return_value = mock_agent ):
427+ mock_create .return_value = MagicMock ()
428+ mock_tools .return_value = [MagicMock ()]
426429
427430 factory = LangChainRunnerFactory ()
428431 result = factory .create_agent (mock_ai_config , {'get-weather' : lambda loc : 'sunny' })
429432
430433 assert isinstance (result , LangChainAgentRunner )
431- assert result ._instructions == "You are a helpful assistant."
434+ assert result ._agent is mock_agent
432435
433436 def test_creates_agent_runner_with_no_tools (self ):
434437 """Should create LangChainAgentRunner with no tool definitions."""
@@ -442,73 +445,72 @@ def test_creates_agent_runner_with_no_tools(self):
442445 'provider' : {'name' : 'openai' },
443446 }
444447
445- with patch ('ldai_langchain.langchain_runner_factory.create_langchain_model' ) as mock_create :
448+ mock_agent = MagicMock ()
449+ with patch ('ldai_langchain.langchain_runner_factory.create_langchain_model' ) as mock_create , \
450+ patch ('ldai_langchain.langchain_runner_factory.build_structured_tools' , return_value = []), \
451+ patch ('ldai_langchain.langchain_runner_factory.lc_create_agent' , return_value = mock_agent ):
446452 mock_create .return_value = MagicMock ()
447453
448454 factory = LangChainRunnerFactory ()
449455 result = factory .create_agent (mock_ai_config , {})
450456
451457 assert isinstance (result , LangChainAgentRunner )
452- assert result ._tools == {}
458+ assert result ._agent is mock_agent
453459
454460
455461class TestLangChainAgentRunner :
456462 """Tests for LangChainAgentRunner.run."""
457463
458464 @pytest .mark .asyncio
459- async def test_runs_agent_and_returns_result_with_no_tool_calls (self ):
460- """Should return AgentResult when model responds with no tool calls ."""
465+ async def test_runs_agent_and_returns_result (self ):
466+ """Should return AgentResult with the last message content from the graph ."""
461467 from ldai_langchain import LangChainAgentRunner
462- from langchain_core .messages import AIMessage
463468
464- mock_llm = MagicMock ( )
465- mock_response = AIMessage ( content = "The answer is 42." )
466- mock_llm .ainvoke = AsyncMock (return_value = mock_response )
469+ final_msg = AIMessage ( content = "The answer is 42." )
470+ mock_agent = MagicMock ( )
471+ mock_agent .ainvoke = AsyncMock (return_value = { "messages" : [ final_msg ]} )
467472
468- runner = LangChainAgentRunner (mock_llm , "You are helpful." , {} )
473+ runner = LangChainAgentRunner (mock_agent )
469474 result = await runner .run ("What is the answer?" )
470475
471476 assert result .output == "The answer is 42."
472477 assert result .metrics .success is True
478+ mock_agent .ainvoke .assert_called_once_with (
479+ {"messages" : [{"role" : "user" , "content" : "What is the answer?" }]}
480+ )
473481
474482 @pytest .mark .asyncio
475- async def test_executes_tool_calls_and_returns_final_response (self ):
476- """Should execute tool calls and continue loop until final response ."""
483+ async def test_aggregates_token_usage_across_messages (self ):
484+ """Should sum token usage from all messages in the graph result ."""
477485 from ldai_langchain import LangChainAgentRunner
478- from langchain_core .messages import AIMessage
479-
480- # First response: has a tool call
481- first_response = AIMessage (content = "" )
482- first_response .tool_calls = [
483- {"name" : "get-weather" , "args" : {"location" : "Paris" }, "id" : "call_123" }
484- ]
485486
486- # Second response: final answer
487- second_response = AIMessage (content = "It is sunny in Paris." )
487+ msg1 = AIMessage (content = "intermediate" )
488+ msg1 .usage_metadata = {'total_tokens' : 10 , 'input_tokens' : 6 , 'output_tokens' : 4 }
489+ msg2 = AIMessage (content = "final answer" )
490+ msg2 .usage_metadata = {'total_tokens' : 20 , 'input_tokens' : 12 , 'output_tokens' : 8 }
488491
489- mock_llm = MagicMock ()
490- mock_llm .ainvoke = AsyncMock (side_effect = [ first_response , second_response ] )
492+ mock_agent = MagicMock ()
493+ mock_agent .ainvoke = AsyncMock (return_value = { "messages" : [ msg1 , msg2 ]} )
491494
492- weather_fn = MagicMock (return_value = "Sunny, 25°C" )
493- runner = LangChainAgentRunner (
494- mock_llm , "You are helpful." ,
495- {'get-weather' : weather_fn },
496- )
497- result = await runner .run ("What is the weather in Paris?" )
495+ runner = LangChainAgentRunner (mock_agent )
496+ result = await runner .run ("Hello" )
498497
499- assert result .output == "It is sunny in Paris. "
498+ assert result .output == "final answer "
500499 assert result .metrics .success is True
501- weather_fn .assert_called_once_with (location = "Paris" )
500+ assert result .metrics .usage is not None
501+ assert result .metrics .usage .total == 30
502+ assert result .metrics .usage .input == 18
503+ assert result .metrics .usage .output == 12
502504
503505 @pytest .mark .asyncio
504506 async def test_returns_failure_when_exception_thrown (self ):
505507 """Should return unsuccessful AgentResult when exception is thrown."""
506508 from ldai_langchain import LangChainAgentRunner
507509
508- mock_llm = MagicMock ()
509- mock_llm .ainvoke = AsyncMock (side_effect = Exception ("LLM Error" ))
510+ mock_agent = MagicMock ()
511+ mock_agent .ainvoke = AsyncMock (side_effect = Exception ("Graph Error" ))
510512
511- runner = LangChainAgentRunner (mock_llm , "" , {} )
513+ runner = LangChainAgentRunner (mock_agent )
512514 result = await runner .run ("Hello" )
513515
514516 assert result .output == ""
0 commit comments