-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat: support structured outputs in OpenAIChatGenerator
#9754
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
Conversation
Pull Request Test Coverage Report for Build 17760482097Details
💛 - Coveralls |
releasenotes/notes/add-openai-structured-outputs-906be78a984a1079.yaml
Outdated
Show resolved
Hide resolved
releasenotes/notes/add-openai-structured-outputs-906be78a984a1079.yaml
Outdated
Show resolved
Hide resolved
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 left some comments.
I also suggest merging main to see the actual coverage. My impression is that several new code paths are not covered by unit tests. I would like to have them covered, since this component is crucial.
if response_format: | ||
if is_streaming: | ||
raise ValueError( | ||
"OpenAI does not support `streaming_callback` with `response_format`, please choose one." |
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 would like to understand better. It seems that OpenAI supports streaming + structured outputs.
If we are making this choice for simplicity reasons, I would be more specific: "The OpenAIChatGenerator does not ..."
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.
Hmm interesting. I think because of the beta code example in documentation I misunderstood that its an unstable feature (not available in completions API). But you are right its supported. I’ll update the function to enable this.
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 did not notice that it was beta
- It might also be reasonable to skip it if it requires rewriting significantly the component. (Unfortunately, we have many other components depending on this implementation)
- If there are stable ways to do this, let's do it. Otherwise, let's create an issue to track this once that API is no longer in beta.
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 looked into their stream function and here looks like the stable version also supports
response_format
. - I will test this locally and will update the code.
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.
@anakin87 Looked into this and here are some points:
- The
response_format
with streaming will be passed tochat.completions.create
. This endpoint allowsresponse_format
to be either a json schema or{ "type": "json_object" }
. But it cannot be apydantic.BaseModel
. - From the documentation, it seems like the beta version supports pydantic models with
stream
endpoint. Which I dont want to introduce for the reasons you mentioned above.
So for now, I believe we can support the first point and mention the limitation in docstrings. Anyways, the error is handled by OpenAI itself if the user passes a pydantic model with streaming.
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.
Agree... Let's do what you propose and mention in docstrings that Pydantic response_format
won't work with streaming.
releasenotes/notes/add-openai-structured-outputs-906be78a984a1079.yaml
Outdated
Show resolved
Hide resolved
if "stream" in api_args.keys(): | ||
chat_completion = self.client.chat.completions.create(**api_args) |
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 find this hard to understand. Something similar to this would work?
We could always return a dictionary with the same fields from _prepare_api_call
.
if api_args.get("response_format"):
# We cannot pass stream param to chat.completions.parse endpoint
api_args.pop("stream", None)
chat_completion = self.client.chat.completions.parse(**api_args)
else:
...
(I might be wrong, in any case I'd appreciate it if we can make the code more intuitive to follow.)
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.
Hmm we allow passing response_format
with stream
param to create
endpoint, for streaming structured outputs so this wont work.
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.
Ok I now understand, but it's hard to follow.
What I would recommend is to
- include in
api_args
an item calledendpoint
/method
containing "parse" or "create" (in_prepare_api_call
) - (add comments where this value is set to explain why we are doing that.)
- reuse the value in
run
@@ -5,7 +5,9 @@ | |||
import os | |||
from typing import Any, Optional, Union | |||
|
|||
from openai.lib._pydantic import to_strict_json_schema |
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.
Is there a different way to import this function that doesn't go through a private file? I'm a little worried the import path is subject to break/change
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.
Hmm, for now seems like this is the only way to import this.
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.
hmm I think we can do this a different way. We should be able to directly use the one from pydantic which looks like this parameters_schema = model.model_json_schema()
. This is from the _create_tool_parameters_schema
function in ComponentTool
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.
OpenAI expect a stricter JSON Schema than Pydantic’s default. For example, objects must set additionalProperties
and Optional
keys are handled differently. As a result, model_json_schema()
often isn’t accepted as-is.
Its also discussed here where another solution is offered but I prefer using the openai method over some unpopular library.
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.
Nevertheless, I spotted a bug in to_dict
where schema wasn't stored properly. Fixing this.
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.
ahh okay thanks for the info
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 left some comments/suggestions, taking also #9776 into account
releasenotes/notes/add-openai-structured-outputs-906be78a984a1079.yaml
Outdated
Show resolved
Hide resolved
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.
Looks good!
Once you've incorporated my final suggestions, feel free to merge.
Related Issues
Proposed Changes:
response_format
inOpenAIChatGenerator
andAzureOpenAIChatGenerator
.How did you test it?
response_format
using pydantic model and json schema.Notes for the reviewer
Checklist
fix:
,feat:
,build:
,chore:
,ci:
,docs:
,style:
,refactor:
,perf:
,test:
and added!
in case the PR includes breaking changes.