refactor(llms): address review feedback on parameter rebinding and test placement

This commit is contained in:
Raushankumar0720 2026-09-24 11:53:27 +05:30
parent 883db28407
commit ccb34a88d0
14 changed files with 290 additions and 193 deletions

View file

@ -363,12 +363,9 @@ class DeepSeekChatConfig(OpenAIGPTConfig):
"""
If api_base is not provided, use the default DeepSeek /chat/completions endpoint.
"""
if not api_base:
api_base = "https://api.deepseek.com/beta"
clean_api_base: Final = (api_base or "https://api.deepseek.com/beta").rstrip("/")
api_base = api_base.rstrip("/")
if not clean_api_base.endswith("/chat/completions"):
return f"{clean_api_base}/chat/completions"
if not api_base.endswith("/chat/completions"):
api_base = f"{api_base}/chat/completions"
return api_base
return clean_api_base

View file

@ -64,16 +64,16 @@ class HerokuChatConfig(OpenAIGPTConfig):
litellm_params: dict,
stream: bool | None = None,
) -> str:
api_base, _ = self._get_openai_compatible_provider_info(api_base, api_key)
resolved_base, _ = self._get_openai_compatible_provider_info(api_base, api_key)
if not api_base:
if not resolved_base:
raise HerokuError(
"No api base was set. Please provide an api_base, or set the HEROKU_API_BASE environment variable."
)
api_base = api_base.rstrip("/")
clean_api_base: Final = resolved_base.rstrip("/")
if not api_base.endswith("/v1/chat/completions"):
api_base = f"{api_base}/v1/chat/completions"
if not clean_api_base.endswith("/v1/chat/completions"):
return f"{clean_api_base}/v1/chat/completions"
return api_base
return clean_api_base

View file

