Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,11 @@ def transform_request(
_anthropic_request.pop("stream", None)
# Bedrock Invoke doesn't support output_format parameter
_anthropic_request.pop("output_format", None)
# Bedrock Invoke doesn't support output_config parameter
# Fixes: https://github.com/BerriAI/litellm/issues/22797
_anthropic_request.pop("output_config", None)
# Bedrock Invoke supports output_config (effort) for Claude 4.6+ models,
# but older models do not — strip it to avoid request rejection.
# Ref: https://github.com/BerriAI/litellm/issues/22797
if not AnthropicConfig._is_claude_4_6_model(model):
_anthropic_request.pop("output_config", None)
Comment on lines +113 to +114
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Hardcoded model check violates AGENTS.md rule #8

_is_claude_4_6_model is a hardcoded string-match check. Per the project's custom rule and AGENTS.md §8 ("Do not hardcode model-specific flags"), model capability decisions must be driven by model_prices_and_context_window.json read via get_model_info (or an existing helper like supports_reasoning). Without this, any future model that gains output_config.effort support on Bedrock Invoke requires a code change here before users benefit — exactly the problem the rule was written to avoid.

All Claude 4.6 models already carry "supports_reasoning": true in the model-prices JSON, so supports_reasoning (or a new supports_output_config_effort flag added to the JSON) could drive this decision without touching the code for future models.

# Option A — reuse existing supports_reasoning helper (reads from model_prices_and_context_window.json)
from litellm.utils import supports_reasoning

if not (AnthropicConfig._is_claude_4_6_model(model) or supports_reasoning(model=model, custom_llm_provider=self.custom_llm_provider)):
    _anthropic_request.pop("output_config", None)

This same violation also exists at litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py:425.

Rule Used: What: Do not hardcode model-specific flags in the ... (source)

if "anthropic_version" not in _anthropic_request:
_anthropic_request["anthropic_version"] = self.anthropic_version

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,11 @@ def transform_anthropic_messages_request(
anthropic_messages_request=anthropic_messages_request,
)

# 5b. Strip `output_config` — Bedrock Invoke doesn't support it
# Fixes: https://github.com/BerriAI/litellm/issues/22797
anthropic_messages_request.pop("output_config", None)
# 5b. Bedrock Invoke supports output_config (effort) for Claude 4.6+ models,
# but older models do not — strip it to avoid request rejection.
# Ref: https://github.com/BerriAI/litellm/issues/22797
if not AnthropicModelInfo._is_claude_4_6_model(model):
anthropic_messages_request.pop("output_config", None)

# 5a. Remove `custom` field from tools (Bedrock doesn't support it)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Comment numbering out of order (5b before 5a)

The comment label # 5b. on line 422 appears in the file before # 5a. on line 428, which reverses the expected ordering and makes the numbering misleading when reading the code sequentially.

Suggested change
# 5a. Remove `custom` field from tools (Bedrock doesn't support it)
# 5a. Remove `custom` field from tools (Bedrock doesn't support it)

# Claude Code sends `custom: {defer_loading: true}` on tool definitions,
Expand Down
2 changes: 1 addition & 1 deletion litellm/types/llms/anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class AnthropicOutputSchema(TypedDict, total=False):
class AnthropicOutputConfig(TypedDict, total=False):
"""Configuration for controlling Claude's output behavior."""

effort: Literal["high", "medium", "low"]
effort: Literal["high", "medium", "low", "max"]


class AnthropicMessagesTool(TypedDict, total=False):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,82 @@ def test_output_config_removed_from_bedrock_chat_invoke_request():
assert result["max_tokens"] == 100


def test_bedrock_chat_invoke_preserves_output_config_for_claude_4_6():
"""
Claude 4.6 models support output_config.effort on Bedrock Invoke.
Verify that output_config is preserved (not stripped) for these models
on the /v1/chat/completions -> Bedrock Invoke path.
"""
config = AmazonAnthropicClaudeConfig()
messages = [{"role": "user", "content": "test"}]

# Sonnet 4.6 supports effort=high (not max)
optional_params = {
"max_tokens": 128000,
"thinking": {"type": "adaptive"},
"output_config": {"effort": "high"},
}
result = config.transform_request(
model="global.anthropic.claude-sonnet-4-6",
messages=messages,
optional_params=optional_params,
litellm_params={},
headers={},
)
assert "output_config" in result, (
"output_config should be preserved for Claude Sonnet 4.6"
)
assert result["output_config"]["effort"] == "high"

# Opus 4.6 supports effort=max
optional_params = {
"max_tokens": 128000,
"thinking": {"type": "adaptive"},
"output_config": {"effort": "max"},
}
result = config.transform_request(
model="global.anthropic.claude-opus-4-6-v1",
messages=messages,
optional_params=optional_params,
litellm_params={},
headers={},
)
assert "output_config" in result, (
"output_config should be preserved for Claude Opus 4.6"
)
assert result["output_config"]["effort"] == "max"


def test_bedrock_chat_invoke_strips_output_config_for_pre_4_6():
"""
Pre-4.6 models do not support output_config on Bedrock Invoke.
Verify that output_config is still stripped for these models.
"""
config = AmazonAnthropicClaudeConfig()
messages = [{"role": "user", "content": "test"}]

for model_id in [
"anthropic.claude-sonnet-4-20250514-v1:0",
"global.anthropic.claude-opus-4-5-20251101-v1:0",
]:
optional_params = {
"max_tokens": 100,
"output_config": {"effort": "high"},
}

result = config.transform_request(
model=model_id,
messages=messages,
optional_params=optional_params,
litellm_params={},
headers={},
)

assert "output_config" not in result, (
f"output_config should be stripped for pre-4.6 model {model_id}"
)


def test_output_format_removed_from_bedrock_invoke_request():
"""
Test that output_format parameter is removed from Bedrock Invoke requests.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,90 @@ def test_bedrock_messages_strips_output_config_with_output_format():

assert "output_config" not in result
assert "output_format" not in result


def test_bedrock_messages_preserves_output_config_for_claude_4_6():
"""
Claude 4.6 models support output_config.effort on Bedrock Invoke.
Verify that output_config is preserved (not stripped) for these models.
"""
from litellm.types.router import GenericLiteLLMParams

cfg = AmazonAnthropicClaudeMessagesConfig()
messages = [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}]

# Opus 4.6 supports effort=max
for model_id in [
"global.anthropic.claude-opus-4-6-v1",
"anthropic.claude-opus-4-6-v1:0",
]:
optional_params = {
"max_tokens": 128000,
"thinking": {"type": "adaptive"},
"output_config": {"effort": "max"},
}

result = cfg.transform_anthropic_messages_request(
model=model_id,
messages=messages,
anthropic_messages_optional_request_params=optional_params,
litellm_params=GenericLiteLLMParams(),
headers={},
)

assert "output_config" in result, (
f"output_config should be preserved for Claude 4.6 model {model_id}"
)
assert result["output_config"]["effort"] == "max"
assert result["thinking"]["type"] == "adaptive"

# Sonnet 4.6 supports effort=high (not max)
optional_params = {
"max_tokens": 128000,
"thinking": {"type": "adaptive"},
"output_config": {"effort": "high"},
}
result = cfg.transform_anthropic_messages_request(
model="global.anthropic.claude-sonnet-4-6",
messages=messages,
anthropic_messages_optional_request_params=optional_params,
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert "output_config" in result, (
"output_config should be preserved for Claude Sonnet 4.6"
)
assert result["output_config"]["effort"] == "high"


def test_bedrock_messages_strips_output_config_for_pre_4_6():
"""
Pre-4.6 models do not support output_config on Bedrock Invoke.
Verify that output_config is stripped for these models.
"""
from litellm.types.router import GenericLiteLLMParams

cfg = AmazonAnthropicClaudeMessagesConfig()
messages = [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}]

for model_id in [
"anthropic.claude-3-haiku-20240307-v1:0",
"anthropic.claude-sonnet-4-20250514-v1:0",
"global.anthropic.claude-opus-4-5-20251101-v1:0",
]:
optional_params = {
"max_tokens": 4096,
"output_config": {"effort": "high"},
}

result = cfg.transform_anthropic_messages_request(
model=model_id,
messages=messages,
anthropic_messages_optional_request_params=optional_params,
litellm_params=GenericLiteLLMParams(),
headers={},
)

assert "output_config" not in result, (
f"output_config should be stripped for pre-4.6 model {model_id}"
)
Loading