feat(ui): let admins supply a dark-mode variant of their custom logo - #37662
Conversation
A deployment branded through UI_LOGO_PATH got its light artwork on the dark sidebar, and there was nothing an admin could set to change that. Adds UI_LOGO_PATH_DARK, exposed as the logo_url_dark theme setting and a second field on the UI theme page. /get_image now walks an ordered list of candidates for the requested theme and serves the first usable one: the dark logo, then the light logo, then the bundled default. Falling through rather than failing is the point. An admin who never sets a dark logo keeps their own light one instead of reverting to LiteLLM's, and a dark logo that goes missing later degrades to their light logo rather than dropping their branding entirely.
Greptile SummaryThis PR adds separately configurable dark-mode branding while retaining the existing light-logo and bundled-logo fallback behavior.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| litellm/proxy/proxy_server.py | Adds validated dark-logo candidate selection while preserving light and bundled fallback behavior. |
| litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py | Adds the dark-logo theme field and keeps its process and persisted environment values synchronized. |
| ui/litellm-dashboard/src/app/(dashboard)/ui-theme/UIThemeSettings.tsx | Adds editing, saving, and resetting support for the dark-mode logo URL. |
| ui/litellm-dashboard/src/contexts/ThemeContext.tsx | Makes the configured dark-logo URL available through shared dashboard theme state. |
| ui/litellm-dashboard/src/components/leftnav.tsx | Selects the configured dark logo and switches to the light-logo fallback when loading fails. |
Reviews (2): Last reviewed commit: "fix(ui): recover from a dark logo the br..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
A dark logo given as an http(s) URL is loaded by the browser straight from the sidebar, so it never passes through the proxy's fallback chain. A URL that 404s left a broken image where the admin's light logo should have been, while the same logo given as a local path fell back cleanly. The sidebar now remembers the dark URL that failed and drops to the light logo, matching how the proxy resolves an unusable dark logo and how the provider Logo component already handles a broken image.
|
Fixed: the sidebar now remembers a dark logo URL that fails to load and falls back to the light logo. @greptileai |
tin-berri
left a comment
There was a problem hiding this comment.
Small, well-scoped feature — new UI_LOGO_PATH_DARK env var / logo_url_dark theme setting, with sensible fallback chain (no dark logo set -> keep light logo in dark mode; broken dark logo -> also falls back to the light logo, never reverts to LiteLLM's bundled branding, which is the right call for a white-labeled proxy). Proof-of-fix is concrete: real served-byte comparisons (cmp/file) across all 4 cases (unset, both set, broken dark, both unset falling back to bundled defaults) rather than just describing behavior. No auth/billing/security surface — pure static asset serving. CI fully green (78 checks).
122675c
into
litellm_internal_staging
TLDR
Problem this solves:
How it solves it:
UI_LOGO_PATH_DARKand alogo_url_darktheme settingUser Flow
Before: an admin who branded the dashboard with their own logo sees that light-background logo on the dark sidebar, and there is nothing they can set to fix it
UI_LOGO_PATHto their company logo and restart the proxyAfter: the same admin can supply a dark logo, and doing nothing still keeps their branding
Relevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Shared setup: two stand-in customer logos, deliberately different shapes so the served bytes identify themselves, and a proxy on
http://localhost:4001restarted for each caseBefore (edbb342)
Only UI_LOGO_PATH set, asking for the dark logo
UI_LOGO_PATH=customer-light.png, no dark var exists to setcurl -s -o dark.out 'http://localhost:4001/get_image?theme=dark'; file dark.outThe light artwork, which is the only outcome reachable here:
grep -c UI_LOGO_PATH_DARK litellm/proxy/proxy_server.pyreturns 0 at this commitBoth logos set
UI_LOGO_PATH_DARK, so this case cannot be expressedA broken dark logo
UI_LOGO_PATH_DARK, so this case cannot be expressedAfter (dab098b)
Only UI_LOGO_PATH set, asking for the dark logo
curl -s -o light.out http://localhost:4001/get_imageandcurl -s -o dark.out 'http://localhost:4001/get_image?theme=dark'cmp light.out dark.outexits silently, so an admin who never sets a dark logo keeps their own logo in dark modeThis case is deliberately identical to Before. The fallback already existed, and the point here is that adding the dark option does not disturb it
Both logos set
UI_LOGO_PATH=customer-light.png UI_LOGO_PATH_DARK=customer-dark.png, then the same two requestsfile -b light.out dark.outA broken dark logo
UI_LOGO_PATH_DARK=does-not-exist.pngwith the light logo still validcurl -s -o dark.out 'http://localhost:4001/get_image?theme=dark'; file -b dark.outNo custom logo at all
cmpagainst the bundled files passes on each themeType
🆕 New Feature
Caveats (if any)
logo_url_darkaccepts http(s) URLs only, likelogo_urlFinal Attestation