Python: Fix tool normalization and provider sample consolidation (#3953)

* Fix tool normalization and provider samples

- restore callable/single-tool normalization paths and unset tool-choice behavior\n- consolidate and expand chat/provider samples (OpenAI/Azure/Anthropic/Ollama/Bedrock)\n- migrate Bedrock lazy import surface to agent_framework.amazon and move provider samples

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* small fix in sample

* Finalize provider, samples, and core cleanup

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix CopilotTool passthrough in agent

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix link

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Eduard van Valkenburg
2026-02-16 16:30:38 +00:00
committed by GitHub
co-authored by Copilot
parent ed113f941c
commit aab621f5eb
99 changed files with 1190 additions and 969 deletions
@@ -701,6 +701,7 @@ def test_prepare_options_basic(mock_async_openai: MagicMock) -> None:
assert run_options["model"] == "gpt-4"
assert run_options["temperature"] == 0.7
assert run_options["top_p"] == 0.9
assert "tool_choice" not in run_options
assert tool_results is None
@@ -733,6 +734,52 @@ def test_prepare_options_with_tool_tool(mock_async_openai: MagicMock) -> None:
assert run_options["tool_choice"] == "auto"
def test_prepare_options_with_tools_without_tool_choice(mock_async_openai: MagicMock) -> None:
"""Test _prepare_options keeps tool_choice unset when not provided."""
client = create_test_openai_assistants_client(mock_async_openai)
@tool(approval_mode="never_require")
def test_function(query: str) -> str:
"""A test function."""
return f"Result for {query}"
options = {
"tools": [test_function],
}
messages = [Message(role="user", text="Hello")]
run_options, _ = client._prepare_options(messages, options) # type: ignore
assert "tools" in run_options
assert "tool_choice" not in run_options
def test_prepare_options_with_single_tool_tool(mock_async_openai: MagicMock) -> None:
"""Test _prepare_options with a single FunctionTool (non-sequence)."""
client = create_test_openai_assistants_client(mock_async_openai)
@tool(approval_mode="never_require")
def test_function(query: str) -> str:
"""A test function."""
return f"Result for {query}"
options = {
"tools": test_function,
"tool_choice": "auto",
}
messages = [Message(role="user", text="Hello")]
run_options, tool_results = client._prepare_options(messages, options) # type: ignore
assert "tools" in run_options
assert len(run_options["tools"]) == 1
assert run_options["tools"][0]["type"] == "function"
assert "function" in run_options["tools"][0]
assert run_options["tool_choice"] == "auto"
assert tool_results is None
def test_prepare_options_with_code_interpreter(mock_async_openai: MagicMock) -> None:
"""Test _prepare_options with code interpreter tool."""
client = create_test_openai_assistants_client(mock_async_openai)
@@ -190,6 +190,21 @@ def test_unsupported_tool_handling(openai_unit_test_env: dict[str, str]) -> None
assert result["tools"] == [dict_tool]
def test_prepare_tools_with_single_function_tool(openai_unit_test_env: dict[str, str]) -> None:
"""Test that a single FunctionTool is accepted for tool preparation."""
client = OpenAIChatClient()
@tool(approval_mode="never_require")
def test_function(query: str) -> str:
"""A test function."""
return f"Result for {query}"
result = client._prepare_tools_for_openai(test_function)
assert "tools" in result
assert len(result["tools"]) == 1
assert result["tools"][0]["type"] == "function"
@tool(approval_mode="never_require")
def get_story_text() -> str:
"""Returns a story about Emily and David."""