Skip to content
Merged
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 @@ -327,6 +327,81 @@ curl --location 'http://0.0.0.0:4000/v1/messages' \
</TabItem>
</Tabs>

## Usage - Azure Anthropic (Azure Foundry Claude)

LiteLLM funnels Azure Claude deployments through the `azure_ai/` provider so Claude Opus models on Azure Foundry keep working with Tool Search, Effort, streaming, and the rest of the advanced feature set. Point `AZURE_AI_API_BASE` to `https://<resource>.services.ai.azure.com/anthropic` (LiteLLM appends `/v1/messages` automatically) and authenticate with `AZURE_AI_API_KEY` or an Azure AD token.

<Tabs>
<TabItem value="sdk" label="LiteLLM Python SDK">

```python
import os
from litellm import completion

# Configure Azure credentials
os.environ["AZURE_AI_API_KEY"] = "your-azure-ai-api-key"
os.environ["AZURE_AI_API_BASE"] = "https://my-resource.services.ai.azure.com/anthropic"

response = completion(
model="azure_ai/claude-opus-4-1",
messages=[{"role": "user", "content": "Explain how Azure Anthropic hosts Claude Opus differently from the public Anthropic API."}],
max_tokens=1200,
temperature=0.7,
stream=True,
)

for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
```

</TabItem>
<TabItem value="proxy" label="LiteLLM Proxy">

**1. Set environment variables**

```bash
export AZURE_AI_API_KEY="your-azure-ai-api-key"
export AZURE_AI_API_BASE="https://my-resource.services.ai.azure.com/anthropic"
```

**2. Configure the proxy**

```yaml
model_list:
- model_name: claude-4-azure
litellm_params:
model: azure_ai/claude-opus-4-1
api_key: os.environ/AZURE_AI_API_KEY
api_base: os.environ/AZURE_AI_API_BASE
```

**3. Start LiteLLM**

```bash
litellm --config /path/to/config.yaml
```

**4. Test the Azure Claude route**

```bash
curl --location 'http://0.0.0.0:4000/chat/completions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer $LITELLM_KEY' \
--data '{
"model": "claude-4-azure",
"messages": [
{
"role": "user",
"content": "How do I use Claude Opus 4 via Azure Anthropic in LiteLLM?"
}
],
"max_tokens": 1024
}'
```

</TabItem>
</Tabs>


## Tool Search {#tool-search}
Expand Down
18 changes: 9 additions & 9 deletions docs/my-website/docs/providers/azure/azure_anthropic.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Azure Foundry supports the following Claude models:
| Property | Details |
|-------|-------|
| Description | Claude models deployed via Microsoft Azure Foundry. Uses the same API as Anthropic's Messages API but with Azure authentication. |
| Provider Route on LiteLLM | `azure/` (add this prefix to Claude model names - e.g. `azure/claude-sonnet-4-5`) |
| Provider Route on LiteLLM | `azure_ai/` (add this prefix to Claude model names - e.g. `azure_ai/claude-sonnet-4-5`) |
| Provider Doc | [Azure Foundry Claude Models ↗](https://learn.microsoft.com/en-us/azure/ai-services/foundry-models/claude) |
| API Endpoint | `https://<resource-name>.services.ai.azure.com/anthropic/v1/messages` |
| Supported Endpoints | `/chat/completions`, `/anthropic/v1/messages`|
Expand Down Expand Up @@ -68,7 +68,7 @@ os.environ["AZURE_API_BASE"] = "https://<resource-name>.services.ai.azure.com/an

# Make a completion request
response = completion(
model="azure/claude-sonnet-4-5",
model="azure_ai/claude-sonnet-4-5",
messages=[
{"role": "user", "content": "What are 3 things to visit in Seattle?"}
],
Expand All @@ -85,7 +85,7 @@ print(response)
import litellm

response = litellm.completion(
model="azure/claude-sonnet-4-5",
model="azure_ai/claude-sonnet-4-5",
api_base="https://<resource-name>.services.ai.azure.com/anthropic",
api_key="your-azure-api-key",
messages=[
Expand All @@ -101,7 +101,7 @@ response = litellm.completion(
import litellm

response = litellm.completion(
model="azure/claude-sonnet-4-5",
model="azure_ai/claude-sonnet-4-5",
api_base="https://<resource-name>.services.ai.azure.com/anthropic",
azure_ad_token="your-azure-ad-token",
messages=[
Expand All @@ -117,7 +117,7 @@ response = litellm.completion(
from litellm import completion

response = completion(
model="azure/claude-sonnet-4-5",
model="azure_ai/claude-sonnet-4-5",
messages=[
{"role": "user", "content": "Write a short story"}
],
Expand All @@ -136,7 +136,7 @@ for chunk in response:
from litellm import completion

response = completion(
model="azure/claude-sonnet-4-5",
model="azure_ai/claude-sonnet-4-5",
messages=[
{"role": "user", "content": "What's the weather in Seattle?"}
],
Expand Down Expand Up @@ -181,7 +181,7 @@ export AZURE_API_BASE="https://<resource-name>.services.ai.azure.com/anthropic"
model_list:
- model_name: claude-sonnet-4-5
litellm_params:
model: azure/claude-sonnet-4-5
model: azure_ai/claude-sonnet-4-5
api_base: https://<resource-name>.services.ai.azure.com/anthropic
api_key: os.environ/AZURE_API_KEY
```
Expand Down Expand Up @@ -331,7 +331,7 @@ os.environ["AZURE_API_BASE"] = "https://my-resource.services.ai.azure.com/anthro

# Make a request
response = completion(
model="azure/claude-sonnet-4-5",
model="azure_ai/claude-sonnet-4-5",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum computing in simple terms."}
Expand All @@ -358,7 +358,7 @@ Or pass it directly:

```python
response = completion(
model="azure/claude-sonnet-4-5",
model="azure_ai/claude-sonnet-4-5",
api_base="https://<resource-name>.services.ai.azure.com/anthropic",
# ...
)
Expand Down
2 changes: 1 addition & 1 deletion litellm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1120,7 +1120,7 @@ def add_known_models():
from .llms.datarobot.chat.transformation import DataRobotConfig
from .llms.anthropic.chat.transformation import AnthropicConfig
from .llms.anthropic.common_utils import AnthropicModelInfo
from .llms.azure.anthropic.transformation import AzureAnthropicConfig
from .llms.azure_ai.anthropic.transformation import AzureAnthropicConfig
from .llms.groq.stt.transformation import GroqSTTConfig
from .llms.anthropic.completion.transformation import AnthropicTextConfig
from .llms.triton.completion.transformation import TritonConfig
Expand Down
22 changes: 8 additions & 14 deletions litellm/litellm_core_utils/get_llm_provider_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,16 @@ def _is_non_openai_azure_model(model: str) -> bool:
return False


def _is_azure_anthropic_model(model: str) -> Optional[str]:
def _is_azure_claude_model(model: str) -> bool:
"""
Check if a model name contains 'claude' (case-insensitive).
Used to detect Claude models that need Anthropic-specific handling.
"""
try:
model_parts = model.split("/", 1)
if len(model_parts) > 1:
model_name = model_parts[1].lower()
# Check if model name contains claude
if "claude" in model_name or model_name.startswith("claude"):
return model_parts[1] # Return model name without "azure/" prefix
model_lower = model.lower()
return "claude" in model_lower or model_lower.startswith("claude")
except Exception:
pass
return None
return False


def handle_cohere_chat_model_custom_llm_provider(
Expand Down Expand Up @@ -136,11 +135,6 @@ def get_llm_provider( # noqa: PLR0915
# AZURE AI-Studio Logic - Azure AI Studio supports AZURE/Cohere
# If User passes azure/command-r-plus -> we should send it to cohere_chat/command-r-plus
if model.split("/", 1)[0] == "azure":
# Check if it's an Azure Anthropic model (claude models)
azure_anthropic_model = _is_azure_anthropic_model(model)
if azure_anthropic_model:
custom_llm_provider = "azure_anthropic"
return azure_anthropic_model, custom_llm_provider, dynamic_api_key, api_base
if _is_non_openai_azure_model(model):
custom_llm_provider = "openai"
return model, custom_llm_provider, dynamic_api_key, api_base
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import httpx

import litellm
from litellm.llms.anthropic.chat.handler import AnthropicChatCompletion
from litellm.llms.custom_httpx.http_handler import (
AsyncHTTPHandler,
Expand Down Expand Up @@ -55,7 +54,6 @@ def completion(
Completion method that uses Azure authentication instead of Anthropic's x-api-key.
All other logic is the same as AnthropicChatCompletion.
"""
from litellm.utils import ProviderConfigManager

optional_params = copy.deepcopy(optional_params)
stream = optional_params.pop("stream", None)
Expand All @@ -64,8 +62,10 @@ def completion(
_is_function_call = False
messages = copy.deepcopy(messages)

# Use AzureAnthropicConfig instead of AnthropicConfig
headers = AzureAnthropicConfig().validate_environment(
# Use AzureAnthropicConfig for both azure_anthropic and azure_ai Claude models
config = AzureAnthropicConfig()

headers = config.validate_environment(
api_key=api_key,
headers=headers,
model=model,
Expand All @@ -74,15 +74,6 @@ def completion(
litellm_params=litellm_params,
)

config = ProviderConfigManager.get_provider_chat_config(
model=model,
provider=litellm.types.utils.LlmProviders(custom_llm_provider),
)
if config is None:
raise ValueError(
f"Provider config not found for model: {model} and provider: {custom_llm_provider}"
)

data = config.transform_request(
model=model,
messages=messages,
Expand Down Expand Up @@ -183,7 +174,7 @@ def completion(
return CustomStreamWrapper(
completion_stream=completion_stream,
model=model,
custom_llm_provider="azure_anthropic",
custom_llm_provider="azure_ai",
logging_obj=logging_obj,
_response_headers=process_anthropic_headers(response_headers),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class AzureAnthropicConfig(AnthropicConfig):

@property
def custom_llm_provider(self) -> Optional[str]:
return "azure_anthropic"
return "azure_ai"

def validate_environment(
self,
Expand Down Expand Up @@ -94,3 +94,29 @@ def validate_environment(

return headers

def transform_request(
self,
model: str,
messages: List[AllMessageValues],
optional_params: dict,
litellm_params: dict,
headers: dict,
) -> dict:
"""
Transform request using parent AnthropicConfig, then remove extra_body if present.
Azure Anthropic doesn't support extra_body parameter.
"""
# Call parent transform_request
data = super().transform_request(
model=model,
messages=messages,
optional_params=optional_params,
litellm_params=litellm_params,
headers=headers,
)

# Remove extra_body if present (Azure Anthropic doesn't support it)
data.pop("extra_body", None)

return data

Loading
Loading