fix(proxy): keep internal callbacks when litellm_settings.callbacks is a single string

When litellm_settings.callbacks was a single string instead of a list, the proxy
overwrote litellm.callbacks with just that callback, dropping the internal
callbacks registered at import, including the per-model max budget limiter. Keys
with model_max_budget then went over budget without ever being blocked. The
string form now appends, the same way the list form already extends
This commit is contained in:
Maha Rana Yadavalli 2026-10-09 10:28:09 -04:00
parent 63a4f3f2f3
commit b83a135999
2 changed files with 14 additions and 2 deletions

View file

@ -404,7 +404,7 @@ def initialize_callbacks_on_proxy(
PrometheusLogger._mount_metrics_endpoint()
else:
litellm.callbacks = [
litellm.callbacks.append(
_loaded_callback_or_raise(
entry=value,
loaded=get_instance_fn(
@ -412,7 +412,7 @@ def initialize_callbacks_on_proxy(
config_file_path=config_file_path,
),
)
]
)
verbose_proxy_logger.debug("%s Initialized Callbacks - %s %s", blue_color_code, litellm.callbacks, reset_color_code)

View file

@ -762,3 +762,15 @@ def test_initialize_callbacks_on_proxy_accepts_instance_non_list_value(probe_con
assert len(litellm.callbacks) == 1
assert isinstance(litellm.callbacks[0], CustomLogger)
@pytest.mark.parametrize("as_list", [False, True], ids=["string_value", "list_value"])
def test_initialize_callbacks_on_proxy_keeps_previously_registered_callbacks(probe_config_path: str, as_list: bool):
entry: Final = f"{_PROBE_MODULE_NAME}.proxy_handler_instance"
internal_callback: Final = CustomLogger()
litellm.callbacks.append(internal_callback)
_load_callbacks([entry] if as_list else entry, probe_config_path)
assert litellm.callbacks[0] is internal_callback
assert [type(cb).__name__ for cb in litellm.callbacks[1:]] == ["FloorMaxTokens"]