Skip to content

Python: Fix OpenAIChatCompletionClient passing raw JSON-Schema dict response_format through unwrapped#7199

Open
exp-ouroborous wants to merge 2 commits into
microsoft:mainfrom
exp-ouroborous:fix/chat-completions-response-format-wrap
Open

Python: Fix OpenAIChatCompletionClient passing raw JSON-Schema dict response_format through unwrapped#7199
exp-ouroborous wants to merge 2 commits into
microsoft:mainfrom
exp-ouroborous:fix/chat-completions-response-format-wrap

Conversation

@exp-ouroborous

Copy link
Copy Markdown

Motivation and Context

Raw JSON-Schema dicts (e.g. {"type": "object", "properties": {...}}) passed as response_format are forwarded to the Chat Completions API verbatim, and OpenAI rejects them with a 400 (Invalid value: 'object'). The Responses-based OpenAIChatClient already auto-wraps the identical input (see _convert_response_format), so the two clients disagree on the same input. This bites the declarative path in particular, where the loader passes outputSchema.to_json_schema() output as a bare dict.

Description

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible (test_openai_chat_completion_client.py: 77 passed, 20 skipped)
  • Is this a breaking change? No — previously-rejected inputs now work; valid inputs are unchanged.

Copilot AI review requested due to automatic review settings July 19, 2026 15:27
@giles17 giles17 added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python .NET Usage: [Issues, PRs], Target: .Net labels Jul 19, 2026
@github-actions github-actions Bot changed the title Python: Fix OpenAIChatCompletionClient passing raw JSON-Schema dict response_format through unwrapped .NET: Python: Fix OpenAIChatCompletionClient passing raw JSON-Schema dict response_format through unwrapped Jul 19, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Python OpenAIChatCompletionClient handling of response_format when callers pass a raw JSON-Schema dict by normalizing/wrapping it into the Chat Completions json_schema envelope (bringing it in line with the Responses-based client), and adds unit tests for the new behavior. It also introduces a new .NET “AgentLearn” learning project plus several large research/ markdown documents and repo-level developer settings.

Changes:

  • Python: normalize raw JSON-Schema dict response_format values in OpenAIChatCompletionClient and add tests for wrapping/title→name behavior.
  • .NET: add dotnet/learn/AgentLearn sample app (workflows + HITL + tests) and a dotnet/samples/run.bash helper script.
  • Docs/config: add research/ documentation, new dotnet/learn docs/config, and repo-level VS Code/default-solution and gitignore updates.

Reviewed changes

Copilot reviewed 40 out of 41 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
research/README.md Adds a README describing the new research/ folder structure.
research/python/agentic-capabilities.md Adds Python agentic-capabilities research document.
research/dotnet/agentic-capabilities.md Adds .NET agentic-capabilities research document.
research/agentic-capabilities-overview.md Adds cross-language agentic-capabilities overview research document.
python/packages/openai/tests/openai/test_openai_chat_completion_client.py Adds tests for raw-schema wrapping and passthrough behaviors.
python/packages/openai/agent_framework_openai/_chat_completion_client.py Normalizes dict response_format by wrapping raw JSON Schema into json_schema.
dotnet/samples/run.bash Adds a helper script to run a .NET project with .env loaded.
dotnet/learn/README.md Adds README for the new dotnet/learn area.
dotnet/learn/Directory.Packages.props Disables central package management within dotnet/learn.
dotnet/learn/Directory.Build.targets Adds empty targets file to isolate from parent build targets.
dotnet/learn/Directory.Build.props Adds empty props file to isolate from parent build props.
dotnet/learn/CLAUDE.md Adds contributor guidance for the dotnet/learn area.
dotnet/learn/AgentLearn/tests/integration/StoryGeneratorWorkflowTests.cs Adds integration tests for StoryGenerator HITL workflow.
dotnet/learn/AgentLearn/tests/integration/MockChatClientHelper.cs Adds a helper to mock IChatClient responses for tests.
dotnet/learn/AgentLearn/tests/integration/JokeWriterWorkflowTests.cs Adds integration tests for JokeWriter workflows (single/sequential/concurrent).
dotnet/learn/AgentLearn/tests/integration/AgentLearn.IntegrationTests.csproj Adds integration test project definition.
dotnet/learn/AgentLearn/TaskKinds/StoryGenerator/StoryGenerator.http Adds REST client requests for StoryGenerator.
dotnet/learn/AgentLearn/TaskKinds/StoryGenerator/StoryGenerator.cs Adds StoryGenerator task kind (agents/workflow/handler/DI).
dotnet/learn/AgentLearn/TaskKinds/JokeWriter/JokeWriter.http Adds REST client requests for JokeWriter.
dotnet/learn/AgentLearn/TaskKinds/JokeWriter/JokeWriter.cs Adds JokeWriter task kind (models/executors/agents/workflows/handler/DI).
dotnet/learn/AgentLearn/Services/ITaskHandler.cs Adds task-handler abstraction for routing TaskKinds.
dotnet/learn/AgentLearn/Services/ChatClientFactory.cs Adds provider-based IChatClient factory + mock chat client.
dotnet/learn/AgentLearn/run.sh Adds script to start Jaeger and run AgentLearn.
dotnet/learn/AgentLearn/Properties/launchSettings.json Adds local launch settings for HTTP/HTTPS profiles.
dotnet/learn/AgentLearn/Program.cs Adds DI setup, OTel tracing, DevUI, and OpenAI-compatible endpoints.
dotnet/learn/AgentLearn/Models/TaskResponse.cs Adds DTO for /task responses.
dotnet/learn/AgentLearn/Models/TaskRequest.cs Adds DTO for /task requests.
dotnet/learn/AgentLearn/Models/TaskKind.cs Adds TaskKind enum for handler selection.
dotnet/learn/AgentLearn/Models/LlmOptions.cs Adds LLM provider config options DTO.
dotnet/learn/AgentLearn/Models/AgentMode.cs Adds workflow mode enum (Single/Sequential/Concurrent).
dotnet/learn/AgentLearn/Extensions/AIAgentBuilderExtensions.cs Adds tool-invocation logging middleware for agents.
dotnet/learn/AgentLearn/Controllers/TaskController.cs Adds /task endpoint controller.
dotnet/learn/AgentLearn/CLAUDE.md Adds project-specific conventions for AgentLearn.
dotnet/learn/AgentLearn/appsettings.json Adds baseline configuration including LLM defaults.
dotnet/learn/AgentLearn/appsettings.Development.json Adds dev configuration (debug logging).
dotnet/learn/AgentLearn/AgentLearn.csproj Adds AgentLearn web project definition and package references.
dotnet/learn/AgentLearn.sln Adds a Visual Studio solution for AgentLearn.
dotnet/learn/.gitignore Adds ignores for .env* files under dotnet/learn.
CLAUDE.md Adds repo-level Claude guidance file.
.vscode/settings.json Sets a repo-wide default .NET solution.
.gitignore Ignores .workspaces/ personal VS Code workspace files.

