fix(router): clear discarded selectors via a callback-list helper

`_unregister_router_selectors` guarded each callback list with
`isinstance(..., list)` on statically-`List` globals and reached into
`litellm._async_success_callback` / `_async_failure_callback` directly, which
the basedpyright delta gate flags as `reportUnnecessaryIsInstance` and
`reportPrivateUsage`. Route the cleanup through a new
`LoggingCallbackManager.remove_callbacks_by_id`, which mirrors
`remove_callback_from_all_lists` (plus `input_callback`) and filters each list
in place by object id.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
zhongerdeajie 2026-10-10 17:27:39 +08:00
parent 024843a4bf
commit de8856df73
2 changed files with 31 additions and 13 deletions

View file

@ -1,4 +1,4 @@
from collections.abc import Callable
from collections.abc import Callable, Sequence, Set
from typing import TYPE_CHECKING, Final
from weakref import WeakSet
@ -451,6 +451,35 @@ class LoggingCallbackManager:
):
self.remove_callback_from_list_by_object(callback_list, obj, require_self=require_self)
def remove_callbacks_by_id(self, selector_ids: Set[int]) -> None:
"""
Remove every callback whose object identity is in ``selector_ids`` from
each callback list it may have been promoted into.
Router strategy selectors are registered by identity (see
``add_litellm_callback(..., dedupe_by_identity=True)``), so discarding a
Router must drop them by ``id()`` rather than by class key.
"""
for callback_list in (
litellm.callbacks,
litellm.input_callback,
litellm.success_callback,
litellm.failure_callback,
litellm._async_success_callback, # pyright: ignore[reportPrivateUsage] # internal callback list promoted into by `function_setup`
litellm._async_failure_callback, # pyright: ignore[reportPrivateUsage] # internal callback list promoted into by `function_setup`
):
self._remove_callbacks_by_id_in_place(callback_list, selector_ids)
def _remove_callbacks_by_id_in_place(
self,
callback_list: Sequence[CustomLogger | Callable[..., object] | str],
selector_ids: Set[int],
) -> None:
if not isinstance(callback_list, list): # immutable sequence -> nothing to filter
return
for callback in [c for c in callback_list if id(c) in selector_ids]:
callback_list.remove(callback)
def get_active_additional_logging_utils_from_custom_logger(
self,
) -> set[AdditionalLoggingUtils]:

View file

@ -1561,18 +1561,7 @@ class Router:
selector_ids: Final = {id(s) for s in selectors if s is not None}
if not selector_ids:
return
if isinstance(litellm.callbacks, list):
litellm.callbacks = [c for c in litellm.callbacks if id(c) not in selector_ids]
if isinstance(litellm.input_callback, list):
litellm.input_callback = [c for c in litellm.input_callback if id(c) not in selector_ids]
if isinstance(litellm.success_callback, list):
litellm.success_callback = [c for c in litellm.success_callback if id(c) not in selector_ids]
if isinstance(litellm.failure_callback, list):
litellm.failure_callback = [c for c in litellm.failure_callback if id(c) not in selector_ids]
if isinstance(litellm._async_success_callback, list):
litellm._async_success_callback = [c for c in litellm._async_success_callback if id(c) not in selector_ids]
if isinstance(litellm._async_failure_callback, list):
litellm._async_failure_callback = [c for c in litellm._async_failure_callback if id(c) not in selector_ids]
litellm.logging_callback_manager.remove_callbacks_by_id(selector_ids)
def _apply_updated_routing_strategy_args(self) -> None:
"""