-
Notifications
You must be signed in to change notification settings - Fork 811
Add OpenAI Responses API #1256
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add OpenAI Responses API #1256
Conversation
Docs Preview
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also needs to be added to docs.
@@ -399,6 +404,298 @@ async def _map_user_prompt(part: UserPromptPart) -> chat.ChatCompletionUserMessa | |||
return chat.ChatCompletionUserMessageParam(role='user', content=content) | |||
|
|||
|
|||
@dataclass(init=False) | |||
class OpenAIResponsesModel(Model): | |||
"""A model that uses the OpenAI Responses API.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs a better docstring including:
- link to some resource about the responses API
- why you might use it
- brief example of how to use it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't seem we are adding examples in the analogous classes, but I added a more complete docstring.
self, | ||
model_name: OpenAIModelName, | ||
*, | ||
provider: Literal['openai', 'deepseek', 'azure'] | Provider[AsyncOpenAI] = 'openai', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only partially related, but should we add ollama
or local
here? Do all the local models default to the same port?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should, we just didn't yet. We need to implement their providers.
# standalone function to make it easier to override | ||
if not tools: | ||
tool_choice: Literal['none', 'required', 'auto'] | None = None | ||
elif not model_request_parameters.allow_text_result: | ||
tool_choice = 'required' | ||
else: | ||
tool_choice = 'auto' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we move this into a separate function and reuse it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I avoided, because I have a PR to do it already.
reasoning_effort = model_settings.get('openai_reasoning_effort', NOT_GIVEN) | ||
if not isinstance(reasoning_effort, NotGiven): | ||
reasoning = Reasoning(effort=reasoning_effort) | ||
else: | ||
reasoning = NOT_GIVEN |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... Actually... I can't make this work type wise 🤔
It seems the overload from OpenAI doesn't work when I do this:
@staticmethod
def _get_reasoning(model_settings: OpenAIModelSettings) -> Reasoning | None | NotGiven:
reasoning_effort = model_settings.get('openai_reasoning_effort', NOT_GIVEN)
if not isinstance(reasoning_effort, NotGiven):
return Reasoning(effort=reasoning_effort)
return NOT_GIVEN
And assign that to reasoning
.
elif isinstance(chunk, responses.ResponseContentPartAddedEvent): | ||
pass # there's nothing we need to do here | ||
|
||
elif isinstance(chunk, responses.ResponseContentPartDoneEvent): | ||
pass # there's nothing we need to do here | ||
|
||
elif isinstance(chunk, responses.ResponseCreatedEvent): | ||
pass # there's nothing we need to do here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are these parts? Are we sure we shouldn't use them, or if we don't know how to use them, then error.
Could you add a comment explaining what each part is to make it easy to understand in future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I specifically took @dmontagu time yesterday on this part of the code. We went through the events together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please can you add a TODO to review and either warn or error if we get parts we don't yet support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please can you add a TODO to review and either warn or error if we get parts we don't yet support.
We already have a warning below on events that we don't do anything. Those events here are the ones we checked and we are not supposed to do anything. At least, there's no need given the other events we already handle.
pass # there's nothing we need to do here | ||
|
||
elif isinstance(chunk, responses.ResponseFunctionCallArgumentsDeltaEvent): | ||
maybe_event = self._parts_manager.handle_tool_call_delta( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment explaining what's going on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be hard for me to explain this, my understanding here is shallow. That's why I took David's time to help me in the streaming part.
I just know the obvious... If we see chunk of type x, then we apply the delta...
pass # there's nothing we need to do here | ||
|
||
elif isinstance(chunk, responses.ResponseInProgressEvent): | ||
pass # there's nothing we need to do here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again comment.
|
||
elif isinstance(chunk, responses.ResponseOutputItemDoneEvent): | ||
# Note: We only need this if the tool call deltas don't include the final info: | ||
# if isinstance(chunk.item, responses.ResponseFunctionToolCall): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we error here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it looks like the tool call deltas do include the final info. We left that there just in case it ends up being possible in some way to cause OpenAI to not send this info. But for now I think this is the right implementation. I'll add a note above the commented out code explaining this more clearly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we remove the commented out code then.
PR Change SummaryAdded support for OpenAI's Responses API in the documentation, including usage examples and links to relevant resources.
Modified Files
How can I customize these reviews?Check out the Hyperlint AI Reviewer docs for more information on how to customize the review. If you just want to ignore it on this PR, you can add the Note specifically for link checks, we only check the first 30 links in a file and we cache the results for several hours (for instance, if you just added a page, you might experience this). Our recommendation is to add |
""" | ||
Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Kludex we need to add support for setting hosted tools here.
|
||
model = OpenAIResponsesModel('gpt-4o') | ||
agent = Agent(model) | ||
... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example should be complete, if minimal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well... There are 4 examples above that are exactly the same as this one. 🤔
Should I update all of them? Did you see them?
- [Web search](https://platform.openai.com/docs/guides/tools-web-search) | ||
- [File search](https://platform.openai.com/docs/guides/tools-file-search) | ||
- [Computer use](https://platform.openai.com/docs/guides/tools-computer-use) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need a note saying this is not yet supported in PydanticAI, will be soon.
|
||
elif isinstance(chunk, responses.ResponseOutputItemDoneEvent): | ||
# Note: We only need this if the tool call deltas don't include the final info: | ||
# if isinstance(chunk.item, responses.ResponseFunctionToolCall): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we remove the commented out code then.
elif isinstance(chunk, responses.ResponseContentPartAddedEvent): | ||
pass # there's nothing we need to do here | ||
|
||
elif isinstance(chunk, responses.ResponseContentPartDoneEvent): | ||
pass # there's nothing we need to do here | ||
|
||
elif isinstance(chunk, responses.ResponseCreatedEvent): | ||
pass # there's nothing we need to do here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please can you add a TODO to review and either warn or error if we get parts we don't yet support.
I would like on a follow-up PR support the native OpenAI Responses function tools, and all parameters via
OpenAIModelSettings
.