mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
fix(ui): treat router redis as configured for the no-redis banner
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
e368eeac49
commit
48fa4a0f06
3 changed files with 123 additions and 8 deletions
75
.agents/skills/testing-admin-ui-banners/SKILL.md
Normal file
75
.agents/skills/testing-admin-ui-banners/SKILL.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
name: testing-admin-ui-banners
|
||||
description: How to run the LiteLLM Admin UI dev server against a live proxy (including a second BEFORE/base worktree) to test dashboard shell banners and /health/readiness/details driven UI state.
|
||||
---
|
||||
|
||||
# Testing Admin UI dashboard banners against a live proxy
|
||||
|
||||
## Bring up AFTER (branch under test)
|
||||
|
||||
```
|
||||
sudo service postgresql start
|
||||
cd <repo> && (setsid uv run --no-sync litellm --config litellm/proxy/dev_config.yaml --detailed_debug --port 4000 > /tmp/proxy.log 2>&1 < /dev/null &)
|
||||
cd ui/litellm-dashboard && (npm run dev > /tmp/ui_dev.log 2>&1 &) # port 3000
|
||||
```
|
||||
|
||||
Proxy startup takes ~45-60s before `/health/readiness/details` answers. Log in at
|
||||
http://localhost:3000/ (it redirects to the proxy's login page) with `admin` /
|
||||
the `general_settings.master_key` from `litellm/proxy/dev_config.yaml` (`sk-1234` by default).
|
||||
In dev (`NODE_ENV=development`) the UI defaults its API base to `http://localhost:4000`, so no
|
||||
extra env var is needed for the main dev server.
|
||||
|
||||
Launcher gotcha: if an `exec` shell call runs longer than ~10s it gets backgrounded and can take
|
||||
the freshly spawned proxy with it. Keep the launch command short (`setsid ... & ; sleep 6`) and
|
||||
poll readiness in a separate call.
|
||||
|
||||
## Bring up BEFORE (base commit) side by side
|
||||
|
||||
```
|
||||
git worktree add /home/ubuntu/repos/litellm-base <base-branch>
|
||||
cp -al <repo>/ui/litellm-dashboard/node_modules /home/ubuntu/repos/litellm-base/ui/litellm-dashboard/node_modules
|
||||
```
|
||||
|
||||
Do NOT symlink `node_modules` into a worktree: Turbopack panics with
|
||||
"Symlink [project]/node_modules is invalid, it points out of the filesystem root". A hardlink copy
|
||||
(`cp -al`) works and is fast.
|
||||
|
||||
Run the base proxy with the main venv but the base source tree, and point the base UI at it:
|
||||
|
||||
```
|
||||
cd /home/ubuntu/repos/litellm-base && PYTHONPATH=$PWD <repo>/.venv/bin/python -m litellm.proxy.proxy_cli --config <repo>/litellm/proxy/dev_config.yaml --detailed_debug --port 4001
|
||||
cd /home/ubuntu/repos/litellm-base/ui/litellm-dashboard && NEXT_PUBLIC_BASE_URL=http://localhost:4001 npm run dev -- --port 3001
|
||||
```
|
||||
|
||||
`PYTHONPATH` wins over the editable install, so the base proxy really runs base code (verify with
|
||||
`python -c "import litellm; print(litellm.__file__)"`).
|
||||
|
||||
## Banner-specific notes
|
||||
|
||||
Dashboard shell banners (`DebugWarningBanner`, `NoRedisWarningBanner`, `LicenseExpiryBanner`) all
|
||||
read `useHealthReadinessDetails`, which has `staleTime: 5 min` and `retry: false`. After restarting
|
||||
the proxy with different env, hard-reload the page (ctrl+shift+r) or the cached readiness payload
|
||||
keeps the old banner state. Running the proxy with `--detailed_debug` always shows the yellow debug
|
||||
banner, which is a handy control: if it is present but the banner under test is not, the readiness
|
||||
call succeeded and the banner condition really is false.
|
||||
|
||||
Coordination Redis (`litellm.proxy.proxy_server.redis_usage_cache`, which drives
|
||||
`show_no_redis_warning`) is NOT populated by `REDIS_HOST`/`REDIS_PORT` alone: the env fallback only
|
||||
runs inside `_init_cache`, which requires a cache block in the config. To get a real coordination
|
||||
Redis, run `docker run -d -p 6379:6379 redis:7` and add to the config:
|
||||
|
||||
```
|
||||
litellm_settings:
|
||||
cache: true
|
||||
cache_params:
|
||||
type: redis
|
||||
host: localhost
|
||||
port: 6379
|
||||
```
|
||||
|
||||
`general_settings.coordination_redis` is the other supported path.
|
||||
|
||||
## Devin Secrets Needed
|
||||
|
||||
None for banner/UI-state testing; the proxy boots with the bundled dev config and a local Postgres.
|
||||
Provider keys are only needed when a test actually issues LLM requests.
|
||||
|
|
@ -1453,17 +1453,23 @@ DISABLE_NO_REDIS_WARNING_ENV_VAR: Final = "LITELLM_DISABLE_NO_REDIS_WARNING"
|
|||
|
||||
def _show_no_redis_warning() -> bool:
|
||||
"""
|
||||
Whether the UI should warn that no coordination Redis is configured.
|
||||
Whether the UI should warn that no Redis is configured.
|
||||
|
||||
Redis is what makes rate limits, budgets, router state, and cache
|
||||
invalidation consistent across workers, so a proxy running without it is
|
||||
only safe as a single worker. Operators who know that can silence the
|
||||
only safe as a single worker. Both places a Redis can land count: the
|
||||
coordination cache (from a Redis response cache, general_settings.
|
||||
coordination_redis, or the REDIS_* env fallback) and the router's own
|
||||
Redis (router_settings.redis_host), which backs cooldowns and usage-based
|
||||
routing on its own. Operators who know they run one worker can silence the
|
||||
warning with LITELLM_DISABLE_NO_REDIS_WARNING=true.
|
||||
"""
|
||||
from litellm.proxy.proxy_server import redis_usage_cache
|
||||
from litellm.proxy.proxy_server import llm_router, redis_usage_cache
|
||||
|
||||
if redis_usage_cache is not None:
|
||||
return False
|
||||
if llm_router is not None and llm_router.cache.redis_cache is not None:
|
||||
return False
|
||||
return get_secret_bool(DISABLE_NO_REDIS_WARNING_ENV_VAR, False) is not True
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2463,25 +2463,58 @@ class TestConfigBaseForHealthCheck:
|
|||
class TestNoRedisWarning:
|
||||
"""`show_no_redis_warning` drives the Admin UI's default-on "no Redis" banner."""
|
||||
|
||||
def test_warns_when_no_coordination_redis_is_configured(self, monkeypatch):
|
||||
@staticmethod
|
||||
def _router(redis_cache):
|
||||
return SimpleNamespace(cache=SimpleNamespace(redis_cache=redis_cache))
|
||||
|
||||
def test_warns_when_no_redis_is_configured(self, monkeypatch):
|
||||
monkeypatch.delenv("LITELLM_DISABLE_NO_REDIS_WARNING", raising=False)
|
||||
with patch("litellm.proxy.proxy_server.redis_usage_cache", None):
|
||||
with (
|
||||
patch("litellm.proxy.proxy_server.redis_usage_cache", None),
|
||||
patch("litellm.proxy.proxy_server.llm_router", self._router(None)),
|
||||
):
|
||||
assert _show_no_redis_warning() is True
|
||||
|
||||
def test_warns_when_there_is_no_router_at_all(self, monkeypatch):
|
||||
monkeypatch.delenv("LITELLM_DISABLE_NO_REDIS_WARNING", raising=False)
|
||||
with (
|
||||
patch("litellm.proxy.proxy_server.redis_usage_cache", None),
|
||||
patch("litellm.proxy.proxy_server.llm_router", None),
|
||||
):
|
||||
assert _show_no_redis_warning() is True
|
||||
|
||||
def test_stays_quiet_when_a_coordination_redis_is_configured(self, monkeypatch):
|
||||
monkeypatch.delenv("LITELLM_DISABLE_NO_REDIS_WARNING", raising=False)
|
||||
with patch("litellm.proxy.proxy_server.redis_usage_cache", MagicMock()):
|
||||
with (
|
||||
patch("litellm.proxy.proxy_server.redis_usage_cache", MagicMock()),
|
||||
patch("litellm.proxy.proxy_server.llm_router", self._router(None)),
|
||||
):
|
||||
assert _show_no_redis_warning() is False
|
||||
|
||||
def test_stays_quiet_when_only_the_router_has_redis(self, monkeypatch):
|
||||
"""router_settings.redis_host alone backs cooldowns and usage-based routing."""
|
||||
monkeypatch.delenv("LITELLM_DISABLE_NO_REDIS_WARNING", raising=False)
|
||||
with (
|
||||
patch("litellm.proxy.proxy_server.redis_usage_cache", None),
|
||||
patch("litellm.proxy.proxy_server.llm_router", self._router(MagicMock())),
|
||||
):
|
||||
assert _show_no_redis_warning() is False
|
||||
|
||||
@pytest.mark.parametrize("value", ["true", "True"])
|
||||
def test_env_var_suppresses_the_warning(self, monkeypatch, value):
|
||||
monkeypatch.setenv("LITELLM_DISABLE_NO_REDIS_WARNING", value)
|
||||
with patch("litellm.proxy.proxy_server.redis_usage_cache", None):
|
||||
with (
|
||||
patch("litellm.proxy.proxy_server.redis_usage_cache", None),
|
||||
patch("litellm.proxy.proxy_server.llm_router", self._router(None)),
|
||||
):
|
||||
assert _show_no_redis_warning() is False
|
||||
|
||||
def test_env_var_set_false_keeps_the_warning(self, monkeypatch):
|
||||
monkeypatch.setenv("LITELLM_DISABLE_NO_REDIS_WARNING", "false")
|
||||
with patch("litellm.proxy.proxy_server.redis_usage_cache", None):
|
||||
with (
|
||||
patch("litellm.proxy.proxy_server.redis_usage_cache", None),
|
||||
patch("litellm.proxy.proxy_server.llm_router", self._router(None)),
|
||||
):
|
||||
assert _show_no_redis_warning() is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -2492,6 +2525,7 @@ class TestNoRedisWarning:
|
|||
with (
|
||||
patch("litellm.proxy.proxy_server.prisma_client", prisma_client),
|
||||
patch("litellm.proxy.proxy_server.redis_usage_cache", None),
|
||||
patch("litellm.proxy.proxy_server.llm_router", self._router(None)),
|
||||
patch.object(
|
||||
_health_endpoints_module,
|
||||
"_db_health_readiness_check",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue