How to Achieve Dynamic Response Schema

How can I define my structured output to be dynamic?

I want my response schema to be different depending on what GPT decides.

Look at my response_schema below.

{
        "type": "json_schema",
        "json_schema": {
            "name": "my_response",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "my_response": {
                        "type": "object",
                        "properties": {
                            "title": {
                                "type": "string",
                                "description": "The section's title.",
                            },
                            "component": {
                                "type": "string",
                                "enum": ["text", "image"],
                                "description": "Choose text if this section should have text. Choose image if it should have an image."
                            },
                            "body": {
                                ...
                            },,
                        },
                        "required": ["title", "component", "body"],
                        "additionalProperties": False,
                    },
                },
                "required": ["my_response"],
                "additionalProperties": False,
            },
        },
    }

Where if component is of type “text” I want “body” to be:

{
    "type": "object",
        "properties": {
            "content": {
                "type": "string"
            }
        }
    }
}

But if component is of type “image” I want “body” to be:

{
    "type": "object",
        "properties": {
            "url": {
                "type": "string"
            }
        }
    }
}

Hi @expertise.ai.chat and welcome to the forums!

Outside of the recursive mode (which is not relevant here), there is no way to do the dynamic fields, to the best of my knowledge.

But I came up with another solution :slight_smile: .

What I did was I put content and img_url within body, and made them two optional, i.e. anyOf. So in your response you basically just do the following check:

if my_response.component == "text":
    print(my_response.body.content)
else:
    print(my_response.body.img_url)

This is the full code here so you can try to reproduce it.

My JSON schema:

json_schema = {
    "name": "my_response",
    "strict": True,
    "schema": {
        "type": "object",
        "properties": {
            "my_response": {
                "type": "object",
                "properties": {
                    "title": {
                        "type": "string",
                        "description": "The section's title.",
                    },
                    "component": {
                        "type": "string",
                        "enum": ["text", "image"],
                        "description": "Choose text if this section should have text. Choose image if it should have an image."
                    },
                    "body": {
                        "type": "object",
                        "properties": {
                            "content": {
                                "type": ["string", "null"],
                                "description": "The body content; only set to non-null value if the component is set to 'text'"
                            },
                            "img_url": {
                                "type": ["string", "null"],
                                "description": "The url of the image; only set to non-null value if the component is set to 'image'"
                            }
                        },
                        "required": ["content", "img_url"],
                        "additionalProperties": False
                    },
                },
                "required": ["title", "component", "body"],
                "additionalProperties": False,
            },
        },
        "required": ["my_response"],
        "additionalProperties": False,
    }
}

My API call with the prompt:

response = client.beta.chat.completions.parse(
    model="gpt-4o-2024-08-06",
    messages=[
        {
            "role": "system",
            "content": """
            You are to parse HTML and return the parsed content as per the provided JSON schema.
            If there is an <image url=...> tag within the <body> segment, set the component to "image" and return the image url as "img_url".
            If there is a <text> tag within the <body> segment, set the component to "text" and return the text content as "content".
            """
        },
        {
            "role": "user",
            "content": """
            <html>
            <title>Structured Outputs Demo</title>
            <body>
            <image url="test.gif"></image>
            </body>
            </html>
            """
        }
    ],
    response_format={
        "type": "json_schema",
        "json_schema": json_schema,
    }
)

And finally, the response:

{   'my_response': {   'body': {'content': None, 'img_url': 'test.gif'},
                       'component': 'image',
                       'title': 'Structured Outputs Demo'}}

I tried for the case where I inserted a <text>Test!</text> and removed the image, and it also worked as expected.

2 Likes

Thanks for your response. I will play around and see if this fits my use case.

1 Like