diff --git a/litellm/integrations/custom_logger.py b/litellm/integrations/custom_logger.py index fd8ab2bad9..2a08408f7d 100644 --- a/litellm/integrations/custom_logger.py +++ b/litellm/integrations/custom_logger.py @@ -81,6 +81,44 @@ class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callbac self.turn_off_message_logging = turn_off_message_logging pass + @staticmethod + def get_callback_env_vars(callback_name: Optional[str] = None) -> List[str]: + """ + Return the environment variables associated with a given callback + name as defined in the proxy callback registry. + + Args: + callback_name: The name of the callback to look up. + + Returns: + List[str]: A list of required environment variable names. + """ + if callback_name is None: + return [] + + normalized_name = callback_name.lower() + + alias_map = { + "langfuse_otel": "langfuse", + } + lookup_name = alias_map.get(normalized_name, normalized_name) + + try: + from litellm.proxy._types import AllCallbacks + except Exception: + return [] + + callbacks = AllCallbacks() + callback_info = getattr(callbacks, lookup_name, None) + if callback_info is None: + return [] + + params = getattr(callback_info, "litellm_callback_params", None) + if not params: + return [] + + return list(params) + def log_pre_api_call(self, model, messages, kwargs): pass diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index d739727cca..397bfc9c3b 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -2503,6 +2503,14 @@ class AllCallbacks(LiteLLMPydanticObjectBase): ui_callback_name="Lago Billing", ) + traceloop: CallbackOnUI = CallbackOnUI( + litellm_callback_name="traceloop", + litellm_callback_params=[ + "TRACELoop_API_KEY", + ], + ui_callback_name="Traceloop", + ) + class SpendLogsMetadata(TypedDict): """ diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 4a0d39f451..9a582814a5 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -153,6 +153,7 @@ from litellm.constants import ( ) from litellm.exceptions import RejectedRequestError from litellm.integrations.SlackAlerting.slack_alerting import SlackAlerting +from litellm.integrations.custom_logger import CustomLogger from litellm.litellm_core_utils.core_helpers import ( _get_parent_otel_span_from_kwargs, get_litellm_metadata_from_kwargs, @@ -9575,35 +9576,7 @@ async def get_config(): # noqa: PLR0915 # Helper function to process callbacks and get environment variables def process_callback(_callback: str, callback_type: str) -> dict: """Process a single callback and return its data with environment variables""" - if _callback == "langfuse" or _callback == "langfuse_otel": - env_vars = [ - "LANGFUSE_PUBLIC_KEY", - "LANGFUSE_SECRET_KEY", - "LANGFUSE_HOST", - ] - elif _callback == "openmeter": - env_vars = [ - "OPENMETER_API_KEY", - ] - elif _callback == "braintrust": - env_vars = [ - "BRAINTRUST_API_KEY", - "BRAINTRUST_API_BASE", - ] - elif _callback == "traceloop": - env_vars = ["TRACELOOP_API_KEY"] - elif _callback == "custom_callback_api": - env_vars = ["GENERIC_LOGGER_ENDPOINT"] - elif _callback == "otel": - env_vars = ["OTEL_EXPORTER", "OTEL_ENDPOINT", "OTEL_HEADERS"] - elif _callback == "langsmith": - env_vars = [ - "LANGSMITH_API_KEY", - "LANGSMITH_PROJECT", - "LANGSMITH_DEFAULT_RUN_NAME", - ] - else: - env_vars = [] + env_vars = CustomLogger.get_callback_env_vars(_callback) env_vars_dict = {} for _var in env_vars: @@ -9625,7 +9598,7 @@ async def get_config(): # noqa: PLR0915 _success_callbacks = _litellm_settings.get("success_callback", []) _failure_callbacks = _litellm_settings.get("failure_callback", []) - _generic_callbacks = _litellm_settings.get("callbacks", []) + _success_and_failure_callbacks = _litellm_settings.get("callbacks", []) _data_to_return = [] """ @@ -9649,8 +9622,8 @@ async def get_config(): # noqa: PLR0915 for _callback in _failure_callbacks: _data_to_return.append(process_callback(_callback, "failure")) - for _callback in _generic_callbacks: - _data_to_return.append(process_callback(_callback, "generic")) + for _callback in _success_and_failure_callbacks: + _data_to_return.append(process_callback(_callback, "success_and_failure")) # Check if slack alerting is on _alerting = _general_settings.get("alerting", []) diff --git a/tests/local_testing/test_custom_logger.py b/tests/local_testing/test_custom_logger.py index 00e7c2d5aa..f3dc6a0a7a 100644 --- a/tests/local_testing/test_custom_logger.py +++ b/tests/local_testing/test_custom_logger.py @@ -103,6 +103,24 @@ class TmpFunction: ) +def test_get_callback_env_vars(): + env_vars = CustomLogger.get_callback_env_vars("langfuse") + assert env_vars == [ + "LANGFUSE_PUBLIC_KEY", + "LANGFUSE_SECRET_KEY", + "LANGFUSE_HOST", + ] + + alias_env_vars = CustomLogger.get_callback_env_vars("langfuse_otel") + assert alias_env_vars == env_vars + + missing_env_vars = CustomLogger.get_callback_env_vars("does_not_exist") + assert missing_env_vars == [] + + none_env_vars = CustomLogger.get_callback_env_vars(None) + assert none_env_vars == [] + + @pytest.mark.asyncio async def test_async_chat_openai_stream(): try: diff --git a/ui/litellm-dashboard/src/components/settings.tsx b/ui/litellm-dashboard/src/components/settings.tsx index a275214918..e8ac54e99d 100644 --- a/ui/litellm-dashboard/src/components/settings.tsx +++ b/ui/litellm-dashboard/src/components/settings.tsx @@ -58,7 +58,7 @@ interface AlertingVariables { interface AlertingObject { name: string; - type?: "success" | "failure" | "generic"; + type?: "success" | "failure" | "success_and_failure"; variables: AlertingVariables; } @@ -430,17 +430,24 @@ const Settings: React.FC = ({ accessToken, userRole, userID, const tooltipMessage = callback.type === "failure" ? "Modifications and deletion of failure type callbacks are not yet supported in the UI" - : callback.type === "generic" - ? "Modifications and deletion of generic type callbacks are not yet supported in the UI" + : callback.type === "success_and_failure" + ? "Modifications and deletion of success and failure type callbacks are not yet supported in the UI" : ""; const getBadgeColor = (type?: string) => { if (type === "success") return "green"; if (type === "failure") return "red"; - if (type === "generic") return "blue"; + if (type === "success_and_failure") return "blue"; return "gray"; }; + const getBadgeLabel = (type?: string) => { + if (type === "success") return "Success Only"; + if (type === "failure") return "Failure Only"; + if (type === "success_and_failure") return "Success & Failure"; + return "Unknown"; + }; + return ( @@ -448,9 +455,9 @@ const Settings: React.FC = ({ accessToken, userRole, userID, {callback.type ? ( - {callback.type} + {getBadgeLabel(callback.type)} ) : ( - success + Unknown )}