Skip to content
Open
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
13 changes: 12 additions & 1 deletion pydantic_ai_slim/pydantic_ai/models/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -1223,11 +1223,22 @@ async def _responses_create(
if model_settings.get('openai_include_web_search_sources'):
include.append('web_search_call.action.sources')

# When there are no input messages and we're not reusing a previous response,
# the OpenAI API will reject a request without any input. To avoid this provide
# an explicit empty user message.
Comment on lines +1226 to +1228
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should clarify that there are instructions, because if not, the request would really be empty and we should error.

Suggested change
# When there are no input messages and we're not reusing a previous response,
# the OpenAI API will reject a request without any input. To avoid this provide
# an explicit empty user message.
# When there are no input messages and we're not reusing a previous response,
# the OpenAI API will reject a request without any input,
# even if there are instructions.
# To avoid this provide an explicit empty user message.

if not openai_messages and not previous_response_id:
openai_messages = [
responses.EasyInputMessageParam(
role='user',
content='',
)
]

try:
extra_headers = model_settings.get('extra_headers', {})
extra_headers.setdefault('User-Agent', get_user_agent())
return await self.client.responses.create(
input=openai_messages,
input=cast(responses.ResponseInputParam, openai_messages),
model=self._model_name,
instructions=instructions,
parallel_tool_calls=model_settings.get('parallel_tool_calls', OMIT),
Expand Down
49 changes: 49 additions & 0 deletions tests/models/test_openai_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -7442,3 +7442,52 @@ def get_meaning_of_life() -> int:
},
]
)


async def test_openai_responses_runs_with_deps_only_and_sends_input(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I think we can remove the deps stuff, just having instructions should be enough.
  • We should do a real model request and verify the API accepts this payload and behaves correctly; we shouldn't mock the request. If you remove the mock stuff, you can run the test with pytest <path> --record-mode=rewrite and it'll automatically store a pyvcr cassette with the recorded request/response.

allow_model_requests: None,
):
from pydantic import BaseModel

c = response_message(
[
ResponseOutputMessage(
id='output-1',
content=[ResponseOutputText(text='ok', type='output_text', annotations=[])],
role='assistant',
status='completed',
type='message',
)
]
)

mock_client = MockOpenAIResponses.create_mock(c)
model = OpenAIResponsesModel('gpt-4o', provider=OpenAIProvider(openai_client=mock_client))

class Payload(BaseModel):
topic: str
sentences: int

payload = Payload(topic='artificial intelligence', sentences=3)

agent = Agent(model=model, instructions='Generate an article.', deps_type=Payload)

@agent.instructions
def instr(ctx: Any) -> str:
# no explicit input passed to run(); this uses ctx.deps
return f'Topic: {ctx.deps.topic}\nSentences: {ctx.deps.sentences}'

# Run with only deps
result = await agent.run(deps=payload)
assert result.output == 'ok'

response_kwargs = get_mock_responses_kwargs(mock_client)
assert response_kwargs, 'Responses API was not called'

kw = response_kwargs[0]
assert 'input' in kw or 'text' in kw, "Expected 'input' or 'text' in responses.create kwargs"

if 'input' in kw:
assert kw['input'], "Responses 'input' should not be empty when deps are provided"
else:
assert kw['text'], "Responses 'text' config should be set when no input messages were built"
Loading