diff --git a/litellm/utils.py b/litellm/utils.py index dd35c17809f..e6926b6780a 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -3124,6 +3124,15 @@ _CACHE_PRICING_FIELDS: Final = ( "cache_read_input_token_cost_above_200k_tokens", ) +_PROVIDERS_WITH_RESPONSE_REPORTED_COST: Final = frozenset({"openrouter", "perplexity"}) + + +def _has_response_reported_cost(key: str, provider: object) -> bool: + provider_name: Final = provider.lower() if isinstance(provider, str) else "" + if provider_name in _PROVIDERS_WITH_RESPONSE_REPORTED_COST: + return True + return key.partition("/")[0].lower() in _PROVIDERS_WITH_RESPONSE_REPORTED_COST + def _resolve_builtin_model_cost_entry(key: str, provider: str) -> dict[str, object] | None: """Best-effort lookup of a built-in ``model_cost`` entry for a custom key @@ -3296,11 +3305,13 @@ def register_model( _runtime_registered_model_cost[_registered_key] = dict(_registered_value) # mutable-ok: caller-owned _skip_get_model_info_providers: Final = PROVIDERS_THAT_AUTHENTICATE_ON_PROVIDER_INFO + warned_model_names: set[str] = set() for key, value in loaded_model_cost.items(): ## get model info ## provider = value.get("litellm_provider", "") _key_str = str(key) + warning_model_name: Final = str(warning_display_name or key) if provider in _skip_get_model_info_providers or any( _key_str.startswith(f"{p}/") for p in _skip_get_model_info_providers ): @@ -3329,11 +3340,17 @@ def register_model( and ( value.get("input_cost_per_token") is not None or value.get("output_cost_per_token") is not None ) + and "*" not in _key_str + and "*" not in warning_model_name + and not _has_response_reported_cost(key=_key_str, provider=provider) + and not _has_response_reported_cost(key=warning_model_name, provider=provider) ): - verbose_logger.warning( - "register_model: model=%s has custom pricing but not in built-in cost map and no prefix/region variant matched; cache_creation_input_token_cost and cache_read_input_token_cost will default to 0 for this model (input/output cost tracking is unaffected). To track cache cost, add them to model_info", - warning_display_name or key, - ) + if warning_model_name not in warned_model_names: + verbose_logger.warning( + "register_model: model=%s has custom pricing but not in built-in cost map and no prefix/region variant matched; cache_creation_input_token_cost and cache_read_input_token_cost will default to 0 for this model (input/output cost tracking is unaffected). To track cache cost, add them to model_info", + warning_model_name, + ) + warned_model_names.add(warning_model_name) # ``get_model_info`` returns ``litellm_provider: None`` when the # provider is unknown (e.g. custom deployments registered via # ``Router.add_deployment``). Persisting that None into diff --git a/tests/test_litellm/test_register_model_custom_pricing.py b/tests/test_litellm/test_register_model_custom_pricing.py index 452a15334ef..35acc215d5e 100644 --- a/tests/test_litellm/test_register_model_custom_pricing.py +++ b/tests/test_litellm/test_register_model_custom_pricing.py @@ -14,7 +14,6 @@ import os import pytest - import litellm from litellm.main import _build_custom_pricing_entry from litellm.utils import _invalidate_model_cost_lowercase_map @@ -435,6 +434,120 @@ def test_register_model_warns_when_no_builtin_match_for_cache_pricing(caplog): litellm.model_cost.pop(registered_key, None) +def test_register_model_does_not_warn_for_wildcard_models(caplog): + import logging + + from litellm._logging import verbose_logger + + registered_key = "lit39169-wildcard/*" + litellm.model_cost.pop(registered_key, None) + + try: + with caplog.at_level(logging.WARNING, logger=verbose_logger.name): + litellm.register_model( + { + registered_key: { + "input_cost_per_token": 0.001, + "output_cost_per_token": 0.002, + "litellm_provider": "custom", + } + } + ) + + assert not any("register_model" in record.message for record in caplog.records) + finally: + litellm.model_cost.pop(registered_key, None) + + +def test_router_openrouter_wildcard_does_not_warn(caplog): + import logging + + from litellm import Router + from litellm._logging import verbose_logger + + deployment_model = "openrouter/*" + deployment_id = "lit39169-openrouter-wildcard" + snapshot = _snapshot_model_cost_entries([deployment_model, deployment_id]) + + try: + with caplog.at_level(logging.WARNING, logger=verbose_logger.name): + router = Router( + model_list=[ + { + "model_name": "all-openrouter", + "litellm_params": { + "model": deployment_model, + "api_key": "fake-key", + "input_cost_per_token": 0.0, + "output_cost_per_token": 0.0, + }, + "model_info": {"id": deployment_id}, + } + ] + ) + + assert not any("register_model" in record.message for record in caplog.records) + finally: + _restore_model_cost_entries(snapshot) + del router + + +def test_register_model_does_not_warn_for_response_priced_providers(caplog): + import logging + + from litellm._logging import verbose_logger + + registered_key = "openrouter/lit39169-unmapped-model" + litellm.model_cost.pop(registered_key, None) + + try: + with caplog.at_level(logging.WARNING, logger=verbose_logger.name): + litellm.register_model( + { + registered_key: { + "input_cost_per_token": 0.001, + "output_cost_per_token": 0.002, + "litellm_provider": "openrouter", + } + } + ) + + assert not any("register_model" in record.message for record in caplog.records) + finally: + litellm.model_cost.pop(registered_key, None) + + +def test_register_model_warns_once_for_duplicate_model_aliases(caplog): + import logging + + from litellm._logging import verbose_logger + + registered_keys = ["lit39169-deployment-id", "bedrock/lit39169-unmapped-model"] + for key in registered_keys: + litellm.model_cost.pop(key, None) + + try: + with caplog.at_level(logging.WARNING, logger=verbose_logger.name): + litellm.register_model( + { + key: { + "input_cost_per_token": 0.001, + "output_cost_per_token": 0.002, + "litellm_provider": "bedrock", + } + for key in registered_keys + }, + warning_display_name=registered_keys[1], + ) + + warnings = [record.message for record in caplog.records if "register_model" in record.message] + assert len(warnings) == 1 + assert registered_keys[1] in warnings[0] + finally: + for key in registered_keys: + litellm.model_cost.pop(key, None) + + def test_register_model_no_warning_without_custom_pricing(caplog): """LIT-6318: an entry with no custom pricing (e.g. router deployment metadata) never drives cost calculation, so registering it under an