diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 176fd38fbae..429e3d7697e 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -709,8 +709,25 @@ def _register_config_custom_logger_callback(callback: str, logging_event: Litera """Register a config-file custom-logger name as an instance, not a string. Pass-through endpoints log through the async success path only; a string in - ``litellm.success_callback`` never reaches them. Mirrors the DB-config path. + ``litellm.success_callback`` never reaches them. Falls back to the string + when the custom-logger class cannot be initialized (e.g. missing env + credentials), so standard-route logging still works. Mirrors the DB-config path. """ + from litellm.litellm_core_utils.litellm_logging import ( + _init_custom_logger_compatible_class, + ) + + if ( + _init_custom_logger_compatible_class( # pyright: ignore[reportPrivateUsage] # mirrors DB-config path + callback, internal_usage_cache=None, llm_router=None + ) + is None + ): + if logging_event == "success": + litellm.logging_callback_manager.add_litellm_success_callback(callback) + else: + litellm.logging_callback_manager.add_litellm_failure_callback(callback) + return _add_custom_logger_callback_to_specific_event( # pyright: ignore[reportPrivateUsage] # mirrors DB-config path callback, logging_event ) @@ -5134,11 +5151,6 @@ class ProxyConfig: # these are litellm callbacks - "langfuse", "sentry", "wandb" else: if callback in litellm._known_custom_logger_compatible_callbacks: - # drop the string first: _add_custom_logger_callback_to_specific_event - # skips registration (and the string removal inside it) when an - # instance already exists from an earlier registration - if callback in litellm.success_callback: - litellm.success_callback.remove(callback) _register_config_custom_logger_callback(callback, "success") else: litellm.logging_callback_manager.add_litellm_success_callback(callback) @@ -5169,8 +5181,6 @@ class ProxyConfig: # these are litellm callbacks - "langfuse", "sentry", "wandb" else: if callback in litellm._known_custom_logger_compatible_callbacks: - if callback in litellm.failure_callback: - litellm.failure_callback.remove(callback) _register_config_custom_logger_callback(callback, "failure") else: litellm.logging_callback_manager.add_litellm_failure_callback(callback) diff --git a/tests/proxy_unit_tests/test_proxy_config_unit_test.py b/tests/proxy_unit_tests/test_proxy_config_unit_test.py index c2eb9a2b64f..e428124c33f 100644 --- a/tests/proxy_unit_tests/test_proxy_config_unit_test.py +++ b/tests/proxy_unit_tests/test_proxy_config_unit_test.py @@ -180,6 +180,12 @@ async def test_config_file_success_callback_registers_custom_logger_instance(mon monkeypatch.setattr(litellm, "_async_success_callback", []) monkeypatch.setattr(litellm, "failure_callback", []) monkeypatch.setattr(litellm, "_async_failure_callback", []) + # the custom-logger instance path needs env credentials; without them the + # config path falls back to the string and this test would not exercise + # the instance registration + monkeypatch.setenv("LANGFUSE_PUBLIC_KEY", "pk-lf-test") + monkeypatch.setenv("LANGFUSE_SECRET_KEY", "sk-lf-test") + monkeypatch.setenv("LANGFUSE_HOST", "http://localhost:3000") config_content = { "litellm_settings": {