Comment thread dotnet/learn/AgentLearn/AgentLearn.csproj Outdated
Comment thread research/README.md Outdated
Comment thread dotnet/learn/CLAUDE.md Outdated
Comment thread .vscode/settings.json Outdated
Comment thread dotnet/learn/AgentLearn/Services/ChatClientFactory.cs Outdated
…esponse_format through unwrapped

Raw schema dicts (e.g. {"type": "object", ...}) were forwarded to the
Chat Completions API verbatim, which OpenAI rejects with a 400. The
Responses client already auto-wraps the same input. Mirror its raw-schema
detection (primitive types / schema keywords), wrap into the
{"type": "json_schema", "json_schema": {...}} envelope with
additionalProperties: false injection and title -> name promotion, and
leave already-valid response_format dicts untouched.

Fixes microsoft#7197

(cherry picked from commit dce5c3b)
@exp-ouroborous
exp-ouroborous force-pushed the fix/chat-completions-response-format-wrap branch from dce5c3b to 2351bd5 Compare July 19, 2026 15:32
@exp-ouroborous

Copy link
Copy Markdown
Author

Note for reviewers: the earlier review threads (research/, dotnet/learn/, .vscode/settings.json, AgentLearn.csproj) referenced unrelated files accidentally included from a stale fork base — the branch has been rebuilt as a single commit on top of main and those files are no longer part of this PR (now 2 files: _chat_completion_client.py + its tests).

@exp-ouroborous
exp-ouroborous marked this pull request as ready for review July 19, 2026 15:52
@exp-ouroborous

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

@exp-ouroborous exp-ouroborous changed the title .NET: Python: Fix OpenAIChatCompletionClient passing raw JSON-Schema dict response_format through unwrapped Python: Fix OpenAIChatCompletionClient passing raw JSON-Schema dict response_format through unwrapped Jul 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs a integration test, because if I recall correctly there are differences in the format for the structured output between Chat Completions and Responses API's.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 47e3a08 — a response_format_raw_json_schema param in test_integration_options for both clients, passing the same bare WeatherDigest schema dict (with title, without additionalProperties) so the wrapping, title→name promotion, and additionalProperties: false injection paths are all exercised against the live APIs.

You're right that the wire formats differ — Chat Completions nests under response_format.json_schema while Responses flattens into text.format. Both new params pass live (verified locally with a real key): the identical raw input round-trips through both APIs and yields parsed structured output (response.value validated). The Responses-side param also adds live coverage for the pre-existing raw-schema handling in _convert_response_format, which previously had none — so if either implementation drifts, one of the twin params breaks.

@github-actions

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/openai/agent_framework_openai
   _chat_completion_client.py3871995%440, 536–537, 541, 729, 811, 813, 818, 821, 912, 914, 931, 952, 960, 984, 997, 1021, 1041, 1356
TOTAL44697521288% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
9083 33 💤 0 ❌ 0 🔥 2m 30s ⏱️

…rmat dicts

Adds a response_format_raw_json_schema param to test_integration_options in
both the Chat Completions and Responses client test suites, proving the same
bare schema dict (title set, additionalProperties omitted) round-trips through
both live APIs and yields parsed structured output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net python Usage: [Issues, PRs], Target: Python

Projects

None yet

4 participants