Python: updated declarative samples and handling of non-pydantic response formats (#5022)

* updated declarative samples and handling of non-pydantic response formats

* fixed from comments

* update docstring
This commit is contained in:
Eduard van Valkenburg
2026-04-01 21:16:00 +02:00
committed by GitHub
Unverified
parent 6acab3d1d6
commit 519bb0cb2b
21 changed files with 370 additions and 90 deletions
@@ -1912,9 +1912,7 @@ class RawOpenAIChatClient( # type: ignore[misc]
args["usage_details"] = usage_details
if structured_response:
args["value"] = structured_response
elif (response_format := options.get("response_format")) and isinstance(response_format, type):
# Only pass response_format to ChatResponse if it's a Pydantic model type,
# not a runtime JSON schema dict
elif response_format := options.get("response_format"):
args["response_format"] = response_format
# Set continuation_token when background operation is still in progress
if response.status and response.status in ("in_progress", "queued"):
@@ -485,6 +485,46 @@ async def test_response_format_parse_path_with_conversation_id() -> None:
assert response.model == "test-model"
async def test_response_format_dict_parse_path() -> None:
"""Test get_response response_format parsing path for runtime JSON schema mappings."""
client = OpenAIChatClient(model="test-model", api_key="test-key")
response_format = {"type": "object", "properties": {"answer": {"type": "string"}}}
mock_response = MagicMock()
mock_response.id = "response_123"
mock_response.model = "test-model"
mock_response.created_at = 1000000000
mock_response.metadata = {}
mock_response.output_parsed = None
mock_response.output = []
mock_response.usage = None
mock_response.finish_reason = None
mock_response.conversation = None
mock_response.status = "completed"
mock_message_content = MagicMock()
mock_message_content.type = "output_text"
mock_message_content.text = '{"answer": "Parsed"}'
mock_message_content.annotations = []
mock_message_content.logprobs = None
mock_message_item = MagicMock()
mock_message_item.type = "message"
mock_message_item.content = [mock_message_content]
mock_response.output = [mock_message_item]
with patch.object(client.client.responses, "create", return_value=mock_response):
response = await client.get_response(
messages=[Message(role="user", text="Test message")],
options={"response_format": response_format},
)
assert response.response_id == "response_123"
assert response.value is not None
assert isinstance(response.value, dict)
assert response.value["answer"] == "Parsed"
async def test_bad_request_error_non_content_filter() -> None:
"""Test get_response BadRequestError without content_filter."""
client = OpenAIChatClient(model="test-model", api_key="test-key")
@@ -3297,12 +3337,10 @@ async def test_integration_options(
assert isinstance(response.value, OutputStruct)
assert "seattle" in response.value.location.lower()
else:
# Runtime JSON schema
assert response.value is None, "No structured output, can't parse any json."
response_value = json.loads(response.text)
assert isinstance(response_value, dict)
assert "location" in response_value
assert "seattle" in response_value["location"].lower()
assert response.value is not None
assert isinstance(response.value, dict)
assert "location" in response.value
assert "seattle" in response.value["location"].lower()
@pytest.mark.timeout(300)
@@ -2,7 +2,6 @@
from __future__ import annotations
import json
import os
from functools import wraps
from pathlib import Path
@@ -322,11 +321,10 @@ async def test_integration_options(
assert isinstance(response.value, OutputStruct)
assert "seattle" in response.value.location.lower()
else:
assert response.value is None
response_value = json.loads(response.text)
assert isinstance(response_value, dict)
assert "location" in response_value
assert "seattle" in response_value["location"].lower()
assert response.value is not None
assert isinstance(response.value, dict)
assert "location" in response.value
assert "seattle" in response.value["location"].lower()
@pytest.mark.flaky
@@ -1421,6 +1421,31 @@ def test_response_format_dict_passthrough(openai_unit_test_env: dict[str, str])
assert prepared_options["response_format"] == custom_format
def test_parse_response_with_dict_response_format(openai_unit_test_env: dict[str, str]) -> None:
"""Chat completions should parse dict response_format values into response.value."""
client = OpenAIChatCompletionClient()
response = client._parse_response_from_openai(
ChatCompletion(
id="test-response",
object="chat.completion",
created=1234567890,
model="gpt-4o-mini",
choices=[
Choice(
index=0,
message=ChatCompletionMessage(role="assistant", content='{"answer": "Hello"}'),
finish_reason="stop",
)
],
),
options={"response_format": {"type": "object", "properties": {"answer": {"type": "string"}}}},
)
assert response.value is not None
assert isinstance(response.value, dict)
assert response.value["answer"] == "Hello"
def test_multiple_function_calls_in_single_message(
openai_unit_test_env: dict[str, str],
) -> None:
@@ -1635,12 +1660,10 @@ async def test_integration_options(
assert isinstance(response.value, OutputStruct)
assert "seattle" in response.value.location.lower()
else:
# Runtime JSON schema
assert response.value is None, "No structured output, can't parse any json."
response_value = json.loads(response.text)
assert isinstance(response_value, dict)
assert "location" in response_value
assert "seattle" in response_value["location"].lower()
assert response.value is not None
assert isinstance(response.value, dict)
assert "location" in response.value
assert "seattle" in response.value["location"].lower()
@pytest.mark.flaky