From a26c78497455ad376e7f8fe4e166e68a4eebdff9 Mon Sep 17 00:00:00 2001 From: Benke Qu Date: Mon, 27 Apr 2026 15:18:27 -0700 Subject: [PATCH 1/2] Fix FoundryAgent stripping model from PromptAgent requests Move run_options.pop('model', None) inside the _uses_foundry_agent_session() conditional so that model is only stripped for hosted agent sessions (where the server manages the model) and preserved for PromptAgent requests that require it in the Responses API call. Fixes #5525 --- .../foundry/agent_framework_foundry/_agent.py | 2 +- .../tests/foundry/test_foundry_agent.py | 36 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/python/packages/foundry/agent_framework_foundry/_agent.py b/python/packages/foundry/agent_framework_foundry/_agent.py index fbf165ab21..37e89de9e8 100644 --- a/python/packages/foundry/agent_framework_foundry/_agent.py +++ b/python/packages/foundry/agent_framework_foundry/_agent.py @@ -341,6 +341,7 @@ async def _prepare_options( if _uses_foundry_agent_session(conversation_id): run_options.pop("previous_response_id", None) run_options.pop("conversation", None) + run_options.pop("model", None) extra_body["agent_session_id"] = conversation_id if extra_body: run_options["extra_body"] = extra_body @@ -350,7 +351,6 @@ async def _prepare_options( # Strip tools from request body - Foundry API rejects requests with both # agent endpoint and tools present. FunctionTools are invoked client-side # by the function invocation layer, not sent to the service. - run_options.pop("model", None) if not self.allow_preview: run_options.pop("tools", None) run_options.pop("tool_choice", None) diff --git a/python/packages/foundry/tests/foundry/test_foundry_agent.py b/python/packages/foundry/tests/foundry/test_foundry_agent.py index e110e540fe..fd2c388632 100644 --- a/python/packages/foundry/tests/foundry/test_foundry_agent.py +++ b/python/packages/foundry/tests/foundry/test_foundry_agent.py @@ -200,7 +200,7 @@ def my_func() -> str: async def test_raw_foundry_agent_chat_client_prepare_options_strips_client_side_fields() -> None: - """Test that _prepare_options strips model and tool-loop fields from run_options.""" + """Test that _prepare_options strips tool-loop fields but preserves model for non-session requests.""" mock_project = MagicMock() mock_openai = MagicMock() @@ -232,11 +232,41 @@ def my_func() -> str: options={"tools": [my_func]}, ) - assert "model" not in result + # model is preserved for non-session (PromptAgent) requests + assert result["model"] == "gpt-4.1" assert "tools" not in result assert "tool_choice" not in result assert "parallel_tool_calls" not in result - assert result == {} + + +async def test_raw_foundry_agent_chat_client_prepare_options_strips_model_for_hosted_session() -> None: + """Test that model is stripped when using a hosted agent session (not a PromptAgent).""" + + 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", + ) + + with patch( + "agent_framework_openai._chat_client.RawOpenAIChatClient._prepare_options", + new_callable=AsyncMock, + return_value={ + "model": "gpt-4.1", + "previous_response_id": "resp_abc", + }, + ): + result = await client._prepare_options( + messages=[Message(role="user", contents="hi")], + options={"conversation_id": "agent-session-123"}, + ) + + assert "model" not in result + assert "previous_response_id" not in result + assert result["extra_body"]["agent_session_id"] == "agent-session-123" async def test_raw_foundry_agent_chat_client_prepare_options_maps_agent_session_id_to_extra_body() -> None: From 1cf139ff83b5b4ad9f2405e6e92dd829b0d49037 Mon Sep 17 00:00:00 2001 From: Benke Qu Date: Thu, 30 Apr 2026 16:09:23 -0700 Subject: [PATCH 2/2] test: add coverage for resp_* continuation preserving model Adds test_raw_foundry_agent_chat_client_prepare_options_preserves_model_for_resp_continuation to explicitly verify that HostedAgent v1 / v2-no-session paths (where conversation_id starts with resp_) preserve model and previous_response_id without triggering the hosted-session gate. --- .../tests/foundry/test_foundry_agent.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/python/packages/foundry/tests/foundry/test_foundry_agent.py b/python/packages/foundry/tests/foundry/test_foundry_agent.py index fd2c388632..0125acb356 100644 --- a/python/packages/foundry/tests/foundry/test_foundry_agent.py +++ b/python/packages/foundry/tests/foundry/test_foundry_agent.py @@ -269,6 +269,39 @@ async def test_raw_foundry_agent_chat_client_prepare_options_strips_model_for_ho assert result["extra_body"]["agent_session_id"] == "agent-session-123" +async def test_raw_foundry_agent_chat_client_prepare_options_preserves_model_for_resp_continuation() -> None: + """Test that model is preserved when conversation_id is a resp_* continuation (HostedAgent v1 / v2-no-session).""" + + 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", + ) + + with patch( + "agent_framework_openai._chat_client.RawOpenAIChatClient._prepare_options", + new_callable=AsyncMock, + return_value={ + "model": "gpt-4.1", + "previous_response_id": "resp_abc123", + }, + ): + result = await client._prepare_options( + messages=[Message(role="user", contents="hi")], + options={"conversation_id": "resp_abc123"}, + ) + + # model preserved — resp_* is standard Responses API continuity, not a hosted session + assert result["model"] == "gpt-4.1" + # previous_response_id preserved — not stripped outside hosted session path + assert result["previous_response_id"] == "resp_abc123" + # no agent_session_id injected + assert "extra_body" not in result or "agent_session_id" not in result.get("extra_body", {}) + + async def test_raw_foundry_agent_chat_client_prepare_options_maps_agent_session_id_to_extra_body() -> None: """Test that service_session_id is forwarded as agent_session_id for hosted sessions."""