From 6c2c0d963dc292badc634918392f5a5b0b8ec716 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 18:44:10 +0000 Subject: [PATCH] fix(proxy): type callback delete helpers to satisfy basedpyright budget Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/proxy/proxy_server.py | 25 +++++++++++------ .../proxy/proxy_server/test_routes_config.py | 28 ++++++++++++++++--- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 003e8ae7af0..6f8068d392d 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -22,6 +22,7 @@ from collections.abc import ( Awaitable, Callable, Collection, + Iterable, Iterator, Mapping, MutableMapping, @@ -18997,15 +18998,15 @@ async def delete_config_general_settings( _CALLBACK_LIST_KEYS: Final = ("success_callback", "failure_callback", "callbacks") -def _configured_callback_names(value: object) -> tuple[object, ...]: +def _configured_callback_names(value: object) -> tuple[JsonValue, ...]: if isinstance(value, str): return (value,) if isinstance(value, (list, tuple, dict)): - return tuple(value) + return tuple(cast(Iterable[JsonValue], value)) # cast-ok: config.yaml values are untyped return () -def _remaining_callback_names(settings: Mapping[str, object]) -> Iterator[object]: +def _remaining_callback_names(settings: Mapping[str, object]) -> Iterator[JsonValue]: for key in _CALLBACK_LIST_KEYS: yield from _configured_callback_names(settings.get(key)) @@ -19049,11 +19050,17 @@ async def delete_callback( try: # Get current configuration - config: Final = await proxy_config.get_config() + config: Final[dict[str, object]] = cast( # cast-ok: get_config returns an untyped config dict + dict[str, object], + await proxy_config.get_config(), + ) callback_name: Final = data.callback_name.lower() # Check if callback exists in current configuration - litellm_settings: Final = config.get("litellm_settings", {}) + litellm_settings: Final[Mapping[str, object]] = cast( # cast-ok: the config dict's values are untyped + Mapping[str, object], + config.get("litellm_settings", {}), + ) configured_lists: Final = { key: _configured_callback_names(litellm_settings.get(key)) for key in _CALLBACK_LIST_KEYS } @@ -19069,12 +19076,13 @@ async def delete_callback( detail={"error": f"Callback '{callback_name}' not found in active configuration"}, ) - before_callbacks: Final = {key: list(configured_lists[key]) for key in matching_keys} - after_callbacks: Final = { + before_callbacks: Final[dict[str, JsonValue]] = {key: list(configured_lists[key]) for key in matching_keys} + after_callbacks: Final[dict[str, JsonValue]] = { key: [entry for entry in configured_lists[key] if not _is_callback_name(entry, callback_name)] for key in matching_keys } - config["litellm_settings"] = {**litellm_settings, **after_callbacks} + updated_settings: Final = {**litellm_settings, **after_callbacks} + config["litellm_settings"] = updated_settings # Save the updated configuration await proxy_config.save_config(new_config=config) @@ -19092,7 +19100,6 @@ async def delete_callback( # Restart the proxy to apply changes await proxy_config.add_deployment(prisma_client=prisma_client, proxy_logging_obj=proxy_logging_obj) - updated_settings: Final = config["litellm_settings"] return { "message": f"Successfully deleted callback: {callback_name}", "removed_callback": callback_name, diff --git a/tests/unit/proxy/proxy_server/test_routes_config.py b/tests/unit/proxy/proxy_server/test_routes_config.py index bd143de99ce..01dd877eb6d 100644 --- a/tests/unit/proxy/proxy_server/test_routes_config.py +++ b/tests/unit/proxy/proxy_server/test_routes_config.py @@ -978,7 +978,12 @@ def _delete_callback_roundtrip( return response, saved -def test_config_callback_delete_from_failure_callback(client, auth_as, mock_prisma, monkeypatch): +def test_config_callback_delete_from_failure_callback( + client: TestClient, + auth_as: Callable[..., contextlib.AbstractContextManager[None]], + mock_prisma: MagicMock, + monkeypatch: pytest.MonkeyPatch, +): """A callback configured only under litellm_settings.failure_callback is listed by GET /get/config/callbacks, so the delete route must remove it.""" response, saved = _delete_callback_roundtrip( @@ -995,7 +1000,12 @@ def test_config_callback_delete_from_failure_callback(client, auth_as, mock_pris assert response.json()["remaining_callbacks"] == ["slack", "sentry"] -def test_config_callback_delete_from_callbacks_list(client, auth_as, mock_prisma, monkeypatch): +def test_config_callback_delete_from_callbacks_list( + client: TestClient, + auth_as: Callable[..., contextlib.AbstractContextManager[None]], + mock_prisma: MagicMock, + monkeypatch: pytest.MonkeyPatch, +): """A callback configured only under litellm_settings.callbacks is deleted while unrelated lists pass through untouched.""" response, saved = _delete_callback_roundtrip( @@ -1012,7 +1022,12 @@ def test_config_callback_delete_from_callbacks_list(client, auth_as, mock_prisma assert response.json()["remaining_callbacks"] == ["langfuse"] -def test_config_callback_delete_from_multiple_lists(client, auth_as, mock_prisma, monkeypatch): +def test_config_callback_delete_from_multiple_lists( + client: TestClient, + auth_as: Callable[..., contextlib.AbstractContextManager[None]], + mock_prisma: MagicMock, + monkeypatch: pytest.MonkeyPatch, +): """A name present in both success_callback and callbacks is removed from both, and remaining_callbacks reports the leftovers of every list.""" response, saved = _delete_callback_roundtrip( @@ -1036,7 +1051,12 @@ def test_config_callback_delete_from_multiple_lists(client, auth_as, mock_prisma assert response.json()["remaining_callbacks"] == ["slack", "sentry", "langfuse"] -def test_config_callback_delete_case_insensitive(client, auth_as, mock_prisma, monkeypatch): +def test_config_callback_delete_case_insensitive( + client: TestClient, + auth_as: Callable[..., contextlib.AbstractContextManager[None]], + mock_prisma: MagicMock, + monkeypatch: pytest.MonkeyPatch, +): """A mixed-case request name still matches the configured lowercase entry.""" response, saved = _delete_callback_roundtrip( client,