diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index cbfc978a882..7dced4e26b6 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -5040,7 +5040,6 @@ class ProxyConfig: # these are litellm callbacks - "langfuse", "sentry", "wandb" else: litellm.logging_callback_manager.add_litellm_success_callback(callback) - _add_custom_logger_callback_to_specific_event(callback, "success") if "prometheus" in callback: from litellm.integrations.prometheus import ( PrometheusLogger, @@ -5068,7 +5067,6 @@ class ProxyConfig: # these are litellm callbacks - "langfuse", "sentry", "wandb" else: litellm.logging_callback_manager.add_litellm_failure_callback(callback) - _add_custom_logger_callback_to_specific_event(callback, "failure") print( # noqa: T201 f"{blue_color_code} Initialized Failure Callbacks - {litellm.failure_callback} {reset_color_code}" ) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 86d954c0913..58bb3823e81 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -173,6 +173,7 @@ from litellm.types.mcp import ( ) from litellm.types.proxy.policy_engine.pipeline_types import PipelineExecutionResult from litellm.types.utils import LLMResponseTypes, LoggedLiteLLMParams +from litellm.utils import _add_custom_logger_callback_to_specific_event if TYPE_CHECKING: from mcp.types import CallToolResult @@ -851,6 +852,16 @@ class ProxyLogging: litellm.logging_callback_manager.add_litellm_async_success_callback(callback) litellm.logging_callback_manager.add_litellm_async_failure_callback(callback) + # Runs after load_config applied every litellm_settings key: logger __init__s read e.g. s3_callback_params + success_callbacks: Final = tuple(cast(list[str | CustomLogger], litellm.success_callback)) + failure_callbacks: Final = tuple(cast(list[str | CustomLogger], litellm.failure_callback)) + for callback in success_callbacks: + if isinstance(callback, str): + _add_custom_logger_callback_to_specific_event(callback, "success") + for callback in failure_callbacks: + if isinstance(callback, str): + _add_custom_logger_callback_to_specific_event(callback, "failure") + async def update_request_status(self, litellm_call_id: str, status: Literal["success", "fail"]): # only use this if slack alerting is being used if self.alerting is None: diff --git a/tests/test_litellm/proxy/test_proxy_server.py b/tests/test_litellm/proxy/test_proxy_server.py index bd784249aa5..ae09d2be1b6 100644 --- a/tests/test_litellm/proxy/test_proxy_server.py +++ b/tests/test_litellm/proxy/test_proxy_server.py @@ -2952,26 +2952,44 @@ async def test_custom_ui_sso_sign_in_handler_config_loading(): @pytest.mark.asyncio -async def test_load_config_eagerly_initializes_string_success_callback(tmp_path, monkeypatch): - from litellm.integrations.generic_api.generic_api_callback import GenericAPILogger +async def test_startup_initializes_string_callbacks_after_all_litellm_settings_load(tmp_path, monkeypatch): + from litellm.integrations.s3_v2 import S3Logger + from litellm.litellm_core_utils import litellm_logging from litellm.proxy.proxy_server import ProxyConfig + from litellm.proxy.utils import ProxyLogging config_file = tmp_path / "config.yaml" config_file.write_text( "model_list: []\n" "litellm_settings:\n" " success_callback:\n" - " - generic_api\n" + " - s3_v2\n" + " failure_callback:\n" + " - s3_v2\n" + " s3_callback_params:\n" + " s3_bucket_name: ordering-regression-bucket\n" + " s3_region_name: us-west-2\n" ) - monkeypatch.setenv("GENERIC_LOGGER_ENDPOINT", "http://127.0.0.1:8899/") monkeypatch.setattr(litellm, "success_callback", []) monkeypatch.setattr(litellm, "_async_success_callback", []) monkeypatch.setattr(litellm, "failure_callback", []) monkeypatch.setattr(litellm, "_async_failure_callback", []) + monkeypatch.setattr(litellm, "callbacks", []) + monkeypatch.setattr(litellm, "s3_callback_params", None) + monkeypatch.setattr(litellm_logging, "_in_memory_loggers", []) + await ProxyConfig().load_config(router=MagicMock(), config_file_path=str(config_file)) - assert any(isinstance(callback, GenericAPILogger) for callback in litellm._async_success_callback) - assert "generic_api" not in litellm.success_callback + ProxyLogging(user_api_key_cache=MagicMock())._init_litellm_callbacks(llm_router=None) + + success_loggers = [cb for cb in litellm._async_success_callback if isinstance(cb, S3Logger)] + failure_loggers = [cb for cb in litellm._async_failure_callback if isinstance(cb, S3Logger)] + assert len(success_loggers) == 1 + assert len(failure_loggers) == 1 + assert success_loggers[0].s3_bucket_name == "ordering-regression-bucket" + assert success_loggers[0].s3_region_name == "us-west-2" + assert "s3_v2" not in litellm.success_callback + assert "s3_v2" not in litellm.failure_callback @pytest.mark.asyncio