From 745252e3bf4795f567df82d11180f04fd94e0fc6 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Mon, 31 Jul 2023 13:25:28 -0700 Subject: [PATCH] cleaning up client, and fixing replicate bug --- litellm/__pycache__/main.cpython-311.pyc | Bin 19936 -> 20758 bytes litellm/main.py | 348 ++++++++++++----------- litellm/tests/test_bad_params.py | 7 +- litellm/tests/test_client.py | 20 +- litellm/tests/test_model_fallback.py | 25 ++ setup.py | 2 +- 6 files changed, 216 insertions(+), 186 deletions(-) create mode 100644 litellm/tests/test_model_fallback.py diff --git a/litellm/__pycache__/main.cpython-311.pyc b/litellm/__pycache__/main.cpython-311.pyc index 80675f031a2da9c99f7527b9902e5d7bc9ef56f6..f652890678cf18cd9a6bf1782e5afa70b117a4ad 100644 GIT binary patch literal 20758 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 Q`MW2uJV3ApATss;0bnfhzW@LL literal 19936 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 ```litellm.verbose = True``` +def print_verbose(print_statement): + if set_verbose: + print(f"LiteLLM: {print_statement}") + print("Get help - https://discord.com/invite/wuPM9dRgDw") ####### COMPLETION MODELS ################### open_ai_chat_completion_models = [ @@ -36,13 +54,39 @@ open_ai_embedding_models = [ 'text-embedding-ada-002' ] -############################################# +####### CLIENT ################### make it easy to log completion/embedding runs +def client(original_function): + def function_setup(): #just run once to check if user wants to send their data anywhere + try: + if len(success_callback) > 0 or len(failure_callback) > 0 and len(callback_list) == 0: + callback_list = list(set(success_callback + failure_callback)) + set_callbacks(callback_list=callback_list) + except: # DO NOT BLOCK running the function because of this + print_verbose(f"[Non-Blocking] {traceback.format_exc()}") + pass + def wrapper(*args, **kwargs): + # Code to be executed before the embedding function + try: + function_setup() + ## EMBEDDING CALL + result = original_function(*args, **kwargs) + ## LOG SUCCESS + my_thread = threading.Thread(target=handle_success, args=(args, kwargs)) # don't interrupt execution of main thread + my_thread.start() + return result + except Exception as e: + traceback_exception = traceback.format_exc() + my_thread = threading.Thread(target=handle_failure, args=(e, traceback.format_exc(), args, kwargs)) # don't interrupt execution of main thread + my_thread.start() + raise e + return wrapper ####### COMPLETION ENDPOINTS ################ ############################################# -@func_set_timeout(10, allowOverride=True) ## https://pypi.org/project/func-timeout/ - timeouts, in case calls hang (e.g. Azure) -def completion(model, messages, max_tokens=None, forceTimeout=10, azure=False, logger_fn=None): +@client +@func_set_timeout(60, allowOverride=True) ## https://pypi.org/project/func-timeout/ - timeouts, in case calls hang (e.g. Azure) +def completion(model, messages, max_tokens=None, *, forceTimeout=60, azure=False, logger_fn=None): # ,*,.. requires optional params like forceTimeout, azure and logger_fn to be passed in as keyword arguments try: if azure == True: # azure configs @@ -64,7 +108,7 @@ def completion(model, messages, max_tokens=None, forceTimeout=10, azure=False, l replicate_api_token = os.environ.get("REPLICATE_API_KEY") os.environ["REPLICATE_API_TOKEN"] = replicate_api_token prompt = " ".join([message["content"] for message in messages]) - input = [{"prompt": prompt}] + input = {"prompt": prompt} if max_tokens: input["max_length"] = max_tokens # for t5 models input["max_new_tokens"] = max_tokens # for llama2 models @@ -127,7 +171,7 @@ def completion(model, messages, max_tokens=None, forceTimeout=10, azure=False, l } ] } - print(f"new response: {new_response}") + print_verbose(f"new response: {new_response}") response = new_response elif model in cohere_models: cohere_key = os.environ.get("COHERE_API_KEY") @@ -188,6 +232,7 @@ def completion(model, messages, max_tokens=None, forceTimeout=10, azure=False, l ### EMBEDDING ENDPOINTS #################### +@client @func_set_timeout(60, allowOverride=True) ## https://pypi.org/project/func-timeout/ def embedding(model, input=[], azure=False, forceTimeout=60, logger_fn=None): response = None @@ -214,170 +259,104 @@ def embedding(model, input=[], azure=False, forceTimeout=60, logger_fn=None): print_verbose(f"response_value: {str(response)[:50]}") else: logging(model=model, input=input, azure=azure, logger_fn=logger_fn) + args = locals() + raise ValueError(f"No valid embedding model args passed in - {args}") return response -### CLIENT CLASS #################### make it easy to push completion/embedding runs to different sources -> sentry/posthog/slack, etc. -class litellm_client: - def __init__(self, success_callback=[], failure_callback=[], verbose=False): # Constructor - set_verbose = verbose - self.success_callback = success_callback - self.failure_callback = failure_callback - self.logger_fn = None # if user passes in their own logging function - self.callback_list = list(set(self.success_callback + self.failure_callback)) - self.set_callbacks() - - ## COMPLETION CALL - def completion(self, model, messages, max_tokens=None, forceTimeout=10, azure=False, logger_fn=None, additional_details={}) -> Any: - try: - self.logger_fn = logger_fn - response = completion(model=model, messages=messages, max_tokens=max_tokens, forceTimeout=forceTimeout, azure=azure, logger_fn=self.handle_input) - my_thread = threading.Thread(target=self.handle_success, args=(model, messages, additional_details)) # don't interrupt execution of main thread - my_thread.start() - return response - except Exception as e: - args = locals() # get all the param values - self.handle_failure(e, args) - raise e +####### HELPER FUNCTIONS ################ - ## EMBEDDING CALL - def embedding(self, model, input=[], azure=False, logger_fn=None, forceTimeout=60, additional_details={}) -> Any: - try: - self.logger_fn = logger_fn - response = embedding(model, input, azure=azure, logger_fn=self.handle_input) - my_thread = threading.Thread(target=self.handle_success, args=(model, input, additional_details)) # don't interrupt execution of main thread - my_thread.start() - return response - except Exception as e: - args = locals() # get all the param values - self.handle_failure(e, args) - raise e - - - def set_callbacks(self): #instantiate any external packages - for callback in self.callback_list: # only install what's required - if callback == "sentry": - try: +def set_callbacks(callback_list): + global sentry_sdk_instance, capture_exception, add_breadcrumb, posthog, slack_app, alerts_channel + for callback in callback_list: + if callback == "sentry": + try: import sentry_sdk - except ImportError: + except ImportError: print_verbose("Package 'sentry_sdk' is missing. Installing it...") subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'sentry_sdk']) import sentry_sdk - self.sentry_sdk = sentry_sdk - self.sentry_sdk.init(dsn=os.environ.get("SENTRY_API_URL"), traces_sample_rate=float(os.environ.get("SENTRY_API_TRACE_RATE"))) - self.capture_exception = self.sentry_sdk.capture_exception - self.add_breadcrumb = self.sentry_sdk.add_breadcrumb - elif callback == "posthog": - try: + sentry_sdk_instance = sentry_sdk + sentry_sdk_instance.init(dsn=os.environ.get("SENTRY_API_URL"), traces_sample_rate=float(os.environ.get("SENTRY_API_TRACE_RATE"))) + capture_exception = sentry_sdk_instance.capture_exception + add_breadcrumb = sentry_sdk_instance.add_breadcrumb + elif callback == "posthog": + try: from posthog import Posthog - except: + except ImportError: print_verbose("Package 'posthog' is missing. Installing it...") subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'posthog']) from posthog import Posthog - self.posthog = Posthog( - project_api_key=os.environ.get("POSTHOG_API_KEY"), - host=os.environ.get("POSTHOG_API_URL")) - elif callback == "slack": - try: + posthog = Posthog( + project_api_key=os.environ.get("POSTHOG_API_KEY"), + host=os.environ.get("POSTHOG_API_URL")) + elif callback == "slack": + try: from slack_bolt import App - except ImportError: + except ImportError: print_verbose("Package 'slack_bolt' is missing. Installing it...") subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'slack_bolt']) from slack_bolt import App - self.slack_app = App( - token=os.environ.get("SLACK_API_TOKEN"), - signing_secret=os.environ.get("SLACK_API_SECRET") - ) - self.alerts_channel = os.environ["SLACK_API_CHANNEL"] + slack_app = App( + token=os.environ.get("SLACK_API_TOKEN"), + signing_secret=os.environ.get("SLACK_API_SECRET") + ) + alerts_channel = os.environ["SLACK_API_CHANNEL"] + print_verbose(f"Initialized Slack App: {slack_app}") - def handle_input(self, model_call_details={}): - if len(model_call_details.keys()) > 0: - model = model_call_details["model"] if "model" in model_call_details else None - if model: - for callback in self.callback_list: - if callback == "sentry": # add a sentry breadcrumb if user passed in sentry integration - self.add_breadcrumb( - category=f'{model}', - message='Trying request model {} input {}'.format(model, json.dumps(model_call_details)), - level='info', - ) - if self.logger_fn and callable(self.logger_fn): - self.logger_fn(model_call_details) - pass - def handle_success(self, model, messages, additional_details): - success_handler = additional_details.pop("success_handler", None) - failure_handler = additional_details.pop("failure_handler", None) - additional_details["litellm_model"] = str(model) - additional_details["litellm_messages"] = str(messages) - for callback in self.success_callback: - try: - if callback == "posthog": - ph_obj = {} - for detail in additional_details: - ph_obj[detail] = additional_details[detail] - event_name = additional_details["successful_event"] if "successful_event" in additional_details else "litellm.succes_query" - if "user_id" in additional_details: - self.posthog.capture(additional_details["user_id"], event_name, ph_obj) - else: - self.posthog.capture(event_name, ph_obj) - pass - elif callback == "slack": - slack_msg = "" - if len(additional_details.keys()) > 0: - for detail in additional_details: - slack_msg += f"{detail}: {additional_details[detail]}\n" - slack_msg += f"Successful call" - self.slack_app.client.chat_postMessage(channel=self.alerts_channel, text=slack_msg) - except: - pass +def handle_failure(exception, traceback_exception, args, kwargs): + print_verbose(f"handle_failure args: {args}") + print_verbose(f"handle_failure kwargs: {kwargs}") - if success_handler and callable(success_handler): - call_details = { - "model": model, - "messages": messages, - "additional_details": additional_details - } - success_handler(call_details) - pass - - def handle_failure(self, exception, args): - args.pop("self") - additional_details = args.pop("additional_details", {}) - success_handler = additional_details.pop("success_handler", None) failure_handler = additional_details.pop("failure_handler", None) + + additional_details["Event_Name"] = additional_details.pop("failed_event_name", "litellm.failed_query") + print_verbose(f"self.failure_callback: {failure_callback}") - for callback in self.failure_callback: + print_verbose(f"additional_details: {additional_details}") + for callback in failure_callback: try: if callback == "slack": slack_msg = "" - for param in args: - slack_msg += f"{param}: {args[param]}\n" - if len(additional_details.keys()) > 0: - for detail in additional_details: - slack_msg += f"{detail}: {additional_details[detail]}\n" - slack_msg += f"Traceback: {traceback.format_exc()}" - self.slack_app.client.chat_postMessage(channel=self.alerts_channel, text=slack_msg) + if len(kwargs) > 0: + for key in kwargs: + slack_msg += f"{key}: {kwargs[key]}\n" + if len(args) > 0: + for i, arg in enumerate(args): + slack_msg += f"LiteLLM_Args_{str(i)}: {arg}" + for detail in additional_details: + slack_msg += f"{detail}: {additional_details[detail]}\n" + slack_msg += f"Traceback: {traceback_exception}" + print_verbose(f"This is the slack message: {slack_msg}") + slack_app.client.chat_postMessage(channel=alerts_channel, text=slack_msg) elif callback == "sentry": - self.capture_exception(exception) - elif callback == "posthog": - if len(additional_details.keys()) > 0: - ph_obj = {} - for param in args: - ph_obj[param] += args[param] - for detail in additional_details: - ph_obj[detail] = additional_details[detail] - event_name = additional_details["failed_event"] if "failed_event" in additional_details else "litellm.failed_query" - if "user_id" in additional_details: - self.posthog.capture(additional_details["user_id"], event_name, ph_obj) - else: - self.posthog.capture(event_name, ph_obj) - else: - pass + capture_exception(exception) + elif callback == "posthog": + print_verbose(f"inside posthog, additional_details: {len(additional_details.keys())}") + ph_obj = {} + if len(kwargs) > 0: + ph_obj = kwargs + if len(args) > 0: + for i, arg in enumerate(args): + ph_obj["litellm_args_" + str(i)] = arg + print_verbose(f"ph_obj: {ph_obj}") + for detail in additional_details: + ph_obj[detail] = additional_details[detail] + event_name = additional_details["Event_Name"] + print_verbose(f"PostHog Event Name: {event_name}") + if "user_id" in additional_details: + posthog.capture(additional_details["user_id"], event_name, ph_obj) + else: # PostHog calls require a unique id to identify a user - https://posthog.com/docs/libraries/python + print(f"ph_obj: {ph_obj})") + unique_id = str(uuid.uuid4()) + posthog.capture(unique_id, event_name) + print_verbose(f"successfully logged to PostHog!") except: - print(f"got an error calling {callback} - {traceback.format_exc()}") + print_verbose(f"Error Occurred while logging failure: {traceback.format_exc()}") + pass if failure_handler and callable(failure_handler): call_details = { @@ -386,7 +365,51 @@ class litellm_client: } failure_handler(call_details) pass -####### HELPER FUNCTIONS ################ + + +def handle_input(model_call_details={}): + if len(model_call_details.keys()) > 0: + model = model_call_details["model"] if "model" in model_call_details else None + if model: + for callback in callback_list: + if callback == "sentry": # add a sentry breadcrumb if user passed in sentry integration + add_breadcrumb( + category=f'{model}', + message='Trying request model {} input {}'.format(model, json.dumps(model_call_details)), + level='info', + ) + if user_logger_fn and callable(user_logger_fn): + user_logger_fn(model_call_details) + pass + +def handle_success(*args, **kwargs): + success_handler = additional_details.pop("success_handler", None) + failure_handler = additional_details.pop("failure_handler", None) + additional_details["Event_Name"] = additional_details.pop("successful_event_name", "litellm.succes_query") + for callback in success_callback: + try: + if callback == "posthog": + ph_obj = {} + for detail in additional_details: + ph_obj[detail] = additional_details[detail] + event_name = additional_details["Event_Name"] + if "user_id" in additional_details: + posthog.capture(additional_details["user_id"], event_name, ph_obj) + else: # PostHog calls require a unique id to identify a user - https://posthog.com/docs/libraries/python + unique_id = str(uuid.uuid4()) + posthog.capture(unique_id, event_name, ph_obj) + pass + elif callback == "slack": + slack_msg = "" + for detail in additional_details: + slack_msg += f"{detail}: {additional_details[detail]}\n" + slack_app.client.chat_postMessage(channel=alerts_channel, text=slack_msg) + except: + pass + + if success_handler and callable(success_handler): + success_handler(args, kwargs) + pass #Logging function -> log the exact model details + what's being sent | Non-Blocking def logging(model, input, azure=False, additional_args={}, logger_fn=None): @@ -395,35 +418,26 @@ def logging(model, input, azure=False, additional_args={}, logger_fn=None): model_call_details["model"] = model model_call_details["input"] = input model_call_details["azure"] = azure + # log additional call details -> api key, etc. + if azure == True or model in open_ai_chat_completion_models or model in open_ai_chat_completion_models or model in 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 anthropic_models: + model_call_details["api_key"] = os.environ.get("ANTHROPIC_API_KEY") + elif model in cohere_models: + model_call_details["api_key"] = os.environ.get("COHERE_API_KEY") model_call_details["additional_args"] = additional_args + ## Logging + print_verbose(f"Basic model call details: {model_call_details}") if logger_fn and callable(logger_fn): try: - # log additional call details -> api key, etc. - if azure == True or model in open_ai_chat_completion_models or model in open_ai_chat_completion_models or model in 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 anthropic_models: - model_call_details["api_key"] = os.environ.get("ANTHROPIC_API_KEY") - elif model in cohere_models: - model_call_details["api_key"] = os.environ.get("COHERE_API_KEY") - logger_fn(model_call_details) # Expectation: any logger function passed in by the user should accept a dict object except: - print_verbose(f"Basic model call details: {model_call_details}") print_verbose(f"[Non-Blocking] Exception occurred while logging {traceback.format_exc()}") pass - else: - print_verbose(f"Basic model call details: {model_call_details}") - pass except: pass - -## Set verbose to true -> ```litellm.verbose = True``` -def print_verbose(print_statement): - if set_verbose: - print(f"LiteLLM: {print_statement}") - print("Get help - https://discord.com/invite/wuPM9dRgDw") \ No newline at end of file diff --git a/litellm/tests/test_bad_params.py b/litellm/tests/test_bad_params.py index 2b2e4bbcf..8e06b15e9 100644 --- a/litellm/tests/test_bad_params.py +++ b/litellm/tests/test_bad_params.py @@ -2,10 +2,9 @@ import sys, os import traceback sys.path.append('..') # Adds the parent directory to the system path import main -from main import litellm_client -client = litellm_client(success_callback=["posthog"], failure_callback=["slack", "sentry", "posthog"], verbose=True) -completion = client.completion -embedding = client.embedding +from main import embedding, completion +main.success_callback = ["posthog"] +main.failure_callback = ["slack", "sentry", "posthog"] main.set_verbose = True diff --git a/litellm/tests/test_client.py b/litellm/tests/test_client.py index 06850ea18..f9399d42c 100644 --- a/litellm/tests/test_client.py +++ b/litellm/tests/test_client.py @@ -2,27 +2,19 @@ import sys, os import traceback sys.path.append('..') # Adds the parent directory to the system path import main -from main import litellm_client -client = litellm_client(success_callback=["posthog"], failure_callback=["slack", "sentry", "posthog"], verbose=True) -completion = client.completion -embedding = client.embedding +from main import embedding, completion +main.success_callback = ["posthog"] +main.failure_callback = ["slack", "sentry", "posthog"] -main.set_verbose = True +# main.set_verbose = True def logger_fn(model_call_object: dict): - print(f"model call details: {model_call_object}") + # print(f"model call details: {model_call_object}") + pass user_message = "Hello, how are you?" messages = [{ "content": user_message,"role": "user"}] -# test on openai completion call -try: - response = completion(model="gpt-3.5-turbo", messages=messages, logger_fn=logger_fn) -except: - print(f"error occurred: {traceback.format_exc()}") - pass - - # test on openai completion call try: response = completion(model="gpt-3.5-turbo", messages=messages, logger_fn=logger_fn) diff --git a/litellm/tests/test_model_fallback.py b/litellm/tests/test_model_fallback.py new file mode 100644 index 000000000..cdb4bffa2 --- /dev/null +++ b/litellm/tests/test_model_fallback.py @@ -0,0 +1,25 @@ +import sys, os +import traceback +sys.path.append('..') # Adds the parent directory to the system path +import main +from main import embedding, completion +main.success_callback = ["posthog"] +main.failure_callback = ["slack", "sentry", "posthog"] + +main.set_verbose = True + +model_fallback_list = ["replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1", "claude-instant-1", "gpt-3.5-turbo"] + +user_message = "Hello, how are you?" +messages = [{ "content": user_message,"role": "user"}] + +# for _ in range(10): +for model in model_fallback_list: + try: + response = completion(model=model, messages=messages) + print(response) + if response != None: + break + except: + print(f"error occurred: {traceback.format_exc()}") + pass diff --git a/setup.py b/setup.py index aed2c2747..54ee48713 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name='litellm', - version='0.1.2', + version='0.1.203', description='Library to easily interface with LLM API providers', author='BerriAI', packages=[