Description
BedrockChatClient hard-codes response_format: None in BedrockChatOptions, which blocks structured output entirely. The Converse API now supports outputConfig.textFormat with a jsonSchema type for API-enforced structured output (GA since February 2026, works with Converse, ConverseStream, InvokeModel, and InvokeModelWithResponseStream). The Anthropic, Gemini, and OpenAI clients in MAF all support response_format already. Bedrock is the odd one out.
What needs to change
1. Remove the response_format: None override so ChatOptions.response_format can flow through naturally.
2. Add a _prepare_output_config() helper that converts Pydantic models or dict schemas into the Bedrock wire format:
def _prepare_output_config(self, response_format):
if response_format is None:
return None
if isinstance(response_format, dict):
schema = response_format.get("json_schema", {}).get("schema", response_format)
name = response_format.get("json_schema", {}).get("name", "output_schema")
else:
schema = response_format.model_json_schema()
name = response_format.__name__
self._set_additional_properties_false(schema)
return {
"textFormat": {
"type": "jsonSchema",
"jsonSchema": {
"name": name,
"schema": json.dumps(schema), # Bedrock wants a JSON string, not a dict
},
}
}
The recursive _set_additional_properties_false helper mirrors what AnthropicChatClient._prepare_response_format() does (lines 659-693).
3. Wire it into _prepare_options() after the toolConfig block:
if output_config := self._prepare_output_config(options.get("response_format")):
run_options["outputConfig"] = output_config
4. Populate ChatResponse.value by passing response_format through to _build_response, same as the Anthropic client does.
Compatibility notes
- When
response_format is omitted, nothing changes. No outputConfig gets sent.
- Models that don't support
outputConfig.textFormat will return a ValidationException from Bedrock. Surface that clearly rather than silently falling back to unstructured text.
Agent.run(response_format=...) might need a separate follow-up to pass the option down to the client layer.
Acceptance criteria
Description
BedrockChatClienthard-codesresponse_format: NoneinBedrockChatOptions, which blocks structured output entirely. The Converse API now supportsoutputConfig.textFormatwith ajsonSchematype for API-enforced structured output (GA since February 2026, works with Converse, ConverseStream, InvokeModel, and InvokeModelWithResponseStream). The Anthropic, Gemini, and OpenAI clients in MAF all supportresponse_formatalready. Bedrock is the odd one out.What needs to change
1. Remove the
response_format: Noneoverride soChatOptions.response_formatcan flow through naturally.2. Add a
_prepare_output_config()helper that converts Pydantic models or dict schemas into the Bedrock wire format:The recursive
_set_additional_properties_falsehelper mirrors whatAnthropicChatClient._prepare_response_format()does (lines 659-693).3. Wire it into
_prepare_options()after thetoolConfigblock:4. Populate
ChatResponse.valueby passingresponse_formatthrough to_build_response, same as the Anthropic client does.Compatibility notes
response_formatis omitted, nothing changes. NooutputConfiggets sent.outputConfig.textFormatwill return aValidationExceptionfrom Bedrock. Surface that clearly rather than silently falling back to unstructured text.Agent.run(response_format=...)might need a separate follow-up to pass the option down to the client layer.Acceptance criteria
outputConfig.textFormat.jsonSchema.schemais serialized as a JSON string (not a dict)additionalProperties: falseis set recursively on all object schemasoutputConfigin the request whenresponse_formatis not providedChatResponse.valueis populated with a parsed Pydantic model