diff --git a/litellm/batches/main.py b/litellm/batches/main.py index 1a03b172d3..e176dc4292 100644 --- a/litellm/batches/main.py +++ b/litellm/batches/main.py @@ -199,7 +199,8 @@ def create_batch( # noqa: PLR0915 ) ### TIMEOUT LOGIC ### timeout = _resolve_timeout(optional_params, kwargs, custom_llm_provider) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, user=None, optional_params=optional_params.model_dump(), @@ -207,7 +208,6 @@ def create_batch( # noqa: PLR0915 "litellm_call_id": litellm_call_id, "proxy_server_request": proxy_server_request, "model_info": model_info, - "metadata": metadata, "preset_cache_key": None, "stream_response": {}, **optional_params.model_dump(exclude_unset=True), @@ -584,7 +584,8 @@ def retrieve_batch( **kwargs, ) if litellm_logging_obj is not None: - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, user=None, optional_params=optional_params.model_dump(), diff --git a/litellm/containers/endpoint_factory.py b/litellm/containers/endpoint_factory.py index 22fd4226de..1d8e50856f 100644 --- a/litellm/containers/endpoint_factory.py +++ b/litellm/containers/endpoint_factory.py @@ -91,7 +91,8 @@ def create_sync_endpoint_function(endpoint_config: Dict) -> Callable: optional_params = {k: kwargs.get(k) for k in path_params if k in kwargs} # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", optional_params=optional_params, litellm_params={"litellm_call_id": litellm_call_id}, diff --git a/litellm/containers/main.py b/litellm/containers/main.py index 88318ee039..916fc26351 100644 --- a/litellm/containers/main.py +++ b/litellm/containers/main.py @@ -233,7 +233,8 @@ def create_container( ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", optional_params=dict(container_create_request_params), litellm_params={ @@ -438,7 +439,8 @@ def list_containers( ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", optional_params=dict(container_list_optional_params), litellm_params={ @@ -626,7 +628,8 @@ def retrieve_container( ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", optional_params={}, litellm_params={ @@ -811,7 +814,8 @@ def delete_container( ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", optional_params={}, litellm_params={ @@ -1010,7 +1014,8 @@ def list_container_files( ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", optional_params={ "container_id": container_id, @@ -1255,7 +1260,8 @@ def upload_container_file( ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", optional_params={"container_id": container_id}, litellm_params={ diff --git a/litellm/evals/main.py b/litellm/evals/main.py index e57c75bd9b..eab909a6b1 100644 --- a/litellm/evals/main.py +++ b/litellm/evals/main.py @@ -193,7 +193,8 @@ def create_eval( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params=request_body, litellm_params={ @@ -382,7 +383,8 @@ def list_evals( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params=query_params, litellm_params={ @@ -536,7 +538,8 @@ def get_eval( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"eval_id": eval_id}, litellm_params={ @@ -760,7 +763,8 @@ def update_eval( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params=request_body, litellm_params={ @@ -914,7 +918,8 @@ def delete_eval( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"eval_id": eval_id}, litellm_params={ @@ -1071,7 +1076,8 @@ def cancel_eval( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"eval_id": eval_id}, litellm_params={ @@ -1262,7 +1268,8 @@ def create_run( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params=request_body, litellm_params={ @@ -1450,7 +1457,8 @@ def list_runs( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"eval_id": eval_id, **query_params}, litellm_params={ @@ -1610,7 +1618,8 @@ def get_run( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"eval_id": eval_id, "run_id": run_id}, litellm_params={ @@ -1773,7 +1782,8 @@ def cancel_run( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"eval_id": eval_id, "run_id": run_id}, litellm_params={ @@ -1941,7 +1951,8 @@ def delete_run( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"eval_id": eval_id, "run_id": run_id}, litellm_params={ diff --git a/litellm/google_genai/main.py b/litellm/google_genai/main.py index a937a35da2..bdbb483dcf 100644 --- a/litellm/google_genai/main.py +++ b/litellm/google_genai/main.py @@ -185,7 +185,8 @@ class GenerateContentHelper: if litellm_logging_obj is None: raise ValueError("litellm_logging_obj is required, but got None") - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, optional_params=dict(generate_content_config_dict), litellm_params={ diff --git a/litellm/images/main.py b/litellm/images/main.py index a3ae97b57d..a5ae154190 100644 --- a/litellm/images/main.py +++ b/litellm/images/main.py @@ -40,6 +40,9 @@ from litellm.utils import exception_type, get_litellm_params llm_http_handler: BaseLLMHTTPHandler = BaseLLMHTTPHandler() from openai.types.audio.transcription_create_params import FileTypes # type: ignore +# BFL handlers +from litellm.llms.black_forest_labs.image_edit.handler import bfl_image_edit +from litellm.llms.black_forest_labs.image_generation.handler import bfl_image_generation from litellm.main import ( azure_chat_completions, base_llm_aiohttp_handler, @@ -50,10 +53,6 @@ from litellm.main import ( openai_image_variations, ) -# BFL handlers -from litellm.llms.black_forest_labs.image_edit.handler import bfl_image_edit -from litellm.llms.black_forest_labs.image_generation.handler import bfl_image_generation - ########################################### from litellm.secret_managers.main import get_secret_str from litellm.types.images.main import ImageEditOptionalRequestParams @@ -297,7 +296,8 @@ def image_generation( # noqa: PLR0915 litellm_params_dict = get_litellm_params(**kwargs) logging: Logging = litellm_logging_obj - logging.update_environment_variables( + logging.update_from_kwargs( + kwargs=kwargs, model=model, user=user, optional_params=optional_params, @@ -308,7 +308,6 @@ def image_generation( # noqa: PLR0915 "logger_fn": logger_fn, "proxy_server_request": proxy_server_request, "model_info": model_info, - "metadata": metadata, "preset_cache_key": None, "stream_response": {}, }, @@ -894,7 +893,8 @@ def image_edit( # noqa: PLR0915 ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, user=user, optional_params=dict(image_edit_request_params), @@ -902,7 +902,6 @@ def image_edit( # noqa: PLR0915 **image_edit_request_params, "litellm_call_id": litellm_call_id, "model_info": model_info, - "metadata": metadata, }, custom_llm_provider=custom_llm_provider, ) diff --git a/litellm/interactions/main.py b/litellm/interactions/main.py index 2b1786ac3a..ab429ef6db 100644 --- a/litellm/interactions/main.py +++ b/litellm/interactions/main.py @@ -34,16 +34,7 @@ Usage: import asyncio import contextvars from functools import partial -from typing import ( - Any, - AsyncIterator, - Coroutine, - Dict, - Iterator, - List, - Optional, - Union, -) +from typing import Any, AsyncIterator, Coroutine, Dict, Iterator, List, Optional, Union import httpx @@ -306,7 +297,8 @@ def create( **kwargs, ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, optional_params=dict(optional_params), litellm_params={"litellm_call_id": litellm_call_id}, @@ -416,7 +408,8 @@ def get( f"Interactions API not supported for: {custom_llm_provider}" ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"interaction_id": interaction_id}, litellm_params={"litellm_call_id": litellm_call_id}, @@ -519,7 +512,8 @@ def delete( f"Interactions API not supported for: {custom_llm_provider}" ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"interaction_id": interaction_id}, litellm_params={"litellm_call_id": litellm_call_id}, @@ -622,7 +616,8 @@ def cancel( f"Interactions API not supported for: {custom_llm_provider}" ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"interaction_id": interaction_id}, litellm_params={"litellm_call_id": litellm_call_id}, diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 27bdaee116..a92f4cb9ec 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -568,6 +568,42 @@ class Logging(LiteLLMLoggingBaseClass): if "custom_llm_provider" in self.model_call_details: self.custom_llm_provider = self.model_call_details["custom_llm_provider"] + def update_from_kwargs( + self, + kwargs: Dict, + litellm_params: Optional[Dict] = None, + optional_params: Optional[Dict] = None, + model: Optional[str] = None, + user: Optional[str] = None, + **additional_params, + ): + """ + Convenience wrapper around update_environment_variables that + automatically extracts metadata/litellm_metadata from kwargs, + so callers don't need to manually plumb them into litellm_params. + """ + base_litellm_params: Dict[str, Any] = {} + + if "metadata" in kwargs: + base_litellm_params["metadata"] = kwargs["metadata"] + if "litellm_metadata" in kwargs and isinstance( + kwargs["litellm_metadata"], dict + ): + base_litellm_params["litellm_metadata"] = kwargs["litellm_metadata"] + if "metadata" not in base_litellm_params: + base_litellm_params["metadata"] = kwargs["litellm_metadata"].copy() + + if litellm_params: + base_litellm_params.update(litellm_params) + + self.update_environment_variables( + litellm_params=base_litellm_params, + optional_params=optional_params or {}, + model=model, + user=user, + **additional_params, + ) + def update_messages(self, messages: List[AllMessageValues]): """ Update the logged value of the messages in the model_call_details diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index 4394343c8e..27da8a1900 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -1882,12 +1882,11 @@ class BaseLLMHTTPHandler: headers=headers, provider=custom_llm_provider ) - logging_obj.update_environment_variables( + logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, optional_params=dict(anthropic_messages_optional_request_params), litellm_params={ - "metadata": kwargs.get("metadata", {}), - "litellm_metadata": kwargs.get("litellm_metadata", {}), "preset_cache_key": None, "stream_response": {}, **anthropic_messages_optional_request_params, diff --git a/litellm/llms/litellm_proxy/skills/transformation.py b/litellm/llms/litellm_proxy/skills/transformation.py index cd000829ca..4622bda4e8 100644 --- a/litellm/llms/litellm_proxy/skills/transformation.py +++ b/litellm/llms/litellm_proxy/skills/transformation.py @@ -69,7 +69,8 @@ class LiteLLMSkillsTransformationHandler: """ # Pre-call logging if logging_obj: - logging_obj.update_environment_variables( + logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"display_title": display_title}, litellm_params={"litellm_call_id": litellm_call_id}, @@ -172,7 +173,8 @@ class LiteLLMSkillsTransformationHandler: """ # Pre-call logging if logging_obj: - logging_obj.update_environment_variables( + logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"limit": limit, "offset": offset}, litellm_params={"litellm_call_id": litellm_call_id}, @@ -231,7 +233,8 @@ class LiteLLMSkillsTransformationHandler: """ # Pre-call logging if logging_obj: - logging_obj.update_environment_variables( + logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"skill_id": skill_id}, litellm_params={"litellm_call_id": litellm_call_id}, @@ -277,7 +280,8 @@ class LiteLLMSkillsTransformationHandler: """ # Pre-call logging if logging_obj: - logging_obj.update_environment_variables( + logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"skill_id": skill_id}, litellm_params={"litellm_call_id": litellm_call_id}, diff --git a/litellm/ocr/main.py b/litellm/ocr/main.py index 47cff8a2c0..d90a931b59 100644 --- a/litellm/ocr/main.py +++ b/litellm/ocr/main.py @@ -298,7 +298,8 @@ def ocr( verbose_logger.debug(f"OCR optional_params after mapping: {optional_params}") # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, optional_params=optional_params, litellm_params={ diff --git a/litellm/realtime_api/main.py b/litellm/realtime_api/main.py index 38964bd61f..842e5ea485 100644 --- a/litellm/realtime_api/main.py +++ b/litellm/realtime_api/main.py @@ -136,7 +136,8 @@ async def acreate_realtime_client_secret( dynamic_api_key=dynamic_api_key, litellm_params=litellm_params, ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model_name, optional_params={"expires_after": expires_after, "session": session}, litellm_params={"api_base": resolved_api_base}, @@ -186,7 +187,8 @@ async def arealtime_calls( dynamic_api_key=dynamic_api_key, litellm_params=litellm_params, ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model_name, optional_params={"realtime_calls": True, "session": session}, litellm_params={"api_base": resolved_api_base}, @@ -247,7 +249,8 @@ async def _arealtime( # noqa: PLR0915 if query_params is not None: query_params = {**query_params, "model": model} - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, user=user, optional_params={}, diff --git a/litellm/rerank_api/main.py b/litellm/rerank_api/main.py index e9766771dd..9868634362 100644 --- a/litellm/rerank_api/main.py +++ b/litellm/rerank_api/main.py @@ -108,7 +108,6 @@ def rerank( # noqa: PLR0915 litellm_call_id: Optional[str] = kwargs.get("litellm_call_id", None) proxy_server_request = kwargs.get("proxy_server_request", None) model_info = kwargs.get("model_info", None) - metadata = kwargs.get("metadata", {}) user = kwargs.get("user", None) client = kwargs.get("client", None) try: @@ -164,7 +163,8 @@ def rerank( # noqa: PLR0915 model_response = RerankResponse() - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, user=user, optional_params=dict(optional_rerank_params), @@ -172,7 +172,6 @@ def rerank( # noqa: PLR0915 "litellm_call_id": litellm_call_id, "proxy_server_request": proxy_server_request, "model_info": model_info, - "metadata": metadata, "preset_cache_key": None, "stream_response": {}, **optional_params.model_dump(exclude_unset=True), diff --git a/litellm/responses/main.py b/litellm/responses/main.py index b83cfb6e3c..cd9ce67c26 100644 --- a/litellm/responses/main.py +++ b/litellm/responses/main.py @@ -692,11 +692,11 @@ def responses( return run_async_function(aresponses_api_with_mcp, **mcp_call_kwargs) # get provider config - responses_api_provider_config: Optional[ - BaseResponsesAPIConfig - ] = ProviderConfigManager.get_provider_responses_api_config( - model=model, - provider=custom_llm_provider, + responses_api_provider_config: Optional[BaseResponsesAPIConfig] = ( + ProviderConfigManager.get_provider_responses_api_config( + model=model, + provider=custom_llm_provider, + ) ) local_vars.update(kwargs) @@ -738,11 +738,9 @@ def responses( ) ) - # Pre Call logging - preserve metadata for custom callbacks - # When called from completion bridge (codex models), metadata is in litellm_metadata - metadata_for_callbacks = metadata or kwargs.get("litellm_metadata") or {} - - litellm_logging_obj.update_environment_variables( + # Pre Call logging + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, user=user, optional_params=dict(responses_api_request_params), @@ -750,8 +748,6 @@ def responses( **responses_api_request_params, "aresponses": _is_async, "litellm_call_id": litellm_call_id, - "metadata": metadata_for_callbacks, - "litellm_metadata": kwargs.get("litellm_metadata", {}), }, custom_llm_provider=custom_llm_provider, ) @@ -912,11 +908,11 @@ def delete_responses( raise ValueError("custom_llm_provider is required but passed as None") # get provider config - responses_api_provider_config: Optional[ - BaseResponsesAPIConfig - ] = ProviderConfigManager.get_provider_responses_api_config( - model=None, - provider=custom_llm_provider, + responses_api_provider_config: Optional[BaseResponsesAPIConfig] = ( + ProviderConfigManager.get_provider_responses_api_config( + model=None, + provider=custom_llm_provider, + ) ) if responses_api_provider_config is None: @@ -927,7 +923,8 @@ def delete_responses( local_vars.update(kwargs) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=local_vars, model=None, optional_params={ "response_id": response_id, @@ -1092,11 +1089,11 @@ def get_responses( raise ValueError("custom_llm_provider is required but passed as None") # get provider config - responses_api_provider_config: Optional[ - BaseResponsesAPIConfig - ] = ProviderConfigManager.get_provider_responses_api_config( - model=None, - provider=custom_llm_provider, + responses_api_provider_config: Optional[BaseResponsesAPIConfig] = ( + ProviderConfigManager.get_provider_responses_api_config( + model=None, + provider=custom_llm_provider, + ) ) if responses_api_provider_config is None: @@ -1107,7 +1104,8 @@ def get_responses( local_vars.update(kwargs) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=local_vars, model=None, optional_params={ "response_id": response_id, @@ -1249,11 +1247,11 @@ def list_input_items( if custom_llm_provider is None: raise ValueError("custom_llm_provider is required but passed as None") - responses_api_provider_config: Optional[ - BaseResponsesAPIConfig - ] = ProviderConfigManager.get_provider_responses_api_config( - model=None, - provider=custom_llm_provider, + responses_api_provider_config: Optional[BaseResponsesAPIConfig] = ( + ProviderConfigManager.get_provider_responses_api_config( + model=None, + provider=custom_llm_provider, + ) ) if responses_api_provider_config is None: @@ -1263,7 +1261,8 @@ def list_input_items( local_vars.update(kwargs) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=local_vars, model=None, optional_params={"response_id": response_id}, litellm_params={"litellm_call_id": litellm_call_id}, @@ -1407,11 +1406,11 @@ def cancel_responses( raise ValueError("custom_llm_provider is required but passed as None") # get provider config - responses_api_provider_config: Optional[ - BaseResponsesAPIConfig - ] = ProviderConfigManager.get_provider_responses_api_config( - model=None, - provider=custom_llm_provider, + responses_api_provider_config: Optional[BaseResponsesAPIConfig] = ( + ProviderConfigManager.get_provider_responses_api_config( + model=None, + provider=custom_llm_provider, + ) ) if responses_api_provider_config is None: @@ -1422,7 +1421,8 @@ def cancel_responses( local_vars.update(kwargs) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=local_vars, model=None, optional_params={ "response_id": response_id, @@ -1594,11 +1594,11 @@ def compact_responses( raise ValueError("custom_llm_provider is required but passed as None") # get provider config - responses_api_provider_config: Optional[ - BaseResponsesAPIConfig - ] = ProviderConfigManager.get_provider_responses_api_config( - model=model, - provider=custom_llm_provider, + responses_api_provider_config: Optional[BaseResponsesAPIConfig] = ( + ProviderConfigManager.get_provider_responses_api_config( + model=model, + provider=custom_llm_provider, + ) ) if responses_api_provider_config is None: @@ -1626,7 +1626,8 @@ def compact_responses( ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=local_vars, model=model, optional_params=dict(responses_api_request_params), litellm_params={ @@ -1729,7 +1730,8 @@ async def _aresponses_websocket( api_key=api_key, ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, user=user, optional_params={}, diff --git a/litellm/search/main.py b/litellm/search/main.py index fdfe9a1fee..6b2c837fd5 100644 --- a/litellm/search/main.py +++ b/litellm/search/main.py @@ -286,7 +286,8 @@ def search( # Pre Call logging model_name = f"{search_provider}/search" - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model_name, optional_params=optional_params, litellm_params={ diff --git a/litellm/skills/main.py b/litellm/skills/main.py index f3b670d5d6..c6ef6f28fb 100644 --- a/litellm/skills/main.py +++ b/litellm/skills/main.py @@ -204,7 +204,8 @@ def create_skill( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params=request_body, litellm_params={ @@ -389,7 +390,8 @@ def list_skills( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params=query_params, litellm_params={ @@ -556,7 +558,8 @@ def get_skill( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"skill_id": skill_id}, litellm_params={ @@ -722,7 +725,8 @@ def delete_skill( ) # Pre-call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"skill_id": skill_id}, litellm_params={ diff --git a/litellm/vector_store_files/main.py b/litellm/vector_store_files/main.py index de191bd212..0d4d516d03 100644 --- a/litellm/vector_store_files/main.py +++ b/litellm/vector_store_files/main.py @@ -146,7 +146,8 @@ def create( ) create_request["file_id"] = file_id - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={ "vector_store_id": vector_store_id, @@ -279,7 +280,8 @@ def list( VectorStoreFileRequestUtils.get_list_query_params(local_vars) ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"vector_store_id": vector_store_id, **list_query}, litellm_params={ @@ -387,7 +389,8 @@ def retrieve( f"Vector store file retrieve is not supported for {custom_llm_provider}" ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={ "vector_store_id": vector_store_id, @@ -498,7 +501,8 @@ def retrieve_content( f"Vector store file content retrieve is not supported for {custom_llm_provider}" ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={ "vector_store_id": vector_store_id, @@ -619,7 +623,8 @@ def update( ) update_request["attributes"] = attributes - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={ "vector_store_id": vector_store_id, @@ -733,7 +738,8 @@ def delete( f"Vector store file delete is not supported for {custom_llm_provider}" ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={ "vector_store_id": vector_store_id, diff --git a/litellm/vector_stores/main.py b/litellm/vector_stores/main.py index 36799b4a9d..6d28d67097 100644 --- a/litellm/vector_stores/main.py +++ b/litellm/vector_stores/main.py @@ -3,6 +3,7 @@ LiteLLM SDK Functions for Creating and Searching Vector Stores """ import asyncio +import builtins import contextvars from functools import partial from typing import Any, Coroutine, Dict, List, Optional, Union @@ -233,7 +234,8 @@ def create( ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={ "name": name, @@ -395,11 +397,11 @@ def search( ## MOCK RESPONSE LOGIC if litellm_params.mock_response and isinstance( - litellm_params.mock_response, (str, list) + litellm_params.mock_response, (str, builtins.list) ): mock_results = None - if isinstance(litellm_params.mock_response, list): - mock_results = litellm_params.mock_response + if isinstance(litellm_params.mock_response, builtins.list): + mock_results = litellm_params.mock_response # type: ignore[assignment] return mock_vector_store_search_response(mock_results=mock_results) # Default to OpenAI for vector stores @@ -440,7 +442,8 @@ def search( ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=api_type, optional_params={ "vector_store_id": vector_store_id, @@ -585,7 +588,8 @@ def retrieve( f"Vector store retrieve is not supported for {custom_llm_provider}" ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"vector_store_id": vector_store_id}, litellm_params={"litellm_call_id": litellm_call_id}, @@ -732,7 +736,8 @@ def list( f"Vector store list is not supported for {custom_llm_provider}" ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={ "after": after, @@ -895,7 +900,8 @@ def update( ) ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={ "vector_store_id": vector_store_id, @@ -1035,7 +1041,8 @@ def delete( f"Vector store delete is not supported for {custom_llm_provider}" ) - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=None, optional_params={"vector_store_id": vector_store_id}, litellm_params={"litellm_call_id": litellm_call_id}, diff --git a/litellm/videos/main.py b/litellm/videos/main.py index f6c9bb0057..9de082f3d1 100644 --- a/litellm/videos/main.py +++ b/litellm/videos/main.py @@ -14,10 +14,7 @@ from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler from litellm.main import base_llm_http_handler from litellm.types.router import GenericLiteLLMParams from litellm.types.utils import CallTypes, FileTypes -from litellm.types.videos.main import ( - VideoCreateOptionalRequestParams, - VideoObject, -) +from litellm.types.videos.main import VideoCreateOptionalRequestParams, VideoObject from litellm.types.videos.utils import decode_video_id_with_provider from litellm.utils import ProviderConfigManager, client from litellm.videos.utils import VideoGenerationRequestUtils @@ -119,17 +116,18 @@ async def avideo_generation( def video_generation( prompt: str, model: Optional[str] = None, - input_reference: Optional[str] = None, + input_reference: Optional[FileTypes] = None, + seconds: Optional[str] = None, size: Optional[str] = None, user: Optional[str] = None, - timeout=600, # default to 10 minutes - api_key: Optional[str] = None, - api_base: Optional[str] = None, - api_version: Optional[str] = None, - custom_llm_provider=None, + timeout: int = 600, + custom_llm_provider: Optional[str] = None, + extra_headers: Optional[Dict[str, Any]] = None, + extra_query: Optional[Dict[str, Any]] = None, + extra_body: Optional[Dict[str, Any]] = None, *, avideo_generation: Literal[True], - **kwargs, + **kwargs: Any, ) -> Coroutine[Any, Any, VideoObject]: ... @@ -138,18 +136,18 @@ def video_generation( def video_generation( prompt: str, model: Optional[str] = None, - input_reference: Optional[str] = None, + input_reference: Optional[FileTypes] = None, seconds: Optional[str] = None, size: Optional[str] = None, user: Optional[str] = None, - timeout=600, # default to 10 minutes - api_key: Optional[str] = None, - api_base: Optional[str] = None, - api_version: Optional[str] = None, - custom_llm_provider=None, + timeout: int = 600, + custom_llm_provider: Optional[str] = None, + extra_headers: Optional[Dict[str, Any]] = None, + extra_query: Optional[Dict[str, Any]] = None, + extra_body: Optional[Dict[str, Any]] = None, *, avideo_generation: Literal[False] = False, - **kwargs, + **kwargs: Any, ) -> VideoObject: ... @@ -231,7 +229,8 @@ def video_generation( # noqa: PLR0915 ) # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model=model, user=user, optional_params=dict(video_generation_request_params), @@ -348,7 +347,8 @@ def video_content( } # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", user=kwargs.get("user"), optional_params=dict(video_content_request_params), @@ -528,14 +528,14 @@ async def avideo_remix( def video_remix( video_id: str, prompt: str, - timeout=600, # default to 10 minutes - api_key: Optional[str] = None, - api_base: Optional[str] = None, - api_version: Optional[str] = None, - custom_llm_provider=None, + timeout: int = 600, + custom_llm_provider: Optional[str] = None, + extra_headers: Optional[Dict[str, Any]] = None, + extra_query: Optional[Dict[str, Any]] = None, + extra_body: Optional[Dict[str, Any]] = None, *, avideo_remix: Literal[True], - **kwargs, + **kwargs: Any, ) -> Coroutine[Any, Any, VideoObject]: ... @@ -544,14 +544,14 @@ def video_remix( def video_remix( video_id: str, prompt: str, - timeout=600, # default to 10 minutes - api_key: Optional[str] = None, - api_base: Optional[str] = None, - api_version: Optional[str] = None, - custom_llm_provider=None, + timeout: int = 600, + custom_llm_provider: Optional[str] = None, + extra_headers: Optional[Dict[str, Any]] = None, + extra_query: Optional[Dict[str, Any]] = None, + extra_body: Optional[Dict[str, Any]] = None, *, avideo_remix: Literal[False] = False, - **kwargs, + **kwargs: Any, ) -> VideoObject: ... @@ -618,7 +618,8 @@ def video_remix( # noqa: PLR0915 } # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", user=kwargs.get("user"), optional_params=dict(video_remix_request_params), @@ -744,14 +745,14 @@ def video_list( after: Optional[str] = None, limit: Optional[int] = None, order: Optional[str] = None, - timeout=600, # default to 10 minutes - api_key: Optional[str] = None, - api_base: Optional[str] = None, - api_version: Optional[str] = None, - custom_llm_provider=None, + timeout: int = 600, + custom_llm_provider: Optional[str] = None, + extra_headers: Optional[Dict[str, Any]] = None, + extra_query: Optional[Dict[str, Any]] = None, + extra_body: Optional[Dict[str, Any]] = None, *, avideo_list: Literal[True], - **kwargs, + **kwargs: Any, ) -> Coroutine[Any, Any, List[VideoObject]]: ... @@ -761,14 +762,14 @@ def video_list( after: Optional[str] = None, limit: Optional[int] = None, order: Optional[str] = None, - timeout=600, # default to 10 minutes - api_key: Optional[str] = None, - api_base: Optional[str] = None, - api_version: Optional[str] = None, - custom_llm_provider=None, + timeout: int = 600, + custom_llm_provider: Optional[str] = None, + extra_headers: Optional[Dict[str, Any]] = None, + extra_query: Optional[Dict[str, Any]] = None, + extra_body: Optional[Dict[str, Any]] = None, *, avideo_list: Literal[False] = False, - **kwargs, + **kwargs: Any, ) -> List[VideoObject]: ... @@ -834,7 +835,8 @@ def video_list( # noqa: PLR0915 } # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", user=kwargs.get("user"), optional_params=dict(video_list_request_params), @@ -849,7 +851,7 @@ def video_list( # noqa: PLR0915 litellm_logging_obj.call_type = CallTypes.video_list.value # Call the handler with _is_async flag instead of directly calling the async handler - return base_llm_http_handler.video_list_handler( + return base_llm_http_handler.video_list_handler( # type: ignore[return-value] after=after, limit=limit, order=order, @@ -945,14 +947,14 @@ async def avideo_status( @overload def video_status( video_id: str, - timeout=600, # default to 10 minutes - api_key: Optional[str] = None, - api_base: Optional[str] = None, - api_version: Optional[str] = None, - custom_llm_provider=None, + timeout: int = 600, + custom_llm_provider: Optional[str] = None, + extra_headers: Optional[Dict[str, Any]] = None, + extra_query: Optional[Dict[str, Any]] = None, + extra_body: Optional[Dict[str, Any]] = None, *, avideo_status: Literal[True], - **kwargs, + **kwargs: Any, ) -> Coroutine[Any, Any, VideoObject]: ... @@ -960,14 +962,14 @@ def video_status( @overload def video_status( video_id: str, - timeout=600, # default to 10 minutes - api_key: Optional[str] = None, - api_base: Optional[str] = None, - api_version: Optional[str] = None, - custom_llm_provider=None, + timeout: int = 600, + custom_llm_provider: Optional[str] = None, + extra_headers: Optional[Dict[str, Any]] = None, + extra_query: Optional[Dict[str, Any]] = None, + extra_body: Optional[Dict[str, Any]] = None, *, avideo_status: Literal[False] = False, - **kwargs, + **kwargs: Any, ) -> VideoObject: ... @@ -1054,7 +1056,8 @@ def video_status( # noqa: PLR0915 } # Pre Call logging - litellm_logging_obj.update_environment_variables( + litellm_logging_obj.update_from_kwargs( + kwargs=kwargs, model="", user=kwargs.get("user"), optional_params=dict(video_status_request_params), diff --git a/tests/test_litellm/images/test_image_edit_utils.py b/tests/test_litellm/images/test_image_edit_utils.py index 7a950375d3..a4456af624 100644 --- a/tests/test_litellm/images/test_image_edit_utils.py +++ b/tests/test_litellm/images/test_image_edit_utils.py @@ -202,13 +202,16 @@ class TestImageEditCustomPricing: mock_logging_obj = MagicMock() mock_logging_obj.model_call_details = {} - original_update = mock_logging_obj.update_environment_variables + original_update = mock_logging_obj.update_from_kwargs - def capturing_update(**kwargs): - captured_litellm_params.update(kwargs.get("litellm_params", {})) - return original_update(**kwargs) + def capturing_update(**update_kwargs): + captured_litellm_params.update(update_kwargs.get("litellm_params", {})) + inner_kwargs = update_kwargs.get("kwargs", {}) + if "metadata" in inner_kwargs: + captured_litellm_params["metadata"] = inner_kwargs["metadata"] + return original_update(**update_kwargs) - mock_logging_obj.update_environment_variables = capturing_update + mock_logging_obj.update_from_kwargs = capturing_update with patch( "litellm.images.main.get_llm_provider", diff --git a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py index 34162bba3f..d9b7f14d29 100644 --- a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py +++ b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py @@ -180,6 +180,110 @@ def test_use_custom_pricing_not_detected_litellm_metadata_no_pricing(): assert use_custom_pricing_for_model(litellm_params) is False +class TestUpdateFromKwargs: + """Tests for the update_from_kwargs convenience wrapper.""" + + def test_extracts_metadata_from_kwargs(self, logging_obj): + metadata = {"user_api_key": "sk-test", "model_info": {"id": "abc"}} + kwargs = {"metadata": metadata, "other_key": "ignored"} + + logging_obj.update_from_kwargs( + kwargs=kwargs, + litellm_params={"litellm_call_id": "call-1"}, + ) + + assert logging_obj.litellm_params["metadata"] == metadata + assert logging_obj.litellm_params["litellm_call_id"] == "call-1" + + def test_extracts_litellm_metadata_from_kwargs(self, logging_obj): + lm_meta = { + "model_info": { + "id": "deploy-1", + "input_cost_per_token": 0.001, + "output_cost_per_token": 0.002, + } + } + kwargs = {"litellm_metadata": lm_meta} + + logging_obj.update_from_kwargs( + kwargs=kwargs, + litellm_params={"litellm_call_id": "call-2"}, + ) + + assert logging_obj.litellm_params["litellm_metadata"] == lm_meta + assert logging_obj.litellm_params["litellm_call_id"] == "call-2" + + def test_backfills_metadata_from_litellm_metadata(self, logging_obj): + """When only litellm_metadata is present, metadata should be backfilled.""" + lm_meta = {"model_info": {"id": "deploy-1"}} + kwargs = {"litellm_metadata": lm_meta} + + logging_obj.update_from_kwargs(kwargs=kwargs) + + assert logging_obj.litellm_params["metadata"] == lm_meta + + def test_no_backfill_when_metadata_already_present(self, logging_obj): + metadata = {"user_api_key": "sk-real"} + lm_meta = {"model_info": {"id": "deploy-1"}} + kwargs = {"metadata": metadata, "litellm_metadata": lm_meta} + + logging_obj.update_from_kwargs(kwargs=kwargs) + + assert logging_obj.litellm_params["metadata"] == metadata + assert logging_obj.litellm_params["litellm_metadata"] == lm_meta + + def test_caller_litellm_params_win_over_kwargs(self, logging_obj): + """Explicit litellm_params from the caller should override auto-extracted values.""" + kwargs = {"metadata": {"from_kwargs": True}} + + logging_obj.update_from_kwargs( + kwargs=kwargs, + litellm_params={"metadata": {"from_caller": True}, "litellm_call_id": "x"}, + ) + + assert logging_obj.litellm_params["metadata"] == {"from_caller": True} + + def test_custom_pricing_detected_via_litellm_metadata(self, logging_obj): + """Custom pricing in litellm_metadata.model_info should set custom_pricing flag.""" + from litellm.litellm_core_utils.litellm_logging import ( + use_custom_pricing_for_model, + ) + + lm_meta = { + "model_info": { + "id": "deploy-custom", + "input_cost_per_token": 0.005, + "output_cost_per_token": 0.015, + } + } + kwargs = {"litellm_metadata": lm_meta} + + logging_obj.update_from_kwargs(kwargs=kwargs) + + assert use_custom_pricing_for_model(logging_obj.litellm_params) is True + + def test_additional_params_forwarded(self, logging_obj): + kwargs = {"metadata": {}} + logging_obj.update_from_kwargs( + kwargs=kwargs, + model="gpt-5", + user="test-user", + optional_params={"temperature": 0.7}, + custom_llm_provider="openai", + ) + + assert logging_obj.model == "gpt-5" + assert logging_obj.user == "test-user" + assert logging_obj.model_call_details["custom_llm_provider"] == "openai" + + def test_empty_kwargs_no_error(self, logging_obj): + logging_obj.update_from_kwargs( + kwargs={}, + litellm_params={"litellm_call_id": "call-empty"}, + ) + assert logging_obj.litellm_params["litellm_call_id"] == "call-empty" + + def test_logging_prevent_double_logging(logging_obj): """ When using a bridge, log only once from the underlying bridge call. diff --git a/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py b/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py index c418650aa8..a3512bc6e7 100644 --- a/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py +++ b/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py @@ -156,12 +156,11 @@ async def test_async_anthropic_messages_handler_extra_headers(): @pytest.mark.asyncio async def test_async_anthropic_messages_handler_passes_litellm_metadata(): - """Ensure litellm_metadata from kwargs is included in litellm_params - passed to update_environment_variables. + """Ensure litellm_metadata from kwargs is forwarded via update_from_kwargs. Routes like /messages store model_info under kwargs['litellm_metadata']. - The handler must forward this into litellm_params so that - use_custom_pricing_for_model can detect custom pricing. Regression test for #23185. + The handler must forward this so that use_custom_pricing_for_model can + detect custom pricing. Regression test for #23185. """ handler = BaseLLMHTTPHandler() @@ -187,7 +186,7 @@ async def test_async_anthropic_messages_handler_passes_litellm_metadata(): mock_client.post = AsyncMock(return_value=mock_response) mock_logging_obj = Mock() - mock_logging_obj.update_environment_variables = Mock() + mock_logging_obj.update_from_kwargs = Mock() mock_logging_obj.model_call_details = {} mock_logging_obj.stream = False @@ -218,14 +217,14 @@ async def test_async_anthropic_messages_handler_passes_litellm_metadata(): except Exception: pass - mock_logging_obj.update_environment_variables.assert_called_once() - call_kwargs = mock_logging_obj.update_environment_variables.call_args - litellm_params_arg = call_kwargs.kwargs.get( - "litellm_params", call_kwargs[1].get("litellm_params", {}) - ) if call_kwargs.kwargs else call_kwargs[1].get("litellm_params", {}) + mock_logging_obj.update_from_kwargs.assert_called_once() + call_kwargs = mock_logging_obj.update_from_kwargs.call_args + kwargs_arg = call_kwargs.kwargs.get( + "kwargs", call_kwargs[1].get("kwargs", {}) + ) if call_kwargs.kwargs else call_kwargs[1].get("kwargs", {}) - assert "litellm_metadata" in litellm_params_arg - assert litellm_params_arg["litellm_metadata"]["model_info"] == custom_model_info + assert "litellm_metadata" in kwargs_arg + assert kwargs_arg["litellm_metadata"]["model_info"] == custom_model_info @pytest.mark.asyncio