mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-28 06:23:07 +00:00
fix(logging): skip replaced global callbacks on the input path too
replace_global_callback already kept a same-key global logger out of success and failure dispatch. pre_call and post_call still sent it the input events, so a logger with an input hook could count a request start it never saw finish. Both now share one filter with the success path
This commit is contained in:
parent
a92ffe05b4
commit
f0d3436496
2 changed files with 45 additions and 12 deletions
|
|
@ -1572,7 +1572,10 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
"api_call_start_time"
|
||||
]
|
||||
# Input Integration Logging -> If you want to log the fact that an attempt to call the model was made
|
||||
callbacks: Final = litellm.input_callback + (self.dynamic_input_callbacks or [])
|
||||
callbacks: Final = (
|
||||
*self._without_replaced_global_callbacks(litellm.input_callback),
|
||||
*(self.dynamic_input_callbacks or ()),
|
||||
)
|
||||
for callback in callbacks:
|
||||
try:
|
||||
if callback == "supabase" and supabaseClient is not None:
|
||||
|
|
@ -1745,7 +1748,10 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
)
|
||||
# Input Integration Logging -> If you want to log the fact that an attempt to call the model was made
|
||||
|
||||
callbacks: Final = litellm.input_callback + (self.dynamic_input_callbacks or [])
|
||||
callbacks: Final = (
|
||||
*self._without_replaced_global_callbacks(litellm.input_callback),
|
||||
*(self.dynamic_input_callbacks or ()),
|
||||
)
|
||||
for callback in callbacks:
|
||||
try:
|
||||
if callback == "sentry" and add_breadcrumb:
|
||||
|
|
@ -4492,23 +4498,26 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
_filtered_failure_callbacks = self._remove_internal_litellm_callbacks(_filtered_failure_callbacks)
|
||||
return len(_filtered_failure_callbacks) > 0
|
||||
|
||||
def _without_replaced_global_callbacks(
|
||||
self, global_callbacks: Iterable[str | Callable[..., object] | CustomLogger]
|
||||
) -> tuple[str | Callable[..., object] | CustomLogger, ...]:
|
||||
if not self.replaced_global_callback_keys:
|
||||
return tuple(global_callbacks)
|
||||
return tuple(
|
||||
litellm.logging_callback_manager.without_custom_logger_keys(
|
||||
global_callbacks, self.replaced_global_callback_keys
|
||||
)
|
||||
)
|
||||
|
||||
def get_combined_callback_list(
|
||||
self,
|
||||
dynamic_success_callbacks: list[str | Callable[..., object] | CustomLogger] | None,
|
||||
global_callbacks: list[str | Callable[..., object] | CustomLogger],
|
||||
) -> list[str | Callable[..., object] | CustomLogger]:
|
||||
kept_global_callbacks: Final = (
|
||||
list(
|
||||
litellm.logging_callback_manager.without_custom_logger_keys(
|
||||
global_callbacks, self.replaced_global_callback_keys
|
||||
)
|
||||
)
|
||||
if self.replaced_global_callback_keys
|
||||
else global_callbacks
|
||||
)
|
||||
kept_global_callbacks: Final = self._without_replaced_global_callbacks(global_callbacks)
|
||||
if dynamic_success_callbacks is None:
|
||||
return list(kept_global_callbacks)
|
||||
return list(dict.fromkeys(dynamic_success_callbacks + kept_global_callbacks))
|
||||
return list(dict.fromkeys((*dynamic_success_callbacks, *kept_global_callbacks)))
|
||||
|
||||
def _remove_internal_litellm_callbacks(self, callbacks: list) -> list:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -402,6 +402,30 @@ def test_get_combined_callback_list_preserves_insertion_order(logging_obj):
|
|||
) == ["prometheus", "langfuse", "datadog", "otel", "s3", "gcs_bucket", "arize", "logfire"]
|
||||
|
||||
|
||||
class _InputEventRecorder(CustomLogger):
|
||||
def __init__(self, label: str, events: list[str]) -> None:
|
||||
self._label = label
|
||||
self.events = events
|
||||
|
||||
def log_pre_api_call(self, model, messages, kwargs) -> None:
|
||||
self.events.append(f"{self._label} pre")
|
||||
|
||||
def log_post_api_call(self, kwargs, response_obj, start_time, end_time) -> None:
|
||||
self.events.append(f"{self._label} post")
|
||||
|
||||
|
||||
def test_input_events_reach_the_replacement_and_not_the_global_callback_it_replaced(logging_obj, monkeypatch):
|
||||
events: Final[list[str]] = []
|
||||
monkeypatch.setattr(litellm, "input_callback", [_InputEventRecorder("global", events)])
|
||||
logging_obj.replace_global_callback(_InputEventRecorder("replacement", events))
|
||||
logging_obj.update_environment_variables(litellm_params={}, optional_params={})
|
||||
|
||||
logging_obj.pre_call(input="", api_key="")
|
||||
logging_obj.post_call(original_response="ok", input="", api_key="")
|
||||
|
||||
assert events == ["replacement pre", "replacement post"]
|
||||
|
||||
|
||||
def test_get_masked_api_base(logging_obj):
|
||||
api_base = "https://api.openai.com/v1"
|
||||
masked_api_base = logging_obj._get_masked_api_base(api_base)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue