Python: feat(bedrock): implement native structured output support via Converse API#6052
Python: feat(bedrock): implement native structured output support via Converse API#6052karthik-0306 wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds structured output support for Bedrock Converse by translating response_format into Bedrock’s outputConfig.textFormat=json_schema, and ensures resulting ChatResponse.value is populated (including streaming), with tests covering schema wiring and an unsupported-model error path.
Changes:
- Implement
outputConfiggeneration from Pydantic models or dict-based JSON schemas and attach it to Converse requests. - Plumb
response_formatthrough response processing/stream building soChatResponse.valuecan be parsed. - Add a new pytest suite validating wire shape, strict-schema behavior, streaming parsing, and a ValidationException-to-ValueError mapping.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| python/packages/bedrock/agent_framework_bedrock/_chat_client.py | Adds outputConfig(json_schema) request support, strict schema mutation, and a clearer error for unsupported models. |
| python/packages/bedrock/tests/test_bedrock_structured_output.py | Introduces tests for outputConfig shape, schema encoding, recursive strictness, parsing into .value, streaming, and unsupported-model handling. |
| # "outputConfig" in error_message catches cases where Bedrock explicitly | ||
| # rejects the outputConfig field (unsupported model). Other ValidationExceptions | ||
| # (e.g. malformed schema shape, invalid property values) will not mention | ||
| # "outputConfig" and will bubble up as raw ClientError without being misdiagnosed. | ||
| if error_code == "ValidationException" and ( | ||
| "outputconfig" in error_message.lower() or "outputconfig" in str(e).lower() | ||
| ): | ||
| raise ValueError( | ||
| f"Model '{self.model}' does not support structured output via outputConfig.textFormat. " | ||
| "Check the model's Bedrock Converse outputConfig/textFormat support. " | ||
| f"AWS error Code: {error_code}. AWS error Message: {error_message}" | ||
| ) from e |
There was a problem hiding this comment.
Checked the existing MAF exception hierarchy — there is no UnsupportedFeature-style exception in the codebase. Two options: use the existing ChatClientInvalidRequestException which semantically fits ("the model rejected this request configuration"), or keep ValueError since it's standard Python for bad argument values and is consistent with how other validation errors are surfaced across MAF. Flagging for human reviewer input before making this call — happy to go either direction.
…or check, docs + test
Motivation and Context
BedrockChatClient was the only chat client in MAF without structured output support. It actively blocked the feature by hardcoding response_format: None in BedrockChatOptions, silently discarding any schema passed by the caller regardless of what the user specified.
AWS Bedrock's Converse API added native structured output support via outputConfig.textFormat (GA February 4, 2026), making this workaround unnecessary. Every other MAF provider client — Anthropic, OpenAI, and Gemini — already supports response_format. This PR brings Bedrock to full parity.
Fixes #5966.
Description
Removed the response_format: None override in BedrockChatOptions so the field flows through from the parent ChatOptions naturally.
Added _prepare_output_config() which translates MAF's response_format (either a Pydantic model class or an OpenAI-style dict schema) into the exact wire format the Converse API requires:
Added _set_additional_properties_false(), a recursive helper that mirrors the identical method in AnthropicChatClient. It walks the full schema tree and sets additionalProperties: false on every object type, as required by AWS for strict schema enforcement. A copy.deepcopy() guards against mutating the caller's original dict schema.
Threaded response_format through _process_converse_response() and _build_response_stream() so MAF's base class machinery handles response parsing and lazily hydrates ChatResponse.value with the validated Pydantic model — no custom JSON parsing logic needed.
Wrapped _invoke_converse() in a try/except that catches botocore.exceptions.ClientError. When AWS returns a ValidationException referencing outputConfig (which happens when a model like Claude 3.x, Nova, or Llama receives the parameter), it is re-raised as a descriptive ValueError naming the model and listing supported alternatives. No silent fallback to unstructured text.
When response_format is not provided, behaviour is identical to before — no outputConfig is sent and no existing functionality is affected.
Contribution Checklist