mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
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:
co-authored by
Copilot
parent
ed113f941c
commit
aab621f5eb
@@ -56,6 +56,36 @@ async def test_base_client_with_function_calling(chat_client_base: SupportsChatG
|
||||
assert response.messages[2].text == "done"
|
||||
|
||||
|
||||
async def test_base_client_with_function_calling_tools_in_kwargs(chat_client_base: SupportsChatGetResponse):
|
||||
exec_counter = 0
|
||||
|
||||
@tool(name="test_function", approval_mode="never_require")
|
||||
def ai_func(arg1: str) -> str:
|
||||
nonlocal exec_counter
|
||||
exec_counter += 1
|
||||
return f"Processed {arg1}"
|
||||
|
||||
chat_client_base.run_responses = [
|
||||
ChatResponse(
|
||||
messages=Message(
|
||||
role="assistant",
|
||||
contents=[
|
||||
Content.from_function_call(call_id="1", name="test_function", arguments='{"arg1": "value1"}')
|
||||
],
|
||||
)
|
||||
),
|
||||
ChatResponse(messages=Message(role="assistant", text="done")),
|
||||
]
|
||||
|
||||
response = await chat_client_base.get_response("hello", tools=[ai_func])
|
||||
|
||||
assert exec_counter == 1
|
||||
assert len(response.messages) == 3
|
||||
assert response.messages[1].role == "tool"
|
||||
assert response.messages[1].contents[0].type == "function_result"
|
||||
assert response.messages[1].contents[0].result == "Processed value1"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("max_iterations", [3])
|
||||
async def test_base_client_with_function_calling_resets(chat_client_base: SupportsChatGetResponse):
|
||||
exec_counter = 0
|
||||
|
||||
@@ -921,8 +921,8 @@ def test_chat_options_tool_choice_validation():
|
||||
}
|
||||
assert validate_tool_mode({"mode": "none"}) == {"mode": "none"}
|
||||
|
||||
# None should return mode==none
|
||||
assert validate_tool_mode(None) == {"mode": "none"}
|
||||
# None should remain unset
|
||||
assert validate_tool_mode(None) is None
|
||||
|
||||
with raises(ContentError):
|
||||
validate_tool_mode("invalid_mode")
|
||||
|
||||
@@ -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."""
|
||||
|
||||
Reference in New Issue
Block a user