llamaIndex - ✅(Solved) Fix Feature Request: Native Structured Outputs for AWS Bedrock Converse API [2 pull requests, 1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
run-llama/llama_index#20703Fetched 2026-04-08 00:31:22
View on GitHub
Comments
1
Participants
1
Timeline
9
Reactions
0
Participants
Timeline (top)
referenced ×5cross-referenced ×2commented ×1subscribed ×1

AWS Bedrock launched native structured outputs in February 2026 via the outputConfig.textFormat parameter. LlamaIndex's llama-index-llms-bedrock-converse currently uses function calling as a workaround. This should be updated to use the native API for better reliability and performance.

Root Cause

AWS Bedrock launched native structured outputs in February 2026 via the outputConfig.textFormat parameter. LlamaIndex's llama-index-llms-bedrock-converse currently uses function calling as a workaround. This should be updated to use the native API for better reliability and performance.

Fix Action

Solution

Use Bedrock's native outputConfig.textFormat API for supported models:

  • Guaranteed JSON schema compliance via token-level constrained generation
  • Direct API support without conversion overhead
  • Single-pass generation
  • Better performance and reliability

PR fix notes

PR #20753: Feat/bedrock structured outputs

Description (problem / solution / changelog)

Description

AWS Bedrock launched native structured outputs in February 2026 via the outputConfig.textFormat parameter. This feature adds native support for structured outputs to the llama-index-llms-bedrock-converse integration, replacing the function calling workaround for supported models.

Changes:

  • Add BEDROCK_STRUCTURED_OUTPUT_SUPPORTED_MODELS tuple with supported models (Claude 4.5+, Amazon Nova, DeepSeek, Mistral)
  • Add is_bedrock_structured_output_supported() function with region prefix support
  • Implement structured_predict() and astructured_predict() methods
  • Fall back to function calling for unsupported models (backward compatible)
  • Add 4 unit tests for model support detection

Supported Models:

  • Claude 4.5+ (haiku, sonnet, opus)
  • Amazon Nova (pro, lite, micro)
  • DeepSeek R1
  • Mistral Large

Fixes #20703

New Package?

  • Yes
  • No

Version Bump?

  • Yes
  • No

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

  • I added new unit tests to cover this change
  • I believe this change is already covered by existing unit tests

Suggested Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added Google Colab support for the newly added notebooks.
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I ran uv run make format; uv run make lint to appease the lint gods

Changed files

  • llama-index-integrations/llms/llama-index-llms-bedrock-converse/llama_index/llms/bedrock_converse/base.py (modified, +178/-14)
  • llama-index-integrations/llms/llama-index-llms-bedrock-converse/llama_index/llms/bedrock_converse/utils.py (modified, +34/-0)
  • llama-index-integrations/llms/llama-index-llms-bedrock-converse/pyproject.toml (modified, +1/-1)
  • llama-index-integrations/llms/llama-index-llms-bedrock-converse/tests/test_llms_bedrock_converse.py (modified, +59/-0)
  • llama-index-integrations/llms/llama-index-llms-bedrock-converse/tests/test_structured_output.py (added, +51/-0)

PR #20769: Feat/bedrock structured outputs v2

Description (problem / solution / changelog)

Description

AWS Bedrock launched native structured outputs in February 2026 via the outputConfig.textFormat parameter. This feature adds native support for structured outputs to the llama-index-llms-bedrock-converse integration, replacing the function calling workaround for supported models.

Changes:

  • Add BEDROCK_STRUCTURED_OUTPUT_SUPPORTED_MODELS tuple with supported models (Claude 4.5+, Amazon Nova, DeepSeek, Mistral)
  • Add is_bedrock_structured_output_supported() function with region prefix support
  • Implement structured_predict() and astructured_predict() methods
  • Fall back to function calling for unsupported models (backward compatible)
  • Add 4 unit tests for model support detection

Supported Models:

  • Claude 4.5+ (haiku, sonnet, opus)
  • Amazon Nova (pro, lite, micro)
  • DeepSeek R1
  • Mistral Large

Fixes #20703

New Package?

  • Yes
  • No

Version Bump?

  • Yes
  • No

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

  • I added new unit tests to cover this change
  • I believe this change is already covered by existing unit tests

Suggested Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added Google Colab support for the newly added notebooks.
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I ran uv run make format; uv run make lint to appease the lint gods

Changed files

  • llama-index-integrations/llms/llama-index-llms-bedrock-converse/llama_index/llms/bedrock_converse/base.py (modified, +139/-0)
  • llama-index-integrations/llms/llama-index-llms-bedrock-converse/llama_index/llms/bedrock_converse/utils.py (modified, +34/-0)
  • llama-index-integrations/llms/llama-index-llms-bedrock-converse/pyproject.toml (modified, +1/-1)
  • llama-index-integrations/llms/llama-index-llms-bedrock-converse/tests/test_llms_bedrock_converse.py (modified, +59/-0)
  • llama-index-integrations/llms/llama-index-llms-bedrock-converse/tests/test_structured_output.py (added, +51/-0)

Code Example

BEDROCK_STRUCTURED_OUTPUT_SUPPORTED_MODELS = (
    # Claude 4.5+
    "anthropic.claude-haiku-4-5-20251001-v1:0",
    "anthropic.claude-sonnet-4-5-20250929-v1:0",
    "anthropic.claude-opus-4-5-20251101-v1:0",
    "anthropic.claude-opus-4-6-v1",
    # + Qwen, OpenAI OSS, DeepSeek, Google Gemma, MiniMax, Mistral, Moonshot, NVIDIA
    # See: https://docs.aws.amazon.com/bedrock/latest/userguide/structured-output.html#structured-output-models
)

def is_bedrock_structured_output_supported(model_name: str) -> bool:
    return get_model_name(model_name) in BEDROCK_STRUCTURED_OUTPUT_SUPPORTED_MODELS

---

def structured_predict(self, output_cls, prompt, llm_kwargs=None, **prompt_args):
    llm_kwargs = llm_kwargs or {}

    # Fall back to function calling if not supported
    if (self.pydantic_program_mode != PydanticProgramMode.DEFAULT
        or not is_bedrock_structured_output_supported(self.model)):
        return super().structured_predict(output_cls, prompt, llm_kwargs, **prompt_args)

    # Use native structured outputs
    json_schema = output_cls.model_json_schema()
    messages = prompt.format_messages(**prompt_args)
    converse_messages, system_prompt = messages_to_converse_messages(messages, self.model)

    all_kwargs = self._get_all_kwargs(**llm_kwargs)
    all_kwargs["outputConfig"] = {
        "textFormat": {
            "type": "json_schema",
            "structure": {
                "jsonSchema": {
                    "schema": json.dumps(json_schema),
                    "name": output_cls.__name__,
                    "description": output_cls.__doc__ or f"Schema for {output_cls.__name__}"
                }
            }
        }
    }

    response = converse_with_retry(
        client=self._client,
        messages=converse_messages,
        system_prompt=system_prompt,
        system_prompt_caching=self.system_prompt_caching,
        max_retries=self.max_retries,
        stream=False,
        **all_kwargs,
    )

    blocks, _, _, _ = self._get_content_and_tool_calls(response)
    text_content = next((b.text for b in blocks if isinstance(b, TextBlock)), "")
    return output_cls.model_validate_json(text_content)
RAW_BUFFERClick to expand / collapse

Summary

AWS Bedrock launched native structured outputs in February 2026 via the outputConfig.textFormat parameter. LlamaIndex's llama-index-llms-bedrock-converse currently uses function calling as a workaround. This should be updated to use the native API for better reliability and performance.

Current Behavior

BedrockConverse inherits structured_predict() from the base LLM class, which:

  1. Wraps Pydantic schemas in FunctionCallingProgram
  2. Converts schemas to tool definitions
  3. Forces the model to "call" a tool with tool_required=True
  4. Extracts tool call parameters as structured output

Code path: BedrockConverseLLM.structured_predict()get_program_for_llm()FunctionCallingProgram

Location: llama-index-core/llama_index/core/llms/llm.py:307-364

Problem

Function calling is designed for tool invocation, not data extraction:

  • Semantic mismatch (using tools for JSON extraction)
  • Unnecessary overhead (tool calling infrastructure)
  • Indirect validation (through tool parameters, not JSON grammar)
  • Additional latency (multiple conversion steps)
  • No guarantee of schema compliance

Solution

Use Bedrock's native outputConfig.textFormat API for supported models:

  • Guaranteed JSON schema compliance via token-level constrained generation
  • Direct API support without conversion overhead
  • Single-pass generation
  • Better performance and reliability

Proposed Changes

1. Add model support check (utils.py)

BEDROCK_STRUCTURED_OUTPUT_SUPPORTED_MODELS = (
    # Claude 4.5+
    "anthropic.claude-haiku-4-5-20251001-v1:0",
    "anthropic.claude-sonnet-4-5-20250929-v1:0",
    "anthropic.claude-opus-4-5-20251101-v1:0",
    "anthropic.claude-opus-4-6-v1",
    # + Qwen, OpenAI OSS, DeepSeek, Google Gemma, MiniMax, Mistral, Moonshot, NVIDIA
    # See: https://docs.aws.amazon.com/bedrock/latest/userguide/structured-output.html#structured-output-models
)

def is_bedrock_structured_output_supported(model_name: str) -> bool:
    return get_model_name(model_name) in BEDROCK_STRUCTURED_OUTPUT_SUPPORTED_MODELS

2. Override structured_predict() methods (base.py)

def structured_predict(self, output_cls, prompt, llm_kwargs=None, **prompt_args):
    llm_kwargs = llm_kwargs or {}

    # Fall back to function calling if not supported
    if (self.pydantic_program_mode != PydanticProgramMode.DEFAULT
        or not is_bedrock_structured_output_supported(self.model)):
        return super().structured_predict(output_cls, prompt, llm_kwargs, **prompt_args)

    # Use native structured outputs
    json_schema = output_cls.model_json_schema()
    messages = prompt.format_messages(**prompt_args)
    converse_messages, system_prompt = messages_to_converse_messages(messages, self.model)

    all_kwargs = self._get_all_kwargs(**llm_kwargs)
    all_kwargs["outputConfig"] = {
        "textFormat": {
            "type": "json_schema",
            "structure": {
                "jsonSchema": {
                    "schema": json.dumps(json_schema),
                    "name": output_cls.__name__,
                    "description": output_cls.__doc__ or f"Schema for {output_cls.__name__}"
                }
            }
        }
    }

    response = converse_with_retry(
        client=self._client,
        messages=converse_messages,
        system_prompt=system_prompt,
        system_prompt_caching=self.system_prompt_caching,
        max_retries=self.max_retries,
        stream=False,
        **all_kwargs,
    )

    blocks, _, _, _ = self._get_content_and_tool_calls(response)
    text_content = next((b.text for b in blocks if isinstance(b, TextBlock)), "")
    return output_cls.model_validate_json(text_content)

Also implement: astructured_predict(), stream_structured_predict(), astream_structured_predict()

3. Tests and docs

  • Unit tests for model support detection
  • Integration tests with Bedrock API
  • Fallback tests for unsupported models
  • README examples

References

Notes

  • Falls back to function calling for unsupported models
  • No breaking changes to existing API
  • Incompatible with citations on Anthropic models (per AWS docs)
  • Handles region-prefixed model names (e.g., us.anthropic.claude-...)

Related to #18392

extent analysis

Fix Plan

1. Add model support check (utils.py)

BEDROCK_STRUCTURED_OUTPUT_SUPPORTED_MODELS = (
    # Claude 4.5+
    "anthropic.claude-haiku-4-5-20251001-v1:0",
    "anthropic.claude-sonnet-4-5-20250929-v1:0",
    "anthropic.claude-opus-4-5-20251101-v1:0",
    "anthropic.claude-opus-4-6-v1",
    # + Qwen, OpenAI OSS, DeepSeek, Google Gemma, MiniMax, Mistral, Moonshot, NVIDIA
    # See: https://docs.aws.amazon.com/bedrock/latest/userguide/structured-output.html#structured-output-models
)

def is_bedrock_structured_output_supported(model_name: str) -> bool:
    return get_model_name(model_name) in BEDROCK_STRUCTURED_OUTPUT_SUPPORTED_MODELS

2. Override structured_predict() methods (base.py)

def structured_predict(self, output_cls, prompt, llm_kwargs=None, **prompt_args):
    # ... (rest of the method remains the same)

def astructured_predict(self, output_cls, prompt, llm_kwargs=None, **prompt_args):
    # ... (rest of the method remains the same)

def stream_structured_predict(self, output_cls, prompt, llm_kwargs=None, **prompt_args):
    # ... (rest of the method remains the same)

def astream_structured_predict(self, output_cls, prompt, llm_kwargs=None, **prompt_args):
    # ... (rest of the method remains the same)

3. Implement messages_to_converse_messages() and _get_all_kwargs() functions

def messages_to_converse_messages(messages, model):
    #

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING