Skip to content

build_strict_openai_schema emits oneOf for discriminated unions, which OpenAI strict mode rejects #5302

@inickt

Description

@inickt

Bug Description

When a tool input model contains a Pydantic discriminated union, build_strict_openai_schema(...) emits oneOf in the generated schema.

That appears to be incompatible with OpenAI strict tool schemas. The resulting tool registration fails with:

Invalid schema for function '<function>': In context=(), 'oneOf' is not permitted.

The same union without an explicit discriminator produces anyOf instead and works.

Expected Behavior

to_strict_json_schema(...) should normalize discriminated unions into a strict-mode-compatible schema shape:
https://github.com/livekit/agents/blob/main/livekit-agents/livekit/agents/llm/_strict.py

Reproduction Steps

from typing import Annotated, Literal

from pydantic import BaseModel, Field

class Car(BaseModel):
    vehicle: Literal["Car"]
    brand: str
    color: str


class Bike(BaseModel):
    vehicle: Literal["Bike"]
    brand: str
    color: str


type CarOrBike = Annotated[Car | Bike, Field(discriminator="vehicle")]
# type CarOrBike = Annotated[Car | Bike, Field()]  # this version works

and then use `CarOrBike` in a `@function_tool` parameter.

Relevant schema difference:

With discriminator:


{
  "$defs": {
    "CarOrBike": {
      "oneOf": [
        { "$ref": "#/$defs/Car" },
        { "$ref": "#/$defs/Bike" }
      ]
    }
  }
}


Without discriminator:


{
  "$defs": {
    "CarOrBike": {
      "anyOf": [
        { "$ref": "#/$defs/Car" },
        { "$ref": "#/$defs/Bike" }
      ]
    }
  }
}

Operating System

macOS/Linux

Models Used

OpenAI GPT 4.1, should occur with any OpenAI model with strict mode enabled

Package Versions

livekit-agents==1.4.6

Session/Room/Call IDs

N/A

Proposed Solution

Normalize `oneOf` back to `anyOf` when building the strict OpenAI schema.

Additional Context

Discriminated unions are the recommended Pydantic shape for tagged unions:
https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions

Screenshots and Recordings

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions