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).
This commit is contained in:
wadrzl55333 2026-08-28 15:45:57 +08:00
parent ca0b951a43
commit 4f06fa8a50
2 changed files with 52 additions and 2 deletions

View file

@ -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}"
)

View file

@ -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