Skip to content

Structured outputs- what in the ever living f*** is this shambles Google...? #460

@fraserprice

Description

@fraserprice

Environment details

  • Programming language: Python
  • OS: Macos
  • Language runtime version: 3.12.2
  • Package version: 1.5.0

List of Issues and steps to reproduce

1. You do not support dictionaries in structured output.

This is one of the most basic requirements; dictionaries are literally the language of JSON and you have... just chosen to not support them or what? Please genuinely could someone make it make sense, because this is utterly wild to me- this is not rhetorical, I want someone to explain exactly why this use-case is not supported. I am convinced there must be something I am missing as this is such a core, fundamental data type. PLEASE could someone explain this choice to me in simple terms?

client = genai.Client()

async def main():
    class Foo(BaseModel):
        foo: dict[str, int]

    await client.aio.models.generate_content(
        model="gemini-2.0-flash",
        contents="Please populate all fields of the following model with random values",
        config={
            "response_mime_type": "application/json",
            "response_schema": Foo,
        }
    )


if __name__ == "__main__":
    asyncio.run(main())
pydantic_core._pydantic_core.ValidationError: 1 validation error for Schema
properties.foo.additionalProperties
  Extra inputs are not permitted [type=extra_forbidden, input_value={'type': 'integer'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.10/v/extra_forbidden

To achieve the above, I would need to refactor my model into something utterly disgusting:

client = genai.Client()

async def main():

    class Bar(BaseModel):
        key: str
        value: int

    class Foo(BaseModel):
        foo: list[Bar]

    return (await client.aio.models.generate_content(

        model="gemini-2.0-flash",
        contents="Please populate all fields of the following model with random values",
        config={
            "response_mime_type": "application/json",
            "response_schema": Foo,
        }
    )).parsed


if __name__ == "__main__":
    print(asyncio.run(main()))

???????

Using a straight up dict also does not work:

client = genai.Client()

async def main():
    return (await client.aio.models.generate_content(
        model="gemini-2.0-flash",
        contents="Please populate all fields of the following model with random values",
        config={
            "response_mime_type": "application/json",
            "response_schema": dict[str, int],
        }
    )).parsed


if __name__ == "__main__":
    print(asyncio.run(main()))
additionalProperties
  Extra inputs are not permitted [type=extra_forbidden, input_value={'type': 'integer'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.10/v/extra_forbidden

2. You seem to have your own little highly constrained schema representation...?

Why are we not just supporting pure JSON schema in the same way that literally every other provider has from the beginning? JSON schema is an actual industry standard and you are just straight up ignoring it and throwing it out for this barely functional, provider-specific mess. PLEASE MAKE IT MAKE SENSE.

3. You do not support union types (?????????)

At this point I don't know what to say to be honest. This is simply not acceptable.

client = genai.Client()

async def main():

    class Foo(BaseModel):
        maybe_int: int | str


    await client.aio.models.generate_content(

        model="gemini-2.0-flash",
        contents="Please populate all fields of the following model with random values",
        config={
            "response_mime_type": "application/json",
            "response_schema": Foo,
        }
    )
ValueError: AnyOf is not supported in the response schema for the Gemini API.

4. We don't support self-referencing models and you actually get a maximum recursion error if you try this FFS.

client = genai.Client()

async def main():

    class MyNode(BaseModel):
        parent: Self | None
        child: Self | None


    await client.aio.models.generate_content(

        model="gemini-2.0-flash",
        contents="Please populate all fields of the following model with random values",
        config={
            "response_mime_type": "application/json",
            "response_schema": MyNode,
        }
    )


if __name__ == "__main__":
    asyncio.run(main())
RecursionError: maximum recursion depth exceeded

5. Another random bug (can't just have a None type)

Idk if this is ever a supported case, but the fact that an edge case as basic as this throws an error says everything about the fragility and unreliability of working with this API. There are so many random edge cases/bugs- how can anyone trust this SDK when it's this unreliable...?

client = genai.Client()

async def main():

    class Foo(BaseModel):
        a: None


    await client.aio.models.generate_content(

        model="gemini-2.0-flash",
        contents="Please populate all fields of the following model with random values",
        config={
            "response_mime_type": "application/json",
            "response_schema": Foo,
        }
    )


if __name__ == "__main__":
    asyncio.run(main())
  ... line 625, in process_schema
    schema_type = schema_type.upper()
                  ^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'upper'

6. sets are also not supported

client = genai.Client()

async def main():

    class Foo(BaseModel):
        foo: set[int]

    return (await client.aio.models.generate_content(

        model="gemini-2.0-flash",
        contents="Please populate all fields of the following model with random values",
        config={
            "response_mime_type": "application/json",
            "response_schema": Foo,
        }
    )).parsed


if __name__ == "__main__":
    print(asyncio.run(main()))
pydantic_core._pydantic_core.ValidationError: 1 validation error for Schema
properties.foo.uniqueItems
  Extra inputs are not permitted [type=extra_forbidden, input_value=True, input_type=bool]
    For further information visit https://errors.pydantic.dev/2.10/v/extra_forbidden

7. You don't support fixed-length tuples

I'm legit losing my mind at this point- is this real life? So tuple[int, ...] is fine, but I can't have a fixed length tuple[int, int]...

client = genai.Client()

async def main():

    class Foo(BaseModel):
        foo: tuple[int, ...]   # This is fine!
        bar: tuple[int, int]  # This breaks ???

    return (await client.aio.models.generate_content(
        model="gemini-2.0-flash",
        contents="Please populate all fields of the following model with random values",
        config={
            "response_mime_type": "application/json",
            "response_schema": Foo,
        }
    )).parsed


if __name__ == "__main__":
    print(asyncio.run(main()))

8. Your documentation is misleading about capabilities

On top of this huge list of flaws, you also seem to outright lie about capabilities. Dicts are simply not supported, but it seems to imply that they are here.

Please make it make sense

Why would ANYONE choose to use this utter mess instead of a competitor for agentic/workflow-based LLM systems? It is nothing short of embarrassing. Flash 2.0 is meant to be your "agentic model offering"; but it doesn't even support basic structured output use-cases, breaks at the slightest gust of wind, and requires devs to refactor all of their output schemas if they're switching from a different provider...?

Have I got that correct or am I just missing something massive and losing my mind here?

Please for the love of god reach out if you are unironically struggling with this, because this stuff should be really basic.

Metadata

Metadata

Assignees

Labels

priority: p2Moderately-important priority. Fix may not be included in next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions