mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
docs: Add proxy configuration guide and examples for custom User-Agent
Related to #19017 This commit adds comprehensive documentation and examples for configuring custom User-Agent headers in LiteLLM proxy for Anthropic requests. Changes: - Added example proxy config: anthropic_custom_user_agent_config.yaml showing all three methods to customize User-Agent - Added detailed README: README_ANTHROPIC_USER_AGENT.md explaining: * Problem statement (Claude Code credential restrictions) * Three configuration methods (per-model, env var, extra_headers) * Priority order for User-Agent resolution * Complete examples for proxy and Python SDK usage - Added comprehensive proxy unit tests in test_anthropic_custom_user_agent.py testing all configuration methods and priority order Users can now configure custom User-Agent in proxy YAML: ```yaml model_list: - model_name: claude-code litellm_params: model: anthropic/claude-3-5-sonnet-20241022 custom_user_agent: "Claude Code/1.0" ``` Or via environment variable: ```bash export ANTHROPIC_USER_AGENT="Claude Code/1.0" ```
This commit is contained in:
parent
509dfd9a5f
commit
055fa040b0
3 changed files with 313 additions and 0 deletions
118
litellm/proxy/example_config_yaml/README_ANTHROPIC_USER_AGENT.md
Normal file
118
litellm/proxy/example_config_yaml/README_ANTHROPIC_USER_AGENT.md
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# Custom User-Agent Configuration for Anthropic
|
||||
|
||||
This guide explains how to configure custom User-Agent headers for Anthropic (Claude) API requests in LiteLLM Proxy.
|
||||
|
||||
## Problem
|
||||
|
||||
By default, LiteLLM adds `User-Agent: litellm/{version}` to all API requests. However, some Anthropic credentials are restricted to specific User-Agent values. For example, Claude Code credentials may return an error:
|
||||
|
||||
```
|
||||
This credential is only authorized for use with Claude Code and cannot be used for other API requests.
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
LiteLLM now supports customizing the User-Agent header for Anthropic requests in three ways:
|
||||
|
||||
### Option 1: Per-Model Configuration (Recommended)
|
||||
|
||||
Add `custom_user_agent` to your model's `litellm_params` in the proxy config YAML:
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: claude-code
|
||||
litellm_params:
|
||||
model: anthropic/claude-3-5-sonnet-20241022
|
||||
api_key: os.environ/ANTHROPIC_API_KEY
|
||||
custom_user_agent: "Claude Code/1.0"
|
||||
```
|
||||
|
||||
### Option 2: Global Environment Variable
|
||||
|
||||
Set the `ANTHROPIC_USER_AGENT` environment variable to apply a custom User-Agent to all Anthropic requests:
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_USER_AGENT="Claude Code/1.0"
|
||||
```
|
||||
|
||||
Then start your proxy:
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
### Option 3: Via Extra Headers
|
||||
|
||||
You can also set the User-Agent through `extra_headers`:
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: claude-code
|
||||
litellm_params:
|
||||
model: anthropic/claude-3-5-sonnet-20241022
|
||||
api_key: os.environ/ANTHROPIC_API_KEY
|
||||
extra_headers:
|
||||
User-Agent: "Claude Code/1.0"
|
||||
```
|
||||
|
||||
## Priority Order
|
||||
|
||||
If multiple User-Agent configurations are present, they are applied in this priority order:
|
||||
|
||||
1. **`custom_user_agent` parameter** (highest priority)
|
||||
2. **`ANTHROPIC_USER_AGENT` environment variable**
|
||||
3. **`User-Agent` in `extra_headers`**
|
||||
4. **Default `litellm/{version}`** (lowest priority)
|
||||
|
||||
## Complete Example
|
||||
|
||||
See [`anthropic_custom_user_agent_config.yaml`](./anthropic_custom_user_agent_config.yaml) for a complete working example.
|
||||
|
||||
## Testing
|
||||
|
||||
To verify your configuration works:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:4000/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-proxy-key" \
|
||||
-d '{
|
||||
"model": "claude-code",
|
||||
"messages": [{"role": "user", "content": "Hello!"}]
|
||||
}'
|
||||
```
|
||||
|
||||
## Python SDK Usage
|
||||
|
||||
This feature also works when using LiteLLM as a Python SDK:
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
# Option 1: Via parameter
|
||||
response = litellm.completion(
|
||||
model="anthropic/claude-3-5-sonnet-20241022",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
custom_user_agent="Claude Code/1.0"
|
||||
)
|
||||
|
||||
# Option 2: Via environment variable
|
||||
import os
|
||||
os.environ["ANTHROPIC_USER_AGENT"] = "Claude Code/1.0"
|
||||
response = litellm.completion(
|
||||
model="anthropic/claude-3-5-sonnet-20241022",
|
||||
messages=[{"role": "user", "content": "Hello"}]
|
||||
)
|
||||
|
||||
# Option 3: Via extra_headers
|
||||
response = litellm.completion(
|
||||
model="anthropic/claude-3-5-sonnet-20241022",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
extra_headers={"User-Agent": "Claude Code/1.0"}
|
||||
)
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- GitHub Issue: [#19017](https://github.com/BerriAI/litellm/issues/19017)
|
||||
- Anthropic API Documentation: https://docs.anthropic.com/
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# Example configuration for custom User-Agent header with Anthropic
|
||||
#
|
||||
# This is useful when you have Anthropic credentials that are restricted
|
||||
# to specific User-Agent values (e.g., Claude Code credentials)
|
||||
#
|
||||
# See: https://github.com/BerriAI/litellm/issues/19017
|
||||
|
||||
model_list:
|
||||
# Option 1: Set custom User-Agent per model
|
||||
- model_name: claude-code
|
||||
litellm_params:
|
||||
model: anthropic/claude-3-5-sonnet-20241022
|
||||
api_key: os.environ/ANTHROPIC_API_KEY
|
||||
# Custom User-Agent to avoid credential blocks
|
||||
custom_user_agent: "Claude Code/1.0"
|
||||
|
||||
# Option 2: Use default litellm User-Agent (no custom_user_agent specified)
|
||||
- model_name: claude-regular
|
||||
litellm_params:
|
||||
model: anthropic/claude-3-5-sonnet-20241022
|
||||
api_key: os.environ/ANTHROPIC_API_KEY_REGULAR
|
||||
# Will use default "litellm/{version}" User-Agent
|
||||
|
||||
# Option 3: Set via environment variable (affects all Anthropic models)
|
||||
# Set: export ANTHROPIC_USER_AGENT="Claude Code/1.0"
|
||||
- model_name: claude-env
|
||||
litellm_params:
|
||||
model: anthropic/claude-3-5-sonnet-20241022
|
||||
api_key: os.environ/ANTHROPIC_API_KEY
|
||||
# ANTHROPIC_USER_AGENT env var will be used if set
|
||||
|
||||
# Alternative: Use extra_headers for complete control
|
||||
model_list_with_extra_headers:
|
||||
- model_name: claude-extra-headers
|
||||
litellm_params:
|
||||
model: anthropic/claude-3-5-sonnet-20241022
|
||||
api_key: os.environ/ANTHROPIC_API_KEY
|
||||
extra_headers:
|
||||
User-Agent: "Claude Code/1.0"
|
||||
156
tests/proxy_unit_tests/test_anthropic_custom_user_agent.py
Normal file
156
tests/proxy_unit_tests/test_anthropic_custom_user_agent.py
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
"""
|
||||
Test custom User-Agent configuration for Anthropic provider in LiteLLM proxy.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../.."))
|
||||
|
||||
import pytest
|
||||
from litellm.llms.anthropic.common_utils import AnthropicModelInfo
|
||||
from litellm.llms.anthropic.chat.transformation import AnthropicConfig
|
||||
|
||||
|
||||
def test_anthropic_config_supports_custom_user_agent():
|
||||
"""Test that custom_user_agent is in supported params for Anthropic"""
|
||||
config = AnthropicConfig()
|
||||
supported_params = config.get_supported_openai_params("claude-3-5-sonnet-20241022")
|
||||
|
||||
assert "custom_user_agent" in supported_params, \
|
||||
"custom_user_agent should be in supported OpenAI params"
|
||||
|
||||
|
||||
def test_proxy_litellm_params_with_custom_user_agent():
|
||||
"""Test that custom_user_agent from litellm_params is properly set"""
|
||||
config = AnthropicModelInfo()
|
||||
custom_agent = "Claude Code/1.0"
|
||||
|
||||
# Simulate litellm_params from proxy YAML config
|
||||
litellm_params = {
|
||||
"model": "anthropic/claude-3-5-sonnet-20241022",
|
||||
"api_key": "sk-ant-test-key",
|
||||
"custom_user_agent": custom_agent,
|
||||
}
|
||||
|
||||
updated_headers = config.validate_environment(
|
||||
headers={},
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
optional_params={"custom_user_agent": custom_agent},
|
||||
litellm_params={},
|
||||
api_key="sk-ant-test-key",
|
||||
api_base=None,
|
||||
)
|
||||
|
||||
assert updated_headers["User-Agent"] == custom_agent, \
|
||||
f"Expected User-Agent to be '{custom_agent}', got '{updated_headers.get('User-Agent')}'"
|
||||
|
||||
|
||||
def test_proxy_env_var_anthropic_user_agent():
|
||||
"""Test that ANTHROPIC_USER_AGENT environment variable works"""
|
||||
config = AnthropicModelInfo()
|
||||
custom_agent = "Claude Code Proxy/1.0"
|
||||
|
||||
os.environ["ANTHROPIC_USER_AGENT"] = custom_agent
|
||||
|
||||
try:
|
||||
updated_headers = config.validate_environment(
|
||||
headers={},
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
api_key="sk-ant-test-key",
|
||||
api_base=None,
|
||||
)
|
||||
|
||||
assert updated_headers["User-Agent"] == custom_agent, \
|
||||
f"Expected User-Agent to be '{custom_agent}', got '{updated_headers.get('User-Agent')}'"
|
||||
finally:
|
||||
del os.environ["ANTHROPIC_USER_AGENT"]
|
||||
|
||||
|
||||
def test_proxy_extra_headers_user_agent():
|
||||
"""Test that User-Agent can be set via extra_headers"""
|
||||
config = AnthropicModelInfo()
|
||||
custom_agent = "Claude Code via Extra Headers/1.0"
|
||||
|
||||
# Headers would come from extra_headers in litellm_params
|
||||
headers = {"User-Agent": custom_agent}
|
||||
|
||||
updated_headers = config.validate_environment(
|
||||
headers=headers,
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
api_key="sk-ant-test-key",
|
||||
api_base=None,
|
||||
)
|
||||
|
||||
# Should preserve the User-Agent from extra_headers when no custom_user_agent is set
|
||||
assert updated_headers["User-Agent"] == custom_agent, \
|
||||
f"Expected User-Agent to be '{custom_agent}', got '{updated_headers.get('User-Agent')}'"
|
||||
|
||||
|
||||
def test_proxy_priority_order():
|
||||
"""Test priority: custom_user_agent > ANTHROPIC_USER_AGENT > extra_headers"""
|
||||
config = AnthropicModelInfo()
|
||||
|
||||
param_agent = "Param Agent"
|
||||
env_agent = "Env Agent"
|
||||
header_agent = "Header Agent"
|
||||
|
||||
os.environ["ANTHROPIC_USER_AGENT"] = env_agent
|
||||
|
||||
try:
|
||||
# Test 1: custom_user_agent parameter overrides environment variable
|
||||
updated_headers = config.validate_environment(
|
||||
headers={"User-Agent": header_agent},
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
optional_params={"custom_user_agent": param_agent},
|
||||
litellm_params={},
|
||||
api_key="sk-ant-test-key",
|
||||
api_base=None,
|
||||
)
|
||||
|
||||
assert updated_headers["User-Agent"] == param_agent, \
|
||||
"custom_user_agent parameter should have highest priority"
|
||||
|
||||
# Test 2: Environment variable overrides extra_headers
|
||||
updated_headers = config.validate_environment(
|
||||
headers={"User-Agent": header_agent},
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
api_key="sk-ant-test-key",
|
||||
api_base=None,
|
||||
)
|
||||
|
||||
assert updated_headers["User-Agent"] == env_agent, \
|
||||
"ANTHROPIC_USER_AGENT env var should override extra_headers"
|
||||
|
||||
finally:
|
||||
del os.environ["ANTHROPIC_USER_AGENT"]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Running Anthropic custom User-Agent proxy tests...")
|
||||
test_anthropic_config_supports_custom_user_agent()
|
||||
print("✓ Test 1 passed: custom_user_agent is supported")
|
||||
|
||||
test_proxy_litellm_params_with_custom_user_agent()
|
||||
print("✓ Test 2 passed: custom_user_agent from litellm_params works")
|
||||
|
||||
test_proxy_env_var_anthropic_user_agent()
|
||||
print("✓ Test 3 passed: ANTHROPIC_USER_AGENT env var works")
|
||||
|
||||
test_proxy_extra_headers_user_agent()
|
||||
print("✓ Test 4 passed: User-Agent via extra_headers works")
|
||||
|
||||
test_proxy_priority_order()
|
||||
print("✓ Test 5 passed: Priority order is correct")
|
||||
|
||||
print("\n✅ All proxy tests passed!")
|
||||
Loading…
Add table
Reference in a new issue