diff --git a/litellm/llms/openai/common_utils.py b/litellm/llms/openai/common_utils.py index 5f9559df77..28de9f1303 100644 --- a/litellm/llms/openai/common_utils.py +++ b/litellm/llms/openai/common_utils.py @@ -3,9 +3,10 @@ Common helpers / utils across al OpenAI endpoints """ import hashlib +import inspect import json import ssl -from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union import httpx import openai @@ -14,8 +15,6 @@ from openai import AsyncAzureOpenAI, AsyncOpenAI, AzureOpenAI, OpenAI if TYPE_CHECKING: from aiohttp import ClientSession -import inspect - import litellm from litellm.llms.base_llm.chat.transformation import BaseLLMException from litellm.llms.custom_httpx.http_handler import ( @@ -25,13 +24,13 @@ from litellm.llms.custom_httpx.http_handler import ( ) -def _get_client_init_params(cls: type) -> List[str]: +def _get_client_init_params(cls: type) -> Tuple[str, ...]: """Extract __init__ parameter names (excluding 'self') from a class.""" - return [p for p in inspect.signature(cls.__init__).parameters if p != "self"] # type: ignore[misc] + return tuple(p for p in inspect.signature(cls.__init__).parameters if p != "self") # type: ignore[misc] -_OPENAI_INIT_PARAMS: List[str] = _get_client_init_params(OpenAI) -_AZURE_OPENAI_INIT_PARAMS: List[str] = _get_client_init_params(AzureOpenAI) +_OPENAI_INIT_PARAMS: Tuple[str, ...] = _get_client_init_params(OpenAI) +_AZURE_OPENAI_INIT_PARAMS: Tuple[str, ...] = _get_client_init_params(AzureOpenAI) class OpenAIError(BaseLLMException): @@ -170,12 +169,12 @@ class BaseOpenAILLM: f"is_async={client_initialization_params.get('is_async')}", ] - LITELLM_CLIENT_SPECIFIC_PARAMS = [ + LITELLM_CLIENT_SPECIFIC_PARAMS = ( "timeout", "max_retries", "organization", "api_base", - ] + ) openai_client_fields = ( BaseOpenAILLM.get_openai_client_initialization_param_fields( client_type=client_type @@ -192,8 +191,8 @@ class BaseOpenAILLM: @staticmethod def get_openai_client_initialization_param_fields( client_type: Literal["openai", "azure"] - ) -> List[str]: - """Returns a list of fields that are used to initialize the OpenAI client""" + ) -> Tuple[str, ...]: + """Returns a tuple of fields that are used to initialize the OpenAI client""" if client_type == "openai": return _OPENAI_INIT_PARAMS else: diff --git a/tests/test_litellm/llms/openai/test_openai_common_utils.py b/tests/test_litellm/llms/openai/test_openai_common_utils.py index f2740be642..8489040660 100644 --- a/tests/test_litellm/llms/openai/test_openai_common_utils.py +++ b/tests/test_litellm/llms/openai/test_openai_common_utils.py @@ -146,12 +146,12 @@ def test_precomputed_init_params_match_inspect_signature(): _OPENAI_INIT_PARAMS, ) - expected_openai = [ + expected_openai = tuple( p for p in inspect.signature(OpenAI.__init__).parameters if p != "self" - ] - expected_azure = [ + ) + expected_azure = tuple( p for p in inspect.signature(AzureOpenAI.__init__).parameters if p != "self" - ] + ) assert _OPENAI_INIT_PARAMS == expected_openai assert _AZURE_OPENAI_INIT_PARAMS == expected_azure @@ -161,6 +161,17 @@ def test_precomputed_init_params_match_inspect_signature(): def test_get_openai_client_initialization_param_fields(client_type): """Verify the method returns the correct pre-computed params for each client type.""" result = BaseOpenAILLM.get_openai_client_initialization_param_fields(client_type) - assert isinstance(result, list) + assert isinstance(result, tuple) assert len(result) > 0 assert "self" not in result + + +@pytest.mark.parametrize("client_type", ["openai", "azure"]) +def test_get_openai_client_cache_key(client_type): + """Verify get_openai_client_cache_key doesn't raise on tuple + tuple concatenation.""" + key = BaseOpenAILLM.get_openai_client_cache_key( + client_initialization_params={"api_key": "sk-test"}, + client_type=client_type, + ) + assert isinstance(key, str) + assert "api_key=sk-test" in key