Replies: 1 comment 1 reply
-
Interface looks good! 👍
I would hesitate here too, conversion is the whole other subject prone to errors & confusions of its own and handling it would leave user always guessing if something went wrong during the conversion or the generation itself. Leaving it to the user to decide what to do with the result (load this json string or pass it further as it is) will allow not to spend resources on unnecessary cycle of serialization/deserialization due to the format (twice actually) and even don't bring unnecessary dependencies for user just for serialization. Also strings are widely compatible with numerous languages, libraries, and tools. Perhaps, being highly reliable and predictable structured output provider might be best. |
Beta Was this translation helpful? Give feedback.
-
This discussion follows #1220 which I suggest to read beforehand.
Context
In #1120 we propose to stop discriminating between generation method by defining new functions, but instead to dispatch the generation method based on output types. This design is more flexible (we can add a new type without extra boilerplate) and more ergonomic (users do not need to memorize new functions).
This discussion focuses on the interface to define the output types itself.
Interfaces with providers
Before working on the user interface we need to figure out what are the internal interfaces. Most APIs only offer JSON Schema specifications as an interface, and every aspect of the structured generation happens internally:
Some also offer regular expressions:
Or grammars:
Outlines can provide the three interfaces for open models.
Output types and definitions
There are two things to consider: the output type and the object that is used to define the type. For instance, we can use a Pydantic
BaseModel
to define an output structure, but we have different options when it comes to the output of the generator function:BaseModel
that contains the information generated by the model;BaseModel
Parsing or not parsing
The behavior of:
is to build a generator that, given a prompt, returns an instance of
User
. If we pass a JSON Schema specification in a dictionary it will return a JSON object parsed into a dictionary. It we pass a JSON Schema specification as a string it will return a string that returns the JSON object.I don't like the fact that it's implicit, and I have come to dislike the fact that we would return parsed objects at all since it does complexify the interface a lot just to avoid at most two lines of code (importing
json
and runjson.loads(result)
). It also makes the streaming interface ambiguous and potentially error-prone by making decisions for the users.Proposal
If we temporarily forget about the interface with model providers, the simplest interface would be to require that the second argument to
generator
andstreamer
be a regular expression. We can then define functions that transform a structure definition into a regular expression. For instance to generatejson
:So we can write:
Similarly, to generate YAML objects, define:
And have
generator
andstreamer
return strings. The major downside to this is that we cannot interface with API providers any more, including our own API since they use JSON Schema as an interface. We can thus introduce new types:And provider classes are responsible for choosing the kind of representation they prefer. To generate text that follows a regular expression or a grammar:
Work with special types
Following the special types already defined in Outlines in such a way that they can be used in Pydantic models:
This generalizes to the case where the special type may be defined via a grammar, cf email adresses.
Open questions
Reviewers
@torymur @lapp0
Beta Was this translation helpful? Give feedback.
All reactions