@ -96,10 +96,10 @@ class JinaAIEmbeddingConfig(BaseEmbeddingConfig):
stream: bool | None = None,
) -> str:
if api_base:
api_base = api_base.rstrip("/")
if not api_base.endswith("/embeddings"):
return f"{api_base}/embeddings"
return api_base
clean_api_base: Final = api_base.rstrip("/")
if not clean_api_base.endswith("/embeddings"):
return f"{clean_api_base}/embeddings"
return clean_api_base
return "https://api.jina.ai/v1/embeddings"
def transform_embedding_request(

View file

@ -79,12 +79,9 @@ class ModelScopeChatConfig(OpenAIGPTConfig):
"""
If api_base is not provided, use the default ModelScope /chat/completions endpoint.
"""
if not api_base:
api_base = self.DEFAULT_BASE_URL
clean_api_base: Final = (api_base or self.DEFAULT_BASE_URL).rstrip("/")
api_base = api_base.rstrip("/")
if not clean_api_base.endswith("/chat/completions"):
return f"{clean_api_base}/chat/completions"
if not api_base.endswith("/chat/completions"):
api_base = f"{api_base}/chat/completions"
return api_base
return clean_api_base

View file

@ -86,15 +86,12 @@ class MoonshotChatConfig(OpenAIGPTConfig):
"""
If api_base is not provided, use the default Moonshot AI /chat/completions endpoint.
"""
if not api_base:
api_base = "https://api.moonshot.ai/v1"
clean_api_base: Final = (api_base or "https://api.moonshot.ai/v1").rstrip("/")
api_base = api_base.rstrip("/")
if not clean_api_base.endswith("/chat/completions"):
return f"{clean_api_base}/chat/completions"
if not api_base.endswith("/chat/completions"):
api_base = f"{api_base}/chat/completions"
return api_base
return clean_api_base
def get_supported_openai_params(self, model: str) -> list:
"""

View file

@ -218,15 +218,10 @@ class OllamaChatConfig(BaseConfig):
Some providers need `model` in `api_base`
"""
if api_base is None:
api_base = "http://localhost:11434"
api_base = api_base.rstrip("/")
if api_base.endswith("/api/chat"):
url = api_base
else:
url = f"{api_base}/api/chat"
return url
clean_api_base: Final = (api_base or "http://localhost:11434").rstrip("/")
if clean_api_base.endswith("/api/chat"):
return clean_api_base
return f"{clean_api_base}/api/chat"
def transform_request(
self,

View file

@ -468,15 +468,10 @@ class OllamaConfig(BaseConfig):
Some providers need `model` in `api_base`
"""
if api_base is None:
api_base = "http://localhost:11434"
api_base = api_base.rstrip("/")
if api_base.endswith("/api/generate"):
url = api_base
else:
url = f"{api_base}/api/generate"
return url
clean_api_base: Final = (api_base or "http://localhost:11434").rstrip("/")
if clean_api_base.endswith("/api/generate"):
return clean_api_base
return f"{clean_api_base}/api/generate"
def get_model_response_iterator(
self,

View file

@ -61,10 +61,10 @@ class PerplexityEmbeddingConfig(BaseEmbeddingConfig):
stream: bool | None = None,
) -> str:
if api_base:
api_base = api_base.rstrip("/")
if not api_base.endswith("/embeddings"):
api_base = f"{api_base}/v1/embeddings"
return api_base
clean_api_base: Final = api_base.rstrip("/")
if not clean_api_base.endswith("/embeddings"):
return f"{clean_api_base}/v1/embeddings"
return clean_api_base
return "https://api.perplexity.ai/v1/embeddings"
def get_supported_openai_params(self, model: str) -> list:

View file

@ -39,8 +39,8 @@ class TopazImageVariationConfig(TopazModelInfo, BaseImageVariationConfig):
litellm_params: dict,
stream: bool | None = None,
) -> str:
api_base = (api_base or "https://api.topazlabs.com").rstrip("/")
return f"{api_base}/image/v1/enhance"
clean_api_base: Final = (api_base or "https://api.topazlabs.com").rstrip("/")
return f"{clean_api_base}/image/v1/enhance"
def map_openai_params(
self,

View file

@ -46,10 +46,10 @@ class VoyageEmbeddingConfig(BaseEmbeddingConfig):
stream: bool | None = None,
) -> str:
if api_base:
api_base = api_base.rstrip("/")
if not api_base.endswith("/embeddings"):
api_base = f"{api_base}/embeddings"
return api_base
clean_api_base: Final = api_base.rstrip("/")
if not clean_api_base.endswith("/embeddings"):
return f"{clean_api_base}/embeddings"
return clean_api_base
return "https://api.voyageai.com/v1/embeddings"
def get_supported_openai_params(self, model: str) -> list:

View file

@ -55,10 +55,10 @@ class VoyageContextualEmbeddingConfig(BaseEmbeddingConfig):
stream: bool | None = None,
) -> str:
if api_base:
api_base = api_base.rstrip("/")
if not api_base.endswith("/contextualizedembeddings"):
api_base = f"{api_base}/contextualizedembeddings"
return api_base
clean_api_base: Final = api_base.rstrip("/")
if not clean_api_base.endswith("/contextualizedembeddings"):
return f"{clean_api_base}/contextualizedembeddings"
return clean_api_base
return "https://api.voyageai.com/v1/contextualizedembeddings"
def get_supported_openai_params(self, model: str) -> list: # mutable-ok: base class signature

View file

@ -55,10 +55,10 @@ class VoyageMultimodalEmbeddingConfig(BaseEmbeddingConfig):
stream: bool | None = None,
) -> str:
if api_base:
api_base = api_base.rstrip("/")
if not api_base.endswith("/multimodalembeddings"):
api_base = f"{api_base}/multimodalembeddings"
return api_base
clean_api_base: Final = api_base.rstrip("/")
if not clean_api_base.endswith("/multimodalembeddings"):
return f"{clean_api_base}/multimodalembeddings"
return clean_api_base
return "https://api.voyageai.com/v1/multimodalembeddings"
def get_supported_openai_params(self, model: str) -> list:

View file

@ -1,126 +0,0 @@
from litellm.llms.deepseek.chat.transformation import DeepSeekChatConfig
from litellm.llms.ollama.chat.transformation import OllamaChatConfig
from litellm.llms.ollama.completion.transformation import OllamaConfig
from litellm.llms.moonshot.chat.transformation import MoonshotChatConfig
from litellm.llms.modelscope.chat.transformation import ModelScopeChatConfig
from litellm.llms.jina_ai.embedding.transformation import JinaAIEmbeddingConfig
from litellm.llms.voyage.embedding.transformation import VoyageEmbeddingConfig
from litellm.llms.voyage.embedding.transformation_contextual import VoyageContextualEmbeddingConfig
from litellm.llms.voyage.embedding.transformation_multimodal import VoyageMultimodalEmbeddingConfig
from litellm.llms.perplexity.embedding.transformation import PerplexityEmbeddingConfig
from litellm.llms.snowflake.chat.transformation import SnowflakeConfig
from litellm.llms.snowflake.embedding.transformation import SnowflakeEmbeddingConfig
from litellm.llms.heroku.chat.transformation import HerokuChatConfig
from litellm.llms.topaz.image_variations.transformation import TopazImageVariationConfig
def test_deepseek_get_complete_url_trailing_slash():
config = DeepSeekChatConfig()
url_without_slash = config.get_complete_url(api_base="https://custom.deepseek.com", api_key=None, model="deepseek-chat", optional_params={}, litellm_params={})
url_with_slash = config.get_complete_url(api_base="https://custom.deepseek.com/", api_key=None, model="deepseek-chat", optional_params={}, litellm_params={})
assert url_without_slash == "https://custom.deepseek.com/chat/completions"
assert url_with_slash == "https://custom.deepseek.com/chat/completions"
assert "//chat/completions" not in url_with_slash
def test_ollama_get_complete_url_trailing_slash():
chat_config = OllamaChatConfig()
url_without_slash = chat_config.get_complete_url(api_base="http://localhost:11434", api_key=None, model="llama3", optional_params={}, litellm_params={})
url_with_slash = chat_config.get_complete_url(api_base="http://localhost:11434/", api_key=None, model="llama3", optional_params={}, litellm_params={})
assert url_without_slash == "http://localhost:11434/api/chat"
assert url_with_slash == "http://localhost:11434/api/chat"
assert "//api/chat" not in url_with_slash
completion_config = OllamaConfig()
comp_url_without_slash = completion_config.get_complete_url(api_base="http://localhost:11434", api_key=None, model="llama3", optional_params={}, litellm_params={})
comp_url_with_slash = completion_config.get_complete_url(api_base="http://localhost:11434/", api_key=None, model="llama3", optional_params={}, litellm_params={})
assert comp_url_without_slash == "http://localhost:11434/api/generate"
assert comp_url_with_slash == "http://localhost:11434/api/generate"
assert "//api/generate" not in comp_url_with_slash
def test_moonshot_get_complete_url_trailing_slash():
config = MoonshotChatConfig()
url_without_slash = config.get_complete_url(api_base="https://api.moonshot.ai/v1", api_key=None, model="moonshot-v1-8k", optional_params={}, litellm_params={})
url_with_slash = config.get_complete_url(api_base="https://api.moonshot.ai/v1/", api_key=None, model="moonshot-v1-8k", optional_params={}, litellm_params={})
assert url_without_slash == "https://api.moonshot.ai/v1/chat/completions"
assert url_with_slash == "https://api.moonshot.ai/v1/chat/completions"
assert "//chat/completions" not in url_with_slash
def test_modelscope_get_complete_url_trailing_slash():
config = ModelScopeChatConfig()
url_without_slash = config.get_complete_url(api_base="https://api-inference.modelscope.cn/v1", api_key=None, model="qwen", optional_params={}, litellm_params={})
url_with_slash = config.get_complete_url(api_base="https://api-inference.modelscope.cn/v1/", api_key=None, model="qwen", optional_params={}, litellm_params={})
assert url_without_slash == "https://api-inference.modelscope.cn/v1/chat/completions"
assert url_with_slash == "https://api-inference.modelscope.cn/v1/chat/completions"
assert "//chat/completions" not in url_with_slash
def test_jina_ai_get_complete_url_trailing_slash():
config = JinaAIEmbeddingConfig()
url_without_slash = config.get_complete_url(api_base="https://api.jina.ai/v1", api_key=None, model="jina-embeddings-v2", optional_params={}, litellm_params={})
url_with_slash = config.get_complete_url(api_base="https://api.jina.ai/v1/", api_key=None, model="jina-embeddings-v2", optional_params={}, litellm_params={})
assert url_without_slash == "https://api.jina.ai/v1/embeddings"
assert url_with_slash == "https://api.jina.ai/v1/embeddings"
assert "//embeddings" not in url_with_slash
def test_voyage_get_complete_url_trailing_slash():
config = VoyageEmbeddingConfig()
url_without_slash = config.get_complete_url(api_base="https://api.voyageai.com/v1", api_key=None, model="voyage-large-2", optional_params={}, litellm_params={})
url_with_slash = config.get_complete_url(api_base="https://api.voyageai.com/v1/", api_key=None, model="voyage-large-2", optional_params={}, litellm_params={})
assert url_without_slash == "https://api.voyageai.com/v1/embeddings"
assert url_with_slash == "https://api.voyageai.com/v1/embeddings"
assert "//embeddings" not in url_with_slash
contextual_config = VoyageContextualEmbeddingConfig()
c_url = contextual_config.get_complete_url(api_base="https://api.voyageai.com/v1/", api_key=None, model="voyage-context-2", optional_params={}, litellm_params={})
assert c_url == "https://api.voyageai.com/v1/contextualizedembeddings"
assert "//contextualizedembeddings" not in c_url
multimodal_config = VoyageMultimodalEmbeddingConfig()
m_url = multimodal_config.get_complete_url(api_base="https://api.voyageai.com/v1/", api_key=None, model="voyage-multimodal-3", optional_params={}, litellm_params={})
assert m_url == "https://api.voyageai.com/v1/multimodalembeddings"
assert "//multimodalembeddings" not in m_url
def test_perplexity_get_complete_url_trailing_slash():
config = PerplexityEmbeddingConfig()
url_without_slash = config.get_complete_url(api_base="https://api.perplexity.ai", api_key=None, model="sonar-medium", optional_params={}, litellm_params={})
url_with_slash = config.get_complete_url(api_base="https://api.perplexity.ai/", api_key=None, model="sonar-medium", optional_params={}, litellm_params={})
assert url_without_slash == "https://api.perplexity.ai/v1/embeddings"
assert url_with_slash == "https://api.perplexity.ai/v1/embeddings"
assert "//v1/embeddings" not in url_with_slash
def test_snowflake_get_complete_url_trailing_slash():
config = SnowflakeConfig()
url_without_slash = config.get_complete_url(api_base="https://myorg-myaccount.snowflakecomputing.com", api_key=None, model="snowflake-arctic", optional_params={}, litellm_params={})
url_with_slash = config.get_complete_url(api_base="https://myorg-myaccount.snowflakecomputing.com/", api_key=None, model="snowflake-arctic", optional_params={}, litellm_params={})
assert url_without_slash == "https://myorg-myaccount.snowflakecomputing.com/api/v2/cortex/v1/chat/completions"
assert url_with_slash == "https://myorg-myaccount.snowflakecomputing.com/api/v2/cortex/v1/chat/completions"
assert "//cortex" not in url_with_slash
embed_config = SnowflakeEmbeddingConfig()
embed_url = embed_config.get_complete_url(api_base="https://myorg-myaccount.snowflakecomputing.com/", api_key=None, model="snowflake-arctic-embed", optional_params={}, litellm_params={})
assert embed_url == "https://myorg-myaccount.snowflakecomputing.com/api/v2/cortex/inference:embed"
assert "//cortex" not in embed_url
def test_heroku_get_complete_url_trailing_slash():
config = HerokuChatConfig()
url_without_slash = config.get_complete_url(api_base="https://my-app.herokuapp.com", api_key=None, model="claude-3-5-sonnet", optional_params={}, litellm_params={})
url_with_slash = config.get_complete_url(api_base="https://my-app.herokuapp.com/", api_key=None, model="claude-3-5-sonnet", optional_params={}, litellm_params={})
assert url_without_slash == "https://my-app.herokuapp.com/v1/chat/completions"
assert url_with_slash == "https://my-app.herokuapp.com/v1/chat/completions"
assert "//v1/chat/completions" not in url_with_slash
def test_topaz_get_complete_url_trailing_slash():
config = TopazImageVariationConfig()
url_without_slash = config.get_complete_url(api_base="https://api.topazlabs.com", api_key=None, model="topaz-v1", optional_params={}, litellm_params={})
url_with_slash = config.get_complete_url(api_base="https://api.topazlabs.com/", api_key=None, model="topaz-v1", optional_params={}, litellm_params={})
assert url_without_slash == "https://api.topazlabs.com/image/v1/enhance"
assert url_with_slash == "https://api.topazlabs.com/image/v1/enhance"
assert "//image/v1/enhance" not in url_with_slash

View file

@ -0,0 +1,242 @@
from litellm.llms.deepseek.chat.transformation import DeepSeekChatConfig
from litellm.llms.heroku.chat.transformation import HerokuChatConfig
from litellm.llms.jina_ai.embedding.transformation import JinaAIEmbeddingConfig
from litellm.llms.modelscope.chat.transformation import ModelScopeChatConfig
from litellm.llms.moonshot.chat.transformation import MoonshotChatConfig
from litellm.llms.ollama.chat.transformation import OllamaChatConfig
from litellm.llms.ollama.completion.transformation import OllamaConfig
from litellm.llms.perplexity.embedding.transformation import PerplexityEmbeddingConfig
from litellm.llms.topaz.image_variations.transformation import TopazImageVariationConfig
from litellm.llms.voyage.embedding.transformation import VoyageEmbeddingConfig
from litellm.llms.voyage.embedding.transformation_contextual import VoyageContextualEmbeddingConfig
from litellm.llms.voyage.embedding.transformation_multimodal import VoyageMultimodalEmbeddingConfig
def test_deepseek_get_complete_url_trailing_slash():
config = DeepSeekChatConfig()
url_without_slash = config.get_complete_url(
api_base="https://custom.deepseek.com",
api_key=None,
model="deepseek-chat",
optional_params={},
litellm_params={},
)
url_with_slash = config.get_complete_url(
api_base="https://custom.deepseek.com/",
api_key=None,
model="deepseek-chat",
optional_params={},
litellm_params={},
)
assert url_without_slash == "https://custom.deepseek.com/chat/completions"
assert url_with_slash == "https://custom.deepseek.com/chat/completions"
assert "//chat/completions" not in url_with_slash
def test_ollama_get_complete_url_trailing_slash():
chat_config = OllamaChatConfig()
url_without_slash = chat_config.get_complete_url(
api_base="http://localhost:11434",
api_key=None,
model="llama3",
optional_params={},
litellm_params={},
)
url_with_slash = chat_config.get_complete_url(
api_base="http://localhost:11434/",
api_key=None,
model="llama3",
optional_params={},
litellm_params={},
)
assert url_without_slash == "http://localhost:11434/api/chat"
assert url_with_slash == "http://localhost:11434/api/chat"
assert "//api/chat" not in url_with_slash
completion_config = OllamaConfig()
comp_url_without_slash = completion_config.get_complete_url(
api_base="http://localhost:11434",
api_key=None,
model="llama3",
optional_params={},
litellm_params={},
)
comp_url_with_slash = completion_config.get_complete_url(
api_base="http://localhost:11434/",
api_key=None,
model="llama3",
optional_params={},
litellm_params={},
)
assert comp_url_without_slash == "http://localhost:11434/api/generate"
assert comp_url_with_slash == "http://localhost:11434/api/generate"
assert "//api/generate" not in comp_url_with_slash
def test_moonshot_get_complete_url_trailing_slash():
config = MoonshotChatConfig()
url_without_slash = config.get_complete_url(
api_base="https://api.moonshot.ai/v1",
api_key=None,
model="moonshot-v1-8k",
optional_params={},
litellm_params={},
)
url_with_slash = config.get_complete_url(
api_base="https://api.moonshot.ai/v1/",
api_key=None,
model="moonshot-v1-8k",
optional_params={},
litellm_params={},
)
assert url_without_slash == "https://api.moonshot.ai/v1/chat/completions"
assert url_with_slash == "https://api.moonshot.ai/v1/chat/completions"
assert "//chat/completions" not in url_with_slash
def test_modelscope_get_complete_url_trailing_slash():
config = ModelScopeChatConfig()
url_without_slash = config.get_complete_url(
api_base="https://api-inference.modelscope.cn/v1",
api_key=None,
model="qwen",
optional_params={},
litellm_params={},
)
url_with_slash = config.get_complete_url(
api_base="https://api-inference.modelscope.cn/v1/",
api_key=None,
model="qwen",
optional_params={},
litellm_params={},
)
assert url_without_slash == "https://api-inference.modelscope.cn/v1/chat/completions"
assert url_with_slash == "https://api-inference.modelscope.cn/v1/chat/completions"
assert "//chat/completions" not in url_with_slash
def test_jina_ai_get_complete_url_trailing_slash():
config = JinaAIEmbeddingConfig()
url_without_slash = config.get_complete_url(
api_base="https://api.jina.ai/v1",
api_key=None,
model="jina-embeddings-v2",
optional_params={},
litellm_params={},
)
url_with_slash = config.get_complete_url(
api_base="https://api.jina.ai/v1/",
api_key=None,
model="jina-embeddings-v2",
optional_params={},
litellm_params={},
)
assert url_without_slash == "https://api.jina.ai/v1/embeddings"
assert url_with_slash == "https://api.jina.ai/v1/embeddings"
assert "//embeddings" not in url_with_slash
def test_voyage_get_complete_url_trailing_slash():
config = VoyageEmbeddingConfig()
url_without_slash = config.get_complete_url(
api_base="https://api.voyageai.com/v1",
api_key=None,
model="voyage-large-2",
optional_params={},
litellm_params={},
)
url_with_slash = config.get_complete_url(
api_base="https://api.voyageai.com/v1/",
api_key=None,
model="voyage-large-2",
optional_params={},
litellm_params={},
)
assert url_without_slash == "https://api.voyageai.com/v1/embeddings"
assert url_with_slash == "https://api.voyageai.com/v1/embeddings"
assert "//embeddings" not in url_with_slash
contextual_config = VoyageContextualEmbeddingConfig()
c_url = contextual_config.get_complete_url(
api_base="https://api.voyageai.com/v1/",
api_key=None,
model="voyage-context-2",
optional_params={},
litellm_params={},
)
assert c_url == "https://api.voyageai.com/v1/contextualizedembeddings"
assert "//contextualizedembeddings" not in c_url
multimodal_config = VoyageMultimodalEmbeddingConfig()
m_url = multimodal_config.get_complete_url(
api_base="https://api.voyageai.com/v1/",
api_key=None,
model="voyage-multimodal-3",
optional_params={},
litellm_params={},
)
assert m_url == "https://api.voyageai.com/v1/multimodalembeddings"
assert "//multimodalembeddings" not in m_url
def test_perplexity_get_complete_url_trailing_slash():
config = PerplexityEmbeddingConfig()
url_without_slash = config.get_complete_url(
api_base="https://api.perplexity.ai",
api_key=None,
model="sonar-medium",
optional_params={},
litellm_params={},
)
url_with_slash = config.get_complete_url(
api_base="https://api.perplexity.ai/",
api_key=None,
model="sonar-medium",
optional_params={},
litellm_params={},
)
assert url_without_slash == "https://api.perplexity.ai/v1/embeddings"
assert url_with_slash == "https://api.perplexity.ai/v1/embeddings"
assert "//v1/embeddings" not in url_with_slash
def test_heroku_get_complete_url_trailing_slash():
config = HerokuChatConfig()
url_without_slash = config.get_complete_url(
api_base="https://my-app.herokuapp.com",
api_key=None,
model="claude-3-5-sonnet",
optional_params={},
litellm_params={},
)
url_with_slash = config.get_complete_url(
api_base="https://my-app.herokuapp.com/",
api_key=None,
model="claude-3-5-sonnet",
optional_params={},
litellm_params={},
)
assert url_without_slash == "https://my-app.herokuapp.com/v1/chat/completions"
assert url_with_slash == "https://my-app.herokuapp.com/v1/chat/completions"
assert "//v1/chat/completions" not in url_with_slash
def test_topaz_get_complete_url_trailing_slash():
config = TopazImageVariationConfig()
url_without_slash = config.get_complete_url(
api_base="https://api.topazlabs.com",
api_key=None,
model="topaz-v1",
optional_params={},
litellm_params={},
)
url_with_slash = config.get_complete_url(
api_base="https://api.topazlabs.com/",
api_key=None,
model="topaz-v1",
optional_params={},
litellm_params={},
)
assert url_without_slash == "https://api.topazlabs.com/image/v1/enhance"
assert url_with_slash == "https://api.topazlabs.com/image/v1/enhance"
assert "//image/v1/enhance" not in url_with_slash