mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: openai updates (#388)
* openai updates * rebuild of openai structure * updated responses structure * renamed sample * added file id support to code interpreter * added hosted file ids to code interpretor * mypy fixes * removed default az cred from codebase * updated agent name setup * added kwargs to entra methods * and further kwargs * extra comment * updated all samples * readded custom get methods for responses * updated int tests with ad credential * missed one
This commit is contained in:
committed by
GitHub
Unverified
parent
19676978e9
commit
df9d85d1f0
@@ -305,48 +305,3 @@ async def test_base_client_with_streaming_function_calling_disabled(chat_client_
|
||||
updates.append(update)
|
||||
assert len(updates) == 1
|
||||
assert exec_counter == 0
|
||||
|
||||
|
||||
def test_chat_options_parsing_tools(chat_client_base, ai_function_tool) -> None:
|
||||
"""Test that chat options can parse tools correctly."""
|
||||
|
||||
def echo() -> str:
|
||||
"""Echo the input."""
|
||||
return "Echo"
|
||||
|
||||
dict_function = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Retrieves current weather for the given location.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string", "description": "City and country e.g. Bogotá, Colombia"},
|
||||
"units": {
|
||||
"type": "string",
|
||||
"enum": ["celsius", "fahrenheit"],
|
||||
"description": "Units the temperature will be returned in.",
|
||||
},
|
||||
},
|
||||
"required": ["location", "units"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
"strict": True,
|
||||
},
|
||||
}
|
||||
|
||||
options = ChatOptions(tools=[ai_function_tool, echo, dict_function], tool_choice="auto")
|
||||
assert len(options.tools) == 3
|
||||
assert options.tools[0] == ai_function_tool
|
||||
assert options.tools[1] != echo
|
||||
assert options.tools[2] == dict_function
|
||||
# after prepare, the tools should be represented as dicts
|
||||
# while ai_tools is still the same.
|
||||
chat_client_base._prepare_tools_and_tool_choice(chat_options=options)
|
||||
assert options._ai_tools[0] == ai_function_tool
|
||||
assert options._ai_tools[2] == dict_function
|
||||
assert len(options.tools) == 3
|
||||
assert options.tools[0]["function"]["name"] == "simple_function"
|
||||
assert options.tools[1]["function"]["name"] == "echo"
|
||||
assert options.tools[2]["function"]["name"] == "get_weather"
|
||||
|
||||
@@ -95,7 +95,7 @@ async def test_ai_function_invoke_telemetry_enabled():
|
||||
|
||||
# Mock the histogram
|
||||
mock_histogram = Mock()
|
||||
telemetry_test_tool.invocation_duration_histogram = mock_histogram
|
||||
telemetry_test_tool._invocation_duration_histogram = mock_histogram
|
||||
|
||||
# Call invoke
|
||||
result = await telemetry_test_tool.invoke(x=1, y=2, tool_call_id="test_call_id")
|
||||
@@ -139,7 +139,7 @@ async def test_ai_function_invoke_telemetry_with_pydantic_args():
|
||||
mock_start_span.return_value = mock_context_manager
|
||||
|
||||
mock_histogram = Mock()
|
||||
pydantic_test_tool.invocation_duration_histogram = mock_histogram
|
||||
pydantic_test_tool._invocation_duration_histogram = mock_histogram
|
||||
|
||||
# Call invoke with Pydantic model
|
||||
result = await pydantic_test_tool.invoke(arguments=args_model, tool_call_id="pydantic_call")
|
||||
@@ -172,7 +172,7 @@ async def test_ai_function_invoke_telemetry_with_exception():
|
||||
mock_start_span.return_value = mock_context_manager
|
||||
|
||||
mock_histogram = Mock()
|
||||
exception_test_tool.invocation_duration_histogram = mock_histogram
|
||||
exception_test_tool._invocation_duration_histogram = mock_histogram
|
||||
|
||||
# Call invoke and expect exception
|
||||
with pytest.raises(ValueError, match="Test exception for telemetry"):
|
||||
@@ -212,7 +212,7 @@ async def test_ai_function_invoke_telemetry_async_function():
|
||||
mock_start_span.return_value = mock_context_manager
|
||||
|
||||
mock_histogram = Mock()
|
||||
async_telemetry_test.invocation_duration_histogram = mock_histogram
|
||||
async_telemetry_test._invocation_duration_histogram = mock_histogram
|
||||
|
||||
# Call invoke
|
||||
result = await async_telemetry_test.invoke(x=3, y=4, tool_call_id="async_call")
|
||||
@@ -251,7 +251,7 @@ async def test_ai_function_invoke_telemetry_no_tool_call_id():
|
||||
mock_start_span.return_value = mock_context_manager
|
||||
|
||||
mock_histogram = Mock()
|
||||
no_id_test_tool.invocation_duration_histogram = mock_histogram
|
||||
no_id_test_tool._invocation_duration_histogram = mock_histogram
|
||||
|
||||
# Call invoke without tool_call_id
|
||||
result = await no_id_test_tool.invoke(x=5)
|
||||
@@ -300,29 +300,19 @@ def test_hosted_code_interpreter_tool_default():
|
||||
|
||||
assert tool.name == "code_interpreter"
|
||||
assert tool.inputs == []
|
||||
assert tool.description is None
|
||||
assert tool.description == ""
|
||||
assert tool.additional_properties is None
|
||||
assert str(tool) == "HostedCodeInterpreterTool(name=code_interpreter)"
|
||||
|
||||
|
||||
def test_hosted_code_interpreter_tool_custom_name():
|
||||
"""Test HostedCodeInterpreterTool with custom name."""
|
||||
tool = HostedCodeInterpreterTool(name="custom_interpreter")
|
||||
|
||||
assert tool.name == "custom_interpreter"
|
||||
assert tool.inputs == []
|
||||
assert str(tool) == "HostedCodeInterpreterTool(name=custom_interpreter)"
|
||||
|
||||
|
||||
def test_hosted_code_interpreter_tool_with_description():
|
||||
"""Test HostedCodeInterpreterTool with description and additional properties."""
|
||||
tool = HostedCodeInterpreterTool(
|
||||
name="test_interpreter",
|
||||
description="A test code interpreter",
|
||||
additional_properties={"version": "1.0", "language": "python"},
|
||||
)
|
||||
|
||||
assert tool.name == "test_interpreter"
|
||||
assert tool.name == "code_interpreter"
|
||||
assert tool.description == "A test code interpreter"
|
||||
assert tool.additional_properties == {"version": "1.0", "language": "python"}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from agent_framework import (
|
||||
AIAnnotation,
|
||||
AIContent,
|
||||
AIContents,
|
||||
AIFunction,
|
||||
AITool,
|
||||
AnnotatedRegion,
|
||||
ChatFinishReason,
|
||||
@@ -723,11 +724,12 @@ def test_chat_options_init_with_args(ai_function_tool, ai_tool) -> None:
|
||||
assert options.presence_penalty == 0.0
|
||||
assert options.frequency_penalty == 0.0
|
||||
assert options.user == "user-123"
|
||||
for tool in options._ai_tools:
|
||||
for tool in options.tools:
|
||||
assert isinstance(tool, AITool)
|
||||
assert tool.name is not None
|
||||
assert tool.description is not None
|
||||
assert tool.parameters() is not None
|
||||
if isinstance(tool, AIFunction):
|
||||
assert tool.parameters() is not None
|
||||
|
||||
settings = options.to_provider_settings()
|
||||
assert settings["model"] == "gpt-4" # uses alias
|
||||
@@ -754,8 +756,6 @@ def test_chat_options_and(ai_function_tool, ai_tool) -> None:
|
||||
options3 = options1 & options2
|
||||
|
||||
assert options3.ai_model_id == "gpt-4.1"
|
||||
assert len(options3._ai_tools) == 2
|
||||
assert options3._ai_tools == [ai_function_tool, ai_tool]
|
||||
assert options3.tools == [ai_function_tool, ai_tool]
|
||||
assert options3.logit_bias == {"x": 1}
|
||||
assert options3.metadata == {"a": "b"}
|
||||
|
||||
@@ -15,7 +15,6 @@ from pydantic import BaseModel
|
||||
|
||||
from agent_framework import ChatMessage, ChatResponseUpdate
|
||||
from agent_framework.exceptions import (
|
||||
ServiceInvalidResponseError,
|
||||
ServiceResponseException,
|
||||
)
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
@@ -192,7 +191,7 @@ async def test_cmc_general_exception(
|
||||
|
||||
|
||||
@patch.object(AsyncChatCompletions, "create", new_callable=AsyncMock)
|
||||
async def test_scmc(
|
||||
async def test_get_streaming(
|
||||
mock_create: AsyncMock,
|
||||
chat_history: list[ChatMessage],
|
||||
openai_unit_test_env: dict[str, str],
|
||||
@@ -231,7 +230,7 @@ async def test_scmc(
|
||||
|
||||
|
||||
@patch.object(AsyncChatCompletions, "create", new_callable=AsyncMock)
|
||||
async def test_scmc_singular(
|
||||
async def test_get_streaming_singular(
|
||||
mock_create: AsyncMock,
|
||||
chat_history: list[ChatMessage],
|
||||
openai_unit_test_env: dict[str, str],
|
||||
@@ -270,7 +269,7 @@ async def test_scmc_singular(
|
||||
|
||||
|
||||
@patch.object(AsyncChatCompletions, "create", new_callable=AsyncMock)
|
||||
async def test_scmc_structured_output_no_fcc(
|
||||
async def test_get_streaming_structured_output_no_fcc(
|
||||
mock_create: AsyncMock,
|
||||
chat_history: list[ChatMessage],
|
||||
openai_unit_test_env: dict[str, str],
|
||||
@@ -308,7 +307,7 @@ async def test_scmc_structured_output_no_fcc(
|
||||
|
||||
|
||||
@patch.object(AsyncChatCompletions, "create", new_callable=AsyncMock)
|
||||
async def test_scmc_no_fcc_in_response(
|
||||
async def test_get_streaming_no_fcc_in_response(
|
||||
mock_create: AsyncMock,
|
||||
chat_history: list[ChatMessage],
|
||||
mock_streaming_chat_completion_response: ChatCompletion,
|
||||
@@ -334,7 +333,7 @@ async def test_scmc_no_fcc_in_response(
|
||||
|
||||
|
||||
@patch.object(AsyncChatCompletions, "create", new_callable=AsyncMock)
|
||||
async def test_scmc_no_stream(
|
||||
async def test_get_streaming_no_stream(
|
||||
mock_create: AsyncMock,
|
||||
chat_history: list[ChatMessage],
|
||||
openai_unit_test_env: dict[str, str],
|
||||
@@ -344,7 +343,7 @@ async def test_scmc_no_stream(
|
||||
chat_history.append(ChatMessage(role="user", text="hello world"))
|
||||
|
||||
openai_chat_completion = OpenAIChatClient()
|
||||
with pytest.raises(ServiceInvalidResponseError):
|
||||
with pytest.raises(ServiceResponseException):
|
||||
[
|
||||
msg
|
||||
async for msg in openai_chat_completion.get_streaming_response(
|
||||
|
||||
@@ -7,7 +7,7 @@ import pytest
|
||||
from pydantic import BaseModel
|
||||
|
||||
from agent_framework import ChatClient, ChatMessage, ChatResponse, ChatResponseUpdate, TextContent, ai_function
|
||||
from agent_framework.exceptions import ServiceInitializationError, ServiceResponseException
|
||||
from agent_framework.exceptions import ServiceInitializationError
|
||||
from agent_framework.openai import OpenAIResponsesClient
|
||||
|
||||
skip_if_openai_integration_tests_disabled = pytest.mark.skipif(
|
||||
@@ -247,23 +247,21 @@ async def test_openai_responses_client_streaming() -> None:
|
||||
messages.append(ChatMessage(role="user", text="The weather in Seattle is sunny"))
|
||||
messages.append(ChatMessage(role="user", text="What is the weather in Seattle?"))
|
||||
|
||||
# This is currently broken. See https://github.com/openai/openai-python/issues/2305
|
||||
with pytest.raises(ServiceResponseException):
|
||||
response = openai_responses_client.get_streaming_response(
|
||||
messages=messages,
|
||||
response_format=OutputStruct,
|
||||
)
|
||||
full_message = ""
|
||||
async for chunk in response:
|
||||
assert chunk is not None
|
||||
assert isinstance(chunk, ChatResponseUpdate)
|
||||
for content in chunk.contents:
|
||||
if isinstance(content, TextContent) and content.text:
|
||||
full_message += content.text
|
||||
response = openai_responses_client.get_streaming_response(
|
||||
messages=messages,
|
||||
response_format=OutputStruct,
|
||||
)
|
||||
full_message = ""
|
||||
async for chunk in response:
|
||||
assert chunk is not None
|
||||
assert isinstance(chunk, ChatResponseUpdate)
|
||||
for content in chunk.contents:
|
||||
if isinstance(content, TextContent) and content.text:
|
||||
full_message += content.text
|
||||
|
||||
output = OutputStruct.model_validate_json(full_message)
|
||||
assert "Seattle" in output.location
|
||||
assert "sunny" in output.weather
|
||||
output = OutputStruct.model_validate_json(full_message)
|
||||
assert "Seattle" in output.location
|
||||
assert "sunny" in output.weather
|
||||
|
||||
|
||||
@skip_if_openai_integration_tests_disabled
|
||||
@@ -294,22 +292,20 @@ async def test_openai_responses_client_streaming_tools() -> None:
|
||||
messages.clear()
|
||||
messages.append(ChatMessage(role="user", text="What is the weather in Seattle?"))
|
||||
|
||||
# This is currently broken. See https://github.com/openai/openai-python/issues/2305
|
||||
with pytest.raises(ServiceResponseException):
|
||||
response = openai_responses_client.get_streaming_response(
|
||||
messages=messages,
|
||||
tools=[get_weather],
|
||||
tool_choice="auto",
|
||||
response_format=OutputStruct,
|
||||
)
|
||||
full_message = ""
|
||||
async for chunk in response:
|
||||
assert chunk is not None
|
||||
assert isinstance(chunk, ChatResponseUpdate)
|
||||
for content in chunk.contents:
|
||||
if isinstance(content, TextContent) and content.text:
|
||||
full_message += content.text
|
||||
response = openai_responses_client.get_streaming_response(
|
||||
messages=messages,
|
||||
tools=[get_weather],
|
||||
tool_choice="auto",
|
||||
response_format=OutputStruct,
|
||||
)
|
||||
full_message = ""
|
||||
async for chunk in response:
|
||||
assert chunk is not None
|
||||
assert isinstance(chunk, ChatResponseUpdate)
|
||||
for content in chunk.contents:
|
||||
if isinstance(content, TextContent) and content.text:
|
||||
full_message += content.text
|
||||
|
||||
output = OutputStruct.model_validate_json(full_message)
|
||||
assert "Seattle" in output.location
|
||||
assert "sunny" in output.weather
|
||||
output = OutputStruct.model_validate_json(full_message)
|
||||
assert "Seattle" in output.location
|
||||
assert "sunny" in output.weather
|
||||
|
||||
Reference in New Issue
Block a user