diff --git a/litellm/proxy/client/cli/commands/auto_sign_in.py b/litellm/proxy/client/cli/commands/auto_sign_in.py index 74aef486933..62afc561db7 100644 --- a/litellm/proxy/client/cli/commands/auto_sign_in.py +++ b/litellm/proxy/client/cli/commands/auto_sign_in.py @@ -5,6 +5,7 @@ import webbrowser from collections.abc import Callable, Mapping from contextlib import AbstractContextManager from dataclasses import dataclass +from http import HTTPStatus from pathlib import Path from typing import Final, Literal, Protocol @@ -27,6 +28,7 @@ class BrowserSignIn(Protocol): _TEXT_MODE_BROWSERS: Final = frozenset({"www-browser", "links", "elinks", "lynx", "w3m"}) _SSH_SESSION_VARIABLES: Final = ("SSH_CONNECTION", "SSH_TTY") _DISPLAY_VARIABLES: Final = ("DISPLAY", "WAYLAND_DISPLAY") +_SIGNED_IN_LOGIN_WITHOUT_KEY_ROW: Final = HTTPStatus.NOT_FOUND def _runs_in_ci(environ: Mapping[str, str]) -> bool: @@ -164,7 +166,7 @@ def _proxy_accepts(base_url: str, api_key: str) -> bool: ) except requests.RequestException: return False - return response.ok + return response.ok or response.status_code == _SIGNED_IN_LOGIN_WITHOUT_KEY_ROW def _sign_in_lock() -> FileLock: diff --git a/tests/unit/proxy/client/cli/test_auto_sign_in.py b/tests/unit/proxy/client/cli/test_auto_sign_in.py index c4ae588e78e..e37aac42390 100644 --- a/tests/unit/proxy/client/cli/test_auto_sign_in.py +++ b/tests/unit/proxy/client/cli/test_auto_sign_in.py @@ -453,14 +453,15 @@ def test_a_sibling_key_the_proxy_also_rejects_falls_through_to_one_sign_in( assert get_stored_api_key(BASE_URL, vault=vault) == SIGNED_IN_KEY +@pytest.mark.parametrize("key_check_status", [200, 404], ids=["virtual_key", "browser_login_without_a_key_row"]) def test_a_stored_login_the_proxy_accepts_but_a_command_refuses_reports_a_missing_role_without_a_browser( - requests_mock: Mocker, secret_vault_factory: type[FakeSecretVault] + requests_mock: Mocker, secret_vault_factory: type[FakeSecretVault], key_check_status: int ) -> None: vault: Final = secret_vault_factory() save_token(_classic_login("sk-member"), vault=vault) requests_mock.get(f"{BASE_URL}/v1/models", status_code=200, json={"data": []}) route: Final = requests_mock.get(f"{BASE_URL}/credentials", status_code=401, json=REJECTED) - key_check: Final = _key_check(requests_mock, 200) + key_check: Final = _key_check(requests_mock, key_check_status) sign_in: Final = FakeBrowserSignIn(_credential()) writes_before: Final = list(vault.writes)