From b93a355b051f3f721db23e8ff59908366fa933d3 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 13:37:14 -0800 Subject: [PATCH 01/16] (fix) user_custom_auth --- litellm/proxy/proxy_server.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index df4dec756..e10a2f3d3 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -252,12 +252,13 @@ async def user_api_key_auth( ) -> UserAPIKeyAuth: global master_key, prisma_client, llm_model_list, user_custom_auth, custom_db_client try: - if isinstance(api_key, str): - api_key = _get_bearer_token(api_key=api_key) - ### USER-DEFINED AUTH FUNCTION ### + ### USER-DEFINED AUTH FUNCTION -> This should always be run first if a user has defined it ### if user_custom_auth is not None: response = await user_custom_auth(request=request, api_key=api_key) return UserAPIKeyAuth.model_validate(response) + + if isinstance(api_key, str): + api_key = _get_bearer_token(api_key=api_key) ### LITELLM-DEFINED AUTH FUNCTION ### if master_key is None: if isinstance(api_key, str): From 5ee62b25f56d894389e06cda365c9e8bc5c7e09c Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 13:47:00 -0800 Subject: [PATCH 02/16] (fix) passed_in_key --- litellm/proxy/proxy_server.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index e10a2f3d3..0e8425998 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -234,8 +234,10 @@ def usage_telemetry( def _get_bearer_token(api_key: str): - assert api_key.startswith("Bearer ") # ensure Bearer token passed in - api_key = api_key.replace("Bearer ", "") # extract the token + if api_key.startswith("Bearer "): # ensure Bearer token passed in + api_key = api_key.replace("Bearer ", "") # extract the token + else: + api_key = "" return api_key @@ -252,13 +254,21 @@ async def user_api_key_auth( ) -> UserAPIKeyAuth: global master_key, prisma_client, llm_model_list, user_custom_auth, custom_db_client try: + if isinstance(api_key, str): + passed_in_key = api_key + api_key = _get_bearer_token(api_key=api_key) + ### USER-DEFINED AUTH FUNCTION -> This should always be run first if a user has defined it ### if user_custom_auth is not None: response = await user_custom_auth(request=request, api_key=api_key) return UserAPIKeyAuth.model_validate(response) - if isinstance(api_key, str): - api_key = _get_bearer_token(api_key=api_key) + if api_key == "": + # missing 'Bearer ' prefix + raise Exception( + f"Malformed API Key passed in. Ensure Key has `Bearer ` prefix. Passed in: {passed_in_key}" + ) + ### LITELLM-DEFINED AUTH FUNCTION ### if master_key is None: if isinstance(api_key, str): From e6bc73d13127c1e13d9cc1ceb3c6d2a8ba9a9cba Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 13:55:50 -0800 Subject: [PATCH 03/16] (test) missing Bearer in custom auth --- litellm/tests/test_proxy_custom_auth.py | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/litellm/tests/test_proxy_custom_auth.py b/litellm/tests/test_proxy_custom_auth.py index b6b833e17..55ab45624 100644 --- a/litellm/tests/test_proxy_custom_auth.py +++ b/litellm/tests/test_proxy_custom_auth.py @@ -65,3 +65,30 @@ def test_custom_auth(client): assert e.code == 401 assert e.message == "Authentication Error, Failed custom auth" pass + + +def test_custom_auth_bearer(client): + try: + # Your test data + test_data = { + "model": "openai-model", + "messages": [ + {"role": "user", "content": "hi"}, + ], + "max_tokens": 10, + } + # Your bearer token + token = os.getenv("PROXY_MASTER_KEY") + + headers = {"Authorization": f"WITHOUT BEAR Er {token}"} + response = client.post("/chat/completions", json=test_data, headers=headers) + pytest.fail("LiteLLM Proxy test failed. This request should have been rejected") + except Exception as e: + print(vars(e)) + print("got an exception") + assert e.code == 401 + assert ( + e.message + == "Authentication Error, CustomAuth - Malformed API Key passed in. Ensure Key has `Bearer` prefix" + ) + pass From 1b4b5185bba87e2d8492af4046b573357f75a389 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 13:56:54 -0800 Subject: [PATCH 04/16] (test) custom_auth bearer tokens --- litellm/tests/test_configs/custom_auth.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/litellm/tests/test_configs/custom_auth.py b/litellm/tests/test_configs/custom_auth.py index e4747ee53..1b6bec43b 100644 --- a/litellm/tests/test_configs/custom_auth.py +++ b/litellm/tests/test_configs/custom_auth.py @@ -9,8 +9,14 @@ load_dotenv() async def user_api_key_auth(request: Request, api_key: str) -> UserAPIKeyAuth: try: print(f"api_key: {api_key}") + if api_key == "": + raise Exception( + f"CustomAuth - Malformed API Key passed in. Ensure Key has `Bearer` prefix" + ) if api_key == f"{os.getenv('PROXY_MASTER_KEY')}-1234": return UserAPIKeyAuth(api_key=api_key) raise Exception - except: + except Exception as e: + if len(str(e)) > 0: + raise e raise Exception("Failed custom auth") From ce37ead178876e0e316ffc1cbcd2491828fc9d95 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 14:14:13 -0800 Subject: [PATCH 05/16] (fix) add literals to _get_bearer_token --- litellm/proxy/proxy_server.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 0e8425998..54a4a087e 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -2,7 +2,7 @@ import sys, os, platform, time, copy, re, asyncio, inspect import threading, ast import shutil, random, traceback, requests from datetime import datetime, timedelta, timezone -from typing import Optional, List +from typing import Optional, List, Literal import secrets, subprocess import hashlib, uuid import warnings @@ -141,6 +141,11 @@ class ProxyException(Exception): self.code = code +# Literals - used by _get_bearer_token +NO_API_KEY = "no-api-key" +MISSING_BEARER = "missing-bearer" + + @app.exception_handler(ProxyException) async def openai_exception_handler(request: Request, exc: ProxyException): # NOTE: DO NOT MODIFY THIS, its crucial to map to Openai exceptions @@ -233,11 +238,15 @@ def usage_telemetry( ).start() -def _get_bearer_token(api_key: str): +def _get_bearer_token( + api_key: str, +) -> Union[str, Literal["no-api-key", "missing-bearer"]]: + if api_key == "": + return NO_API_KEY if api_key.startswith("Bearer "): # ensure Bearer token passed in api_key = api_key.replace("Bearer ", "") # extract the token else: - api_key = "" + api_key = MISSING_BEARER return api_key @@ -258,17 +267,11 @@ async def user_api_key_auth( passed_in_key = api_key api_key = _get_bearer_token(api_key=api_key) - ### USER-DEFINED AUTH FUNCTION -> This should always be run first if a user has defined it ### + ### USER-DEFINED AUTH FUNCTION ### if user_custom_auth is not None: response = await user_custom_auth(request=request, api_key=api_key) return UserAPIKeyAuth.model_validate(response) - if api_key == "": - # missing 'Bearer ' prefix - raise Exception( - f"Malformed API Key passed in. Ensure Key has `Bearer ` prefix. Passed in: {passed_in_key}" - ) - ### LITELLM-DEFINED AUTH FUNCTION ### if master_key is None: if isinstance(api_key, str): @@ -276,6 +279,17 @@ async def user_api_key_auth( else: return UserAPIKeyAuth() + if api_key is None: + raise Exception("No API Key passed in") + if secrets.compare_digest(api_key, MISSING_BEARER): + # missing 'Bearer ' prefix + raise Exception( + f"Malformed API Key passed in. Ensure Key has `Bearer ` prefix. Passed in: {passed_in_key}" + ) + elif secrets.compare_digest(api_key, NO_API_KEY): + # no api key passed in + raise Exception("No API Key passed in. Passed in: {passed_in_key}") + route: str = request.url.path if route == "/user/auth": if general_settings.get("allow_user_auth", False) == True: From ffdac7b239ff05bcada25652cf38053c3e55c360 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 14:33:16 -0800 Subject: [PATCH 06/16] (test) malformed keys --- litellm/tests/test_key_generate_prisma.py | 41 +++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/litellm/tests/test_key_generate_prisma.py b/litellm/tests/test_key_generate_prisma.py index 728514342..f15524a0e 100644 --- a/litellm/tests/test_key_generate_prisma.py +++ b/litellm/tests/test_key_generate_prisma.py @@ -1212,3 +1212,44 @@ async def test_default_key_params(prisma_client): except Exception as e: print("Got Exception", e) pytest.fail(f"Got exception {e}") + + +@pytest.mark.asyncio +async def test_user_api_key_auth(prisma_client): + from litellm.proxy.proxy_server import ProxyException + + setattr(litellm.proxy.proxy_server, "prisma_client", prisma_client) + setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") + setattr(litellm.proxy.proxy_server, "general_settings", {"allow_user_auth": True}) + await litellm.proxy.proxy_server.prisma_client.connect() + + request = Request(scope={"type": "http"}) + request._url = URL(url="/chat/completions") + # Test case: No API Key passed in + try: + await user_api_key_auth(request, api_key=None) + pytest.fail(f"This should have failed!. IT's an invalid key") + except ProxyException as exc: + print(exc.message) + assert ( + exc.message == "Authentication Error, No API Key passed in. api_key is None" + ) + + # Test case: Malformed API Key (missing 'Bearer ' prefix) + try: + await user_api_key_auth(request, api_key="my_token") + pytest.fail(f"This should have failed!. IT's an invalid key") + except ProxyException as exc: + print(exc.message) + assert ( + exc.message + == "Authentication Error, Malformed API Key passed in. Ensure Key has `Bearer ` prefix. Passed in: my_token" + ) + + # Test case: User passes empty string API Key + try: + await user_api_key_auth(request, api_key="") + pytest.fail(f"This should have failed!. IT's an invalid key") + except ProxyException as exc: + print(exc.message) + assert exc.message == "Authentication Error, No API Key passed in. Passed in: " From df70fd5af53517acd90c564ba46ab343667049cd Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 14:38:32 -0800 Subject: [PATCH 07/16] (feat) proxy raise exceptions for different api_key formats --- litellm/proxy/proxy_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 54a4a087e..ffd67d314 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -280,7 +280,7 @@ async def user_api_key_auth( return UserAPIKeyAuth() if api_key is None: - raise Exception("No API Key passed in") + raise Exception("No API Key passed in. api_key is None") if secrets.compare_digest(api_key, MISSING_BEARER): # missing 'Bearer ' prefix raise Exception( @@ -288,7 +288,7 @@ async def user_api_key_auth( ) elif secrets.compare_digest(api_key, NO_API_KEY): # no api key passed in - raise Exception("No API Key passed in. Passed in: {passed_in_key}") + raise Exception(f"No API Key passed in. Passed in: {passed_in_key}") route: str = request.url.path if route == "/user/auth": From 47519c9fdddac2a2bf7ede73f8953ffa65adf23d Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 14:45:19 -0800 Subject: [PATCH 08/16] (fix) test_key_name_null --- litellm/tests/test_key_generate_prisma.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/litellm/tests/test_key_generate_prisma.py b/litellm/tests/test_key_generate_prisma.py index f15524a0e..9e4cef9e3 100644 --- a/litellm/tests/test_key_generate_prisma.py +++ b/litellm/tests/test_key_generate_prisma.py @@ -1154,13 +1154,15 @@ async def test_key_name_null(prisma_client): """ setattr(litellm.proxy.proxy_server, "prisma_client", prisma_client) setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") + setattr(litellm.proxy.proxy_server, "general_settings", {"allow_user_auth": False}) await litellm.proxy.proxy_server.prisma_client.connect() try: request = GenerateKeyRequest() key = await generate_key_fn(request) + print("test_key_name_null key=", key) generated_key = key.key result = await info_key_fn(key=generated_key) - print("result from info_key_fn", result) + print("rtest_key_name_null esult from info_key_fn", result) assert result["info"]["key_name"] is None except Exception as e: print("Got Exception", e) From 9fb68710beedfd2c76568064c634c6398d3ee06e Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 14:51:26 -0800 Subject: [PATCH 09/16] (test) test_user_api_key_auth_without_master_key --- litellm/tests/test_key_generate_prisma.py | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/litellm/tests/test_key_generate_prisma.py b/litellm/tests/test_key_generate_prisma.py index 9e4cef9e3..9525fb424 100644 --- a/litellm/tests/test_key_generate_prisma.py +++ b/litellm/tests/test_key_generate_prisma.py @@ -1255,3 +1255,29 @@ async def test_user_api_key_auth(prisma_client): except ProxyException as exc: print(exc.message) assert exc.message == "Authentication Error, No API Key passed in. Passed in: " + + +@pytest.mark.asyncio +async def test_user_api_key_auth_without_master_key(prisma_client): + # if master key is not set, expect all calls to go through + try: + from litellm.proxy.proxy_server import ProxyException + + setattr(litellm.proxy.proxy_server, "prisma_client", prisma_client) + setattr(litellm.proxy.proxy_server, "master_key", None) + setattr( + litellm.proxy.proxy_server, "general_settings", {"allow_user_auth": True} + ) + await litellm.proxy.proxy_server.prisma_client.connect() + + request = Request(scope={"type": "http"}) + request._url = URL(url="/chat/completions") + # Test case: No API Key passed in + + await user_api_key_auth(request, api_key=None) + await user_api_key_auth(request, api_key="my_token") + await user_api_key_auth(request, api_key="") + await user_api_key_auth(request, api_key="Bearer " + "1234") + except Exception as e: + print("Got Exception", e) + pytest.fail(f"Got exception {e}") From 311957b615dcfe68a38b88a224278e4d57664b11 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 14:54:57 -0800 Subject: [PATCH 10/16] (test) proxy_custom_auth --- litellm/tests/test_configs/custom_auth.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/litellm/tests/test_configs/custom_auth.py b/litellm/tests/test_configs/custom_auth.py index 1b6bec43b..2939aa9f9 100644 --- a/litellm/tests/test_configs/custom_auth.py +++ b/litellm/tests/test_configs/custom_auth.py @@ -8,8 +8,10 @@ load_dotenv() async def user_api_key_auth(request: Request, api_key: str) -> UserAPIKeyAuth: try: + from litellm.proxy.proxy_server import MISSING_BEARER + print(f"api_key: {api_key}") - if api_key == "": + if api_key == MISSING_BEARER: raise Exception( f"CustomAuth - Malformed API Key passed in. Ensure Key has `Bearer` prefix" ) From adf2e98038f602358a370e9f75a4e101f63bb65a Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 15:25:30 -0800 Subject: [PATCH 11/16] (fix) update v --- litellm/proxy/proxy_server.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index ffd67d314..077b3fda4 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -141,11 +141,6 @@ class ProxyException(Exception): self.code = code -# Literals - used by _get_bearer_token -NO_API_KEY = "no-api-key" -MISSING_BEARER = "missing-bearer" - - @app.exception_handler(ProxyException) async def openai_exception_handler(request: Request, exc: ProxyException): # NOTE: DO NOT MODIFY THIS, its crucial to map to Openai exceptions @@ -240,13 +235,11 @@ def usage_telemetry( def _get_bearer_token( api_key: str, -) -> Union[str, Literal["no-api-key", "missing-bearer"]]: - if api_key == "": - return NO_API_KEY +): if api_key.startswith("Bearer "): # ensure Bearer token passed in api_key = api_key.replace("Bearer ", "") # extract the token else: - api_key = MISSING_BEARER + api_key = "" return api_key @@ -281,14 +274,11 @@ async def user_api_key_auth( if api_key is None: raise Exception("No API Key passed in. api_key is None") - if secrets.compare_digest(api_key, MISSING_BEARER): + if secrets.compare_digest(api_key, ""): # missing 'Bearer ' prefix raise Exception( f"Malformed API Key passed in. Ensure Key has `Bearer ` prefix. Passed in: {passed_in_key}" ) - elif secrets.compare_digest(api_key, NO_API_KEY): - # no api key passed in - raise Exception(f"No API Key passed in. Passed in: {passed_in_key}") route: str = request.url.path if route == "/user/auth": From 6d6c268352a66268286172967ed882df20c48940 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 15:26:22 -0800 Subject: [PATCH 12/16] (test) custom_auth.py --- litellm/tests/test_configs/custom_auth.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/litellm/tests/test_configs/custom_auth.py b/litellm/tests/test_configs/custom_auth.py index 2939aa9f9..1b6bec43b 100644 --- a/litellm/tests/test_configs/custom_auth.py +++ b/litellm/tests/test_configs/custom_auth.py @@ -8,10 +8,8 @@ load_dotenv() async def user_api_key_auth(request: Request, api_key: str) -> UserAPIKeyAuth: try: - from litellm.proxy.proxy_server import MISSING_BEARER - print(f"api_key: {api_key}") - if api_key == MISSING_BEARER: + if api_key == "": raise Exception( f"CustomAuth - Malformed API Key passed in. Ensure Key has `Bearer` prefix" ) From 0f1f3b1d9b7f4e1347e4bd9f8129230e20dcbbf3 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 15:27:00 -0800 Subject: [PATCH 13/16] (test) key_gen --- litellm/tests/test_key_generate_prisma.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/litellm/tests/test_key_generate_prisma.py b/litellm/tests/test_key_generate_prisma.py index 9525fb424..f09a1072e 100644 --- a/litellm/tests/test_key_generate_prisma.py +++ b/litellm/tests/test_key_generate_prisma.py @@ -1254,7 +1254,10 @@ async def test_user_api_key_auth(prisma_client): pytest.fail(f"This should have failed!. IT's an invalid key") except ProxyException as exc: print(exc.message) - assert exc.message == "Authentication Error, No API Key passed in. Passed in: " + assert ( + exc.message + == "Authentication Error, Malformed API Key passed in. Ensure Key has `Bearer ` prefix. Passed in: " + ) @pytest.mark.asyncio From ade48dba3979ea72474947c24c411c95b701cf30 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 15:33:03 -0800 Subject: [PATCH 14/16] (test) test _get_bearer_token --- litellm/tests/test_key_generate_prisma.py | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/litellm/tests/test_key_generate_prisma.py b/litellm/tests/test_key_generate_prisma.py index f09a1072e..963e09401 100644 --- a/litellm/tests/test_key_generate_prisma.py +++ b/litellm/tests/test_key_generate_prisma.py @@ -1216,6 +1216,40 @@ async def test_default_key_params(prisma_client): pytest.fail(f"Got exception {e}") +def test_get_bearer_token(): + from litellm.proxy.proxy_server import _get_bearer_token + + # Test valid Bearer token + api_key = "Bearer valid_token" + result = _get_bearer_token(api_key) + assert result == "valid_token", f"Expected 'valid_token', got '{result}'" + + # Test empty API key + api_key = "" + result = _get_bearer_token(api_key) + assert result == "", f"Expected '', got '{result}'" + + # Test API key without Bearer prefix + api_key = "invalid_token" + result = _get_bearer_token(api_key) + assert result == "", f"Expected '', got '{result}'" + + # Test API key with Bearer prefix in lowercase + api_key = "bearer valid_token" + result = _get_bearer_token(api_key) + assert result == "", f"Expected '', got '{result}'" + + # Test API key with Bearer prefix and extra spaces + api_key = " Bearer valid_token " + result = _get_bearer_token(api_key) + assert result == "", f"Expected '', got '{result}'" + + # Test API key with Bearer prefix and no token + api_key = "Bearer sk-1234" + result = _get_bearer_token(api_key) + assert result == "sk-1234", f"Expected 'valid_token', got '{result}'" + + @pytest.mark.asyncio async def test_user_api_key_auth(prisma_client): from litellm.proxy.proxy_server import ProxyException From e65e01f267c0fe6a74e3cfbf8db0ef6d5ea1fc09 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 15:36:10 -0800 Subject: [PATCH 15/16] (test) prisma /key/gen --- litellm/tests/test_key_generate_prisma.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/litellm/tests/test_key_generate_prisma.py b/litellm/tests/test_key_generate_prisma.py index 963e09401..9d4318fe7 100644 --- a/litellm/tests/test_key_generate_prisma.py +++ b/litellm/tests/test_key_generate_prisma.py @@ -1159,10 +1159,9 @@ async def test_key_name_null(prisma_client): try: request = GenerateKeyRequest() key = await generate_key_fn(request) - print("test_key_name_null key=", key) generated_key = key.key result = await info_key_fn(key=generated_key) - print("rtest_key_name_null esult from info_key_fn", result) + print("result from info_key_fn", result) assert result["info"]["key_name"] is None except Exception as e: print("Got Exception", e) From 3507812977d286db1a228d3e6c0ecb25188afd94 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Thu, 1 Feb 2024 15:36:33 -0800 Subject: [PATCH 16/16] (fix) proxy - remove Literal --- litellm/proxy/proxy_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 077b3fda4..c93926c19 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -2,7 +2,7 @@ import sys, os, platform, time, copy, re, asyncio, inspect import threading, ast import shutil, random, traceback, requests from datetime import datetime, timedelta, timezone -from typing import Optional, List, Literal +from typing import Optional, List import secrets, subprocess import hashlib, uuid import warnings