mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-28 01:32:17 +00:00
Merge 6b5d287f2a into e26a6450c8
This commit is contained in:
commit
e779abe9af
3 changed files with 142 additions and 2 deletions
|
|
@ -6156,7 +6156,11 @@ class ProxyConfig:
|
|||
)
|
||||
# these are litellm callbacks - "langfuse", "sentry", "wandb"
|
||||
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"], # mutable-ok: mirrors the DB-config call site
|
||||
existing_callbacks=litellm.success_callback,
|
||||
)
|
||||
if "prometheus" in callback:
|
||||
from litellm.integrations.prometheus import (
|
||||
PrometheusLogger,
|
||||
|
|
@ -6183,7 +6187,11 @@ class ProxyConfig:
|
|||
)
|
||||
# these are litellm callbacks - "langfuse", "sentry", "wandb"
|
||||
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"], # mutable-ok: mirrors the DB-config call site
|
||||
existing_callbacks=litellm.failure_callback,
|
||||
)
|
||||
print( # noqa: T201
|
||||
f"{blue_color_code} Initialized Failure Callbacks - {litellm.failure_callback} {reset_color_code}"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -618,6 +618,15 @@ 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 _custom_logger_class_exists_in_success_callbacks(
|
||||
|
|
|
|||
|
|
@ -158,6 +158,129 @@ 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(monkeypatch):
|
||||
"""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,
|
||||
)
|
||||
|
||||
monkeypatch.setattr(litellm, "success_callback", [])
|
||||
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
|
||||
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": {
|
||||
"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
|
||||
|
||||
try:
|
||||
proxy_config = ProxyConfig()
|
||||
await proxy_config.load_config(
|
||||
router=None,
|
||||
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)
|
||||
|
||||
|
||||
@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", [])
|
||||
# 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,
|
||||
)
|
||||
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue