From 70fe418f197c20883246aa763699ebd5495b90e3 Mon Sep 17 00:00:00 2001 From: VeeraBabu Jakkula Date: Thu, 22 Oct 2026 15:16:57 +0530 Subject: [PATCH 1/2] fix(cost_calculator): resolve pricing when base_model provider differs from transport (#47908) --- litellm/cost_calculator.py | 21 ++++++- litellm/litellm_core_utils/litellm_logging.py | 39 ++++++++++--- .../test_spend_tracking_utils.py | 58 +++++++++++++++++++ 3 files changed, 108 insertions(+), 10 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 550bbfcde98..65f2904c13c 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -2457,9 +2457,26 @@ def pricing_entry_for_cost_calc( ) if bedrock_model_info is not None: return bedrock_model_info["key"], bedrock_model_info - candidates: Final = (selected_model, _get_response_model(completion_response), model) + base_model_provider: Final = ( + _base_model_pricing_provider( + base_model, + _get_provider_for_cost_calc( + model=model, + custom_llm_provider=custom_llm_provider if isinstance(custom_llm_provider, str) else None, + ), + ) + if base_model is not None and custom_pricing is not True + else None + ) + pricing_provider: Final = base_model_provider or custom_llm_provider + candidates: Final = ( + (selected_model, pricing_provider), + (_get_response_model(completion_response), custom_llm_provider), + (model, custom_llm_provider), + *(((selected_model, None),) if base_model is not None else ()), + ) resolved: Final = next( - (info for info in (_cost_map_model_info(name, custom_llm_provider) for name in candidates if name) if info), + (info for info in (_cost_map_model_info(name, provider) for name, provider in candidates if name) if info), None, ) if resolved is None: diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index d75d35e4531..6e7d3f3c806 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -51,6 +51,7 @@ from litellm.constants import ( from litellm.cost_calculator import ( RealtimeAPITokenUsageProcessor, ResponsesWebSocketTokenUsageProcessor, + _base_model_pricing_provider, get_usage_object, pricing_entry_for_cost_calc, select_model_name_for_cost_calc, @@ -6690,13 +6691,19 @@ class StandardLoggingPayloadSetup: if router_model_id is not None else model_cost_name ) + model_cost_information: StandardLoggingModelInformation if model_cost_name is None: model_cost_information = StandardLoggingModelInformation(model_map_key="", model_map_value=None) else: + provider_for_lookup: Final[str | None] = ( + _base_model_pricing_provider(base_model, custom_llm_provider) + if base_model is not None and not custom_pricing + else custom_llm_provider + ) try: _model_cost_information: Final = litellm.get_model_info( model=pricing_model_name or model_cost_name, - custom_llm_provider=custom_llm_provider, + custom_llm_provider=provider_for_lookup, api_base=api_base, ) model_cost_information = StandardLoggingModelInformation( @@ -6704,13 +6711,29 @@ class StandardLoggingPayloadSetup: model_map_value=_model_cost_information, ) except Exception: - verbose_logger.debug( # keep in debug otherwise it will trigger on every call - "Model=%s is not mapped in model cost map. Defaulting to None model_cost_information for standard_logging_payload", - model_cost_name, - ) - model_cost_information = StandardLoggingModelInformation( - model_map_key=model_cost_name, model_map_value=None - ) + if provider_for_lookup is not None and base_model is not None: + try: + _fallback_info: Final = litellm.get_model_info( + model=pricing_model_name or model_cost_name, + custom_llm_provider=None, + api_base=api_base, + ) + model_cost_information = StandardLoggingModelInformation( + model_map_key=model_cost_name, + model_map_value=_fallback_info, + ) + except Exception: + model_cost_information = StandardLoggingModelInformation( + model_map_key=model_cost_name, model_map_value=None + ) + else: + verbose_logger.debug( # keep in debug otherwise it will trigger on every call + "Model=%s is not mapped in model cost map. Defaulting to None model_cost_information for standard_logging_payload", + model_cost_name, + ) + model_cost_information = StandardLoggingModelInformation( + model_map_key=model_cost_name, model_map_value=None + ) return model_cost_information @staticmethod diff --git a/tests/unit/proxy/spend_tracking/test_spend_tracking_utils.py b/tests/unit/proxy/spend_tracking/test_spend_tracking_utils.py index 78f5e1ace0c..192aa93c4c3 100644 --- a/tests/unit/proxy/spend_tracking/test_spend_tracking_utils.py +++ b/tests/unit/proxy/spend_tracking/test_spend_tracking_utils.py @@ -7400,3 +7400,61 @@ def test_get_logging_payload_stores_end_user_only_while_end_user_cost_tracking_i end_time=datetime.datetime.now(timezone.utc), ) assert payload["end_user"] == expected_end_user + + + +def test_base_model_different_custom_llm_provider_resolves_pricing() -> None: + """ + Verify pricing_entry_for_cost_calc and get_model_cost_information resolve + when base_model names a priced model whose provider differs from the transport provider (#47908). + """ + from litellm.cost_calculator import completion_cost, pricing_entry_for_cost_calc + + model: Final = "glm-5.3-flash" + base_model: Final = "zai/glm-5.3-flash" + custom_llm_provider: Final = "openai" + + response = { + "model": model, + "usage": { + "prompt_tokens": 1000, + "completion_tokens": 100, + "total_tokens": 1100, + }, + } + + # 1. pricing_entry_for_cost_calc resolves to the base_model key + entry: Final = pricing_entry_for_cost_calc( + model=model, + completion_response=response, + custom_llm_provider=custom_llm_provider, + custom_pricing=False, + base_model=base_model, + router_model_id=None, + region_name=None, + litellm_logging_obj=None, + ) + assert entry is not None + assert entry[0] == base_model + assert entry[1]["input_cost_per_token"] == 1.5e-07 + + # 2. StandardLoggingPayloadSetup.get_model_cost_information populates model_map_value + info: Final = StandardLoggingPayloadSetup.get_model_cost_information( + base_model=base_model, + custom_pricing=False, + custom_llm_provider=custom_llm_provider, + init_response_obj=response, + ) + assert info.get("model_map_key") == base_model + assert info.get("model_map_value") is not None + assert info["model_map_value"]["key"] == base_model + + # 3. completion_cost computes correct USD cost using base_model pricing + cost: Final = completion_cost( + completion_response=response, + model=model, + custom_llm_provider=custom_llm_provider, + base_model=base_model, + ) + expected_cost: Final = 1000 * 1.5e-07 + 100 * 5e-07 + assert cost == pytest.approx(expected_cost) From 8f310e37dc86acb7a5851dfee0d05ed9c06cfc90 Mon Sep 17 00:00:00 2001 From: VeeraBabu Jakkula Date: Thu, 29 Oct 2026 16:54:28 +0530 Subject: [PATCH 2/2] refactor(logging): replace nested blind except in model cost lookup with a helper --- litellm/litellm_core_utils/litellm_logging.py | 71 ++++++++----------- 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 6e7d3f3c806..318c2490a2f 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -6665,6 +6665,13 @@ class StandardLoggingPayloadSetup: return _raw.model_dump() return _empty + @staticmethod + def _get_model_info_or_none(model: str, custom_llm_provider: str | None, api_base: str | None) -> ModelInfo | None: + try: + return litellm.get_model_info(model=model, custom_llm_provider=custom_llm_provider, api_base=api_base) + except Exception: + return None + @staticmethod def get_model_cost_information( base_model: str | None, @@ -6691,50 +6698,30 @@ class StandardLoggingPayloadSetup: if router_model_id is not None else model_cost_name ) - model_cost_information: StandardLoggingModelInformation if model_cost_name is None: - model_cost_information = StandardLoggingModelInformation(model_map_key="", model_map_value=None) - else: - provider_for_lookup: Final[str | None] = ( - _base_model_pricing_provider(base_model, custom_llm_provider) - if base_model is not None and not custom_pricing - else custom_llm_provider + return StandardLoggingModelInformation(model_map_key="", model_map_value=None) + provider_for_lookup: Final[str | None] = ( + _base_model_pricing_provider(base_model, custom_llm_provider) + if base_model is not None and not custom_pricing + else custom_llm_provider + ) + lookup_model: Final = pricing_model_name or model_cost_name + provider_info: Final = StandardLoggingPayloadSetup._get_model_info_or_none( + model=lookup_model, custom_llm_provider=provider_for_lookup, api_base=api_base + ) + model_info: Final = ( + StandardLoggingPayloadSetup._get_model_info_or_none( + model=lookup_model, custom_llm_provider=None, api_base=api_base ) - try: - _model_cost_information: Final = litellm.get_model_info( - model=pricing_model_name or model_cost_name, - custom_llm_provider=provider_for_lookup, - api_base=api_base, - ) - model_cost_information = StandardLoggingModelInformation( - model_map_key=model_cost_name, - model_map_value=_model_cost_information, - ) - except Exception: - if provider_for_lookup is not None and base_model is not None: - try: - _fallback_info: Final = litellm.get_model_info( - model=pricing_model_name or model_cost_name, - custom_llm_provider=None, - api_base=api_base, - ) - model_cost_information = StandardLoggingModelInformation( - model_map_key=model_cost_name, - model_map_value=_fallback_info, - ) - except Exception: - model_cost_information = StandardLoggingModelInformation( - model_map_key=model_cost_name, model_map_value=None - ) - else: - verbose_logger.debug( # keep in debug otherwise it will trigger on every call - "Model=%s is not mapped in model cost map. Defaulting to None model_cost_information for standard_logging_payload", - model_cost_name, - ) - model_cost_information = StandardLoggingModelInformation( - model_map_key=model_cost_name, model_map_value=None - ) - return model_cost_information + if provider_info is None and provider_for_lookup is not None and base_model is not None + else provider_info + ) + if model_info is None: + verbose_logger.debug( # keep in debug otherwise it will trigger on every call + "Model=%s is not mapped in model cost map. Defaulting to None model_cost_information for standard_logging_payload", + model_cost_name, + ) + return StandardLoggingModelInformation(model_map_key=model_cost_name, model_map_value=model_info) @staticmethod def get_final_response_obj(response_obj: dict, init_response_obj: object, kwargs: dict) -> dict | str | list | None: