@@ -826,50 +826,27 @@ async def process_tool_calls( # noqa: C901
826826
827827 # First, we handle output tool calls
828828 for call in tool_calls_by_kind ['output' ]:
829- if final_result :
830- if final_result .tool_call_id == call .tool_call_id :
831- part = _messages .ToolReturnPart (
832- tool_name = call .tool_name ,
833- content = 'Final result processed.' ,
834- tool_call_id = call .tool_call_id ,
835- )
836- output_parts .append (part )
837- # With early strategy, execute only the first output tool
838- elif ctx .deps .end_strategy == 'early' :
839- yield _messages .FunctionToolCallEvent (call )
840- part = _messages .ToolReturnPart (
841- tool_name = call .tool_name ,
842- content = 'Output tool not used - a final result was already processed.' ,
843- tool_call_id = call .tool_call_id ,
844- )
845- yield _messages .FunctionToolResultEvent (part )
846- output_parts .append (part )
847- # With exhaustive strategy, execute all output tools
848- elif ctx .deps .end_strategy == 'exhaustive' :
849- try :
850- await tool_manager .handle_call (call )
851- except exceptions .UnexpectedModelBehavior as e :
852- ctx .state .increment_retries (
853- ctx .deps .max_result_retries , error = e , model_settings = ctx .deps .model_settings
854- )
855- raise e # pragma: lax no cover
856- except ToolRetryError as e :
857- ctx .state .increment_retries (
858- ctx .deps .max_result_retries , error = e , model_settings = ctx .deps .model_settings
859- )
860- yield _messages .FunctionToolCallEvent (call )
861- output_parts .append (e .tool_retry )
862- yield _messages .FunctionToolResultEvent (e .tool_retry )
863- else :
864- part = _messages .ToolReturnPart (
865- tool_name = call .tool_name ,
866- content = 'Final result processed.' ,
867- tool_call_id = call .tool_call_id ,
868- )
869- output_parts .append (part )
870- else :
871- assert_never (ctx .deps .end_strategy )
872- else :
829+ # In case we got two tool calls with the same ID
830+ if final_result and final_result .tool_call_id == call .tool_call_id :
831+ part = _messages .ToolReturnPart (
832+ tool_name = call .tool_name ,
833+ content = 'Final result processed.' ,
834+ tool_call_id = call .tool_call_id ,
835+ )
836+ output_parts .append (part )
837+ # Early strategy is chosen and final result is already set
838+ elif ctx .deps .end_strategy == 'early' and final_result :
839+ yield _messages .FunctionToolCallEvent (call )
840+ part = _messages .ToolReturnPart (
841+ tool_name = call .tool_name ,
842+ content = 'Output tool not used - a final result was already processed.' ,
843+ tool_call_id = call .tool_call_id ,
844+ )
845+ yield _messages .FunctionToolResultEvent (part )
846+ output_parts .append (part )
847+ # Early strategy is chosen and final result is not yet set
848+ # Or exhaustive strategy is chosen
849+ elif (ctx .deps .end_strategy == 'early' and not final_result ) or ctx .deps .end_strategy == 'exhaustive' :
873850 try :
874851 result_data = await tool_manager .handle_call (call )
875852 except exceptions .UnexpectedModelBehavior as e :
@@ -891,7 +868,13 @@ async def process_tool_calls( # noqa: C901
891868 tool_call_id = call .tool_call_id ,
892869 )
893870 output_parts .append (part )
894- final_result = result .FinalResult (result_data , call .tool_name , call .tool_call_id )
871+
872+ # Even in exhaustive mode, use the first output tool's result as the final result
873+ if not final_result :
874+ final_result = result .FinalResult (result_data , call .tool_name , call .tool_call_id )
875+ # Unknown strategy or invalid state
876+ else :
877+ assert_never (ctx .deps .end_strategy )
895878
896879 # Then, we handle function tool calls
897880 calls_to_run : list [_messages .ToolCallPart ] = []
0 commit comments