mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
perf: replace LlmProviders list comp with LlmProvidersSet + defer locals() in completion()
Replace [p.value for p in LlmProviders] (126-member list + O(n) scan) with
pre-existing LlmProvidersSet (O(1) set lookup) at two hot-path call sites in
main.py and utils.py. Defer locals() in completion() so the 41-key dict is
only created on rare fallback/batch paths, and pass minimal {"messages": messages}
to the exception handler.
Line profile results (6K requests, 1 worker):
- LlmProviders lookup: 987µs → 3.4µs per hit (-99.7%)
- completion() total: 90.21s → 76.52s (-15.2%)
This commit is contained in:
parent
c7fbab4549
commit
4a878ccc64
3 changed files with 55 additions and 11 deletions
|
|
@ -249,6 +249,7 @@ from .types.utils import (
|
|||
FileTypes,
|
||||
HiddenParams,
|
||||
LlmProviders,
|
||||
LlmProvidersSet,
|
||||
PromptTokensDetails,
|
||||
ProviderSpecificHeader,
|
||||
all_litellm_params,
|
||||
|
|
@ -1104,8 +1105,6 @@ def completion( # type: ignore # noqa: PLR0915
|
|||
stop = validate_openai_optional_params(stop=stop)
|
||||
|
||||
######### unpacking kwargs #####################
|
||||
args = locals()
|
||||
|
||||
skip_mcp_handler = kwargs.pop("_skip_mcp_handler", False)
|
||||
if not skip_mcp_handler and tools:
|
||||
from litellm.responses.mcp.chat_completions_handler import (
|
||||
|
|
@ -1284,12 +1283,12 @@ def completion( # type: ignore # noqa: PLR0915
|
|||
logging: LiteLLMLoggingObj = cast(LiteLLMLoggingObj, litellm_logging_obj)
|
||||
fallbacks = fallbacks or litellm.model_fallbacks
|
||||
if fallbacks is not None:
|
||||
return completion_with_fallbacks(**args)
|
||||
return completion_with_fallbacks(**locals())
|
||||
if model_list is not None:
|
||||
deployments = [
|
||||
m["litellm_params"] for m in model_list if m["model_name"] == model
|
||||
]
|
||||
return litellm.batch_completion_models(deployments=deployments, **args)
|
||||
return litellm.batch_completion_models(deployments=deployments, **locals())
|
||||
if litellm.model_alias_map and model in litellm.model_alias_map:
|
||||
model = litellm.model_alias_map[
|
||||
model
|
||||
|
|
@ -1394,9 +1393,7 @@ def completion( # type: ignore # noqa: PLR0915
|
|||
)
|
||||
|
||||
provider_config: Optional[BaseConfig] = None
|
||||
if custom_llm_provider is not None and custom_llm_provider in [
|
||||
provider.value for provider in LlmProviders
|
||||
]:
|
||||
if custom_llm_provider is not None and custom_llm_provider in LlmProvidersSet:
|
||||
provider_config = ProviderConfigManager.get_provider_chat_config(
|
||||
model=model, provider=LlmProviders(custom_llm_provider)
|
||||
)
|
||||
|
|
@ -4306,11 +4303,13 @@ def completion( # type: ignore # noqa: PLR0915
|
|||
model=model,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
original_exception=e,
|
||||
completion_kwargs=args,
|
||||
completion_kwargs={"messages": messages},
|
||||
extra_kwargs=kwargs,
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
def completion_with_retries(*args, **kwargs):
|
||||
"""
|
||||
Executes a litellm.completion() with 3 retries
|
||||
|
|
|
|||
|
|
@ -3874,9 +3874,7 @@ def get_optional_params( # noqa: PLR0915
|
|||
custom_llm_provider=custom_llm_provider,
|
||||
)
|
||||
provider_config: Optional[BaseConfig] = None
|
||||
if custom_llm_provider is not None and custom_llm_provider in [
|
||||
provider.value for provider in LlmProviders
|
||||
]:
|
||||
if custom_llm_provider is not None and custom_llm_provider in LlmProvidersSet:
|
||||
provider_config = ProviderConfigManager.get_provider_chat_config(
|
||||
model=model, provider=LlmProviders(custom_llm_provider)
|
||||
)
|
||||
|
|
|
|||
47
tests/litellm/test_completion_perf_optimizations.py
Normal file
47
tests/litellm/test_completion_perf_optimizations.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
"""
|
||||
Tests for completion() performance optimizations.
|
||||
|
||||
Covers:
|
||||
1. LlmProvidersSet used instead of list comprehension for provider lookup
|
||||
2. Deferred locals() - get_first_chars_messages works with minimal dict
|
||||
"""
|
||||
|
||||
import litellm
|
||||
from litellm.types.utils import LlmProviders, LlmProvidersSet
|
||||
|
||||
|
||||
class TestLlmProvidersSetConsistency:
|
||||
"""Verify LlmProvidersSet matches the enum values exactly."""
|
||||
|
||||
def test_set_matches_enum(self):
|
||||
expected = {p.value for p in LlmProviders}
|
||||
assert LlmProvidersSet == expected
|
||||
|
||||
def test_known_providers_in_set(self):
|
||||
for provider in ["openai", "anthropic", "azure", "bedrock", "vertex_ai"]:
|
||||
assert provider in LlmProvidersSet, f"{provider} not in LlmProvidersSet"
|
||||
|
||||
def test_unknown_provider_not_in_set(self):
|
||||
assert "not_a_real_provider_xyz" not in LlmProvidersSet
|
||||
|
||||
|
||||
class TestGetFirstCharsMessagesWithMinimalKwargs:
|
||||
"""Verify get_first_chars_messages works when completion_kwargs only has 'messages'."""
|
||||
|
||||
def test_with_messages_key(self):
|
||||
"""get_first_chars_messages should work with just {"messages": ...}."""
|
||||
messages = [{"role": "user", "content": "hello"}]
|
||||
result = litellm.get_first_chars_messages(kwargs={"messages": messages})
|
||||
assert "hello" in result
|
||||
|
||||
def test_with_empty_dict(self):
|
||||
"""get_first_chars_messages should handle empty dict gracefully."""
|
||||
result = litellm.get_first_chars_messages(kwargs={})
|
||||
# Should return empty string or "None" — not crash
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_truncates_long_messages(self):
|
||||
"""get_first_chars_messages truncates to 100 chars."""
|
||||
messages = [{"role": "user", "content": "x" * 200}]
|
||||
result = litellm.get_first_chars_messages(kwargs={"messages": messages})
|
||||
assert len(result) <= 100
|
||||
Loading…
Add table
Reference in a new issue