Skip to content

Commit ffe4f5d

Browse files
fix: forward structured output request params for OpenAI Chat Completions (strands-agents#2944)
1 parent 9ed0a03 commit ffe4f5d

2 files changed

Lines changed: 37 additions & 3 deletions

File tree

strands-py/src/strands/models/openai.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -849,10 +849,12 @@ async def structured_output(
849849
# https://github.com/encode/httpx/discussions/2959.
850850
async with self._get_client() as client:
851851
try:
852+
request = self.format_request(prompt, system_prompt=system_prompt)
853+
# parse() is non-streaming; stream=True would raise, so drop the streaming-only fields.
854+
request.pop("stream", None)
855+
request.pop("stream_options", None)
852856
response: ParsedChatCompletion = await client.beta.chat.completions.parse(
853-
model=self.get_config()["model_id"],
854-
messages=self.format_request(prompt, system_prompt=system_prompt)["messages"],
855-
response_format=output_model,
857+
**request, response_format=output_model
856858
)
857859
except openai.BadRequestError as e:
858860
# Check if this is a context length exceeded error

strands-py/tests/strands/models/test_openai.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,6 +1258,38 @@ async def test_structured_output(openai_client, model, test_output_model_cls, al
12581258
assert tru_result == exp_result
12591259

12601260

1261+
@pytest.mark.asyncio
1262+
async def test_structured_output_forwards_request_params(openai_client, model_id, test_output_model_cls, alist):
1263+
messages = [{"role": "user", "content": [{"text": "Generate a person"}]}]
1264+
model = OpenAIModel(model_id=model_id, params={"max_tokens": 100, "temperature": 0.5})
1265+
1266+
mock_parsed_instance = test_output_model_cls(name="John", age=30)
1267+
mock_choice = unittest.mock.Mock()
1268+
mock_choice.message.parsed = mock_parsed_instance
1269+
mock_response = unittest.mock.Mock(choices=[mock_choice])
1270+
openai_client.beta.chat.completions.parse = unittest.mock.AsyncMock(return_value=mock_response)
1271+
1272+
stream = model.structured_output(test_output_model_cls, messages, system_prompt="Be precise.")
1273+
events = await alist(stream)
1274+
1275+
tru_result = events[-1]
1276+
exp_result = {"output": test_output_model_cls(name="John", age=30)}
1277+
assert tru_result == exp_result
1278+
1279+
# Config params are forwarded; the streaming-only fields (stream, stream_options) are dropped.
1280+
openai_client.beta.chat.completions.parse.assert_called_once_with(
1281+
messages=[
1282+
{"role": "system", "content": "Be precise."},
1283+
{"role": "user", "content": [{"text": "Generate a person", "type": "text"}]},
1284+
],
1285+
model=model_id,
1286+
tools=[],
1287+
max_tokens=100,
1288+
temperature=0.5,
1289+
response_format=test_output_model_cls,
1290+
)
1291+
1292+
12611293
def test_config_validation_warns_on_unknown_keys(openai_client, captured_warnings):
12621294
"""Test that unknown config keys emit a warning."""
12631295
OpenAIModel({"api_key": "test"}, model_id="test-model", invalid_param="test")

0 commit comments

Comments
 (0)