Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
address Copilot review comments on structured output
  • Loading branch information
karthik-0306 committed May 23, 2026
commit a5c982da7cd2b3777b71376181b23c4201db8405
31 changes: 26 additions & 5 deletions python/packages/bedrock/agent_framework_bedrock/_chat_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,12 @@ def _invoke_converse(self, request: Mapping[str, Any]) -> dict[str, Any]:
error_details = e.response.get("Error", {})
error_code = error_details.get("Code", "")
error_message = error_details.get("Message", "")
# "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 or "outputConfig" in str(e)
"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. "
Expand Down Expand Up @@ -760,12 +764,27 @@ def _prepare_output_config(self, response_format: Any | None) -> dict[str, Any]
return None

if isinstance(response_format, dict):
Comment thread
karthik-0306 marked this conversation as resolved.
Resolved
# response_format passed as a dict schema (possibly OpenAI-style)
schema_src = response_format.get("json_schema", {}).get("schema", response_format)
if "json_schema" in response_format:
# Shape A — OpenAI-style wrapper
json_schema_config = response_format["json_schema"]
schema_src = json_schema_config.get("schema", {})
name = json_schema_config.get("name", "output_schema")
elif "schema" in response_format:
# Shape B — inner shape directly {"name": ..., "schema": ...}
schema_src = response_format["schema"]
name = response_format.get("name", "output_schema")
else:
# Shape C — assume entire dict is the raw schema
logger.warning(
"response_format dict has no 'json_schema' or 'schema' key; "
"treating entire dict as raw JSON schema."
)
schema_src = response_format
name = "output_schema"

if isinstance(schema_src, str):
schema_src = json.loads(schema_src)
schema = copy.deepcopy(schema_src)
name = response_format.get("json_schema", {}).get("name", "output_schema")
else:
Comment thread
karthik-0306 marked this conversation as resolved.
Resolved
# response_format is a Pydantic model class
schema = response_format.model_json_schema()
Expand Down Expand Up @@ -808,7 +827,9 @@ def walk(node: Any) -> None:
if node_id in visited:
return
visited.add(node_id)
if node.get("type") == "object":
if node.get("type") == "object" or (
"properties" in node and "type" not in node
):
node["additionalProperties"] = False
for value in node.values():
if isinstance(value, (dict, list)):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,10 @@ def test_additional_properties_false_set_recursively() -> None:

# Check $defs for NestedAddress
defs = schema.get("$defs", {})
if "NestedAddress" in defs:
assert defs["NestedAddress"].get("additionalProperties") is False
assert "NestedAddress" in defs, "Expected NestedAddress to be present in $defs"
assert defs["NestedAddress"].get("additionalProperties") is False, (
"Expected additionalProperties=False on nested NestedAddress schema"
)


Comment thread
karthik-0306 marked this conversation as resolved.
Resolved
def test_no_output_config_when_response_format_none() -> None:
Expand Down