mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-11 15:05:47 +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:
+22
-2
@@ -2169,8 +2169,18 @@ def completion( # type: ignore # noqa: PLR0915
|
||||
or "https://api.anthropic.com/v1/complete"
|
||||
)
|
||||
|
||||
if api_base is not None and not api_base.endswith("/v1/complete"):
|
||||
# Check if we should disable automatic URL suffix appending
|
||||
disable_url_suffix = get_secret_bool("LITELLM_ANTHROPIC_DISABLE_URL_SUFFIX")
|
||||
if (
|
||||
api_base is not None
|
||||
and not disable_url_suffix
|
||||
and not api_base.endswith("/v1/complete")
|
||||
):
|
||||
api_base += "/v1/complete"
|
||||
elif disable_url_suffix:
|
||||
verbose_logger.debug(
|
||||
"LITELLM_ANTHROPIC_DISABLE_URL_SUFFIX is set, skipping /v1/complete suffix"
|
||||
)
|
||||
|
||||
response = base_llm_http_handler.completion(
|
||||
model=model,
|
||||
@@ -2206,8 +2216,18 @@ def completion( # type: ignore # noqa: PLR0915
|
||||
or "https://api.anthropic.com/v1/messages"
|
||||
)
|
||||
|
||||
if api_base is not None and not api_base.endswith("/v1/messages"):
|
||||
# Check if we should disable automatic URL suffix appending
|
||||
disable_url_suffix = get_secret_bool("LITELLM_ANTHROPIC_DISABLE_URL_SUFFIX")
|
||||
if (
|
||||
api_base is not None
|
||||
and not disable_url_suffix
|
||||
and not api_base.endswith("/v1/messages")
|
||||
):
|
||||
api_base += "/v1/messages"
|
||||
elif disable_url_suffix:
|
||||
verbose_logger.debug(
|
||||
"LITELLM_ANTHROPIC_DISABLE_URL_SUFFIX is set, skipping /v1/messages suffix"
|
||||
)
|
||||
|
||||
response = anthropic_chat_completions.completion(
|
||||
model=model,
|
||||
|
||||
@@ -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