@@ -419,10 +419,9 @@ def test_orchestration_output_omits_repeated_input_history() -> None:
419419
420420
421421@pytest .mark .parametrize ("span_type" , [WorkflowSpan , AgentSpan ])
422- def test_orchestration_full_history_preserves_all_terminal_assistant_messages (
422+ def test_orchestration_full_history_keeps_last_terminal_message (
423423 span_type : type [WorkflowSpan ] | type [AgentSpan ],
424424) -> None :
425- # Given: a full-history result with two terminal assistant outputs after the exact input history.
426425 user = {"role" : "user" , "content" : "Give me two alternatives" }
427426 first = {"role" : "assistant" , "content" : "First alternative" }
428427 second = {"role" : "assistant" , "content" : "Second alternative" }
@@ -434,13 +433,10 @@ def test_orchestration_full_history_preserves_all_terminal_assistant_messages(
434433 if span_type is AgentSpan :
435434 span_kwargs ["agent_type" ] = AgentType .planner
436435
437- # When: the orchestration content is converted.
438436 attrs = build_span_attributes (span_type (** span_kwargs ))
439437
440- # Then: the repeated input prefix is removed without reducing the terminal outputs to one message.
441438 assert json .loads (attrs ["gen_ai.output.messages" ]) == [
442- _text_message ("assistant" , "First alternative" , finish_reason = "unknown" ),
443- _text_message ("assistant" , "Second alternative" , finish_reason = "unknown" ),
439+ _text_message ("assistant" , "Second alternative" , finish_reason = "unknown" )
444440 ]
445441
446442
@@ -640,6 +636,55 @@ def test_orchestration_message_container_without_input_prefix_match_not_reduced(
640636 assert output_messages [1 ]["parts" ][0 ]["response" ] == "Amlodipine: 5 mg daily"
641637
642638
639+ def test_orchestration_full_history_ends_on_tool_message_keeps_last () -> None :
640+ # return_direct=True tool: run ends on a tool response, no final assistant message.
641+ # The dedup gate fires (prefix matches) but the last message is a tool, not assistant.
642+ # Must return the tool message rather than an empty list.
643+ user = {"role" : "user" , "content" : "Get patient P001" }
644+ ai_toolcall = {
645+ "role" : "assistant" ,
646+ "content" : "" ,
647+ "tool_calls" : [{"id" : "tc1" , "function" : {"name" : "get_patient" , "arguments" : '{"id":"P001"}' }}],
648+ }
649+ tool_resp = {"role" : "tool" , "content" : "George Rivera, Lisinopril 10mg" , "tool_call_id" : "tc1" }
650+ span = AgentSpan (
651+ name = "Agent" ,
652+ agent_type = AgentType .default ,
653+ input = json .dumps ({"messages" : [user , ai_toolcall ]}),
654+ output = json .dumps ({"messages" : [user , ai_toolcall , tool_resp ]}),
655+ )
656+
657+ attrs = build_span_attributes (span )
658+
659+ output_messages = json .loads (attrs ["gen_ai.output.messages" ])
660+ assert len (output_messages ) == 1
661+ assert output_messages [0 ]["parts" ][0 ]["response" ] == "George Rivera, Lisinopril 10mg"
662+
663+
664+ def test_orchestration_full_history_ends_on_tool_call_ai_message_keeps_last () -> None :
665+ # interrupt_before=["tools"]: run ends on a tool-call AIMessage with empty content.
666+ # The last message is assistant role but content="" — must not return empty list.
667+ user = {"role" : "user" , "content" : "Search for Lisinopril" }
668+ ai_toolcall = {
669+ "role" : "assistant" ,
670+ "content" : "" ,
671+ "tool_calls" : [{"id" : "tc1" , "function" : {"name" : "search" , "arguments" : '{"query":"Lisinopril"}' }}],
672+ }
673+ span = AgentSpan (
674+ name = "Agent" ,
675+ agent_type = AgentType .default ,
676+ input = json .dumps ({"messages" : [user ]}),
677+ output = json .dumps ({"messages" : [user , ai_toolcall ]}),
678+ )
679+
680+ attrs = build_span_attributes (span )
681+
682+ output_messages = json .loads (attrs ["gen_ai.output.messages" ])
683+ assert len (output_messages ) == 1
684+ assert output_messages [0 ]["role" ] == "assistant"
685+ assert output_messages [0 ]["finish_reason" ] == "tool_call"
686+
687+
643688def test_orchestration_preserves_schema_valid_parts_and_tool_calls () -> None :
644689 span = WorkflowSpan (
645690 name = "tool-workflow" ,
0 commit comments