Skip to content

feat(bedrock): support native structured outputs API (outputConfig.textFormat)#21222

Merged
Sameerlite merged 87 commits intoBerriAI:litellm_oss_staging_02_16_2026from
ndgigliotti:feat/bedrock-native-structured-outputs
Feb 17, 2026
Merged

feat(bedrock): support native structured outputs API (outputConfig.textFormat)#21222
Sameerlite merged 87 commits intoBerriAI:litellm_oss_staging_02_16_2026from
ndgigliotti:feat/bedrock-native-structured-outputs

Conversation

@ndgigliotti
Copy link
Contributor

@ndgigliotti ndgigliotti commented Feb 14, 2026

Relevant issues

Fixes #21208

Pre-Submission checklist

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Type

🆕 New Feature

Changes

For Bedrock models that support native structured outputs, use the Converse API's outputConfig.textFormat field (true constrained decoding) instead of the synthetic json_tool_call workaround. Unsupported models automatically fall back to the existing tool-call approach.

What changed

  • New TypedDicts in litellm/types/llms/bedrock.pyOutputConfigBlock, OutputFormat, OutputFormatStructure, JsonSchemaDefinition matching the AWS Converse API schema
  • Model detectionBEDROCK_NATIVE_STRUCTURED_OUTPUT_MODELS set with substring matching (same pattern as Anthropic's direct API)
  • Schema normalization_add_additional_properties_to_schema() recursively ensures additionalProperties: false on all object types, which Bedrock requires
  • Branching in _translate_response_format_param — supported models get outputConfig (no synthetic tool, no fake_stream); unsupported models use the existing tool-call path unchanged
  • Request/response plumbingoutputConfig flows through _prepare_request_params_transform_request_helper → final API request; response handling passes JSON text content through directly when no synthetic tool call is present
  • 12 new unit tests covering model detection, schema normalization, outputConfig creation, request building, response handling, and streaming behavior

Supported models (native path)

Anthropic Claude 4.5+, Qwen3, DeepSeek V3.1, Gemma 3, MiniMax M2, Mistral Large 3, Ministral, Voxtral, Moonshot Kimi K2, NVIDIA Nemotron Nano.

GPT-OSS and Magistral Small are excluded despite AWS listing them — their constrained decoding is broken on Bedrock (produces invalid JSON). They fall back to tool-call which works reliably for GPT-OSS.

Why

AWS docs: https://docs.aws.amazon.com/bedrock/latest/userguide/structured-output.html

Harshit28j and others added 25 commits February 3, 2026 07:54
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel
Copy link

vercel bot commented Feb 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Feb 17, 2026 1:29am

Request Review

@CLAassistant
Copy link

CLAassistant commented Feb 14, 2026

CLA assistant check
All committers have signed the CLA.

@ndgigliotti
Copy link
Contributor Author

@greptileai

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 14, 2026

Greptile Overview

Greptile Summary

Adds native structured outputs support for Bedrock models via the Converse API's outputConfig.textFormat field, replacing the synthetic tool-call workaround for supported models. Unsupported models automatically fall back to the existing tool-call approach.

  • Native path for supported models: Anthropic Claude 4.5+, Qwen3, DeepSeek V3.1, Gemma 3, MiniMax M2, Mistral Large 3, Ministral, Voxtral, Moonshot Kimi K2, and NVIDIA Nemotron Nano use outputConfig.textFormat with json_schema type for true constrained decoding. This eliminates fake_stream, enables real streaming, and decouples structured output from tool calling.
  • Fallback path preserved: Models without native support (including GPT-OSS and Magistral Small, which have broken constrained decoding on Bedrock) continue using the existing synthetic tool-call approach unchanged.
  • Schema normalization: Recursively injects additionalProperties: false on all object types, as required by Bedrock's API. Handles properties, items, $defs, and anyOf/allOf/oneOf keywords.
  • Request/response plumbing: outputConfig flows cleanly through _prepare_request_params_transform_request_helper → final API payload. Response handling correctly passes JSON text content through when no synthetic tool call is present (via the elif tools: guard on line 1829).
  • TypedDict types: New OutputConfigBlock, OutputFormat, OutputFormatStructure, and JsonSchemaDefinition types match the AWS Converse API schema.
  • Test coverage: 12 new unit tests covering model detection, schema normalization, outputConfig creation, request building, response handling, and streaming behavior. All tests are mock-only with no network calls.

Confidence Score: 4/5

  • This PR is safe to merge with minor considerations around edge cases in schema normalization.
  • The implementation is well-structured, follows existing patterns (consistent with Anthropic's native structured output approach), and includes comprehensive test coverage. The native/fallback branching is clean and the response handling correctly handles both paths. Two minor style issues were identified: the definitions keyword gap in schema normalization and the empty schema case for json_object type. Neither is likely to cause issues in typical usage but could affect edge cases. All provider-specific code stays within the llms/ directory, and tests make no real network calls.
  • litellm/llms/bedrock/chat/converse_transformation.py — schema normalization and empty schema edge cases

Important Files Changed

Filename Overview
litellm/llms/bedrock/chat/converse_transformation.py Core implementation of native structured outputs via outputConfig.textFormat. Clean branching between native and tool-call fallback paths. Minor gaps: schema normalization misses definitions keyword; empty schema for json_object type may cause issues.
litellm/types/llms/bedrock.py Adds well-structured TypedDict definitions (OutputConfigBlock, OutputFormat, OutputFormatStructure, JsonSchemaDefinition) matching the AWS Converse API schema. Clean addition to CommonRequestObject.
tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py Comprehensive test suite (12 tests) covering model detection, schema normalization, outputConfig creation, request building, response handling, and streaming behavior. All tests are pure unit tests with no network calls.

Flowchart

flowchart TD
    A["response_format param received"] --> B{"Model supports native\nstructured outputs?"}
    B -->|Yes| C["_create_output_config_for_response_format()"]
    C --> D["Normalize schema:\nadditionalProperties: false"]
    D --> E["Set outputConfig.textFormat\nwith json_schema type"]
    E --> F["json_mode = True\nNo fake_stream needed"]
    B -->|No| G["_create_json_tool_call_for_response_format()"]
    G --> H["Inject synthetic tool +\ntool_choice forced"]
    H --> I["json_mode = True\nfake_stream = True if streaming"]
    F --> J["_prepare_request_params()"]
    I --> J
    J --> K["_transform_request_helper()"]
    K -->|Native path| L["outputConfig added as\ntop-level request field"]
    K -->|Fallback path| M["toolConfig contains\nsynthetic tool"]
    L --> N["Bedrock Converse API"]
    M --> N
    N -->|Response| O{"Tool call with\nRESPONSE_FORMAT name?"}
    O -->|Yes: fallback path| P["Extract JSON from\ntool arguments"]
    O -->|No tools / real tools| Q["Pass text content\nthrough directly"]
    P --> R["Return ModelResponse"]
    Q --> R
Loading

Last reviewed commit: 2129061

Sameerlite and others added 6 commits February 16, 2026 21:46
…eta_header

Litellm anthropic doc beta header
…erriAI#21125) (BerriAI#21244)

* Fix tool params reported as supported for models without function calling (BerriAI#21125)

JSON-configured providers (e.g. PublicAI) inherited all OpenAI params
including tools, tool_choice, function_call, and functions — even for
models that don't support function calling. This caused an inconsistency
where get_supported_openai_params included "tools" but
supports_function_calling returned False.

The fix checks supports_function_calling in the dynamic config's
get_supported_openai_params and removes tool-related params when the
model doesn't support it. Follows the same pattern used by OVHCloud
and Fireworks AI providers.

* Style: move verbose_logger to module-level import, remove redundant try/except

Address review feedback from Greptile bot:
- Move verbose_logger import to top-level (matches project convention)
- Remove redundant try/except around supports_function_calling() since it
  already handles exceptions internally via _supports_factory()
…AI#21239)

* fix: handle missing database url in append_query_params

* Update litellm/proxy/proxy_cli.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@krrishdholakia
Copy link
Member

@ndgigliotti unable to merge due to conflicts, can you resolve and bump me please?

Screenshot 2026-02-16 at 9 06 54 AM

@jquinter for monitoring

michelligabriele and others added 8 commits February 16, 2026 09:08
…iAI#21323)

PR BerriAI#19809 changed stateless=True to stateless=False to enable progress
notifications for MCP tool calls. This caused the mcp library to enforce
mcp-session-id headers on all non-initialize requests, breaking MCP
Inspector, curl, and any client without automatic session management.

Revert to stateless=True to restore compatibility with all MCP clients.
The progress notification code already handles missing sessions gracefully
(defensive checks + try/except), so no other changes are needed.

Fixes BerriAI#20242
…ories + go to next page (BerriAI#21223)

* feat(ui/): allow viewing content filter categories on guardrail info

* fix(add_guardrail_form.tsx): add validation check to prevent adding empty content filter guardrails

* feat(ui/): improve ux around adding new content filter categories

easy to skip adding a category, so make it a 1-click thing
[Infra] Bumping proxy extras version
Resolve conflict in test_converse_transformation.py by keeping both
the structured outputs tests (from this branch) and the
TestBedrockMinThinkingBudgetTokens tests (from main).
@ndgigliotti
Copy link
Contributor Author

ndgigliotti commented Feb 16, 2026

@krrishdholakia Conflicts resolved and pushed.

The 6 failing checks (test (llms), test (other), test (proxy-guardrails), test (proxy-unit-a), test (proxy-unit-b), test (root)) appear to be repo-wide — they seem to be failing on other open PRs as well (#21330, #21327, #21326, #21325, #21322). Not related to this PR's changes.

All checks relevant to this PR are passing: unit-test, test, lint, test (core-utils), test (integrations), test (proxy-core), test (proxy-misc), build-ui, validate-model-prices-json.

@krrishdholakia krrishdholakia changed the base branch from main to litellm_oss_staging_02_16_2026 February 16, 2026 22:16
Keep both structured output tests (ours) and min thinking budget tests
(staging). Accept staging poetry.lock.
@ndgigliotti
Copy link
Contributor Author

Resolved merge conflicts with the staging branch.

@Sameerlite Sameerlite merged commit 3a34b63 into BerriAI:litellm_oss_staging_02_16_2026 Feb 17, 2026
4 checks passed
ndgigliotti added a commit to ndgigliotti/litellm that referenced this pull request Mar 16, 2026
…de 4.5+)

For Bedrock InvokeModel Claude models that support native structured outputs
(Haiku 4.5, Sonnet 4.5, Opus 4.5, Opus 4.6), use output_config.format with
json_schema instead of the synthetic json_tool_call workaround. Unsupported
models automatically fall back to the existing tool-call approach.

Completes the Invoke API portion of BerriAI#21208 (Converse was merged in BerriAI#21222).
ndgigliotti added a commit to ndgigliotti/litellm that referenced this pull request Mar 16, 2026
…de 4.5+)

For Bedrock InvokeModel Claude models that support native structured outputs
(Haiku 4.5, Sonnet 4.5, Opus 4.5, Opus 4.6), use output_config.format with
json_schema instead of the synthetic json_tool_call workaround. Unsupported
models automatically fall back to the existing tool-call approach.

Completes the Invoke API portion of BerriAI#21208 (Converse was merged in BerriAI#21222).
ndgigliotti added a commit to ndgigliotti/litellm that referenced this pull request Mar 16, 2026
…de 4.5+)

For Bedrock InvokeModel Claude models that support native structured outputs
(Haiku 4.5, Sonnet 4.5, Opus 4.5, Opus 4.6), use output_config.format with
json_schema instead of the synthetic json_tool_call workaround. Unsupported
models automatically fall back to the existing tool-call approach.

Completes the Invoke API portion of BerriAI#21208 (Converse was merged in BerriAI#21222).
ndgigliotti added a commit to ndgigliotti/litellm that referenced this pull request Mar 16, 2026
…de 4.5+)

For Bedrock InvokeModel Claude models that support native structured outputs
(Haiku 4.5, Sonnet 4.5, Opus 4.5, Opus 4.6), use output_config.format with
json_schema instead of the synthetic json_tool_call workaround. Unsupported
models automatically fall back to the existing tool-call approach.

Completes the Invoke API portion of BerriAI#21208 (Converse was merged in BerriAI#21222).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Support AWS Bedrock native structured outputs API (outputConfig.textFormat)