Python: Strip tools from FoundryAgent request when agent_reference is present (#5101)

_prepare_options() now removes tools, tool_choice, and parallel_tool_calls
from run_options after injecting agent_reference. The Foundry API rejects
requests containing both fields. FunctionTools are still invoked client-side
by the function invocation layer.

Fixes #5087

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
This commit is contained in:
Matt Van Horn
2026-04-07 21:35:01 -07:00
committed by GitHub
Unverified
parent e10d448ae2
commit 18d1ba3624
2 changed files with 46 additions and 0 deletions
@@ -293,6 +293,13 @@ class RawFoundryAgentChatClient( # type: ignore[misc]
# Inject agent reference
run_options["extra_body"] = {"agent_reference": self._get_agent_reference()}
# Strip tools from request body - Foundry API rejects requests with both
# agent_reference and tools present. FunctionTools are invoked client-side
# by the function invocation layer, not sent to the service.
run_options.pop("tools", None)
run_options.pop("tool_choice", None)
run_options.pop("parallel_tool_calls", None)
return run_options
@override
@@ -200,6 +200,45 @@ async def test_raw_foundry_agent_chat_client_prepare_options_accepts_function_to
assert result["extra_body"]["agent_reference"]["name"] == "test-agent"
async def test_raw_foundry_agent_chat_client_prepare_options_strips_tools() -> None:
"""Test that _prepare_options strips tools, tool_choice, and parallel_tool_calls from run_options."""
mock_project = MagicMock()
mock_openai = MagicMock()
mock_project.get_openai_client.return_value = mock_openai
client = RawFoundryAgentChatClient(
project_client=mock_project,
agent_name="test-agent",
)
@tool(approval_mode="never_require")
def my_func() -> str:
"""A test function."""
return "ok"
with patch(
"agent_framework_openai._chat_client.RawOpenAIChatClient._prepare_options",
new_callable=AsyncMock,
return_value={
"tools": [{"type": "function", "function": {"name": "my_func"}}],
"tool_choice": "auto",
"parallel_tool_calls": True,
},
):
result = await client._prepare_options(
messages=[Message(role="user", contents="hi")],
options={"tools": [my_func]},
)
assert "tools" not in result
assert "tool_choice" not in result
assert "parallel_tool_calls" not in result
assert "extra_body" in result
assert result["extra_body"]["agent_reference"]["name"] == "test-agent"
def test_raw_foundry_agent_chat_client_check_model_presence_is_noop() -> None:
"""Test that _check_model_presence does nothing (model is on service)."""