Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions python/packages/azure-ai/agent_framework_azure_ai/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,21 +486,32 @@ def _remove_agent_level_run_options(
runtime_structured_output = self._get_structured_output_signature(chat_options)

if runtime_tools is not None or runtime_structured_output is not None:
tools_changed = runtime_tools is not None
structured_output_changed = runtime_structured_output is not None
tools_changed = False
structured_output_changed = False

if self.warn_runtime_tools_and_structure_changed:
# We created the agent ourselves so we can compare tool sets.
if runtime_tools is not None:
tools_changed = self._extract_tool_names(runtime_tools) != self._created_agent_tool_names
if runtime_structured_output is not None:
structured_output_changed = (
runtime_structured_output != self._created_agent_structured_output_signature
)
else:
# Agent was not created by this client (e.g. use_latest_version
# or explicit agent_version). We have no creation-time baseline
# so only warn when non-empty tools or structured_output are
# supplied — an empty tool list is just the framework default
# and should not trigger a false-positive warning.
if runtime_tools:
tools_changed = True
if runtime_structured_output is not None:
structured_output_changed = True

if tools_changed or structured_output_changed:
logger.warning(
"AzureAIClient does not support runtime tools or structured_output overrides after agent creation. "
"Use AzureOpenAIResponsesClient instead."
"AzureAIClient does not support runtime tools or structured_output overrides "
"after agent creation. Use AzureOpenAIResponsesClient instead."
)

agent_level_option_to_run_keys = {
Expand Down
77 changes: 77 additions & 0 deletions python/packages/azure-ai/tests/test_azure_ai_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,83 @@ async def test_use_latest_version_existing_agent(
assert client.agent_version == "2.5"


async def test_use_latest_version_no_spurious_warning_for_empty_tools(
mock_project_client: MagicMock,
) -> None:
"""Test that use_latest_version=True does not emit a false-positive tools warning.

When the agent is fetched via use_latest_version the framework may still
pass an empty runtime tools list (``[]``). The client should not warn
about a tool mismatch in this case because no user-supplied tools are
actually being overridden. Regression test for
https://github.com/microsoft/agent-framework/issues/4681
"""
client = create_test_azure_ai_client(
mock_project_client, agent_name="existing-agent", use_latest_version=True
)

# Mock existing agent
mock_existing_agent = MagicMock()
mock_existing_agent.name = "existing-agent"
mock_existing_agent.versions.latest.version = "2.5"
mock_project_client.agents.get = AsyncMock(return_value=mock_existing_agent)

messages = [Message(role="user", contents=[Content.from_text(text="Hello")])]

# Patch logger.warning across BOTH calls — neither should warn
with (
patch(
"agent_framework.openai._responses_client.RawOpenAIResponsesClient._prepare_options",
return_value={"model": "test-model", "tools": []},
),
patch("agent_framework_azure_ai._client.logger.warning") as mock_warning,
):
# First call fetches the latest version
await client._prepare_options(messages, {})
# Subsequent call with empty tools
await client._prepare_options(messages, {})

mock_warning.assert_not_called()


async def test_use_latest_version_warns_for_non_empty_tools(
mock_project_client: MagicMock,
) -> None:
"""Test that use_latest_version=True with non-empty tools DOES emit a warning.

Companion to the empty-tools test above. When the user supplies actual
runtime tools while use_latest_version=True, the client should warn that
the tools differ from the agent's creation-time configuration.
"""
client = create_test_azure_ai_client(
mock_project_client, agent_name="existing-agent", use_latest_version=True
)

# Mock existing agent
mock_existing_agent = MagicMock()
mock_existing_agent.name = "existing-agent"
mock_existing_agent.versions.latest.version = "2.5"
mock_project_client.agents.get = AsyncMock(return_value=mock_existing_agent)

messages = [Message(role="user", contents=[Content.from_text(text="Hello")])]

non_empty_tools = [{"type": "function", "function": {"name": "my_tool"}}]

with (
patch(
"agent_framework.openai._responses_client.RawOpenAIResponsesClient._prepare_options",
return_value={"model": "test-model", "tools": non_empty_tools},
),
patch("agent_framework_azure_ai._client.logger.warning") as mock_warning,
):
# First call fetches the latest version
await client._prepare_options(messages, {})
# Subsequent call with non-empty tools — SHOULD warn
await client._prepare_options(messages, {})

mock_warning.assert_called()


async def test_use_latest_version_agent_not_found(
mock_project_client: MagicMock,
) -> None:
Expand Down
Loading