@@ -2292,6 +2292,80 @@ def known_func(arg1: str) -> str:
22922292 assert exec_counter == 0
22932293
22942294
2295+ @pytest .mark .parametrize ("declaration_first" , [True , False ], ids = ["declaration-first" , "approval-first" ])
2296+ async def test_mixed_batch_always_require_approval_takes_precedence_over_declaration_only (
2297+ declaration_first : bool ,
2298+ ) -> None :
2299+ """An approval pause must not depend on the position of a declaration-only sibling."""
2300+ from agent_framework import FunctionTool
2301+ from agent_framework ._tools import _try_execute_function_call_groups
2302+
2303+ @tool (name = "guarded" , approval_mode = "always_require" )
2304+ def guarded () -> str :
2305+ return "should not run"
2306+
2307+ declaration_only = FunctionTool (name = "external" , description = "Handled by the host" , func = None )
2308+ guarded_call = Content .from_function_call (call_id = "guarded-call" , name = "guarded" , arguments = {})
2309+ declaration_call = Content .from_function_call (call_id = "external-call" , name = "external" , arguments = {})
2310+ calls = [declaration_call , guarded_call ] if declaration_first else [guarded_call , declaration_call ]
2311+
2312+ result_groups , should_terminate = await _try_execute_function_call_groups (
2313+ custom_args = {}, function_calls = calls , tools = [guarded , declaration_only ], config = {}
2314+ )
2315+
2316+ assert not should_terminate
2317+ approval_requests = [
2318+ content for group in result_groups for content in group if content .type == "function_approval_request"
2319+ ]
2320+ assert any (request .function_call .name == "guarded" for request in approval_requests )
2321+
2322+
2323+ async def test_mixed_batch_always_require_approval_takes_precedence_over_unknown_termination () -> None :
2324+ """An unknown call cannot mask an approval pause when unknown-call termination is enabled."""
2325+ from agent_framework ._tools import _try_execute_function_call_groups
2326+
2327+ @tool (name = "guarded" , approval_mode = "always_require" )
2328+ def guarded () -> str :
2329+ return "should not run"
2330+
2331+ result_groups , should_terminate = await _try_execute_function_call_groups (
2332+ custom_args = {},
2333+ function_calls = [
2334+ Content .from_function_call (call_id = "unknown-call" , name = "unknown" , arguments = {}),
2335+ Content .from_function_call (call_id = "guarded-call" , name = "guarded" , arguments = {}),
2336+ ],
2337+ tools = [guarded ],
2338+ config = {"terminate_on_unknown_calls" : True },
2339+ )
2340+
2341+ assert not should_terminate
2342+ approval_requests = [
2343+ content for group in result_groups for content in group if content .type == "function_approval_request"
2344+ ]
2345+ assert any (request .function_call .name == "guarded" for request in approval_requests )
2346+
2347+
2348+ async def test_mixed_batch_declaration_only_takes_precedence_over_unknown_termination () -> None :
2349+ """A declaration-only pause also takes precedence over an unknown-call termination."""
2350+ from agent_framework import FunctionTool
2351+ from agent_framework ._tools import _try_execute_function_call_groups
2352+
2353+ declaration_only = FunctionTool (name = "external" , description = "Handled by the host" , func = None )
2354+ result_groups , should_terminate = await _try_execute_function_call_groups (
2355+ custom_args = {},
2356+ function_calls = [
2357+ Content .from_function_call (call_id = "unknown-call" , name = "unknown" , arguments = {}),
2358+ Content .from_function_call (call_id = "external-call" , name = "external" , arguments = {}),
2359+ ],
2360+ tools = [declaration_only ],
2361+ config = {"terminate_on_unknown_calls" : True },
2362+ )
2363+
2364+ assert not should_terminate
2365+ paused_calls = [content for group in result_groups for content in group ]
2366+ assert all (content .type == "function_call" and content .user_input_request for content in paused_calls )
2367+
2368+
22952369async def test_function_invocation_config_additional_tools (chat_client_base : SupportsChatGetResponse ):
22962370 """Test that additional_tools are available but treated as declaration_only."""
22972371 exec_counter_visible = 0
0 commit comments