diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 749f0ce4fcb..c1531f4e4ae 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -62,10 +62,10 @@ if TYPE_CHECKING: from litellm.proxy.proxy_server import UserAPIKeyAuth as _UserAPIKeyAuth Span = _Span | Any - Tracer = _Tracer | Any - Context = _Context | Any - SpanExporter = _SpanExporter | Any - UserAPIKeyAuth = _UserAPIKeyAuth | Any + Tracer = _Tracer + Context = _Context + SpanExporter = _SpanExporter + UserAPIKeyAuth = _UserAPIKeyAuth ManagementEndpointLoggingPayload = _ManagementEndpointLoggingPayload | Any else: Span = Any @@ -2730,7 +2730,7 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger): self.handle_callback_failure(callback_name=self.callback_name or "opentelemetry") verbose_logger.exception("OpenTelemetry logging error in set_attributes %s", str(e)) - def _cast_as_primitive_value_type(self, value) -> str | bool | int | float: + def _cast_as_primitive_value_type(self, value: object) -> str | bool | int | float: """ Casts the value to a primitive OTEL type if it is not already a primitive type. diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 2cecec729c2..a6391a2ae27 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -1566,14 +1566,14 @@ class Logging(LiteLLMLoggingBaseClass): attr = "debug" if json_logs: - callattr = getattr(verbose_logger, attr) + callattr = verbose_logger.warning if attr == "warning" else verbose_logger.debug callattr( "RAW RESPONSE:\n{}\n\n".format( self.model_call_details.get("original_response", self.model_call_details) ), ) else: - callattr = getattr(verbose_logger, attr) + callattr = verbose_logger.warning if attr == "warning" else verbose_logger.debug callattr( "RAW RESPONSE:\n{}\n\n".format( self.model_call_details.get("original_response", self.model_call_details) @@ -5882,7 +5882,7 @@ class StandardLoggingPayloadSetup: base_model: str | None, custom_pricing: bool | None, custom_llm_provider: str | None, - init_response_obj: Any | BaseModel | dict, + init_response_obj: object, api_base: str | None = None, ) -> StandardLoggingModelInformation: model_cost_name: Final = _select_model_name_for_cost_calc( @@ -5915,9 +5915,7 @@ class StandardLoggingPayloadSetup: return model_cost_information @staticmethod - def get_final_response_obj( - response_obj: dict, init_response_obj: Any | BaseModel | dict, kwargs: dict - ) -> dict | str | list | None: + def get_final_response_obj(response_obj: dict, init_response_obj: object, kwargs: dict) -> dict | str | list | None: """ Get final response object after redacting the message input/output from logging """ @@ -6360,7 +6358,7 @@ def _get_status_fields( def _extract_response_obj_and_hidden_params( - init_response_obj: Any | BaseModel | dict, + init_response_obj: object, original_exception: Exception | None, ) -> tuple[dict, dict | None]: """Extract response_obj and hidden_params from init_response_obj.""" diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 8424187dcbc..6fc319c26ae 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -446,7 +446,7 @@ def _render_chat_template(env, chat_template: str, bos_token: str, eos_token: st async def _afetch_and_extract_template( - model: str, chat_template: Any | None, get_config_fn, get_template_fn + model: str, chat_template: str | None, get_config_fn, get_template_fn ) -> tuple[str, str, str]: """ Async version: Fetch template and tokens from HuggingFace. @@ -500,7 +500,7 @@ async def _afetch_and_extract_template( def _fetch_and_extract_template( - model: str, chat_template: Any | None, get_config_fn, get_template_fn + model: str, chat_template: str | None, get_config_fn, get_template_fn ) -> tuple[str, str, str]: """ Sync version: Fetch template and tokens from HuggingFace. diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index 052978c2680..dba0dee38fc 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -12,6 +12,7 @@ from typing import ( Literal, NamedTuple, Optional, + Protocol, TypedDict, TypeVar, Union, @@ -24,6 +25,7 @@ import httpx from httpx import USE_CLIENT_DEFAULT from httpx._types import FileContent from openai.types.file_deleted import FileDeleted +from typing_extensions import ReadOnly import litellm import litellm.litellm_core_utils @@ -206,6 +208,7 @@ if TYPE_CHECKING: FakeAnthropicMessagesStreamIterator, ) from litellm.llms.base_llm.passthrough.transformation import BasePassthroughConfig + from litellm.proxy._types import UserAPIKeyAuth from litellm.types.llms.openai_evals import ( CancelEvalResponse, CancelRunResponse, @@ -221,6 +224,21 @@ if TYPE_CHECKING: else: LiteLLMLoggingObj = Any + +class _RealtimeClientWebSocket(Protocol): + async def send_text(self, data: str) -> None: ... + + async def close(self, code: int = ..., reason: str | None = ...) -> None: ... + + +class _ResponsesClientWebSocket(Protocol): + async def send_text(self, data: str) -> None: ... + + async def receive_text(self) -> str: ... + + async def close(self, code: int = ..., reason: str | None = ...) -> None: ... + + _ResponseT = TypeVar("_ResponseT") @@ -237,6 +255,17 @@ class _MediaUploadKwargs(TypedDict, total=False): timeout: float | httpx.Timeout +class _SignedBodyKwargs(TypedDict, total=False): + data: ReadOnly[bytes] + json: ReadOnly[dict[str, object]] + + +def _signed_body_kwargs(*, signed_body: bytes | None, data: dict[str, object]) -> _SignedBodyKwargs: + if signed_body is not None: + return {"data": signed_body} + return {"json": data} + + def _google_genai_streaming_hidden_params( *, api_base: str, @@ -318,7 +347,9 @@ def _mask_presigned_request_headers(transformed_request: bytes | str | dict) -> } -def _aws_signing_overrides(optional_params: Mapping[str, Any], litellm_params: Mapping[str, Any]) -> Mapping[str, Any]: +def _aws_signing_overrides( + optional_params: Mapping[str, object], litellm_params: Mapping[str, object] +) -> Mapping[str, object]: return MappingProxyType( { key: litellm_params[key] @@ -2739,7 +2770,7 @@ class BaseLLMHTTPHandler: stream=stream, fake_stream=fake_stream, ) - body_kwargs: Final[dict[str, Any]] = {"data": signed_body} if signed_body is not None else {"json": data} + body_kwargs: Final = _signed_body_kwargs(signed_body=signed_body, data=data) ## LOGGING logging_obj.pre_call( @@ -2926,7 +2957,7 @@ class BaseLLMHTTPHandler: stream=stream, fake_stream=fake_stream, ) - body_kwargs: Final[dict[str, Any]] = {"data": signed_body} if signed_body is not None else {"json": data} + body_kwargs: Final = _signed_body_kwargs(signed_body=signed_body, data=data) ## LOGGING logging_obj.pre_call( @@ -4540,7 +4571,7 @@ class BaseLLMHTTPHandler: api_key=litellm_params.api_key, model=model, ) - body_kwargs: Final[dict[str, Any]] = {"data": signed_body} if signed_body is not None else {"json": data} + body_kwargs: Final = _signed_body_kwargs(signed_body=signed_body, data=data) ## LOGGING logging_obj.pre_call( @@ -4634,7 +4665,7 @@ class BaseLLMHTTPHandler: api_key=litellm_params.api_key, model=model, ) - body_kwargs: Final[dict[str, Any]] = {"data": signed_body} if signed_body is not None else {"json": data} + body_kwargs: Final = _signed_body_kwargs(signed_body=signed_body, data=data) ## LOGGING logging_obj.pre_call( @@ -6186,6 +6217,7 @@ class BaseLLMHTTPHandler: "BasePassthroughConfig", "BaseContainerConfig", BaseEvalsAPIConfig, + BaseRealtimeHTTPConfig, ], ): received_status_code: Final = ( @@ -6300,7 +6332,7 @@ class BaseLLMHTTPHandler: async def async_realtime( self, model: str, - websocket: Any, + websocket: _RealtimeClientWebSocket, logging_obj: LiteLLMLoggingObj, provider_config: BaseRealtimeConfig, headers: dict, @@ -6308,7 +6340,7 @@ class BaseLLMHTTPHandler: api_key: str | None = None, client: Any | None = None, timeout: float | None = None, - user_api_key_dict: Any | None = None, + user_api_key_dict: object | None = None, litellm_metadata: dict[str, object] | None = None, query_params: RealtimeQueryParams | None = None, ): @@ -6483,7 +6515,7 @@ class BaseLLMHTTPHandler: request_data: dict[str, object], logging_obj: LiteLLMLoggingObj, timeout: float | httpx.Timeout, - provider_config: Any | None = None, + provider_config: BaseRealtimeHTTPConfig | None = None, model: str | None = None, extra_headers: dict[str, object] | None = None, client: HTTPHandler | AsyncHTTPHandler | None = None, @@ -6555,7 +6587,7 @@ class BaseLLMHTTPHandler: sdp_body: bytes, logging_obj: LiteLLMLoggingObj, timeout: float | httpx.Timeout, - provider_config: Any | None = None, + provider_config: BaseRealtimeHTTPConfig | None = None, model: str | None = None, session_config: dict[str, object] | None = None, extra_headers: dict[str, object] | None = None, @@ -6633,13 +6665,13 @@ class BaseLLMHTTPHandler: async def async_responses_websocket( self, model: str, - websocket: Any, + websocket: _ResponsesClientWebSocket, logging_obj: LiteLLMLoggingObj, responses_api_provider_config: BaseResponsesAPIConfig | None, api_base: str | None = None, api_key: str | None = None, timeout: float | None = None, - user_api_key_dict: Any | None = None, + user_api_key_dict: "UserAPIKeyAuth | None" = None, litellm_metadata: dict[str, object] | None = None, custom_llm_provider: str | None = None, first_message: str | None = None, @@ -7850,7 +7882,7 @@ class BaseLLMHTTPHandler: def video_create_character_handler( self, name: str, - video: Any, + video: FileTypes, video_provider_config: BaseVideoConfig, custom_llm_provider: str, litellm_params, @@ -7934,7 +7966,7 @@ class BaseLLMHTTPHandler: async def async_video_create_character_handler( self, name: str, - video: Any, + video: FileTypes, video_provider_config: BaseVideoConfig, custom_llm_provider: str, litellm_params, diff --git a/litellm/main.py b/litellm/main.py index ceac729d3f0..98bb5126a90 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -57,6 +57,7 @@ from litellm.utils import ( # Logging is imported lazily when needed to avoid loading litellm_logging at import time if TYPE_CHECKING: from litellm.litellm_core_utils.litellm_logging import Logging + from litellm.router import Router from litellm.types.utils import TokenCountResponse from litellm.constants import ( @@ -351,7 +352,7 @@ class LiteLLM: class Chat: - def __init__(self, params, router_obj: Any | None): + def __init__(self, params, router_obj: "Router | None"): self.params = params if self.params.get("acompletion", False) is True: self.params.pop("acompletion") @@ -361,7 +362,7 @@ class Chat: class Completions: - def __init__(self, params, router_obj: Any | None): + def __init__(self, params, router_obj: "Router | None"): self.params = params self.router_obj = router_obj @@ -377,7 +378,7 @@ class Completions: class AsyncCompletions: - def __init__(self, params, router_obj: Any | None): + def __init__(self, params, router_obj: "Router | None"): self.params = params self.router_obj = router_obj diff --git a/litellm/proxy/common_request_processing.py b/litellm/proxy/common_request_processing.py index 904070cfadd..7e1989b0aab 100644 --- a/litellm/proxy/common_request_processing.py +++ b/litellm/proxy/common_request_processing.py @@ -218,7 +218,7 @@ ProxyRouteType: TypeAlias = Literal[ from litellm.llms.anthropic.chat.transformation import AnthropicConfig # Type alias for streaming chunk serializer (chunk after hooks + cost injection -> wire format) -StreamChunkSerializer = Callable[[Any], str] +StreamChunkSerializer = Callable[[object], str] # Type alias for streaming error serializer (ProxyException -> wire format) StreamErrorSerializer = Callable[[ProxyException], str] @@ -459,7 +459,7 @@ async def _bill_partial_streamed_spend_on_disconnect(request_data: dict, respons return True -async def _cancel_pending_gather_tasks(tasks: list["asyncio.Task[Any]"]) -> None: +async def _cancel_pending_gather_tasks(tasks: Sequence["asyncio.Task[object]"]) -> None: pending_tasks: Final = [task for task in tasks if not task.done()] for task in pending_tasks: task.cancel() @@ -3145,7 +3145,7 @@ class ProxyBaseLLMRequestProcessing: logging_obj._on_detached_stream_failure = _on_detached_stream_failure - def _is_streaming_response(self, response: Any) -> bool: + def _is_streaming_response(self, response: object) -> bool: """ Check if the response object is actually a streaming response by inspecting its type. @@ -3259,7 +3259,7 @@ class ProxyBaseLLMRequestProcessing: async def _handle_non_streaming_allm_passthrough_route( self, - response: Any, + response: _UpstreamHttpResponse, proxy_logging_obj: "ProxyLogging", user_api_key_dict: "UserAPIKeyAuth", custom_headers: Mapping[str, str], @@ -3852,7 +3852,7 @@ class ProxyBaseLLMRequestProcessing: @staticmethod async def async_streaming_data_generator( - response: Any, + response: object, user_api_key_dict: UserAPIKeyAuth, request_data: dict, proxy_logging_obj: ProxyLogging, @@ -3993,7 +3993,7 @@ class ProxyBaseLLMRequestProcessing: @staticmethod def async_sse_data_generator( - response: Any, + response: object, user_api_key_dict: UserAPIKeyAuth, request_data: dict, proxy_logging_obj: ProxyLogging, diff --git a/litellm/proxy/management_endpoints/key_management_endpoints.py b/litellm/proxy/management_endpoints/key_management_endpoints.py index 306ea90d7f1..2f1dc270cf6 100644 --- a/litellm/proxy/management_endpoints/key_management_endpoints.py +++ b/litellm/proxy/management_endpoints/key_management_endpoints.py @@ -453,7 +453,7 @@ def _regenerate_request_as_update_request(key: str, data: RegenerateKeyRequest) ) if not changed_fields: return None - return UpdateKeyRequest(key=key, **changed_fields) + return UpdateKeyRequest.model_validate(MappingProxyType({"key": key, **changed_fields})) class _LegacyDumpable(Protocol): diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 9fb72d2d1b5..c0071aa7c81 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -8700,9 +8700,9 @@ class ProxyConfig: @staticmethod def _merge_config_and_db_search_tools( - config_search_tools: list[SearchToolTypedDict], - db_search_tools: list[dict[str, Any]], - ) -> list[dict[str, Any]]: + config_search_tools: Sequence[SearchToolTypedDict], + db_search_tools: Sequence[dict[str, object]], + ) -> list[dict[str, object]]: db_tool_names: Final = {tool.get("search_tool_name") for tool in db_search_tools} return [ *[ @@ -9273,10 +9273,10 @@ _EMPTY_HEADERS: Final[Mapping[str, str]] = MappingProxyType({}) async def _iter_with_keepalive( - aiter: AsyncIterator[Any], + aiter: AsyncIterator[object], resolve_keepalive_seconds: Callable[[object], float], keepalive_seconds: float, -) -> AsyncGenerator[Any, None]: +) -> AsyncGenerator[object, None]: """Wrap `aiter` with idle-gap heartbeats, re-resolving the interval after each real chunk via `resolve_keepalive_seconds`. A mid-stream router fallback can swap in a deployment with a different keepalive policy, including one that @@ -9287,7 +9287,7 @@ async def _iter_with_keepalive( actually produced it, in both directions. While the interval is <= 0, no task is created and no timeout is awaited: a chunk is forwarded the moment it arrives, at the same cost as a bare `async for`.""" - pending: asyncio.Task[Any] | None = None # rebind-ok: rebound each loop iteration + pending: asyncio.Task[object] | None = None # rebind-ok: rebound each loop iteration current_keepalive_seconds = keepalive_seconds # rebind-ok: re-resolved after each chunk try: while True: diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index fe161f5d50b..c617047fad9 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -3956,7 +3956,7 @@ class ProxyLogging: request_data: dict, # mutable-ok: same request-payload shape the hooks mutate pipelines: "tuple[tuple[str, GuardrailPipeline], ...]", translation: "tuple[str, BaseTranslation]", - ) -> "AsyncGenerator[Any, None]": + ) -> "AsyncGenerator[object, None]": """ Execute post_call policy pipelines against a streamed response. diff --git a/litellm/realtime_api/main.py b/litellm/realtime_api/main.py index acc42c44c04..28814741852 100644 --- a/litellm/realtime_api/main.py +++ b/litellm/realtime_api/main.py @@ -127,15 +127,15 @@ def _get_realtime_http_provider_config( @wrapper_client async def acreate_realtime_client_secret( model: str | None = None, - session: dict[str, Any] | None = None, - expires_after: dict[str, Any] | None = None, + session: Mapping[str, object] | None = None, + expires_after: Mapping[str, object] | None = None, timeout: float | None = None, **kwargs, ): req: Final = RealtimeClientSecretRequest( model=model, - session=RealtimeSessionConfig(**session) if session else None, - expires_after=RealtimeExpiresAfter(**expires_after) if expires_after else None, + session=RealtimeSessionConfig.model_validate(session) if session else None, + expires_after=RealtimeExpiresAfter.model_validate(expires_after) if expires_after else None, ) model_name = (req.session.model if req.session is not None else None) or req.model or "gpt-4o-realtime-preview" litellm_logging_obj: Final[LiteLLMLogging] = kwargs.get("litellm_logging_obj") @@ -614,12 +614,14 @@ def _azure_realtime_health_protocol( def _realtime_health_check_auth_headers( - custom_llm_provider: str, api_key: str | None, model_params: Mapping[str, Any] + custom_llm_provider: str, api_key: str | None, model_params: Mapping[str, object] ) -> Mapping[str, str]: if custom_llm_provider == "azure": return azure_realtime.get_auth_headers( api_key=api_key, - azure_ad_token=(None if api_key else get_azure_ad_token(GenericLiteLLMParams(**model_params))), + azure_ad_token=( + None if api_key else get_azure_ad_token(GenericLiteLLMParams.model_validate(dict(model_params))) + ), ) if api_key is None: return _EMPTY_AUTH_HEADERS diff --git a/litellm/responses/main.py b/litellm/responses/main.py index 884ea7e217d..93c72bc2d3b 100644 --- a/litellm/responses/main.py +++ b/litellm/responses/main.py @@ -549,7 +549,7 @@ def _will_bridge_to_chat_completions( @contextmanager def _prompt_management_sees_a_provisional_message_list( - kwargs: dict[str, Any], # mutable-ok: the signal is read and popped out of the caller's own kwargs + kwargs: dict[str, object], # mutable-ok: the signal is read and popped out of the caller's own kwargs bridged: bool, ) -> Generator[None, None]: """Tell the cache-control hook that this layer's messages are not the ones sent upstream. diff --git a/litellm/router.py b/litellm/router.py index 62042e1c969..d328fbbb12f 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -4231,7 +4231,7 @@ class Router: models: Final = [m.strip() for m in model.split(",")] async def _async_completion_no_exceptions( - model_name: str, messages: list[dict[str, str]], stream: bool, **kwargs: Any + model_name: str, messages: list[dict[str, str]], stream: bool, **kwargs: object ) -> ModelResponse | CustomStreamWrapper | Exception: """ Wrapper around self.acompletion that catches exceptions and returns them as a result @@ -6736,7 +6736,7 @@ class Router: # Handle asynchronous call types async def async_wrapper( custom_llm_provider: str | None = None, - client: Any | None = None, + client: AsyncOpenAI | None = None, **kwargs, ): if call_type == "assistants": @@ -8441,7 +8441,7 @@ class Router: return self._has_content_policy_fallback(model, kwargs) def _should_raise_anthropic_refusal_error( - self, model: str, original_generic_function: Callable, response: object, kwargs: Mapping[str, Any] + self, model: str, original_generic_function: Callable, response: object, kwargs: Mapping[str, object] ) -> bool: """ The /v1/messages twin of _should_raise_content_policy_error: an Anthropic safeguard @@ -10318,7 +10318,7 @@ class Router: ) @staticmethod - def _widest_configured_limit(model_infos: Sequence[Mapping[str, Any]], field: str) -> int | None: + def _widest_configured_limit(model_infos: Sequence[Mapping[str, object]], field: str) -> int | None: """The largest usable value of ``field`` across a group's configured model_info blocks.""" limits: Final = tuple( limit @@ -13409,7 +13409,7 @@ class Router: self, model: str, request_kwargs: dict, - messages: list[dict[str, Any]] | None, + messages: list[dict[str, object]] | None, ) -> RoutingContext: """ Build a RoutingContext for `model`, run it through `self.routing_plugins` diff --git a/litellm/utils.py b/litellm/utils.py index 64097021dff..81258b8ca77 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -369,7 +369,7 @@ if TYPE_CHECKING: ) from litellm.litellm_core_utils.rules import Rules from litellm.litellm_core_utils.streaming_handler import CustomStreamWrapper - from litellm.litellm_core_utils.thread_pool_executor import executor + from litellm.litellm_core_utils.thread_pool_executor import BoundedLoggingThreadPoolExecutor from litellm.llms.base_llm.anthropic_messages.transformation import ( BaseAnthropicMessagesConfig, ) @@ -683,12 +683,12 @@ def load_credentials_from_list(kwargs: dict): Updates kwargs with the credentials if credential_name in kwarg """ # Access CredentialAccessor via module to trigger lazy loading if needed - CredentialAccessor: Final = getattr(sys.modules[__name__], "CredentialAccessor") + credential_accessor: Final[type[CredentialAccessor]] = getattr(sys.modules[__name__], "CredentialAccessor") credential_name: Final = kwargs.get("litellm_credential_name") if not credential_name: return - credential: Final = CredentialAccessor.find_credential(credential_name) + credential: Final = credential_accessor.find_credential(credential_name) if credential is None: verbose_logger.warning( "litellm_credential_name=%s matched none of the %d loaded credentials; the request runs without it", @@ -894,6 +894,45 @@ class _NamedFile(Protocol): def name(self) -> object: ... +class _LoggingClassGetter(Protocol): + def __call__(self) -> type[LiteLLMLoggingObject]: ... + + +class _ResponseMetadataUpdater(Protocol): + def __call__( + self, + result: object, + logging_obj: LiteLLMLoggingObject, + model: str | None, + kwargs: dict[str, object], + start_time: datetime.datetime, + end_time: datetime.datetime, + include_overhead: bool = True, + ) -> None: ... + + +class _SupportedOpenAIParamsGetter(Protocol): + def __call__( + self, + model: str, + custom_llm_provider: str | None = None, + request_type: Literal["chat_completion", "embeddings", "transcription"] = "chat_completion", + base_model: str | None = None, + ) -> list[str] | None: ... + + +class _NestedPathChecker(Protocol): + def __call__(self, path: str) -> bool: ... + + +class _NestedValueDeleter(Protocol): + def __call__(self, data: dict[str, object], path: str) -> dict[str, object]: ... + + +class _BaseModelFromMetadataGetter(Protocol): + def __call__(self, metadata: Mapping[str, object] | None) -> str | None: ... + + def _ocr_document_summary(document: object) -> str: if not isinstance(document, Mapping): return "default-message-value" @@ -978,7 +1017,9 @@ def function_setup( len(litellm.input_callback) > 0 or len(litellm.success_callback) > 0 or len(litellm.failure_callback) > 0 ) and len(callback_list) == 0: callback_list = list(set(litellm.input_callback + litellm.success_callback + litellm.failure_callback)) - get_set_callbacks: Final = getattr(sys.modules[__name__], "get_set_callbacks") + get_set_callbacks: Final[Callable[[], Callable[..., None]]] = getattr( + sys.modules[__name__], "get_set_callbacks" + ) get_set_callbacks()(callback_list=callback_list, function_id=function_id) ## ASYNC CALLBACKS - safety net for callbacks added via direct append if len(litellm.input_callback) > 0: @@ -1223,7 +1264,9 @@ def function_setup( call_type=call_type, ): stream = True - get_litellm_logging_class: Final = getattr(sys.modules[__name__], "get_litellm_logging_class") + get_litellm_logging_class: Final[_LoggingClassGetter] = getattr( + sys.modules[__name__], "get_litellm_logging_class" + ) # Victim for object pool logging_obj = get_litellm_logging_class()( # rebind-ok: 2nd assignment to logging_obj (see initial None above) model=model, @@ -1761,7 +1804,9 @@ def client(original_function): return litellm.stream_chunk_builder(chunks, messages=kwargs.get("messages", None)) else: # RETURN RESULT - update_response_metadata = getattr(sys.modules[__name__], "update_response_metadata") + update_response_metadata: _ResponseMetadataUpdater = getattr( + sys.modules[__name__], "update_response_metadata" + ) update_response_metadata( result=result, logging_obj=logging_obj, @@ -1802,7 +1847,9 @@ def client(original_function): kwargs=kwargs, ) - _update_response_metadata: Final = getattr(sys.modules[__name__], "update_response_metadata") + _update_response_metadata: Final[_ResponseMetadataUpdater] = getattr( + sys.modules[__name__], "update_response_metadata" + ) _update_response_metadata( result=result, logging_obj=logging_obj, @@ -1817,7 +1864,7 @@ def client(original_function): # Copy the current context to propagate it to the background thread # This is essential for OpenTelemetry span context propagation ctx: Final = contextvars.copy_context() - executor: Final = getattr(sys.modules[__name__], "executor") + executor: Final[BoundedLoggingThreadPoolExecutor] = getattr(sys.modules[__name__], "executor") executor.submit( ctx.run, logging_obj.success_handler, @@ -1910,7 +1957,9 @@ def client(original_function): print_args_passed_to_litellm(original_function, args, kwargs) start_time: Final = datetime.datetime.now() result = None - _update_response_metadata: Final = getattr(sys.modules[__name__], "update_response_metadata") + _update_response_metadata: Final[_ResponseMetadataUpdater] = getattr( + sys.modules[__name__], "update_response_metadata" + ) logging_obj: LiteLLMLoggingObject | None = kwargs.get("litellm_logging_obj", None) LLMCachingHandler: Final = _get_cached_llm_caching_handler() _llm_caching_handler: Final[LLMCachingHandler] = LLMCachingHandler( @@ -3678,7 +3727,9 @@ def get_optional_params_embeddings( **kwargs, ): # Lazy load get_supported_openai_params - get_supported_openai_params: Final = getattr(sys.modules[__name__], "get_supported_openai_params") + get_supported_openai_params: Final[_SupportedOpenAIParamsGetter] = getattr( + sys.modules[__name__], "get_supported_openai_params" + ) # retrieve all parameters passed to the function passed_params: Final = locals() @@ -4469,7 +4520,9 @@ def get_optional_params( message=f"{custom_llm_provider} does not support parameters: {list(unsupported_params.keys())}, for model={model}. To drop these, set `litellm.drop_params=True` or for proxy:\n\n`litellm_settings:\n drop_params: true`\n. \n If you want to use these params dynamically send allowed_openai_params={list(unsupported_params.keys())} in your request.", ) - get_supported_openai_params: Final = getattr(sys.modules[__name__], "get_supported_openai_params") + get_supported_openai_params: Final[_SupportedOpenAIParamsGetter] = getattr( + sys.modules[__name__], "get_supported_openai_params" + ) supported_params = get_supported_openai_params( model=model, custom_llm_provider=custom_llm_provider, base_model=base_model ) @@ -4640,9 +4693,9 @@ def get_optional_params( drop_params=bool(drop_params), ) elif custom_llm_provider == "bedrock": - BedrockModelInfo: Final = getattr(sys.modules[__name__], "BedrockModelInfo") - bedrock_route: Final = BedrockModelInfo.get_bedrock_route(model) - bedrock_base_model: Final = BedrockModelInfo.get_base_model(model) + bedrock_model_info: Final[type[BedrockModelInfo]] = getattr(sys.modules[__name__], "BedrockModelInfo") + bedrock_route: Final = bedrock_model_info.get_bedrock_route(model) + bedrock_base_model: Final = bedrock_model_info.get_base_model(model) if bedrock_route == "converse" or bedrock_route == "converse_like": optional_params = litellm.AmazonConverseConfig().map_openai_params( model=model, @@ -4680,7 +4733,7 @@ def get_optional_params( drop_params=bool(drop_params), ) if bedrock_route == "claude_platform": - optional_params = BedrockModelInfo.map_claude_platform_auth_params( + optional_params = bedrock_model_info.map_claude_platform_auth_params( passed_params=passed_params, optional_params=optional_params ) elif custom_llm_provider == "cloudflare": @@ -4954,8 +5007,8 @@ def get_optional_params( # Apply nested drops from additional_drop_params if additional_drop_params: - is_nested_path: Final = getattr(sys.modules[__name__], "is_nested_path") - delete_nested_value: Final = getattr(sys.modules[__name__], "delete_nested_value") + is_nested_path: Final[_NestedPathChecker] = getattr(sys.modules[__name__], "is_nested_path") + delete_nested_value: Final[_NestedValueDeleter] = getattr(sys.modules[__name__], "delete_nested_value") nested_paths: Final = [p for p in additional_drop_params if is_nested_path(p)] for path in nested_paths: optional_params = delete_nested_value(optional_params, path) @@ -7852,7 +7905,7 @@ def _get_base_model_from_metadata(model_call_details=None): return _base_model metadata: Final = litellm_params.get("metadata") or {} - _get_base_model_from_litellm_call_metadata: Callable[..., str | None] = getattr( + _get_base_model_from_litellm_call_metadata: _BaseModelFromMetadataGetter = getattr( sys.modules[__name__], "_get_base_model_from_litellm_call_metadata" ) base_model_from_metadata: Final = _get_base_model_from_litellm_call_metadata(metadata=metadata) diff --git a/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py b/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py index 89b377af3a0..12134ba988a 100644 --- a/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py +++ b/tests/test_litellm/litellm_core_utils/test_health_check_helpers.py @@ -2,6 +2,7 @@ import struct import zlib +from types import MappingProxyType from unittest.mock import AsyncMock, MagicMock, patch import pytest @@ -482,3 +483,19 @@ async def test_ocr_health_check_sends_the_document_kind_the_provider_config_acce document = mock_aocr.call_args.kwargs["document"] assert document["type"] == expected_document_type assert document[expected_document_type].startswith(expected_uri_prefix) + + +def test_realtime_health_check_azure_ad_params_drop_reserved_keys(): + from litellm.realtime_api import main as realtime_main + + seen = [] + with patch.object(realtime_main, "get_azure_ad_token", lambda params: seen.append(params) or "ad-token"): + headers = realtime_main._realtime_health_check_auth_headers( + "azure", + None, + MappingProxyType({"api_base": "https://x.openai.azure.com", "self": 1, "params": 2, "__class__": 3}), + ) + + assert dict(headers) == {"Authorization": "Bearer ad-token"} + assert seen[0].api_base == "https://x.openai.azure.com" + assert seen[0].model_extra == {}