mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-07 10:21:54 +00:00
feat: Add support for custom Anthropic-compatible API endpoints
This commit adds support for custom Anthropic-compatible API endpoints that don't follow the standard /v1/messages or /v1/complete path convention. ## Changes - Added LITELLM_ANTHROPIC_DISABLE_URL_SUFFIX environment variable - When set to true, prevents automatic appending of /v1/messages (for anthropic) - When set to true, prevents automatic appending of /v1/complete (for anthropic_text) - Added debug logging to indicate when suffix is being skipped - Maintained full backward compatibility - existing deployments are unaffected
This commit is contained in:
@@ -1121,4 +1121,124 @@ async def test_retrying() -> None:
|
||||
model="gpt-4o-mini",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
)
|
||||
assert mock_request.call_count >= 10, "Expected retrying to be used"
|
||||
|
||||
|
||||
def test_anthropic_disable_url_suffix_env_var():
|
||||
"""Test that LITELLM_ANTHROPIC_DISABLE_URL_SUFFIX prevents /v1/messages suffix."""
|
||||
from unittest.mock import patch, MagicMock
|
||||
import os
|
||||
from litellm import completion
|
||||
from litellm.llms.anthropic.chat.handler import AnthropicLLM
|
||||
|
||||
# Test with environment variable disabled (default behavior)
|
||||
with patch.dict(os.environ, {"ANTHROPIC_API_BASE": "https://api.example.com"}):
|
||||
with patch.object(AnthropicLLM, "__init__", return_value=None) as mock_init:
|
||||
with patch.object(AnthropicLLM, "completion") as mock_completion:
|
||||
mock_completion.return_value = MagicMock()
|
||||
|
||||
# Mock the initialization to capture api_base
|
||||
actual_api_base = None
|
||||
def capture_init(self, **kwargs):
|
||||
nonlocal actual_api_base
|
||||
actual_api_base = kwargs.get("api_base")
|
||||
|
||||
with patch("litellm.main.anthropic_chat_completions") as mock_anthropic:
|
||||
def capture_completion(**kwargs):
|
||||
nonlocal actual_api_base
|
||||
actual_api_base = kwargs.get("api_base")
|
||||
return MagicMock()
|
||||
|
||||
mock_anthropic.completion = capture_completion
|
||||
|
||||
# This should append /v1/messages
|
||||
completion(
|
||||
model="anthropic/claude-3-sonnet",
|
||||
messages=[{"role": "user", "content": "test"}],
|
||||
api_key="test-key"
|
||||
)
|
||||
|
||||
# Verify the api_base has /v1/messages appended
|
||||
assert actual_api_base.endswith("/v1/messages")
|
||||
assert actual_api_base == "https://api.example.com/v1/messages"
|
||||
|
||||
# Test with environment variable enabled
|
||||
with patch.dict(os.environ, {
|
||||
"ANTHROPIC_API_BASE": "https://api.example.com/custom/path",
|
||||
"LITELLM_ANTHROPIC_DISABLE_URL_SUFFIX": "true"
|
||||
}):
|
||||
actual_api_base = None
|
||||
|
||||
with patch("litellm.main.anthropic_chat_completions") as mock_anthropic:
|
||||
def capture_completion(**kwargs):
|
||||
nonlocal actual_api_base
|
||||
actual_api_base = kwargs.get("api_base")
|
||||
return MagicMock()
|
||||
|
||||
mock_anthropic.completion = capture_completion
|
||||
|
||||
# This should NOT append /v1/messages
|
||||
completion(
|
||||
model="anthropic/claude-3-sonnet",
|
||||
messages=[{"role": "user", "content": "test"}],
|
||||
api_key="test-key"
|
||||
)
|
||||
|
||||
# Verify the api_base does not have /v1/messages appended
|
||||
assert actual_api_base == "https://api.example.com/custom/path"
|
||||
assert not actual_api_base.endswith("/v1/messages")
|
||||
|
||||
|
||||
def test_anthropic_text_disable_url_suffix_env_var():
|
||||
"""Test that LITELLM_ANTHROPIC_DISABLE_URL_SUFFIX prevents /v1/complete suffix for anthropic_text."""
|
||||
from unittest.mock import patch, MagicMock
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
# Test with environment variable disabled (default behavior)
|
||||
with patch.dict(os.environ, {"ANTHROPIC_API_BASE": "https://api.example.com"}):
|
||||
actual_api_base = None
|
||||
|
||||
with patch("litellm.main.base_llm_http_handler") as mock_handler:
|
||||
def capture_completion(**kwargs):
|
||||
nonlocal actual_api_base
|
||||
actual_api_base = kwargs.get("api_base")
|
||||
return MagicMock()
|
||||
|
||||
mock_handler.completion = capture_completion
|
||||
|
||||
# This should append /v1/complete
|
||||
completion(
|
||||
model="anthropic_text/claude-instant-1",
|
||||
messages=[{"role": "user", "content": "test"}],
|
||||
api_key="test-key"
|
||||
)
|
||||
|
||||
# Verify the api_base has /v1/complete appended
|
||||
assert actual_api_base.endswith("/v1/complete")
|
||||
assert actual_api_base == "https://api.example.com/v1/complete"
|
||||
|
||||
# Test with environment variable enabled
|
||||
with patch.dict(os.environ, {
|
||||
"ANTHROPIC_API_BASE": "https://api.example.com/custom/complete",
|
||||
"LITELLM_ANTHROPIC_DISABLE_URL_SUFFIX": "true"
|
||||
}):
|
||||
actual_api_base = None
|
||||
|
||||
with patch("litellm.main.base_llm_http_handler") as mock_handler:
|
||||
def capture_completion(**kwargs):
|
||||
nonlocal actual_api_base
|
||||
actual_api_base = kwargs.get("api_base")
|
||||
return MagicMock()
|
||||
|
||||
mock_handler.completion = capture_completion
|
||||
|
||||
# This should NOT append /v1/complete
|
||||
completion(
|
||||
model="anthropic_text/claude-instant-1",
|
||||
messages=[{"role": "user", "content": "test"}],
|
||||
api_key="test-key"
|
||||
)
|
||||
|
||||
# Verify the api_base does not have /v1/complete appended
|
||||
assert actual_api_base == "https://api.example.com/custom/complete"
|
||||
assert not actual_api_base.endswith("/v1/complete")
|
||||
|
||||
Reference in New Issue
Block a user