Skip to content
1 change: 0 additions & 1 deletion litellm/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -1083,7 +1083,6 @@
"openai.gpt-oss-120b-1:0",
"anthropic.claude-haiku-4-5-20251001-v1:0",
"anthropic.claude-sonnet-4-5-20250929-v1:0",
"anthropic.claude-opus-4-6-v1:0",
"anthropic.claude-opus-4-6-v1",
"anthropic.claude-sonnet-4-6",
"anthropic.claude-opus-4-1-20250805-v1:0",
Expand Down
28 changes: 20 additions & 8 deletions litellm/integrations/websearch_interception/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,8 +697,10 @@ async def _execute_search(self, query: str) -> str:
)
llm_router = None

# Determine search provider from router's search_tools
# Determine search provider and credentials from router's search_tools
search_provider: Optional[str] = None
api_key: Optional[str] = None
api_base: Optional[str] = None
if llm_router is not None and hasattr(llm_router, "search_tools"):
if self.search_tool_name:
# Find specific search tool by name
Expand All @@ -709,9 +711,10 @@ async def _execute_search(self, query: str) -> str:
]
if matching_tools:
search_tool = matching_tools[0]
search_provider = search_tool.get("litellm_params", {}).get(
"search_provider"
)
litellm_params = search_tool.get("litellm_params", {})
search_provider = litellm_params.get("search_provider")
api_key = litellm_params.get("api_key")
api_base = litellm_params.get("api_base")
verbose_logger.debug(
f"WebSearchInterception: Found search tool '{self.search_tool_name}' "
f"with provider '{search_provider}'"
Expand All @@ -725,9 +728,10 @@ async def _execute_search(self, query: str) -> str:
# If no specific tool or not found, use first available
if not search_provider and llm_router.search_tools:
first_tool = llm_router.search_tools[0]
search_provider = first_tool.get("litellm_params", {}).get(
"search_provider"
)
litellm_params = first_tool.get("litellm_params", {})
search_provider = litellm_params.get("search_provider")
api_key = litellm_params.get("api_key")
api_base = litellm_params.get("api_base")
verbose_logger.debug(
f"WebSearchInterception: Using first available search tool with provider '{search_provider}'"
)
Expand All @@ -743,7 +747,15 @@ async def _execute_search(self, query: str) -> str:
verbose_logger.debug(
f"WebSearchInterception: Executing search for '{query}' using provider '{search_provider}'"
)
result = await litellm.asearch(query=query, search_provider=search_provider)
search_kwargs: Dict[str, Any] = {
"query": query,
"search_provider": search_provider,
}
if api_key:
search_kwargs["api_key"] = api_key
if api_base:
search_kwargs["api_base"] = api_base
result = await litellm.asearch(**search_kwargs)

# Format using transformation function
search_result_text = WebSearchTransformation.format_search_response(result)
Expand Down
10 changes: 8 additions & 2 deletions litellm/llms/anthropic/chat/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
any_assistant_message_has_thinking_blocks,
get_max_tokens,
has_tool_call_blocks,
last_assistant_message_has_no_thinking_blocks,
last_assistant_with_tool_calls_has_no_thinking_blocks,
supports_reasoning,
token_counter,
Expand Down Expand Up @@ -1341,7 +1342,9 @@ def transform_request(
)

# Drop thinking param if thinking is enabled but thinking_blocks are missing
# This prevents the error: "Expected thinking or redacted_thinking, but found tool_use"
# This prevents Anthropic errors:
# - "Expected thinking or redacted_thinking, but found tool_use" (assistant with tool_calls)
# - "Expected thinking or redacted_thinking, but found text" (assistant with text content)
#
# IMPORTANT: Only drop thinking if NO assistant messages have thinking_blocks.
# If any message has thinking_blocks, we must keep thinking enabled, otherwise
Expand All @@ -1350,7 +1353,10 @@ def transform_request(
if (
optional_params.get("thinking") is not None
and messages is not None
and last_assistant_with_tool_calls_has_no_thinking_blocks(messages)
and (
last_assistant_with_tool_calls_has_no_thinking_blocks(messages)
or last_assistant_message_has_no_thinking_blocks(messages)
)
and not any_assistant_message_has_thinking_blocks(messages)
):
if litellm.modify_params:
Expand Down
3 changes: 2 additions & 1 deletion litellm/llms/base_llm/chat/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ def get_json_schema_from_pydantic_object(
return type_to_response_format_param(response_format=response_format)

def is_thinking_enabled(self, non_default_params: dict) -> bool:
thinking_type = non_default_params.get("thinking", {}).get("type")
return (
non_default_params.get("thinking", {}).get("type") == "enabled"
thinking_type in ("enabled", "adaptive")
or non_default_params.get("reasoning_effort") is not None
)

Expand Down
67 changes: 67 additions & 0 deletions litellm/llms/bedrock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# AWS Bedrock Provider

This directory contains the AWS Bedrock provider implementation for LiteLLM.

## Beta Headers Management

### Overview

Bedrock anthropic-beta header handling uses a centralized whitelist-based filter (`beta_headers_config.py`) across all three Bedrock APIs to ensure:
- Only supported headers reach AWS (prevents API errors)
- Consistent behavior across Invoke Chat, Invoke Messages, and Converse APIs
- Zero maintenance when new Claude models are released

### Key Features

1. **Version-Based Filtering**: Headers specify minimum version (e.g., "requires Claude 4.5+") instead of hardcoded model lists
2. **Family Restrictions**: Can limit headers to specific families (opus/sonnet/haiku)
3. **Automatic Translation**: `advanced-tool-use` → `tool-search-tool` + `tool-examples` for backward compatibility

### Adding New Beta Headers

When AWS Bedrock adds support for a new Anthropic beta header, update `beta_headers_config.py`:

```python
# 1. Add to whitelist
BEDROCK_CORE_SUPPORTED_BETAS.add("new-feature-2027-01-15")

# 2. (Optional) Add version requirement
BETA_HEADER_MINIMUM_VERSION["new-feature-2027-01-15"] = 5.0

# 3. (Optional) Add family restriction
BETA_HEADER_FAMILY_RESTRICTIONS["new-feature-2027-01-15"] = ["opus"]
```

Then add tests in `tests/test_litellm/llms/bedrock/test_beta_headers_config.py`.

### Adding New Claude Models

When Anthropic releases new models (e.g., Claude Opus 5):
- **Required code changes**: ZERO ✅
- The version-based filter automatically handles new models
- No hardcoded lists to update

### Testing

```bash
# Test beta headers filtering
poetry run pytest tests/test_litellm/llms/bedrock/test_beta_headers_config.py -v

# Test API integrations
poetry run pytest tests/test_litellm/llms/bedrock/test_anthropic_beta_support.py -v

# Test everything
poetry run pytest tests/test_litellm/llms/bedrock/ -v
```

### Debug Logging

Enable debug logging to see filtering decisions:
```bash
LITELLM_LOG=DEBUG
```

### References

- [AWS Bedrock Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages-request-response.html)
- [Anthropic Beta Headers](https://docs.anthropic.com/claude/reference/versioning)
Loading
Loading