From 92a13958ceb8a7d5165a197e0f8b46271e37bf92 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 5 Aug 2023 09:52:01 -0700 Subject: [PATCH] fixing exception mapping --- litellm/__init__.py | 2 +- litellm/__pycache__/__init__.cpython-311.pyc | Bin 1432 -> 1656 bytes litellm/__pycache__/main.cpython-311.pyc | Bin 13069 -> 12126 bytes litellm/__pycache__/utils.cpython-311.pyc | Bin 18717 -> 20446 bytes litellm/main.py | 50 +------ litellm/tests/test_exceptions.py | 93 ++++++++----- litellm/utils.py | 134 ++++++++++++++----- setup.py | 24 ++++ 8 files changed, 188 insertions(+), 115 deletions(-) create mode 100644 setup.py diff --git a/litellm/__init__.py b/litellm/__init__.py index 01bfc49b525..57ea5491857 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -69,6 +69,6 @@ open_ai_embedding_models = [ 'text-embedding-ada-002' ] from .timeout import timeout -from .utils import client, logging, exception_type # Import all the symbols from main.py +from .utils import client, logging, exception_type, get_optional_params # Import all the symbols from main.py from .main import * # Import all the symbols from main.py from .integrations import * \ No newline at end of file diff --git a/litellm/__pycache__/__init__.cpython-311.pyc b/litellm/__pycache__/__init__.cpython-311.pyc index eee4d9926b6fad627dd79db3bb4522b5eabe3018..fd307768611934eb142198f4e05aa0b9ee9ff9a7 100644 GIT binary patch delta 568 zcmah_&ubGw6rR~^Oq=ZH_cl!$E%8#eusXZ>SuZ_FDF}jk8V_NzGubZ8Bw@1wn@fxS z1z|5e^yI}qpnpISyb9u}=-VvgkG<~Vz3{7B7rtSfnc@J@xY97QnsdTCGG)r@yf(taixM&;} zWT{Mxw8RZzU;_fl%X~{za7Aq6ZBfNlQNy)c-{3p1;<~}>@=v@3=GNk8BjtlU0HzjI z;N{t$oI4hRjHos8y_1li&R+*?!hM&7>@nv=5X-T;XkfV~Bz)qyb_eyC-Rq#vkWi;* zJIF!QZMW=$CK@^vwGP@YYdVavHbUK&JP7N|ccS$QI5K%N36j z&y|Rh$d!ze1dEB}NQHppL~^8oG;=V6rs%|ficFeZlhqjKOg3N&6RhG-&d<$F%uCVD z%S_KG$*G)tp2?n(c``q8nmmP73m6QnVjq~91eqFmKX6UHz?v*$ Rbb$dUnjFd|&!P=95C9eKO%4D6 diff --git a/litellm/__pycache__/main.cpython-311.pyc b/litellm/__pycache__/main.cpython-311.pyc index 261f8e6ce4477c75239c6a280552cf822ccaa4c8..6b6ee11b22f622075061449c26aad5ee17213c2a 100644 GIT binary patch delta 1719 zcma)+O>7%Q6vt=$6+4dYq>jJs#HkZfC!tBxHpC%m5TOaA5>jbZ6jSPKyz9i&_PVp1 zv?)~OU{wgHAj}D=NIiiNhgO6OH{gbZsvOY@aYebgAP}lJF#mCz(ntu_-k+b}do%N& zH*fc5>E{dn&wRdiBCek<-BUjq+44u}t*yZi#;V=G{{$y&|8M9BP37K>NI%LP=VsXGpe-|4C=5 zlTX@)LRN59GYfj%EbBF;Qm89TsTzF6HnDxv<)ggIId#CR6!mJoq9KLFh4wK2vQ3Np zhF$DV3s__qo0_-m+iwS`i}*%lF7E6qQParyTLlyP8A;Nf=IB0m>r|0&&TVO&c(Kae;&C=0cxl$}Nx>MW0TrKrhM|6G)l zR}xVlN|GIB9mtvCTZ!kweKJ$Iy3Vx1MQxLL5Sr${B;N5@&Qe8JOm>QIBy&O3#qvIG z6N3f$cS-Z;-*djr`MZm>`M19wc;GlAwH(8UrS!cBE)y_wP3L8pgU7Jep$^)H2 z@(Ez}A0sBYYj}KZ-$~(+*m>Y}qz0KBTYzH)C;}>=02&|*hBLA}C#x%(Xv*3uTSLTz z({7lIE%V#MJ<6MKz6D$Z@V?8s_qBDa^is!lB!z$*0*0uKcw$wO3 zdq@7T(PbVU%>`t6p+L&$=NCpxhvk#n*V>BQ*=58}2?&{+hUGFh>riipyTN}Qow&XX z6HB++5Sp16kPZ7GtS$k|ZE7{8ST;AA4?aH-K)@>+RxPNyh;PXE7}F7x9Rbx+UhBmm zpL^(j;GVAP>Uu>x(ak;<|Eai*pm>0rc1r1w{M)hPyg6nbI=w^w*O%Wovcw0*7v^5I z)9I$4gyOf4-DTf|c3wHRv#d1((oOdBzMa^7jl_K?vAG-Jrc1_6erG)IfYQGK7Lbc5 delta 2672 zcmbtWO>7%Q6yCAD8*iL-e*Cv_{+y&u;{1~S(h}Mf+7uO1p|%8SqPiRJIx(^1FzZxJ zm10wZnoC8(6k1T#BNgIOlnbYdBZnU19ALEv4jhmo@sp|&f}VJ9oTh0cQ~{gpx3k}S zGxNPSZ)W}Dxob)99YrY@;F|BfufN~F=OPLomGWgxOMCFi(VWK`+zY{7=>inCkTMFNh#~1$XI!A%qlqk))z~=1Htx zub3BN4Mr7s`*h`L?|(I3K+$1Ry@ayvy(Q_D^GJdTC&zPIX`L3$(gA{E|r}!X(go0RyN)}Q}KkBPSYwNS-o#(kXEC)JfmHV z=dzOq_&d-v>>J8Qi7h76SuGd!QXi1ij}SlzB7_iBgj$3!LLEXqLIXl0LKA>pnVm*C zS~@q=XpD{3+8U?~d1dPE3> z3VoL7U2M4$FT|~#y@l-JWKn9d#G1viRZ(3LRV%s=$ZSz+v&5>!BdcOyMGRQ&yMW9T zr5%=7zMx%+6=GI%8&Km#snHUpg?#gsk-~`8&&@Cg#3#YiyE%VhZ>KlVbdtfFWrL3+v z)Q6BIVfnH{Au4TwrtQ%Z+01m>z_p;gz`V@9cN*n%2Q=5PfR<0W?s|{@@AW=Ice77@ zL(*;-lI~$Y`C6qHv3ZDj{0BlW@Fv1xfT)9(2==yrjI^+O{yuV>{pN3LMpaQKx7yM) z1?9P?dJMC@fo%OT9OyX0D*z_eZzRIQrLVGY1FeB?(DoVv4oHU)s@b1`$XTq}s92gF zqo=6XuGDlrwNb4a9XIJ7bR(2|@aCQhFPk7oY9sw(@dE;Z7K)x46%2EE#w5d z9_$XiiR1E(FoeUD!lA?Lhai2XbWa&n_=a@GLc7~}qBfP%GdMP?vt{FA!r)u%OGqdC zHKZz-l4uv-Ep(98sDr_N?x|gxp+ zbSgnd(Z2)XB3c}YEbU~s!u|DF+a&{9aj8v*o1YEp@bX%Cw2XaSA6h=w^0VtSugLxA z|2)D0go6OEYCNx4rkAQ7%ZXpm(lf@v$l5OS8$>vQ@Djk|9xt#P?YkzntqhMZj;`m? z)mQ2}hJ=BTL>NcV5hf6DdH9;x^0CaA0Yx}9PA8GP0AM@K9Hj}?-_fXDM)Ey`JOcje zh`P6xG3AoEjsiM$B3uP9Av2ptZ|PW&jm`P4fw6-u-|hIrK@#jv*MUl&TvWhiyv}~_ zO7`-d*<1=`5KXfPCjmg_tZ7TR*=badA8-00`=EPYK7mb~x>RvE*dF){>2W;^_6wy-#cu#Vz)H)Gs=2(VD)N$Yk}Iv~kE! zKZkeNPJ(&bAqeYELWsq*o;G&AC%~@uOtlQG2+#Ou4ZUZFgleDV4_Y-ot3t8dvZd5m zo=U4axENTRS*pKYZ-rD#t+l|*qgZ}*y`hWLt_x4YIl?aV9w#I0UT?5_#7PF$h3)vj ZDL9n*x(A}*aL)(VC2pr5_r+Wo+~0qaUa0^8 diff --git a/litellm/__pycache__/utils.cpython-311.pyc b/litellm/__pycache__/utils.cpython-311.pyc index f298d298802f4731c286b6adbbb13522fa14cf7f..c4a41fd5a098f05e28b5f8bea16829d29bc3c926 100644 GIT binary patch delta 6147 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 B`SSn( delta 4467 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 diff --git a/litellm/main.py b/litellm/main.py index 0a5a26953d5..adc8df9d3a6 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -6,7 +6,7 @@ from functools import partial import dotenv import traceback import litellm -from litellm import client, logging, exception_type, timeout +from litellm import client, logging, exception_type, timeout, get_optional_params import random import asyncio from tenacity import ( @@ -20,51 +20,6 @@ dotenv.load_dotenv() # Loading env variables using dotenv # TODO move this to utils.py # TODO add translations # TODO see if this worked - model_name == krrish -def get_optional_params( - # 12 optional params - functions = [], - function_call = "", - temperature = 1, - top_p = 1, - n = 1, - stream = False, - stop = None, - max_tokens = float('inf'), - presence_penalty = 0, - frequency_penalty = 0, - logit_bias = {}, - user = "", - deployment_id = None -): - optional_params = {} - if functions != []: - optional_params["functions"] = functions - if function_call != "": - optional_params["function_call"] = function_call - if temperature != 1: - optional_params["temperature"] = temperature - if top_p != 1: - optional_params["top_p"] = top_p - if n != 1: - optional_params["n"] = n - if stream: - optional_params["stream"] = stream - if stop != None: - optional_params["stop"] = stop - if max_tokens != float('inf'): - optional_params["max_tokens"] = max_tokens - if presence_penalty != 0: - optional_params["presence_penalty"] = presence_penalty - if frequency_penalty != 0: - optional_params["frequency_penalty"] = frequency_penalty - if logit_bias != {}: - optional_params["logit_bias"] = logit_bias - if user != "": - optional_params["user"] = user - if deployment_id != None: - optional_params["deployment_id"] = user - return optional_params - ####### COMPLETION ENDPOINTS ################ ############################################# async def acompletion(*args, **kwargs): @@ -285,12 +240,13 @@ def completion( } response = new_response else: + ## LOGGING logging(model=model, input=messages, azure=azure, logger_fn=logger_fn) args = locals() raise ValueError(f"No valid completion model args passed in - {args}") return response except Exception as e: - # log the original exception + ## LOGGING logging(model=model, input=messages, azure=azure, additional_args={"max_tokens": max_tokens}, logger_fn=logger_fn, exception=e) ## Map to OpenAI Exception raise exception_type(model=model, original_exception=e) diff --git a/litellm/tests/test_exceptions.py b/litellm/tests/test_exceptions.py index 6a690244975..3b9f01d6c3a 100644 --- a/litellm/tests/test_exceptions.py +++ b/litellm/tests/test_exceptions.py @@ -8,6 +8,7 @@ from litellm import embedding, completion from concurrent.futures import ThreadPoolExecutor import pytest +litellm.failure_callback = ["sentry"] # litellm.set_verbose = True #### What this tests #### # This tests exception mapping -> trigger an exception from an llm provider -> assert if output is of the expected type @@ -22,11 +23,16 @@ import pytest # models = ["gpt-3.5-turbo", "chatgpt-test", "claude-instant-1", "command-nightly"] models = ["command-nightly"] def logging_fn(model_call_dict): - print(f"model_call_dict: {model_call_dict['model']}") + if "model" in model_call_dict: + print(f"model_call_dict: {model_call_dict['model']}") + else: + print(f"model_call_dict: {model_call_dict}") + + # Test 1: Context Window Errors @pytest.mark.parametrize("model", models) def test_context_window(model): - sample_text = "how does a court case get to the Supreme Court?" * 100000 + sample_text = "how does a court case get to the Supreme Court?" * 5000 messages = [{"content": sample_text, "role": "user"}] try: azure = model == "chatgpt-test" @@ -41,44 +47,61 @@ def test_context_window(model): return except Exception as e: print("Uncaught Error in test_context_window") - # print(f"Error Type: {type(e).__name__}") + print(f"Error Type: {type(e).__name__}") print(f"Uncaught Exception - {e}") pytest.fail(f"Error occurred: {e}") return test_context_window("command-nightly") -# # Test 2: InvalidAuth Errors -# def logger_fn(model_call_object: dict): -# print(f"model call details: {model_call_object}") - -# @pytest.mark.parametrize("model", models) -# def invalid_auth(model): # set the model key to an invalid key, depending on the model -# messages = [{ "content": "Hello, how are you?","role": "user"}] -# try: -# azure = False -# if model == "gpt-3.5-turbo": -# os.environ["OPENAI_API_KEY"] = "bad-key" -# elif model == "chatgpt-test": -# os.environ["AZURE_API_KEY"] = "bad-key" -# azure = True -# elif model == "claude-instant-1": -# os.environ["ANTHROPIC_API_KEY"] = "bad-key" -# elif model == "command-nightly": -# os.environ["COHERE_API_KEY"] = "bad-key" -# elif model == "replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1": -# os.environ["REPLICATE_API_KEY"] = "bad-key" -# os.environ["REPLICATE_API_TOKEN"] = "bad-key" -# print(f"model: {model}") -# response = completion(model=model, messages=messages, azure=azure) -# print(f"response: {response}") -# except AuthenticationError as e: -# return -# except OpenAIError: # is at least an openai error -> in case of random model errors - e.g. overloaded server -# return -# except Exception as e: -# print(f"Uncaught Exception - {e}") -# pytest.fail(f"Error occurred: {e}") -# return +# Test 2: InvalidAuth Errors +@pytest.mark.parametrize("model", models) +def invalid_auth(model): # set the model key to an invalid key, depending on the model + messages = [{ "content": "Hello, how are you?","role": "user"}] + temporary_key = None + try: + azure = False + if model == "gpt-3.5-turbo": + temporary_key = os.environ["OPENAI_API_KEY"] + os.environ["OPENAI_API_KEY"] = "bad-key" + elif model == "chatgpt-test": + temporary_key = os.environ["AZURE_API_KEY"] + os.environ["AZURE_API_KEY"] = "bad-key" + azure = True + elif model == "claude-instant-1": + temporary_key = os.environ["ANTHROPIC_API_KEY"] + os.environ["ANTHROPIC_API_KEY"] = "bad-key" + elif model == "command-nightly": + temporary_key = os.environ["COHERE_API_KEY"] + os.environ["COHERE_API_KEY"] = "bad-key" + elif model == "replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1": + temporary_key = os.environ["REPLICATE_API_KEY"] + os.environ["REPLICATE_API_KEY"] = "bad-key" + print(f"model: {model}") + response = completion(model=model, messages=messages, azure=azure) + print(f"response: {response}") + except AuthenticationError as e: + print(f"AuthenticationError Caught Exception - {e}") + except OpenAIError: # is at least an openai error -> in case of random model errors - e.g. overloaded server + print(f"OpenAIError Caught Exception - {e}") + except Exception as e: + print(type(e)) + print(e.__class__.__name__) + print(f"Uncaught Exception - {e}") + pytest.fail(f"Error occurred: {e}") + if temporary_key != None: # reset the key + if model == "gpt-3.5-turbo": + os.environ["OPENAI_API_KEY"] = temporary_key + elif model == "chatgpt-test": + os.environ["AZURE_API_KEY"] = temporary_key + azure = True + elif model == "claude-instant-1": + os.environ["ANTHROPIC_API_KEY"] = temporary_key + elif model == "command-nightly": + os.environ["COHERE_API_KEY"] = temporary_key + elif model == "replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1": + os.environ["REPLICATE_API_KEY"] = temporary_key + return +invalid_auth("command-nightly") # # Test 3: Rate Limit Errors # def test_model(model): # try: diff --git a/litellm/utils.py b/litellm/utils.py index b0050226b8b..94880e66990 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -25,41 +25,44 @@ def print_verbose(print_statement): ####### LOGGING ################### #Logging function -> log the exact model details + what's being sent | Non-Blocking -def logging(model, input, azure=False, additional_args={}, logger_fn=None, exception=None): +def logging(model=None, input=None, azure=False, additional_args={}, logger_fn=None, exception=None): try: model_call_details = {} - model_call_details["model"] = model - model_call_details["azure"] = azure - # log exception details + if model: + model_call_details["model"] = model + if azure: + model_call_details["azure"] = azure if exception: model_call_details["original_exception"] = exception if litellm.telemetry: safe_crash_reporting(model=model, exception=exception, azure=azure) # log usage-crash details. Do not log any user details. If you want to turn this off, set `litellm.telemetry=False`. - model_call_details["input"] = input + if input: + model_call_details["input"] = input # log additional call details -> api key, etc. - if azure == True or model in litellm.open_ai_chat_completion_models or model in litellm.open_ai_chat_completion_models or model in litellm.open_ai_embedding_models: - model_call_details["api_type"] = openai.api_type - model_call_details["api_base"] = openai.api_base - model_call_details["api_version"] = openai.api_version - model_call_details["api_key"] = openai.api_key - elif "replicate" in model: - model_call_details["api_key"] = os.environ.get("REPLICATE_API_TOKEN") - elif model in litellm.anthropic_models: - model_call_details["api_key"] = os.environ.get("ANTHROPIC_API_KEY") - elif model in litellm.cohere_models: - model_call_details["api_key"] = os.environ.get("COHERE_API_KEY") - model_call_details["additional_args"] = additional_args + if model: + if azure == True or model in litellm.open_ai_chat_completion_models or model in litellm.open_ai_chat_completion_models or model in litellm.open_ai_embedding_models: + model_call_details["api_type"] = openai.api_type + model_call_details["api_base"] = openai.api_base + model_call_details["api_version"] = openai.api_version + model_call_details["api_key"] = openai.api_key + elif "replicate" in model: + model_call_details["api_key"] = os.environ.get("REPLICATE_API_TOKEN") + elif model in litellm.anthropic_models: + model_call_details["api_key"] = os.environ.get("ANTHROPIC_API_KEY") + elif model in litellm.cohere_models: + model_call_details["api_key"] = os.environ.get("COHERE_API_KEY") + model_call_details["additional_args"] = additional_args ## User Logging -> if you pass in a custom logging function or want to use sentry breadcrumbs - print_verbose(f"Basic model call details: {model_call_details}") + print_verbose(f"Logging Details: logger_fn - {logger_fn} | callable(logger_fn) - {callable(logger_fn)}") if logger_fn and callable(logger_fn): try: logger_fn(model_call_details) # Expectation: any logger function passed in by the user should accept a dict object - except: - print_verbose(f"[Non-Blocking] Exception occurred while logging {traceback.format_exc()}") - except: - traceback.print_exc() + except Exception as e: + print(f"LiteLLM.LoggingError: [Non-Blocking] Exception occurred while logging {traceback.format_exc()}") + except Exception as e: + print(f"LiteLLM.LoggingError: [Non-Blocking] Exception occurred while logging {traceback.format_exc()}") pass ####### CLIENT ################### @@ -67,7 +70,7 @@ def logging(model, input, azure=False, additional_args={}, logger_fn=None, excep def client(original_function): def function_setup(*args, **kwargs): #just run once to check if user wants to send their data anywhere - PostHog/Sentry/Slack/etc. try: - global callback_list, add_breadcrumb + global callback_list, add_breadcrumb, user_logger_fn if (len(litellm.success_callback) > 0 or len(litellm.failure_callback) > 0) and len(callback_list) == 0: callback_list = list(set(litellm.success_callback + litellm.failure_callback)) set_callbacks(callback_list=callback_list,) @@ -77,13 +80,15 @@ def client(original_function): message=f"Positional Args: {args}, Keyword Args: {kwargs}", level="info", ) + if "logger_fn" in kwargs: + user_logger_fn = kwargs["logger_fn"] except: # DO NOT BLOCK running the function because of this print_verbose(f"[Non-Blocking] {traceback.format_exc()}") pass def wrapper(*args, **kwargs): try: - function_setup(args, kwargs) + function_setup(*args, **kwargs) ## MODEL CALL start_time = datetime.datetime.now() result = original_function(*args, **kwargs) @@ -100,6 +105,51 @@ def client(original_function): return wrapper ####### HELPER FUNCTIONS ################ +def get_optional_params( + # 12 optional params + functions = [], + function_call = "", + temperature = 1, + top_p = 1, + n = 1, + stream = False, + stop = None, + max_tokens = float('inf'), + presence_penalty = 0, + frequency_penalty = 0, + logit_bias = {}, + user = "", + deployment_id = None +): + optional_params = {} + if functions != []: + optional_params["functions"] = functions + if function_call != "": + optional_params["function_call"] = function_call + if temperature != 1: + optional_params["temperature"] = temperature + if top_p != 1: + optional_params["top_p"] = top_p + if n != 1: + optional_params["n"] = n + if stream: + optional_params["stream"] = stream + if stop != None: + optional_params["stop"] = stop + if max_tokens != float('inf'): + optional_params["max_tokens"] = max_tokens + if presence_penalty != 0: + optional_params["presence_penalty"] = presence_penalty + if frequency_penalty != 0: + optional_params["frequency_penalty"] = frequency_penalty + if logit_bias != {}: + optional_params["logit_bias"] = logit_bias + if user != "": + optional_params["user"] = user + if deployment_id != None: + optional_params["deployment_id"] = user + return optional_params + def set_callbacks(callback_list): global sentry_sdk_instance, capture_exception, add_breadcrumb, posthog, slack_app, alerts_channel, heliconeLogger try: @@ -150,8 +200,8 @@ def set_callbacks(callback_list): def handle_failure(exception, traceback_exception, args, kwargs): global sentry_sdk_instance, capture_exception, add_breadcrumb, posthog, slack_app, alerts_channel try: - print_verbose(f"handle_failure args: {args}") - print_verbose(f"handle_failure kwargs: {kwargs}") + # print_verbose(f"handle_failure args: {args}") + # print_verbose(f"handle_failure kwargs: {kwargs}") success_handler = additional_details.pop("success_handler", None) failure_handler = additional_details.pop("failure_handler", None) @@ -159,7 +209,8 @@ def handle_failure(exception, traceback_exception, args, kwargs): additional_details["Event_Name"] = additional_details.pop("failed_event_name", "litellm.failed_query") print_verbose(f"self.failure_callback: {litellm.failure_callback}") - print_verbose(f"additional_details: {additional_details}") + + # print_verbose(f"additional_details: {additional_details}") for callback in litellm.failure_callback: try: if callback == "slack": @@ -206,7 +257,9 @@ def handle_failure(exception, traceback_exception, args, kwargs): } failure_handler(call_details) pass - except: + except Exception as e: + ## LOGGING + logging(logger_fn=user_logger_fn, exception=e) pass def handle_success(args, kwargs, result, start_time, end_time): @@ -245,12 +298,16 @@ def handle_success(args, kwargs, result, start_time, end_time): if success_handler and callable(success_handler): success_handler(args, kwargs) pass - except: + except Exception as e: + ## LOGGING + logging(logger_fn=user_logger_fn, exception=e) print_verbose(f"Success Callback Error - {traceback.format_exc()}") pass def exception_type(model, original_exception): + global user_logger_fn + exception_mapping_worked = False try: if isinstance(original_exception, OpenAIError): # Handle the OpenAIError @@ -265,32 +322,46 @@ def exception_type(model, original_exception): if "status_code" in original_exception: print_verbose(f"status_code: {original_exception.status_code}") if original_exception.status_code == 401: + exception_mapping_worked = True raise AuthenticationError(f"AnthropicException - {original_exception.message}") elif original_exception.status_code == 400: + exception_mapping_worked = True raise InvalidRequestError(f"AnthropicException - {original_exception.message}", f"{model}") elif original_exception.status_code == 429: + exception_mapping_worked = True raise RateLimitError(f"AnthropicException - {original_exception.message}") elif "replicate" in model: if "Incorrect authentication token" in error_str: + exception_mapping_worked = True raise AuthenticationError(f"ReplicateException - {error_str}") elif exception_type == "ModelError": + exception_mapping_worked = True raise InvalidRequestError(f"ReplicateException - {error_str}", f"{model}") elif "Request was throttled" in error_str: + exception_mapping_worked = True raise RateLimitError(f"ReplicateException - {error_str}") elif exception_type == "ReplicateError": ## ReplicateError implies an error on Replicate server side, not user side raise ServiceUnavailableError(f"ReplicateException - {error_str}") elif model == "command-nightly": #Cohere if "invalid api token" in error_str or "No API key provided." in error_str: + exception_mapping_worked = True raise AuthenticationError(f"CohereException - {error_str}") elif "too many tokens" in error_str: + exception_mapping_worked = True raise InvalidRequestError(f"CohereException - {error_str}", f"{model}") elif "CohereConnectionError" in exception_type: # cohere seems to fire these errors when we load test it (1k+ messages / min) + exception_mapping_worked = True raise RateLimitError(f"CohereException - {original_exception.message}") raise original_exception # base case - return the original exception else: raise original_exception - except: - raise original_exception + except Exception as e: + ## LOGGING + logging(logger_fn=user_logger_fn, additional_args={"original_exception": original_exception}, exception=e) + if exception_mapping_worked: + raise e + else: # don't let an error with mapping interrupt the user from receiving an error from the llm api calls + raise original_exception def safe_crash_reporting(model=None, exception=None, azure=None): data = { @@ -323,7 +394,6 @@ def litellm_telemetry(data): 'uuid': uuid_value, 'data': data } - print_verbose(f"payload: {payload}") try: # Make the POST request to localhost:3000 response = requests.post('https://litellm.berri.ai/logging', json=payload) diff --git a/setup.py b/setup.py new file mode 100644 index 00000000000..9f54d645208 --- /dev/null +++ b/setup.py @@ -0,0 +1,24 @@ +from setuptools import setup, find_packages + +setup( + name='litellm', + version='0.1.231', + description='Library to easily interface with LLM API providers', + author='BerriAI', + packages=[ + 'litellm' + ], + package_data={ + "litellm": ["integrations/*"], # Specify the directory path relative to your package + }, + install_requires=[ + 'openai', + 'cohere', + 'pytest', + 'anthropic', + 'replicate', + 'python-dotenv', + 'openai[datalib]', + 'tenacity' + ], +)