diff --git a/litellm/proxy/auth/model_checks.py b/litellm/proxy/auth/model_checks.py index dad1a6a7109..4e9154d50dd 100644 --- a/litellm/proxy/auth/model_checks.py +++ b/litellm/proxy/auth/model_checks.py @@ -4,12 +4,11 @@ from typing import Dict, List, Optional, Set import litellm from litellm._logging import verbose_proxy_logger -from litellm.llms.openai_like.json_loader import JSONProviderRegistry +from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider from litellm.proxy._types import SpecialModelNames, UserAPIKeyAuth from litellm.router import Router from litellm.router_utils.fallback_event_handlers import get_fallback_model_group from litellm.types.router import LiteLLM_Params -from litellm.types.utils import LlmProvidersSet from litellm.utils import get_valid_models @@ -66,25 +65,23 @@ def _get_models_from_access_groups( return all_models -def _is_known_provider_qualified_model(model: str) -> bool: +def _is_resolvable_dynamic_model(model: str) -> bool: """ - Return True for provider-qualified model identifiers such as: - - openai/gpt-4o-mini - - bedrock/us.amazon.nova-micro-v1:0 - - openrouter/auto + Return True for model identifiers that LiteLLM can still resolve even when + they are not present in the static model list. - This intentionally allows provider-prefixed models even when the exact - model name is not yet in LiteLLM's static model list, since those routes - may still be valid for pass-through / BYOK flows. + This covers: + - provider-qualified routes such as `bedrock/us.amazon.nova-micro-v1:0` + or JSON-registry providers such as `publicai/some-new-model` + - dynamic model identifiers such as OpenAI fine-tunes + (`ft:gpt-4o:my-org:custom:id`) """ - if "/" not in model: + try: + get_llm_provider(model=model) + return True + except Exception: return False - provider_prefix = model.split("/", 1)[0] - return provider_prefix in LlmProvidersSet or JSONProviderRegistry.exists( - provider_prefix - ) - def _should_include_model_in_complete_list( model: str, @@ -99,11 +96,12 @@ def _should_include_model_in_complete_list( - configured proxy model groups - configured access group names - known base model IDs (e.g. gpt-4o-mini) - - known provider-qualified routes (e.g. openai/gpt-4o-mini, bedrock/*) + - provider-qualified or other dynamically resolvable model IDs + (e.g. openai/gpt-4o-mini, publicai/foo, ft:gpt-4o:org:suffix:id) Drop: - arbitrary strings that don't resolve to a proxy model, access group, or - a recognized LiteLLM/provider model route. + a recognized LiteLLM model route. """ if model in ( SpecialModelNames.all_proxy_models.value, @@ -120,7 +118,7 @@ def _should_include_model_in_complete_list( if model == "*": return True - if _is_known_provider_qualified_model(model): + if _is_resolvable_dynamic_model(model): return True return False diff --git a/tests/test_litellm/proxy/auth/test_model_checks.py b/tests/test_litellm/proxy/auth/test_model_checks.py index 222631b6aae..18b553156e7 100644 --- a/tests/test_litellm/proxy/auth/test_model_checks.py +++ b/tests/test_litellm/proxy/auth/test_model_checks.py @@ -312,3 +312,43 @@ def test_get_complete_model_list_keeps_provider_qualified_models(): ) assert result == ["bedrock/very_new_model"] + + +def test_get_complete_model_list_keeps_json_registry_provider_models(): + """ + JSON-registry-only providers should also survive filtering even when the + exact model name is not present in LiteLLM's static model list. + """ + from litellm.proxy.auth.model_checks import get_complete_model_list + + result = get_complete_model_list( + key_models=["publicai/some-new-model"], + team_models=[], + proxy_model_list=[], + user_model=None, + infer_model_from_keys=False, + model_access_groups={}, + ) + + assert result == ["publicai/some-new-model"] + + +def test_get_complete_model_list_keeps_openai_finetune_model_ids(): + """ + OpenAI fine-tuned model IDs are dynamic valid models and should remain in + the final list even though they are not in LiteLLM's static model list. + """ + from litellm.proxy.auth.model_checks import get_complete_model_list + + finetuned_model = "ft:gpt-4o:my-org:custom-suffix:model-id" + + result = get_complete_model_list( + key_models=[finetuned_model], + team_models=[], + proxy_model_list=[], + user_model=None, + infer_model_from_keys=False, + model_access_groups={}, + ) + + assert result == [finetuned_model]