From 4f06fa8a5083fc93400177423053e3de9fa4d9b8 Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 15:45:57 +0800 Subject: [PATCH 01/14] fix(proxy): register config-file custom loggers as instances, not strings Pass-through requests log through the async success path only, so a config-file success_callback: ["langfuse"] string lands in litellm.success_callback where pass-through never reads it: the async list has no langfuse, and _should_run_sync_callbacks_for_async_calls filters the string because langfuse is in _known_custom_logger_compatible_callbacks. Result: no langfuse traces for pass-through endpoints while standard routes log fine. Route known custom-logger names through _add_custom_logger_callback_to_specific_event, which registers a custom-logger instance in both the sync and async callback lists, so pass-through success/failure logging fires. Mirrors the DB-config path (_add_callbacks_from_db_config). --- litellm/proxy/proxy_server.py | 10 ++++- .../test_proxy_config_unit_test.py | 44 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 7ea6fdee6a5..802056c0db6 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -5121,7 +5121,10 @@ class ProxyConfig: ) # these are litellm callbacks - "langfuse", "sentry", "wandb" else: - litellm.logging_callback_manager.add_litellm_success_callback(callback) + if callback in litellm._known_custom_logger_compatible_callbacks: + _add_custom_logger_callback_to_specific_event(callback, "success") + else: + litellm.logging_callback_manager.add_litellm_success_callback(callback) if "prometheus" in callback: from litellm.integrations.prometheus import ( PrometheusLogger, @@ -5148,7 +5151,10 @@ class ProxyConfig: ) # these are litellm callbacks - "langfuse", "sentry", "wandb" else: - litellm.logging_callback_manager.add_litellm_failure_callback(callback) + if callback in litellm._known_custom_logger_compatible_callbacks: + _add_custom_logger_callback_to_specific_event(callback, "failure") + else: + litellm.logging_callback_manager.add_litellm_failure_callback(callback) print( # noqa: T201 f"{blue_color_code} Initialized Failure Callbacks - {litellm.failure_callback} {reset_color_code}" ) 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 81648dc1158..fa5bed281fa 100644 --- a/tests/proxy_unit_tests/test_proxy_config_unit_test.py +++ b/tests/proxy_unit_tests/test_proxy_config_unit_test.py @@ -158,6 +158,50 @@ async def test_multiple_includes(): assert config["litellm_settings"]["callbacks"] == ["prometheus"] +@pytest.mark.asyncio +async def test_config_file_success_callback_registers_custom_logger_instance(): + """Config-file success_callback must register a custom-logger instance, not just the + string. + + Pass-through endpoints log through the async success path only; string registration + leaves langfuse out of both callback lists because + `_should_run_sync_callbacks_for_async_calls` filters strings in + `_known_custom_logger_compatible_callbacks`. + """ + import tempfile + + import yaml + + from litellm.integrations.langfuse.langfuse_prompt_management import ( + LangfusePromptManagement, + ) + + litellm.success_callback = [] + litellm._async_success_callback = [] + + config_content = {"litellm_settings": {"success_callback": ["langfuse"]}} + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as temp_file: + yaml.dump(config_content, temp_file) + temp_file_path = temp_file.name + + try: + proxy_config = ProxyConfig() + await proxy_config.load_config( + router=None, + config_file_path=temp_file_path, + ) + + assert "langfuse" not in litellm.success_callback + num_langfuse_instances = sum( + isinstance(callback, LangfusePromptManagement) for callback in litellm._async_success_callback + ) + assert num_langfuse_instances == 1 + finally: + litellm.success_callback = [] + litellm._async_success_callback = [] + os.unlink(temp_file_path) + + def test_add_callbacks_from_db_config(): """Test that callbacks are added correctly and duplicates are prevented""" # Setup From 817b0a983770fbb819de3092f9701d989dbb2f4f Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 16:00:47 +0800 Subject: [PATCH 02/14] test(proxy): avoid module-global mutation in callback regression test --- tests/proxy_unit_tests/test_proxy_config_unit_test.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 fa5bed281fa..2434588b12f 100644 --- a/tests/proxy_unit_tests/test_proxy_config_unit_test.py +++ b/tests/proxy_unit_tests/test_proxy_config_unit_test.py @@ -159,7 +159,7 @@ async def test_multiple_includes(): @pytest.mark.asyncio -async def test_config_file_success_callback_registers_custom_logger_instance(): +async def test_config_file_success_callback_registers_custom_logger_instance(monkeypatch): """Config-file success_callback must register a custom-logger instance, not just the string. @@ -176,8 +176,8 @@ async def test_config_file_success_callback_registers_custom_logger_instance(): LangfusePromptManagement, ) - litellm.success_callback = [] - litellm._async_success_callback = [] + monkeypatch.setattr(litellm, "success_callback", []) + monkeypatch.setattr(litellm, "_async_success_callback", []) config_content = {"litellm_settings": {"success_callback": ["langfuse"]}} with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as temp_file: @@ -197,8 +197,6 @@ async def test_config_file_success_callback_registers_custom_logger_instance(): ) assert num_langfuse_instances == 1 finally: - litellm.success_callback = [] - litellm._async_success_callback = [] os.unlink(temp_file_path) From 2bafe2c9782cfebd3c9605f151bf07006c0aabdd Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 16:06:50 +0800 Subject: [PATCH 03/14] test(proxy): cover non-compatible string and failure-callback branches --- .../test_proxy_config_unit_test.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) 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 2434588b12f..c2eb9a2b64f 100644 --- a/tests/proxy_unit_tests/test_proxy_config_unit_test.py +++ b/tests/proxy_unit_tests/test_proxy_config_unit_test.py @@ -178,8 +178,15 @@ async def test_config_file_success_callback_registers_custom_logger_instance(mon monkeypatch.setattr(litellm, "success_callback", []) monkeypatch.setattr(litellm, "_async_success_callback", []) + monkeypatch.setattr(litellm, "failure_callback", []) + monkeypatch.setattr(litellm, "_async_failure_callback", []) - config_content = {"litellm_settings": {"success_callback": ["langfuse"]}} + config_content = { + "litellm_settings": { + "success_callback": ["langfuse", "sentry"], + "failure_callback": ["langfuse"], + } + } with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as temp_file: yaml.dump(config_content, temp_file) temp_file_path = temp_file.name @@ -191,11 +198,18 @@ async def test_config_file_success_callback_registers_custom_logger_instance(mon config_file_path=temp_file_path, ) + # custom-logger-compatible names are registered as instances, not strings assert "langfuse" not in litellm.success_callback + # non-compatible names still take the string path + assert "sentry" in litellm.success_callback num_langfuse_instances = sum( isinstance(callback, LangfusePromptManagement) for callback in litellm._async_success_callback ) assert num_langfuse_instances == 1 + num_failure_instances = sum( + isinstance(callback, LangfusePromptManagement) for callback in litellm._async_failure_callback + ) + assert num_failure_instances == 1 finally: os.unlink(temp_file_path) From 3a210c1b47adea6f05335abffcd8e27a20078298 Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 16:24:37 +0800 Subject: [PATCH 04/14] fix(proxy): strip callback string before custom-logger registration _add_custom_logger_callback_to_specific_event skips both registration and the string removal inside it when an instance already exists from an earlier registration, leaving the config string behind in litellm.success_callback. Remove the string explicitly in the config path so the duplicate check can never strand it. --- litellm/proxy/proxy_server.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 802056c0db6..c97934e9471 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -5122,7 +5122,14 @@ class ProxyConfig: # these are litellm callbacks - "langfuse", "sentry", "wandb" else: if callback in litellm._known_custom_logger_compatible_callbacks: - _add_custom_logger_callback_to_specific_event(callback, "success") + # 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) + _add_custom_logger_callback_to_specific_event( # pyright: ignore[reportPrivateUsage] # mirrors the DB-config registration path + callback, "success" + ) else: litellm.logging_callback_manager.add_litellm_success_callback(callback) if "prometheus" in callback: @@ -5152,7 +5159,11 @@ class ProxyConfig: # these are litellm callbacks - "langfuse", "sentry", "wandb" else: if callback in litellm._known_custom_logger_compatible_callbacks: - _add_custom_logger_callback_to_specific_event(callback, "failure") + if callback in litellm.failure_callback: + litellm.failure_callback.remove(callback) + _add_custom_logger_callback_to_specific_event( # pyright: ignore[reportPrivateUsage] # mirrors the DB-config registration path + callback, "failure" + ) else: litellm.logging_callback_manager.add_litellm_failure_callback(callback) print( # noqa: T201 From 82b789a99dd6b9d7451340d822c3cb5c9522beac Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 16:38:52 +0800 Subject: [PATCH 05/14] style(proxy): keep pyright ignore comment under line limit --- litellm/proxy/proxy_server.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index c97934e9471..493c7b38970 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -5127,8 +5127,9 @@ class ProxyConfig: # instance already exists from an earlier registration if callback in litellm.success_callback: litellm.success_callback.remove(callback) - _add_custom_logger_callback_to_specific_event( # pyright: ignore[reportPrivateUsage] # mirrors the DB-config registration path - callback, "success" + _add_custom_logger_callback_to_specific_event( # pyright: ignore[reportPrivateUsage] + callback, + "success", # mirrors the DB-config registration path ) else: litellm.logging_callback_manager.add_litellm_success_callback(callback) @@ -5161,8 +5162,9 @@ class ProxyConfig: if callback in litellm._known_custom_logger_compatible_callbacks: if callback in litellm.failure_callback: litellm.failure_callback.remove(callback) - _add_custom_logger_callback_to_specific_event( # pyright: ignore[reportPrivateUsage] # mirrors the DB-config registration path - callback, "failure" + _add_custom_logger_callback_to_specific_event( # pyright: ignore[reportPrivateUsage] + callback, + "failure", # mirrors the DB-config registration path ) else: litellm.logging_callback_manager.add_litellm_failure_callback(callback) From 84afc7e92463d87ba1735064e76543aed8c23d99 Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 16:44:38 +0800 Subject: [PATCH 06/14] fix(proxy): route config-file custom loggers via module helper Keeps the private-access suppression on one short line so it carries a reason without exceeding the line budget. --- litellm/proxy/proxy_server.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 493c7b38970..176fd38fbae 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -704,6 +704,18 @@ from litellm.types.utils import CredentialItem, CustomHuggingfaceTokenizer, RawR from litellm.types.utils import ModelInfo as ModelMapInfo from litellm.utils import _add_custom_logger_callback_to_specific_event + +def _register_config_custom_logger_callback(callback: str, logging_event: Literal["success", "failure"]) -> None: + """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. + """ + _add_custom_logger_callback_to_specific_event( # pyright: ignore[reportPrivateUsage] # mirrors DB-config path + callback, logging_event + ) + + try: from litellm._version import version except Exception: @@ -5127,10 +5139,7 @@ class ProxyConfig: # instance already exists from an earlier registration if callback in litellm.success_callback: litellm.success_callback.remove(callback) - _add_custom_logger_callback_to_specific_event( # pyright: ignore[reportPrivateUsage] - callback, - "success", # mirrors the DB-config registration path - ) + _register_config_custom_logger_callback(callback, "success") else: litellm.logging_callback_manager.add_litellm_success_callback(callback) if "prometheus" in callback: @@ -5162,10 +5171,7 @@ class ProxyConfig: if callback in litellm._known_custom_logger_compatible_callbacks: if callback in litellm.failure_callback: litellm.failure_callback.remove(callback) - _add_custom_logger_callback_to_specific_event( # pyright: ignore[reportPrivateUsage] - callback, - "failure", # mirrors the DB-config registration path - ) + _register_config_custom_logger_callback(callback, "failure") else: litellm.logging_callback_manager.add_litellm_failure_callback(callback) print( # noqa: T201 From ed99488fd3e35c37491ef0fcde7359af92ce8761 Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 16:57:50 +0800 Subject: [PATCH 07/14] fix(proxy): fall back to string callback when custom-logger init fails The custom-logger class may not initialize without env credentials; keep the string in litellm.success_callback in that case so standard-route logging still works. Test sets LANGFUSE env so it exercises the instance registration path. --- litellm/proxy/proxy_server.py | 26 +++++++++++++------ .../test_proxy_config_unit_test.py | 6 +++++ 2 files changed, 24 insertions(+), 8 deletions(-) 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": { From 1592bccf54b2efa686b116df79c99a3d0b0d4c85 Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 17:18:18 +0800 Subject: [PATCH 08/14] test(proxy): cover string fallback when custom-logger init fails Covers the _register_config_custom_logger_callback fallback branches (success + failure): when _init_custom_logger_compatible_class returns None, the callback must land in the string lists so standard-route logging keeps working. --- .../test_proxy_config_unit_test.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) 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 e428124c33f..2966bf2af6e 100644 --- a/tests/proxy_unit_tests/test_proxy_config_unit_test.py +++ b/tests/proxy_unit_tests/test_proxy_config_unit_test.py @@ -220,6 +220,53 @@ async def test_config_file_success_callback_registers_custom_logger_instance(mon os.unlink(temp_file_path) +@pytest.mark.asyncio +async def test_config_file_callback_falls_back_to_string_when_init_fails(monkeypatch): + """When the custom-logger class cannot be initialized, config-file callbacks + must fall back to string registration so standard-route logging still works.""" + import tempfile + + import yaml + + from litellm.integrations.langfuse.langfuse_prompt_management import ( + LangfusePromptManagement, + ) + + 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.litellm_core_utils.litellm_logging._init_custom_logger_compatible_class", + lambda callback, internal_usage_cache=None, llm_router=None: None, + ) + + config_content = { + "litellm_settings": { + "success_callback": ["langfuse"], + "failure_callback": ["langfuse"], + } + } + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as temp_file: + yaml.dump(config_content, temp_file) + temp_file_path = temp_file.name + + try: + proxy_config = ProxyConfig() + await proxy_config.load_config( + router=None, + config_file_path=temp_file_path, + ) + + # init failed -> fall back to the string path, standard route keeps logging + assert "langfuse" in litellm.success_callback + assert "langfuse" in litellm.failure_callback + assert not any(isinstance(callback, LangfusePromptManagement) for callback in litellm._async_success_callback) + assert not any(isinstance(callback, LangfusePromptManagement) for callback in litellm._async_failure_callback) + finally: + os.unlink(temp_file_path) + + def test_add_callbacks_from_db_config(): """Test that callbacks are added correctly and duplicates are prevented""" # Setup From 94ce3b1d50e36f68b2218b0601ef19663115f827 Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 17:29:57 +0800 Subject: [PATCH 09/14] test(proxy): pin known-callbacks precondition in config-file logger tests test_add_callbacks_from_db_config clears _known_custom_logger_compatible_callbacks without restoring it, and the conftest module snapshot skips underscore attrs, so later tests depend on file order. Assert the precondition via monkeypatch (which auto-restores) in both custom-logger tests. --- .../test_proxy_config_unit_test.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 2966bf2af6e..21d88dd0269 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,13 @@ 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", []) + # test_add_callbacks_from_db_config clears _known_custom_logger_compatible_callbacks + # without restoring it (and the conftest snapshot skips underscore attrs), so + # re-assert the precondition explicitly instead of depending on test order. + known_callbacks = list(getattr(litellm, "_known_custom_logger_compatible_callbacks", [])) + if "langfuse" not in known_callbacks: + known_callbacks.append("langfuse") + monkeypatch.setattr(litellm, "_known_custom_logger_compatible_callbacks", known_callbacks) # 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 @@ -236,6 +243,13 @@ async def test_config_file_callback_falls_back_to_string_when_init_fails(monkeyp monkeypatch.setattr(litellm, "_async_success_callback", []) monkeypatch.setattr(litellm, "failure_callback", []) monkeypatch.setattr(litellm, "_async_failure_callback", []) + # same precondition as the sibling instance test: langfuse must be a known + # custom-logger name, otherwise the config path skips the helper entirely + # and this test would not exercise the fallback branch + known_callbacks = list(getattr(litellm, "_known_custom_logger_compatible_callbacks", [])) + if "langfuse" not in known_callbacks: + known_callbacks.append("langfuse") + monkeypatch.setattr(litellm, "_known_custom_logger_compatible_callbacks", known_callbacks) monkeypatch.setattr( "litellm.litellm_core_utils.litellm_logging._init_custom_logger_compatible_class", lambda callback, internal_usage_cache=None, llm_router=None: None, From 114777be309ba5053baff528326337893378ff02 Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 20:54:43 +0800 Subject: [PATCH 10/14] refactor(proxy): move config-callback registration to public util wrapper The helper reached into litellm_logging private APIs; the repo's pyright config has enableTypeIgnoreComments disabled and a strict per-rule budget, so each new private import and call site counts against the gate. Move the instance-registration-with-fallback logic behind a public register_config_custom_logger_callback in litellm.utils, and let the existing _add_custom_logger_callback_to_specific_event carry the fallback (its else branch now registers the string when the custom-logger class cannot initialize). Net change in private API surface: zero. --- litellm/proxy/proxy_server.py | 33 +++------------------------------ litellm/utils.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 429e3d7697e..18791687c16 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -703,34 +703,7 @@ from litellm.types.secret_managers.main import ( from litellm.types.utils import CredentialItem, CustomHuggingfaceTokenizer, RawRequestTypedDict, StandardLoggingPayload from litellm.types.utils import ModelInfo as ModelMapInfo from litellm.utils import _add_custom_logger_callback_to_specific_event - - -def _register_config_custom_logger_callback(callback: str, logging_event: Literal["success", "failure"]) -> None: - """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. 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 - ) +from litellm.utils import register_config_custom_logger_callback try: @@ -5151,7 +5124,7 @@ class ProxyConfig: # these are litellm callbacks - "langfuse", "sentry", "wandb" else: if callback in litellm._known_custom_logger_compatible_callbacks: - _register_config_custom_logger_callback(callback, "success") + register_config_custom_logger_callback(callback, "success") else: litellm.logging_callback_manager.add_litellm_success_callback(callback) if "prometheus" in callback: @@ -5181,7 +5154,7 @@ class ProxyConfig: # these are litellm callbacks - "langfuse", "sentry", "wandb" else: if callback in litellm._known_custom_logger_compatible_callbacks: - _register_config_custom_logger_callback(callback, "failure") + register_config_custom_logger_callback(callback, "failure") else: litellm.logging_callback_manager.add_litellm_failure_callback(callback) print( # noqa: T201 diff --git a/litellm/utils.py b/litellm/utils.py index 520c40f67c0..2d5af878778 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -593,6 +593,28 @@ def _add_custom_logger_callback_to_specific_event(callback: str, logging_event: litellm.failure_callback.remove(callback) # remove the string from the callback list if callback in litellm._async_failure_callback: litellm._async_failure_callback.remove(callback) # remove the string from the callback list + else: + # Custom-logger class could not be initialized (e.g. missing env + # credentials). Fall back to string registration so standard-route + # logging still works; pass-through endpoints only reach instance + # callbacks, so they keep missing out in this degraded state. + if logging_event == "success": + litellm.logging_callback_manager.add_litellm_success_callback(callback) + else: + litellm.logging_callback_manager.add_litellm_failure_callback(callback) + + +def register_config_custom_logger_callback(callback: str, logging_event: Literal["success", "failure"]) -> None: + """Register a config-file custom-logger name as an instance, not a string. + + Config-file ``success_callback`` / ``failure_callback`` strings previously + landed in ``litellm.success_callback`` as bare strings; pass-through + endpoints log through the async path only, so they never saw them. + Registering the custom-logger instance covers both paths. Falls back to + string registration when the class cannot be initialized (e.g. missing env + credentials), so standard-route logging keeps working. + """ + _add_custom_logger_callback_to_specific_event(callback, logging_event) def _custom_logger_class_exists_in_success_callbacks( From 32a3265c850b81865aa656e244cbe8dcee471575 Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 21:06:50 +0800 Subject: [PATCH 11/14] style(proxy): merge litellm.utils imports to satisfy ruff organize-imports --- litellm/proxy/proxy_server.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 18791687c16..a1774d047a2 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -702,8 +702,7 @@ from litellm.types.secret_managers.main import ( ) from litellm.types.utils import CredentialItem, CustomHuggingfaceTokenizer, RawRequestTypedDict, StandardLoggingPayload from litellm.types.utils import ModelInfo as ModelMapInfo -from litellm.utils import _add_custom_logger_callback_to_specific_event -from litellm.utils import register_config_custom_logger_callback +from litellm.utils import _add_custom_logger_callback_to_specific_event, register_config_custom_logger_callback try: From d02f573e529f02633226373804deffe3741383b2 Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 21:21:54 +0800 Subject: [PATCH 12/14] style(proxy): remove blank line ruff 0.15.3 isort expects gone Locked ruff is 0.15.3 (local 0.16.5 had different isort output); its organize-imports wants no blank line between the import block and the version try/except. --- litellm/proxy/proxy_server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index a1774d047a2..7abe509fa1d 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -704,7 +704,6 @@ from litellm.types.utils import CredentialItem, CustomHuggingfaceTokenizer, RawR from litellm.types.utils import ModelInfo as ModelMapInfo from litellm.utils import _add_custom_logger_callback_to_specific_event, register_config_custom_logger_callback - try: from litellm._version import version except Exception: From d9da2db0435dab3229e7f62a3502e78311530cd6 Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 21:34:08 +0800 Subject: [PATCH 13/14] refactor(proxy): reuse DB callback helper for config-file registration The config-file loop reached into litellm._known_custom_logger_compatible_callbacks (2 new private accesses) which trips the basedpyright per-rule budget. Route it through the existing _add_callback_from_db_to_in_memory_litellm_callbacks helper instead: same known-list check, same instance registration, plus the string fallback now living in _add_custom_logger_callback_to_specific_event for when the custom-logger class cannot initialize. Net private API surface change vs base: zero. --- litellm/proxy/proxy_server.py | 20 +++++++++++--------- litellm/utils.py | 13 ------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 7abe509fa1d..c2e506ec00b 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -702,7 +702,7 @@ from litellm.types.secret_managers.main import ( ) from litellm.types.utils import CredentialItem, CustomHuggingfaceTokenizer, RawRequestTypedDict, StandardLoggingPayload from litellm.types.utils import ModelInfo as ModelMapInfo -from litellm.utils import _add_custom_logger_callback_to_specific_event, register_config_custom_logger_callback +from litellm.utils import _add_custom_logger_callback_to_specific_event try: from litellm._version import version @@ -5121,10 +5121,11 @@ class ProxyConfig: ) # these are litellm callbacks - "langfuse", "sentry", "wandb" else: - if callback in litellm._known_custom_logger_compatible_callbacks: - register_config_custom_logger_callback(callback, "success") - else: - litellm.logging_callback_manager.add_litellm_success_callback(callback) + self._add_callback_from_db_to_in_memory_litellm_callbacks( + callback=callback, + event_types=["success"], + existing_callbacks=litellm.success_callback, + ) if "prometheus" in callback: from litellm.integrations.prometheus import ( PrometheusLogger, @@ -5151,10 +5152,11 @@ class ProxyConfig: ) # these are litellm callbacks - "langfuse", "sentry", "wandb" else: - if callback in litellm._known_custom_logger_compatible_callbacks: - register_config_custom_logger_callback(callback, "failure") - else: - litellm.logging_callback_manager.add_litellm_failure_callback(callback) + self._add_callback_from_db_to_in_memory_litellm_callbacks( + callback=callback, + event_types=["failure"], + existing_callbacks=litellm.failure_callback, + ) print( # noqa: T201 f"{blue_color_code} Initialized Failure Callbacks - {litellm.failure_callback} {reset_color_code}" ) diff --git a/litellm/utils.py b/litellm/utils.py index 2d5af878778..1c9e14b38eb 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -604,19 +604,6 @@ def _add_custom_logger_callback_to_specific_event(callback: str, logging_event: litellm.logging_callback_manager.add_litellm_failure_callback(callback) -def register_config_custom_logger_callback(callback: str, logging_event: Literal["success", "failure"]) -> None: - """Register a config-file custom-logger name as an instance, not a string. - - Config-file ``success_callback`` / ``failure_callback`` strings previously - landed in ``litellm.success_callback`` as bare strings; pass-through - endpoints log through the async path only, so they never saw them. - Registering the custom-logger instance covers both paths. Falls back to - string registration when the class cannot be initialized (e.g. missing env - credentials), so standard-route logging keeps working. - """ - _add_custom_logger_callback_to_specific_event(callback, logging_event) - - def _custom_logger_class_exists_in_success_callbacks( callback_class: CustomLogger, ) -> bool: From 6b5d287f2a2ea461ddd23ce9762c806eabcf4c1e Mon Sep 17 00:00:00 2001 From: wadrzl55333 Date: Fri, 28 Aug 2026 21:42:59 +0800 Subject: [PATCH 14/14] style(proxy): suppress LIT002 for event-type literals in config registration Passing ["success"]/["failure"] literals constructs a mutable list, which the type-discipline budget counts; mark them mutable-ok mirroring the DB-config call site. --- litellm/proxy/proxy_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index c2e506ec00b..56b9766a6d3 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -5123,7 +5123,7 @@ class ProxyConfig: else: self._add_callback_from_db_to_in_memory_litellm_callbacks( callback=callback, - event_types=["success"], + event_types=["success"], # mutable-ok: mirrors the DB-config call site existing_callbacks=litellm.success_callback, ) if "prometheus" in callback: @@ -5154,7 +5154,7 @@ class ProxyConfig: else: self._add_callback_from_db_to_in_memory_litellm_callbacks( callback=callback, - event_types=["failure"], + event_types=["failure"], # mutable-ok: mirrors the DB-config call site existing_callbacks=litellm.failure_callback, ) print( # noqa: T201