Python: raise clear error for runtime response_format on FoundryAgent (#5467)#5493
Python: raise clear error for runtime response_format on FoundryAgent (#5467)#5493GitAashishG wants to merge 5 commits intomicrosoft:mainfrom
Conversation
The Foundry agent endpoint rejects per-call `text` configuration when an agent is bound (400 invalid_payload, "Not allowed when agent is specified."). Strip `text` and `text_format` from the request body in `_RawFoundryAgentChatClient._prepare_options` so the user-supplied `response_format` is honored client-side via ChatResponse's lazy structured-value parsing instead of failing the request. Fixes microsoft#5467
Python Test Coverage Report •
Python Unit Test Overview
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Pull request overview
This PR fixes a regression in agent-framework-foundry where providing response_format at runtime caused Foundry agent endpoint requests to fail with 400 invalid_payload due to disallowed per-call text configuration. The fix removes text and text_format from the outgoing request payload in the Foundry agent chat client so structured output is handled client-side via ChatResponse’s lazy parsing instead of being sent to the service.
Changes:
- Strip
textandtext_formatfromRawFoundryAgentChatClient._prepare_optionsrequest payload. - Extend the existing
_prepare_optionsstripping test to covertext/text_format. - Add a new test to ensure runtime
response_formatvariants (Pydantic and dict/json_schema) don’t result intext/text_formatbeing sent.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| python/packages/foundry/agent_framework_foundry/_agent.py | Drops text/text_format from prepared run options to avoid Foundry agent endpoint rejection and rely on client-side structured parsing. |
| python/packages/foundry/tests/foundry/test_foundry_agent.py | Adds/extends unit tests to assert text/text_format are stripped, including runtime response_format scenarios. |
| # configured to emit JSON matching the requested schema; otherwise | ||
| # ``response.value`` will raise ``pydantic.ValidationError`` on access. |
There was a problem hiding this comment.
The explanatory comment implies that when the bound agent output doesn’t match the runtime schema, accessing response.value will raise pydantic.ValidationError. That’s only true for Pydantic response_format; for the dict/json_schema variant the lazy parsing path only does json.loads(...) and will raise ValueError on invalid JSON (and does not validate against the schema). Consider adjusting the comment to reflect both behaviors to avoid confusing users.
| # configured to emit JSON matching the requested schema; otherwise | |
| # ``response.value`` will raise ``pydantic.ValidationError`` on access. | |
| # configured to emit JSON compatible with the requested format; for a | |
| # Pydantic ``response_format``, accessing ``response.value`` may raise | |
| # ``pydantic.ValidationError`` if validation fails, while for a dict/ | |
| # ``json_schema`` format the lazy path only parses JSON and may raise | |
| # ``ValueError`` for invalid JSON without validating against the schema. |
There was a problem hiding this comment.
As I eluded to in the original issue: the preferred direction here is to raise a clear runtime error in _prepare_options when response_format is provided, pointing the caller to either configure it at agent creation or use FoundryChatClient + Agent for per-call response formats.
| # dict / ``json_schema`` ``response_format``, the lazy path only parses | ||
| # JSON and may raise ``ValueError`` on invalid JSON; it does not | ||
| # validate against the schema. | ||
| run_options.pop("text", None) |
There was a problem hiding this comment.
The 400 going away isn't equivalent to runtime response_format being supported: silently stripping it turns the option into a client-side parse hint, which contradicts the FoundryAgent design (settings at creation time).
Per maintainer review feedback (microsoft#5493): FoundryAgent is bound to a server-side agent whose response schema is fixed at creation time. Per-call response_format is not a supported runtime option. Replace the previous strip-and-parse approach with an up-front guard in _RawFoundryAgentChatClient._prepare_options that raises ChatClientInvalidRequestException pointing the caller at the two supported paths (recreate the agent server-side, or use FoundryChatClient + Agent for per-call structured output).
Thanks, that makes sense. Pivoted to the guard approach: |
Fixes #5467
Problem
When a user passes
response_format=(per-call or viadefault_options) to aFoundryAgent, the request fails at the wire with an opaque server error:The Foundry agent endpoint owns response-schema configuration at agent
creation time — so per-call
text/response_formatoptions are not asupported runtime knob for
FoundryAgent.Approach
Per @moonbox3's review, silently stripping the option would be wrong — it
would turn
response_formatinto a client-side parse hint that contradictsthe FoundryAgent design. Instead this PR raises an explicit, actionable
error up-front:
The guard sits at the top of
_RawFoundryAgentChatClient._prepare_options,so it covers both streaming and non-streaming entry points and also catches
response_formatset viaAgent.default_options.Scope
FoundryAgent(bound to a server-side agent)FoundryChatClientis unchanged — per-callresponse_formatcontinues towork there (existing
test_response_format_parse_pathcovers this)Testing
test_raw_foundry_agent_chat_client_prepare_options_rejects_runtime_response_formatcovers both the Pydantic
BaseModeland dict /json_schemavariantsruff check,ruff format --check,pyright— all cleanFiles changed
python/packages/foundry/agent_framework_foundry/_agent.py— guard +import of
ChatClientInvalidRequestExceptionpython/packages/foundry/tests/foundry/test_foundry_agent.py— new guardtest