Open streitl opened 7 months ago
This is a bug with Outlines. It can't resolve coinciding anyOf
and properties
properly. If properties
is present it ignores all other directives.
>>> print(json.dumps(MyModel.model_json_schema(), indent=2))
{
"anyOf": [
{
"properties": {
"a": {
"not": {
"type": "null"
}
}
},
"required": [
"a"
]
},
{
"properties": {
"b": {
"not": {
"type": "null"
}
}
},
"required": [
"b"
]
}
],
"properties": {
"a": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "A"
},
"b": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "B"
}
},
"title": "MyModel",
"type": "object"
}
For now I suggest you use
{
"type": "object",
"title": "MyModel",
"anyOf": [
{
"type": "object",
"required": ["a"],
"properties": {
"a": {"anyOf": [{"type": "integer"}]},
"b": {"anyOf": [{"type": "string"}, {"type": "null"}]}
} `
},
{
"type": "object",
"required": ["b"],
"properties": {
"b": {"anyOf": [{"type": "string"}]},
"a": {"anyOf": [{"type": "integer"}, {"type": "null"}]}
}
}
]
}
For anyone who's looking to implement this, it involves &ing two patterns. I recommend using greenery via
combined_pattern = greenery.parse(properties_pattern) & greenery.parse(anyof_pattern)
str(combined_pattern)
Your answer helped me address the bug with a quick hack that uses a callable for pydantic's json_schema_extra, which generates something similar to your output (I need the json schema to be dynamic since I'm changing my pydantic models quite often)
from pydantic.config import JsonDict
def force_at_least_one_field_to_exist(schema: JsonDict) -> None:
properties: JsonDict = schema.pop("properties") # type: ignore
schema |= {
"anyOf": [
{
"properties": {
field_name: {**_type, **rest}, # type: ignore
**other_fields,
},
"required": [field_name],
}
for field_name in properties.keys()
for other_fields in [
{k: v for k, v in properties.items() if k != field_name}
]
for _type in [
el for el in properties[field_name]["anyOf"] if el.get("type") != "null" # type: ignore
]
for rest in [
{k: v for k, v in properties[field_name].items() if k != "anyOf"} # type: ignore
]
]
}
class MyModel(BaseModel):
...
model_config = ConfigDict(json_schema_extra=force_at_least_one_field_to_exist)
...
I hope this will help somebody :smile: Thank you for your help!
Describe the issue as clearly as possible:
Hi! I'm trying to use outlines to generate Pydantic class instances where all of the fields are optional but at least one has to be set. I am enforcing these constraints in a Pydantic model validator and inside the json schema, but outlines does not respect the json schema constraints and the generated object fails Pydantic validation. When I test the json schema on online validators, it correctly disallows empty objects like
{}
, which is precisely what outlines tries to generate. Are some of my json schema constraints not supported yet, or is this a bug? Thank you!Steps/code to reproduce the bug:
Expected result:
An instance of MyModel where at least one of the fields is non-null.
Error message:
Outlines/Python version information:
Version information
Context for the issue:
No response