From f43a0f94ed6d70ace6e6e20daa307fb47739f032 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Fri, 3 Oct 2025 12:10:58 -0700 Subject: [PATCH 1/3] fix: invalid model setting when passing prompt to Agent --- tests/test_openai_responses.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_openai_responses.py b/tests/test_openai_responses.py index 0823d3cac..56b8c3748 100644 --- a/tests/test_openai_responses.py +++ b/tests/test_openai_responses.py @@ -3,6 +3,7 @@ from typing import Any import pytest +from openai import NOT_GIVEN from openai.types.responses import ResponseCompletedEvent from agents import ModelSettings, ModelTracing, __version__ @@ -63,3 +64,35 @@ def __init__(self): assert "extra_headers" in called_kwargs assert called_kwargs["extra_headers"]["User-Agent"] == expected_ua + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_prompt_id_omits_model_parameter(): + called_kwargs: dict[str, Any] = {} + + class DummyResponses: + async def create(self, **kwargs): + nonlocal called_kwargs + called_kwargs = kwargs + return get_response_obj([]) + + class DummyResponsesClient: + def __init__(self): + self.responses = DummyResponses() + + model = OpenAIResponsesModel(model="gpt-4", openai_client=DummyResponsesClient()) # type: ignore[arg-type] + + await model.get_response( + system_instructions=None, + input="hi", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + tracing=ModelTracing.DISABLED, + prompt={"id": "pmpt_123"}, + ) + + assert called_kwargs["prompt"] == {"id": "pmpt_123"} + assert called_kwargs["model"] is NOT_GIVEN From 42166de004ba88b14d9bd9d105216b1e96411853 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Wed, 19 Nov 2025 13:21:49 +0900 Subject: [PATCH 2/3] fix issues with model and tools params --- src/agents/models/openai_provider.py | 12 ++++--- src/agents/models/openai_responses.py | 17 ++++++++-- tests/test_agent_prompt.py | 46 ++++++++++++++++++++++++++- tests/test_openai_responses.py | 41 +++++++++++++++++++++++- 4 files changed, 107 insertions(+), 9 deletions(-) diff --git a/src/agents/models/openai_provider.py b/src/agents/models/openai_provider.py index 91f2366bc..91eeaccc8 100644 --- a/src/agents/models/openai_provider.py +++ b/src/agents/models/openai_provider.py @@ -81,13 +81,17 @@ def _get_client(self) -> AsyncOpenAI: return self._client def get_model(self, model_name: str | None) -> Model: - if model_name is None: - model_name = get_default_model() + model_is_explicit = model_name is not None + resolved_model_name = model_name if model_name is not None else get_default_model() client = self._get_client() return ( - OpenAIResponsesModel(model=model_name, openai_client=client) + OpenAIResponsesModel( + model=resolved_model_name, + openai_client=client, + model_is_explicit=model_is_explicit, + ) if self._use_responses - else OpenAIChatCompletionsModel(model=model_name, openai_client=client) + else OpenAIChatCompletionsModel(model=resolved_model_name, openai_client=client) ) diff --git a/src/agents/models/openai_responses.py b/src/agents/models/openai_responses.py index 4588937cb..952f4654b 100644 --- a/src/agents/models/openai_responses.py +++ b/src/agents/models/openai_responses.py @@ -6,7 +6,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Literal, Union, cast, overload -from openai import APIStatusError, AsyncOpenAI, AsyncStream, Omit, omit +from openai import APIStatusError, AsyncOpenAI, AsyncStream, NotGiven, Omit, NOT_GIVEN, omit from openai.types import ChatModel from openai.types.responses import ( Response, @@ -67,8 +67,11 @@ def __init__( self, model: str | ChatModel, openai_client: AsyncOpenAI, + *, + model_is_explicit: bool = True, ) -> None: self.model = model + self._model_is_explicit = model_is_explicit self._client = openai_client def _non_null_or_omit(self, value: Any) -> Any: @@ -262,6 +265,14 @@ async def _fetch_response( converted_tools = Converter.convert_tools(tools, handoffs) converted_tools_payload = _to_dump_compatible(converted_tools.tools) response_format = Converter.get_response_format(output_schema) + should_omit_model = prompt is not None and not self._model_is_explicit + model_param: str | ChatModel | NotGiven = ( + self.model if not should_omit_model else NOT_GIVEN + ) + should_omit_tools = prompt is not None and len(converted_tools_payload) == 0 + tools_param: list[ToolParam] | NotGiven = ( + converted_tools_payload if not should_omit_tools else NOT_GIVEN + ) include_set: set[str] = set(converted_tools.includes) if model_settings.response_include is not None: @@ -309,10 +320,10 @@ async def _fetch_response( previous_response_id=self._non_null_or_omit(previous_response_id), conversation=self._non_null_or_omit(conversation_id), instructions=self._non_null_or_omit(system_instructions), - model=self.model, + model=model_param, input=list_input, include=include, - tools=converted_tools_payload, + tools=tools_param, prompt=self._non_null_or_omit(prompt), temperature=self._non_null_or_omit(model_settings.temperature), top_p=self._non_null_or_omit(model_settings.top_p), diff --git a/tests/test_agent_prompt.py b/tests/test_agent_prompt.py index 3d5ed5a3f..b919c8226 100644 --- a/tests/test_agent_prompt.py +++ b/tests/test_agent_prompt.py @@ -1,8 +1,12 @@ import pytest +from openai import NOT_GIVEN -from agents import Agent, Prompt, RunContextWrapper, Runner +from agents import Agent, Prompt, RunConfig, RunContextWrapper, Runner +from agents.models.interface import Model, ModelProvider +from agents.models.openai_responses import OpenAIResponsesModel from .fake_model import FakeModel +from .fake_model import get_response_obj from .test_responses import get_text_message @@ -97,3 +101,43 @@ async def test_prompt_is_passed_to_model(): "variables": None, } assert model.last_prompt == expected_prompt + + +class _SingleModelProvider(ModelProvider): + def __init__(self, model: Model): + self._model = model + + def get_model(self, model_name: str | None) -> Model: + return self._model + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_agent_prompt_with_default_model_omits_model_and_tools_parameters(): + called_kwargs: dict[str, object] = {} + + class DummyResponses: + async def create(self, **kwargs): + nonlocal called_kwargs + called_kwargs = kwargs + return get_response_obj([get_text_message("done")]) + + class DummyResponsesClient: + def __init__(self): + self.responses = DummyResponses() + + model = OpenAIResponsesModel( + model="gpt-4.1", + openai_client=DummyResponsesClient(), # type: ignore[arg-type] + model_is_explicit=False, + ) + + run_config = RunConfig(model_provider=_SingleModelProvider(model)) + agent = Agent(name="prompt-agent", prompt={"id": "pmpt_agent"}) + + await Runner.run(agent, input="hi", run_config=run_config) + + expected_prompt = {"id": "pmpt_agent", "version": None, "variables": None} + assert called_kwargs["prompt"] == expected_prompt + assert called_kwargs["model"] is NOT_GIVEN + assert called_kwargs["tools"] is NOT_GIVEN diff --git a/tests/test_openai_responses.py b/tests/test_openai_responses.py index 56b8c3748..3759c1988 100644 --- a/tests/test_openai_responses.py +++ b/tests/test_openai_responses.py @@ -81,7 +81,11 @@ class DummyResponsesClient: def __init__(self): self.responses = DummyResponses() - model = OpenAIResponsesModel(model="gpt-4", openai_client=DummyResponsesClient()) # type: ignore[arg-type] + model = OpenAIResponsesModel( + model="gpt-4", + openai_client=DummyResponsesClient(), # type: ignore[arg-type] + model_is_explicit=False, + ) await model.get_response( system_instructions=None, @@ -96,3 +100,38 @@ def __init__(self): assert called_kwargs["prompt"] == {"id": "pmpt_123"} assert called_kwargs["model"] is NOT_GIVEN + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_prompt_id_omits_tools_parameter_when_no_tools_configured(): + called_kwargs: dict[str, Any] = {} + + class DummyResponses: + async def create(self, **kwargs): + nonlocal called_kwargs + called_kwargs = kwargs + return get_response_obj([]) + + class DummyResponsesClient: + def __init__(self): + self.responses = DummyResponses() + + model = OpenAIResponsesModel( + model="gpt-4", + openai_client=DummyResponsesClient(), # type: ignore[arg-type] + model_is_explicit=False, + ) + + await model.get_response( + system_instructions=None, + input="hi", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + tracing=ModelTracing.DISABLED, + prompt={"id": "pmpt_123"}, + ) + + assert called_kwargs["tools"] is NOT_GIVEN From fc36be62c3801dc13649abdc91739e4e9bf85405 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Wed, 19 Nov 2025 13:29:01 +0900 Subject: [PATCH 3/3] fix --- src/agents/models/openai_responses.py | 10 ++++------ tests/test_agent_prompt.py | 11 ++++++----- tests/test_openai_responses.py | 6 +++--- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/agents/models/openai_responses.py b/src/agents/models/openai_responses.py index 952f4654b..a8695c89c 100644 --- a/src/agents/models/openai_responses.py +++ b/src/agents/models/openai_responses.py @@ -6,7 +6,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Literal, Union, cast, overload -from openai import APIStatusError, AsyncOpenAI, AsyncStream, NotGiven, Omit, NOT_GIVEN, omit +from openai import APIStatusError, AsyncOpenAI, AsyncStream, Omit, omit from openai.types import ChatModel from openai.types.responses import ( Response, @@ -266,12 +266,10 @@ async def _fetch_response( converted_tools_payload = _to_dump_compatible(converted_tools.tools) response_format = Converter.get_response_format(output_schema) should_omit_model = prompt is not None and not self._model_is_explicit - model_param: str | ChatModel | NotGiven = ( - self.model if not should_omit_model else NOT_GIVEN - ) + model_param: str | ChatModel | Omit = self.model if not should_omit_model else omit should_omit_tools = prompt is not None and len(converted_tools_payload) == 0 - tools_param: list[ToolParam] | NotGiven = ( - converted_tools_payload if not should_omit_tools else NOT_GIVEN + tools_param: list[ToolParam] | Omit = ( + converted_tools_payload if not should_omit_tools else omit ) include_set: set[str] = set(converted_tools.includes) diff --git a/tests/test_agent_prompt.py b/tests/test_agent_prompt.py index b919c8226..e3ed40fbe 100644 --- a/tests/test_agent_prompt.py +++ b/tests/test_agent_prompt.py @@ -1,12 +1,13 @@ +from __future__ import annotations + import pytest -from openai import NOT_GIVEN +from openai import omit from agents import Agent, Prompt, RunConfig, RunContextWrapper, Runner from agents.models.interface import Model, ModelProvider from agents.models.openai_responses import OpenAIResponsesModel -from .fake_model import FakeModel -from .fake_model import get_response_obj +from .fake_model import FakeModel, get_response_obj from .test_responses import get_text_message @@ -139,5 +140,5 @@ def __init__(self): expected_prompt = {"id": "pmpt_agent", "version": None, "variables": None} assert called_kwargs["prompt"] == expected_prompt - assert called_kwargs["model"] is NOT_GIVEN - assert called_kwargs["tools"] is NOT_GIVEN + assert called_kwargs["model"] is omit + assert called_kwargs["tools"] is omit diff --git a/tests/test_openai_responses.py b/tests/test_openai_responses.py index 3759c1988..ecd509ac6 100644 --- a/tests/test_openai_responses.py +++ b/tests/test_openai_responses.py @@ -3,7 +3,7 @@ from typing import Any import pytest -from openai import NOT_GIVEN +from openai import omit from openai.types.responses import ResponseCompletedEvent from agents import ModelSettings, ModelTracing, __version__ @@ -99,7 +99,7 @@ def __init__(self): ) assert called_kwargs["prompt"] == {"id": "pmpt_123"} - assert called_kwargs["model"] is NOT_GIVEN + assert called_kwargs["model"] is omit @pytest.mark.allow_call_model_methods @@ -134,4 +134,4 @@ def __init__(self): prompt={"id": "pmpt_123"}, ) - assert called_kwargs["tools"] is NOT_GIVEN + assert called_kwargs["tools"] is omit