diff --git a/litellm/integrations/langfuse.py b/litellm/integrations/langfuse.py index 27aa632790..51ad4d19ab 100644 --- a/litellm/integrations/langfuse.py +++ b/litellm/integrations/langfuse.py @@ -84,6 +84,7 @@ class LangFuseLogger: print_verbose( f"Langfuse Logging - Enters logging function for model {kwargs}" ) + litellm_params = kwargs.get("litellm_params", {}) metadata = ( litellm_params.get("metadata", {}) or {} @@ -373,7 +374,11 @@ class LangFuseLogger: # just log `litellm-{call_type}` as the generation name generation_name = f"litellm-{kwargs.get('call_type', 'completion')}" - system_fingerprint = response_obj.get("system_fingerprint", None) + if response_obj is not None and "system_fingerprint" in response_obj: + system_fingerprint = response_obj.get("system_fingerprint", None) + else: + system_fingerprint = None + if system_fingerprint is not None: optional_params["system_fingerprint"] = system_fingerprint diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 87bf8df5c1..ed6999a9c0 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -3663,6 +3663,17 @@ async def chat_completion( if data["model"] in litellm.model_alias_map: data["model"] = litellm.model_alias_map[data["model"]] + ## LOGGING OBJECT ## - initialize logging object for logging success/failure events for call + data["litellm_call_id"] = str(uuid.uuid4()) + logging_obj, data = litellm.utils.function_setup( + original_function="acompletion", + rules_obj=litellm.utils.Rules(), + start_time=litellm.utils.get_utc_datetime(), + **data, + ) + + data["litellm_logging_obj"] = logging_obj + ### CALL HOOKS ### - modify incoming data before calling the model data = await proxy_logging_obj.pre_call_hook( user_api_key_dict=user_api_key_dict, data=data, call_type="completion" diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index d483f296d5..bde7f654f2 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1,6 +1,6 @@ from typing import Optional, List, Any, Literal, Union import os, subprocess, hashlib, importlib, asyncio, copy, json, aiohttp, httpx, time -import litellm, backoff +import litellm, backoff, traceback from litellm.proxy._types import ( UserAPIKeyAuth, DynamoDBArgs, @@ -199,6 +199,33 @@ class ProxyLogging: print_verbose(f"final data being sent to {call_type} call: {data}") return data except Exception as e: + if "litellm_logging_obj" in data: + logging_obj: litellm.utils.Logging = data["litellm_logging_obj"] + + ## ASYNC FAILURE HANDLER ## + error_message = "" + if isinstance(e, HTTPException): + if isinstance(e.detail, str): + error_message = e.detail + elif isinstance(e.detail, dict): + error_message = json.dumps(e.detail) + else: + error_message = str(e) + else: + error_message = str(e) + error_raised = Exception(f"{error_message}") + await logging_obj.async_failure_handler( + exception=error_raised, + traceback_exception=traceback.format_exc(), + ) + + ## SYNC FAILURE HANDLER ## + try: + logging_obj.failure_handler( + error_raised, traceback.format_exc() + ) # DO NOT MAKE THREADED - router retry fallback relies on this! + except Exception as error_val: + pass raise e async def during_call_hook( diff --git a/litellm/tests/test_function_setup.py b/litellm/tests/test_function_setup.py index 4be36bacca..5cc3ce1230 100644 --- a/litellm/tests/test_function_setup.py +++ b/litellm/tests/test_function_setup.py @@ -25,7 +25,7 @@ def test_empty_content(): pass function_setup( - original_function=completion, + original_function="completion", rules_obj=rules_obj, start_time=datetime.now(), messages=[], diff --git a/litellm/utils.py b/litellm/utils.py index 8beb7c6d27..b79605087d 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -2433,7 +2433,7 @@ class Rules: ####### CLIENT ################### # make it easy to log if completion/embedding runs succeeded or failed + see what happened | Non-Blocking def function_setup( - original_function, rules_obj, start_time, *args, **kwargs + original_function: str, rules_obj, start_time, *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, user_logger_fn, Logging @@ -2457,10 +2457,12 @@ def function_setup( len(litellm.input_callback) > 0 or len(litellm.success_callback) > 0 or len(litellm.failure_callback) > 0 - ) and len(callback_list) == 0: + ) and len( + callback_list # type: ignore + ) == 0: # type: ignore callback_list = list( set( - litellm.input_callback + litellm.input_callback # type: ignore + litellm.success_callback + litellm.failure_callback ) @@ -2469,7 +2471,7 @@ def function_setup( ## ASYNC CALLBACKS if len(litellm.input_callback) > 0: removed_async_items = [] - for index, callback in enumerate(litellm.input_callback): + for index, callback in enumerate(litellm.input_callback): # type: ignore if inspect.iscoroutinefunction(callback): litellm._async_input_callback.append(callback) removed_async_items.append(index) @@ -2480,7 +2482,7 @@ def function_setup( if len(litellm.success_callback) > 0: removed_async_items = [] - for index, callback in enumerate(litellm.success_callback): + for index, callback in enumerate(litellm.success_callback): # type: ignore if inspect.iscoroutinefunction(callback): litellm._async_success_callback.append(callback) removed_async_items.append(index) @@ -2496,7 +2498,7 @@ def function_setup( if len(litellm.failure_callback) > 0: removed_async_items = [] - for index, callback in enumerate(litellm.failure_callback): + for index, callback in enumerate(litellm.failure_callback): # type: ignore if inspect.iscoroutinefunction(callback): litellm._async_failure_callback.append(callback) removed_async_items.append(index) @@ -2539,7 +2541,7 @@ def function_setup( user_logger_fn = kwargs["logger_fn"] # INIT LOGGER - for user-specified integrations model = args[0] if len(args) > 0 else kwargs.get("model", None) - call_type = original_function.__name__ + call_type = original_function if ( call_type == CallTypes.completion.value or call_type == CallTypes.acompletion.value @@ -2721,7 +2723,7 @@ def client(original_function): try: if logging_obj is None: logging_obj, kwargs = function_setup( - original_function, rules_obj, start_time, *args, **kwargs + original_function.__name__, rules_obj, start_time, *args, **kwargs ) kwargs["litellm_logging_obj"] = logging_obj @@ -3030,7 +3032,7 @@ def client(original_function): try: if logging_obj is None: logging_obj, kwargs = function_setup( - original_function, rules_obj, start_time, *args, **kwargs + original_function.__name__, rules_obj, start_time, *args, **kwargs ) kwargs["litellm_logging_obj"] = logging_obj