diff --git a/.circleci/config.yml b/.circleci/config.yml index e3593e81544..a29b76110c3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -208,6 +208,7 @@ jobs: -e AZURE_EUROPE_API_KEY=$AZURE_EUROPE_API_KEY \ -e MISTRAL_API_KEY=$MISTRAL_API_KEY \ -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ + -e GROQ_API_KEY=$GROQ_API_KEY \ -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ -e AWS_REGION_NAME=$AWS_REGION_NAME \ -e AUTO_INFER_REGION=True \ diff --git a/docs/my-website/docs/providers/anthropic.md b/docs/my-website/docs/providers/anthropic.md index 496343f8792..2227b7a6b51 100644 --- a/docs/my-website/docs/providers/anthropic.md +++ b/docs/my-website/docs/providers/anthropic.md @@ -82,6 +82,47 @@ model_list: ```bash litellm --config /path/to/config.yaml ``` + + + +Use this if you want to make requests to `claude-3-haiku-20240307`,`claude-3-opus-20240229`,`claude-2.1` without defining them on the config.yaml + +#### Required env variables +``` +ANTHROPIC_API_KEY=sk-ant**** +``` + +```yaml +model_list: + - model_name: "*" + litellm_params: + model: "*" +``` + +```bash +litellm --config /path/to/config.yaml +``` + +Example Request for this config.yaml + +**Ensure you use `anthropic/` prefix to route the request to Anthropic API** + +```shell +curl --location 'http://0.0.0.0:4000/chat/completions' \ +--header 'Content-Type: application/json' \ +--data ' { + "model": "anthropic/claude-3-haiku-20240307", + "messages": [ + { + "role": "user", + "content": "what llm are you" + } + ] + } +' +``` + + diff --git a/docs/my-website/docs/proxy/configs.md b/docs/my-website/docs/proxy/configs.md index ecd82375e55..cb0841c60c5 100644 --- a/docs/my-website/docs/proxy/configs.md +++ b/docs/my-website/docs/proxy/configs.md @@ -59,6 +59,13 @@ model_list: rpm: 1440 model_info: version: 2 + + # Use this if you want to make requests to `claude-3-haiku-20240307`,`claude-3-opus-20240229`,`claude-2.1` without defining them on the config.yaml + # Default models + # Works for ALL Providers and needs the default provider credentials in .env + - model_name: "*" + litellm_params: + model: "*" litellm_settings: # module level litellm settings - https://github.com/BerriAI/litellm/blob/main/litellm/__init__.py drop_params: True diff --git a/litellm/proxy/proxy_config.yaml b/litellm/proxy/proxy_config.yaml index 8dc03d6e00b..4df51039966 100644 --- a/litellm/proxy/proxy_config.yaml +++ b/litellm/proxy/proxy_config.yaml @@ -8,6 +8,13 @@ model_list: litellm_params: model: fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct api_key: "os.environ/FIREWORKS" + - model_name: "*" + litellm_params: + model: "*" + - model_name: "*" + litellm_params: + model: openai/* + api_key: os.environ/OPENAI_API_KEY - model_name: mistral-small-latest litellm_params: model: mistral/mistral-small-latest diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index bad1abae286..e75f99f31da 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -2894,6 +2894,12 @@ async def chat_completion( llm_router is not None and data["model"] in llm_router.deployment_names ): # model in router deployments, calling a specific deployment on the router tasks.append(llm_router.acompletion(**data, specific_deployment=True)) + elif ( + llm_router is not None + and data["model"] not in router_model_names + and llm_router.router_general_settings.pass_through_all_models is True + ): + tasks.append(litellm.acompletion(**data)) elif ( llm_router is not None and data["model"] not in router_model_names @@ -3154,6 +3160,12 @@ async def completion( llm_router is not None and data["model"] in llm_router.get_model_ids() ): # model in router model list llm_response = asyncio.create_task(llm_router.atext_completion(**data)) + elif ( + llm_router is not None + and data["model"] not in router_model_names + and llm_router.router_general_settings.pass_through_all_models is True + ): + llm_response = asyncio.create_task(litellm.atext_completion(**data)) elif ( llm_router is not None and data["model"] not in router_model_names @@ -3414,6 +3426,12 @@ async def embeddings( llm_router is not None and data["model"] in llm_router.get_model_ids() ): # model in router deployments, calling a specific deployment on the router tasks.append(llm_router.aembedding(**data)) + elif ( + llm_router is not None + and data["model"] not in router_model_names + and llm_router.router_general_settings.pass_through_all_models is True + ): + tasks.append(litellm.aembedding(**data)) elif ( llm_router is not None and data["model"] not in router_model_names diff --git a/litellm/router.py b/litellm/router.py index 53013a75941..d1198aa154e 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -174,7 +174,9 @@ class Router: routing_strategy_args: dict = {}, # just for latency-based routing semaphore: Optional[asyncio.Semaphore] = None, alerting_config: Optional[AlertingConfig] = None, - router_general_settings: Optional[RouterGeneralSettings] = None, + router_general_settings: Optional[ + RouterGeneralSettings + ] = RouterGeneralSettings(), ) -> None: """ Initialize the Router class with the given parameters for caching, reliability, and routing strategy. @@ -253,8 +255,8 @@ class Router: verbose_router_logger.setLevel(logging.INFO) elif debug_level == "DEBUG": verbose_router_logger.setLevel(logging.DEBUG) - self.router_general_settings: Optional[RouterGeneralSettings] = ( - router_general_settings + self.router_general_settings: RouterGeneralSettings = ( + router_general_settings or RouterGeneralSettings() ) self.assistants_config = assistants_config @@ -3554,7 +3556,11 @@ class Router: # Check if user is trying to use model_name == "*" # this is a catch all model for their specific api key if deployment.model_name == "*": - self.default_deployment = deployment.to_json(exclude_none=True) + if deployment.litellm_params.model == "*": + # user wants to pass through all requests to litellm.acompletion for unknown deployments + self.router_general_settings.pass_through_all_models = True + else: + self.default_deployment = deployment.to_json(exclude_none=True) # Azure GPT-Vision Enhancements, users can pass os.environ/ data_sources = deployment.litellm_params.get("dataSources", []) or [] diff --git a/litellm/tests/test_get_llm_provider.py b/litellm/tests/test_get_llm_provider.py index 3ec867af444..6f53b0f8fec 100644 --- a/litellm/tests/test_get_llm_provider.py +++ b/litellm/tests/test_get_llm_provider.py @@ -25,6 +25,11 @@ def test_get_llm_provider(): # test_get_llm_provider() +def test_get_llm_provider_catch_all(): + _, response, _, _ = litellm.get_llm_provider(model="*") + assert response == "openai" + + def test_get_llm_provider_gpt_instruct(): _, response, _, _ = litellm.get_llm_provider(model="gpt-3.5-turbo-instruct-0914") diff --git a/litellm/types/router.py b/litellm/types/router.py index 78dfbc4c195..285732121c0 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -540,3 +540,6 @@ class RouterGeneralSettings(BaseModel): async_only_mode: bool = Field( default=False ) # this will only initialize async clients. Good for memory utils + pass_through_all_models: bool = Field( + default=False + ) # if passed a model not llm_router model list, pass through the request to litellm.acompletion/embedding diff --git a/litellm/utils.py b/litellm/utils.py index 87f50f5ed35..eecc704b719 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -4667,6 +4667,8 @@ def get_llm_provider( custom_llm_provider = "openai" elif model in litellm.empower_models: custom_llm_provider = "empower" + elif model == "*": + custom_llm_provider = "openai" if custom_llm_provider is None or custom_llm_provider == "": if litellm.suppress_debug_info == False: print() # noqa diff --git a/proxy_server_config.yaml b/proxy_server_config.yaml index 5ee7192c88d..f7766b65bfe 100644 --- a/proxy_server_config.yaml +++ b/proxy_server_config.yaml @@ -85,6 +85,13 @@ model_list: litellm_params: model: openai/* api_key: os.environ/OPENAI_API_KEY + + # Pass through all llm requests to litellm.completion/litellm.embedding + # if user passes model="anthropic/claude-3-opus-20240229" proxy will make requests to anthropic claude-3-opus-20240229 using ANTHROPIC_API_KEY + - model_name: "*" + litellm_params: + model: "*" + - model_name: mistral-embed litellm_params: model: mistral/mistral-embed diff --git a/tests/test_openai_endpoints.py b/tests/test_openai_endpoints.py index 59ac1055262..a77da8d52ca 100644 --- a/tests/test_openai_endpoints.py +++ b/tests/test_openai_endpoints.py @@ -7,6 +7,9 @@ from openai import OpenAI, AsyncOpenAI from typing import Optional, List, Union +LITELLM_MASTER_KEY = "sk-1234" + + def response_header_check(response): """ - assert if response headers < 4kb (nginx limit). @@ -467,6 +470,22 @@ async def test_openai_wildcard_chat_completion(): await chat_completion(session=session, key=key, model="gpt-3.5-turbo-0125") +@pytest.mark.asyncio +async def test_proxy_all_models(): + """ + - proxy_server_config.yaml has model = * / * + - Make chat completion call + - groq is NOT defined on /models + + + """ + async with aiohttp.ClientSession() as session: + # call chat/completions with a model that the key was not created for + the model is not on the config.yaml + await chat_completion( + session=session, key=LITELLM_MASTER_KEY, model="groq/llama3-8b-8192" + ) + + @pytest.mark.asyncio async def test_batch_chat_completions(): """