Skip to content
Merged
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
129 changes: 129 additions & 0 deletions tests/entrypoints/openai/test_response_api_with_harmony.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,11 +535,17 @@ def get_place_to_travel():
return "Paris"


def get_horoscope(sign):
return f"{sign}: Next Tuesday you will befriend a baby otter."


def call_function(name, args):
if name == "get_weather":
return get_weather(**args)
elif name == "get_place_to_travel":
return get_place_to_travel()
elif name == "get_horoscope":
return get_horoscope(**args)
else:
raise ValueError(f"Unknown function: {name}")

Expand Down Expand Up @@ -828,3 +834,126 @@ async def test_output_messages_enabled(client: OpenAI, model_name: str, server):
assert response.status == "completed"
assert len(response.input_messages) > 0
assert len(response.output_messages) > 0


@pytest.mark.asyncio
@pytest.mark.parametrize("model_name", [MODEL_NAME])
async def test_function_call_with_previous_input_messages(
client: OpenAI, model_name: str
):
"""Test function calling using previous_input_messages
for multi-turn conversation with a function call"""

# Define the get_horoscope tool
tools = [
{
"type": "function",
"name": "get_horoscope",
"description": "Get today's horoscope for an astrological sign.",
"parameters": {
"type": "object",
"properties": {
"sign": {"type": "string"},
},
"required": ["sign"],
"additionalProperties": False,
},
"strict": True,
}
]

# Step 1: First call with the function tool
stream_response = await client.responses.create(
model=model_name,
input="What is the horoscope for Aquarius today?",
tools=tools,
extra_body={"enable_response_messages": True},
stream=True,
)

response = None
async for event in stream_response:
if event.type == "response.completed":
response = event.response

assert response is not None
assert response.status == "completed"

# Step 2: Parse the first output to find the function_call type
function_call = None
for item in response.output:
if item.type == "function_call":
function_call = item
break

assert function_call is not None, "Expected a function_call in the output"
assert function_call.name == "get_horoscope"
assert function_call.call_id is not None

# Verify the format matches expectations
args = json.loads(function_call.arguments)
assert "sign" in args

# Step 3: Call the get_horoscope function
result = call_function(function_call.name, args)
assert "Aquarius" in result
assert "baby otter" in result

# Get the input_messages and output_messages from the first response
first_input_messages = response.input_messages
first_output_messages = response.output_messages

# Construct the full conversation history using previous_input_messages
previous_messages = (
first_input_messages
+ first_output_messages
+ [
{
"role": "tool",
"name": "functions.get_horoscope",
"content": [{"type": "text", "text": str(result)}],
}
]
)

# Step 4: Make another responses.create() call with previous_input_messages
stream_response_2 = await client.responses.create(
model=model_name,
tools=tools,
input="",
extra_body={
"previous_input_messages": previous_messages,
"enable_response_messages": True,
},
stream=True,
)

async for event in stream_response_2:
if event.type == "response.completed":
response_2 = event.response

assert response_2 is not None
assert response_2.status == "completed"
assert response_2.output_text is not None

# verify only one system message / developer message
num_system_messages_input = 0
num_developer_messages_input = 0
num_function_call_input = 0
for message_dict in response_2.input_messages:
message = Message.from_dict(message_dict)
if message.author.role == "system":
num_system_messages_input += 1
elif message.author.role == "developer":
num_developer_messages_input += 1
elif message.author.role == "tool":
num_function_call_input += 1
assert num_system_messages_input == 1
assert num_developer_messages_input == 1
assert num_function_call_input == 1

# Verify the output makes sense - should contain information about the horoscope
output_text = response_2.output_text.lower()
assert (
"aquarius" in output_text or "otter" in output_text or "tuesday" in output_text
)
22 changes: 22 additions & 0 deletions tests/entrypoints/openai/test_serving_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,28 @@ async def test_initialize_tool_sessions(
# Verify that init_tool_sessions was called
assert mock_context.init_tool_sessions_called

def test_validate_create_responses_input(
self, serving_responses_instance, mock_context, mock_exit_stack
):
request = ResponsesRequest(
input="test input",
previous_input_messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "What is my horoscope? I am an Aquarius.",
}
],
}
],
previous_response_id="lol",
)
error = serving_responses_instance._validate_create_responses_input(request)
assert error is not None
assert error.error.type == "invalid_request_error"


class TestValidateGeneratorInput:
"""Test class for _validate_generator_input method"""
Expand Down
Loading