Fix OpenAI API schema compatibility by flattening discriminated unions#53
Merged
Merged
Conversation
Co-authored-by: JPrier <24302717+JPrier@users.noreply.github.com>
Co-authored-by: JPrier <24302717+JPrier@users.noreply.github.com>
Copilot
AI
changed the title
$ treeagent --model-type openai "hello"
2025-09-28 11:43:35,938 [INFO] Executing root-task (REQUIREMENTS) using openai
2025-09-28 11:43:37,604 [ERROR] Task root-task failed: Error code: 400 - {'error': {'message': "Invalid schema for response_format 'r...
Fix OpenAI API schema compatibility by flattening discriminated unions
Sep 28, 2025
JPrier
approved these changes
Sep 28, 2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the OpenAI API error that occurred when using structured outputs with Pydantic discriminated unions. The CLI command
treeagent --model-type openai "hello"was failing with:Problem
OpenAI's structured outputs API doesn't support
oneOforanyOfanywhere in JSON schemas, but Pydantic discriminated unions (likeModelResponse) generate schemas withoneOfat the root level:{ "oneOf": [ {"$ref": "#/$defs/DecomposedResponse"}, {"$ref": "#/$defs/ImplementedResponse"}, {"$ref": "#/$defs/FollowUpResponse"}, {"$ref": "#/$defs/FailedResponse"} ], "discriminator": {"propertyName": "type"} }The existing wrapping approach still placed
oneOfinsideproperties.response, which OpenAI rejects.Solution
Implemented a comprehensive schema flattening system that converts discriminated unions into OpenAI-compatible flat object schemas:
Key Changes
Schema Flattening: The new
_flatten_discriminated_union()method convertsoneOfschemas by:type)$defsreferences for nested typesNullable Field Handling: Added
_clean_oneof_anyof_recursive()to convert nullable fields from{"anyOf": [{"type": "string"}, {"type": "null"}]}to simple{"type": "string"}(optional)Comprehensive Cleaning: Recursively removes all
oneOf/anyOfstructures throughout the entire schema treeResponse Processing: Updated
_extract_response_from_openai_format()to work with flattened schemas (no unwrapping needed)Before/After Example
Before (incompatible):
{ "type": "object", "properties": { "response": { "oneOf": [...] // ❌ OpenAI rejects this } } }After (compatible):
{ "type": "object", "properties": { "type": {"type": "string", "enum": ["decomposed", "implemented", "follow_up_required", "failed"]}, "content": {"type": "string"}, "artifacts": {"type": "array", "items": {"type": "string"}}, "subtasks": {"type": "array", "items": {"$ref": "#/$defs/Task"}}, "follow_up_ask": {"$ref": "#/$defs/Task"}, "error_message": {"type": "string"}, "retryable": {"type": "boolean"} }, "required": ["type"] }Testing
ModelResponseflatteningClarifierResponse,DesignerResponse,TesterResponse)The fix maintains full backward compatibility while enabling TreeAgent to work seamlessly with OpenAI's structured outputs API.
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.