openai / openai/openai-openapi
Audio transcription response oneOf variants overlap, misclassifying diarized_json
Nobody has claimed this yet.
- Dominant language
- No language data
- Stars
- 2.5k
- Forks
- 527
- Avg merge
- 1h 46m
- Merged PRs (30d)
- 2
Description
Summary
The non-streaming POST /audio/transcriptions JSON response union is not
structurally disjoint. A valid diarized_json response also satisfies
CreateTranscriptionResponseJson, because the basic JSON schema requires only
text and permits additional properties.
This makes the current oneOf invalid for real diarized payloads and causes
generated SDKs that select the first viable branch to deserialize diarized
responses as the basic transcription model, losing typed speaker segments.
This report is against openai/openai-openapi@690521b1753dce0c6d6b275f583d22537679cff9.
Affected schema
POST /audio/transcriptions declares:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/CreateTranscriptionResponseJson"
- $ref: "#/components/schemas/CreateTranscriptionResponseDiarizedJson"
- $ref: "#/components/schemas/CreateTranscriptionResponseVerboseJson"
Source:
- Response union:
https://github.com/openai/openai-openapi/blob/690521b1753dce0c6d6b275f583d22537679cff9/openapi.yaml#L714-L724 - Diarized schema:
https://github.com/openai/openai-openapi/blob/690521b1753dce0c6d6b275f583d22537679cff9/openapi.yaml#L36222-L36261 - Basic JSON schema:
https://github.com/openai/openai-openapi/blob/690521b1753dce0c6d6b275f583d22537679cff9/openapi.yaml#L36293-L36340 - Verbose JSON schema:
https://github.com/openai/openai-openapi/blob/690521b1753dce0c6d6b275f583d22537679cff9/openapi.yaml#L36365-L36395
The basic schema's only required property is:
required:
- text
Neither additionalProperties: false nor another exclusion prevents a richer
diarized or verbose response from also validating as the basic schema.
Minimal valid payload demonstrating the overlap
{
"task": "transcribe",
"duration": 1.5,
"text": "Hello from Alice.",
"segments": [
{
"id": "seg_0",
"type": "transcript.text.segment",
"start": 0.0,
"end": 1.5,
"text": "Hello from Alice.",
"speaker": "Alice"
}
]
}
This payload satisfies CreateTranscriptionResponseDiarizedJson, but it also
satisfies CreateTranscriptionResponseJson because it has text and object schemas
allow additional properties by default. Therefore it matches at least two
branches of oneOf, which should require exactly one.
The same structural problem applies to CreateTranscriptionResponseVerboseJson:
a valid verbose payload has text and therefore also satisfies the basic JSON
schema.
Observed generated SDK impact
In openai/openai-ruby@dd49e31134cf8dd1442f1b3fb428bab4dbc663d1, the generated
response union is ordered:
- Transcription
- TranscriptionDiarized
- TranscriptionVerbose
The Ruby union is undiscriminated and selects the first viable variant. For the
payload above, it returns OpenAI::Models::Audio::Transcription, not
OpenAI::Models::Audio::TranscriptionDiarized. The segments value remains an
untyped array of hashes instead of
OpenAI::Models::Audio::TranscriptionDiarizedSegment instances.
Minimal Ruby reproduction:
stub_request(:post, "http://localhost/audio/transcriptions").to_return(
status: 200,
headers: {"content-type" => "application/json"},
body: payload.to_json
)
response = OpenAI::Client.new(
base_url: "http://localhost",
api_key: "My API Key"
).audio.transcriptions.create(
file: StringIO.new("synthetic audio"),
model: :"gpt-4o-transcribe-diarize",
response_format: :diarized_json
)
response.class
# actual: OpenAI::Models::Audio::Transcription
# expected: OpenAI::Models::Audio::TranscriptionDiarized
This is not just a Ruby hand-written-code issue: the generated ordering mirrors
the response variant ordering in the specification, while the schemas provide no
reliable discriminator for response-shape selection.
Expected behavior
A valid non-streaming diarized_json response should be representable by exactly
one response variant and generated SDKs should select the documented diarized
model, including typed speaker segments.
Likewise, valid verbose_json responses should select the verbose model rather
than the basic text-only model.
Suggested spec-level direction
Please make the response alternatives unambiguous at the OpenAPI/generation
boundary. Possible approaches:
- Model the variants as genuinely mutually exclusive schemas, if the wire
response provides enough distinguishing information. - If the wire payload has no stable discriminator, carry the request's
response_format through generation metadata/operation overloads so SDKs can
select the declared response model without shape guessing. - As a narrow interim generator-oriented mitigation, order the more specific
variants before the permissive basic variant: DiarizedJson, VerboseJson, then
Json. This would address first-match generators, but it does not by itself
make the OpenAPI oneOf semantically valid.
A regression fixture for all three non-streaming JSON response formats would
help prevent this from recurring.
Related issues
- #497 reported the invalid/missing task discriminator and was closed after
that discriminator was removed. - #563 addressed whether task and duration are required in diarized responses.
This report is distinct: after those changes, the remaining response schemas
still overlap structurally, so valid diarized and verbose payloads can be
selected as the basic response model.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with openapi.yaml at the response union and the three transcription response schemas linked in the issue. Compare their required properties and validation behavior, then inspect the Ruby union ordering in transcription_create_response.rb and the existing issue context. Done means the three response formats are represented unambiguously and regression coverage demonstrates correct model selection for basic, diarized, and verbose JSON.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- openapi, ruby
- Domain
- api, backend-api-design
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100