Root cause fix - migrate all logging update to use 1 function - for centralized kwarg updates (#23659)

* fix: Fixes https://github.com/BerriAI/litellm/issues/23185

* fix(responses/main.py): ensure litellm metadata custom cost works

* refactor: move all logging updates to a common function, to have just 1 place to update logging kwarg updates
This commit is contained in:
Krish Dholakia 2026-03-15 23:21:01 -07:00 committed by GitHub
parent 548e7ebd60
commit ca4329aeb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 383 additions and 198 deletions

View File

@ -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(),

View File

@ -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},

View File

@ -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={

View File

@ -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={

View File

@ -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={

View File

@ -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,
)

View File

@ -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},

View File

@ -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

View File

@ -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,

View File

@ -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},

View File

@ -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={

View File

@ -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={},

View File

@ -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),

View File

@ -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={},

View File

@ -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={

View File

@ -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={

View File

@ -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,

View File

@ -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},

View File

@ -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),

View File

@ -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",

View File

@ -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.

View File

@ -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