Merge pull request #16843 from BerriAI/litellm_allow_custom_mount_paths

[Feature] Allow Root Path to Redirect when Docs not on Root Path
This commit is contained in:
yuneng-jiang 2025-12-10 09:52:30 -08:00 committed by GitHub
commit ba554a86b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 74 additions and 14 deletions

View file

@ -796,6 +796,7 @@ router_settings:
| REPLICATE_MODEL_NAME_WITH_ID_LENGTH | Length of Replicate model names with ID. Default is 64
| REPLICATE_POLLING_DELAY_SECONDS | Delay in seconds for Replicate polling operations. Default is 0.5
| REQUEST_TIMEOUT | Timeout in seconds for requests. Default is 6000
| ROOT_REDIRECT_URL | URL to redirect root path (/) to when DOCS_URL is set to something other than "/" (DOCS_URL is "/" by default)
| ROUTER_MAX_FALLBACKS | Maximum number of fallbacks for router. Default is 5
| RUNWAYML_DEFAULT_API_VERSION | Default API version for RunwayML service. Default is "2024-11-06"
| RUNWAYML_POLLING_TIMEOUT | Timeout in seconds for RunwayML image generation polling. Default is 600 (10 minutes)

View file

@ -6,32 +6,31 @@ import TabItem from '@theme/TabItem';
Create keys, track spend, add models without worrying about the config / CRUD endpoints.
<Image img={require('../../img/litellm_ui_create_key.png')} />
<Image img={require('../../img/litellm_ui_create_key.png')} />
## Quick Start
- Requires proxy master key to be set
- Requires db connected
- Requires proxy master key to be set
- Requires db connected
Follow [setup](./virtual_keys.md#setup)
### 1. Start the proxy
```bash
litellm --config /path/to/config.yaml
#INFO: Proxy running on http://0.0.0.0:4000
```
### 2. Go to UI
### 2. Go to UI
```bash
http://0.0.0.0:4000/ui # <proxy_base_url>/ui
```
### 3. Get Admin UI Link on Swagger
### 3. Get Admin UI Link on Swagger
Your Proxy Swagger is available on the root of the Proxy: e.g.: `http://localhost:4000/`
<Image img={require('../../img/ui_link.png')} />
@ -48,9 +47,20 @@ UI_PASSWORD=langchain # password to sign in on UI
On accessing the LiteLLM UI, you will be prompted to enter your username, password
## Invite-other users
### 5. Configure Root Redirect URL
Allow others to create/delete their own keys.
When `DOCS_URL` is set to something other than `"/"`, you can configure where the root path (`/`) redirects to using `ROOT_REDIRECT_URL`:
```shell
DOCS_URL="/docs" # Set docs to a different path
ROOT_REDIRECT_URL="/ui" # Redirect root path (/) to /ui
```
By default, `DOCS_URL` is `"/"`, so this setting is only needed when you've changed `DOCS_URL` to a different path.
## Invite-other users
Allow others to create/delete their own keys.
[**Go Here**](./self_serve.md)
@ -72,11 +82,10 @@ For information on sharing models and agents, see [AI Hub](./ai_hub.md).
## Disable Admin UI
Set `DISABLE_ADMIN_UI="True"` in your environment to disable the Admin UI.
Useful, if your security team has additional restrictions on UI usage.
Set `DISABLE_ADMIN_UI="True"` in your environment to disable the Admin UI.
Useful, if your security team has additional restrictions on UI usage.
**Expected Response**
<Image img={require('../../img/admin_ui_disabled.png')}/>
<Image img={require('../../img/admin_ui_disabled.png')}/>

View file

@ -1084,6 +1084,13 @@ def mount_swagger_ui():
mount_swagger_ui()
docs_url = _get_docs_url()
root_redirect_url = os.getenv("ROOT_REDIRECT_URL")
if docs_url != "/" and root_redirect_url:
@app.get("/", include_in_schema=False)
async def root_redirect():
return RedirectResponse(url=root_redirect_url)
from typing import Dict
user_api_base = None

View file

@ -2619,6 +2619,49 @@ def test_get_prompt_spec_for_db_prompt_with_versions():
assert prompt_spec_v2.prompt_id == "chat_prompt.v2"
def test_root_redirect_when_docs_url_not_root_and_redirect_url_set(monkeypatch):
from litellm.proxy.proxy_server import cleanup_router_config_variables
from litellm.proxy.utils import _get_docs_url
from fastapi.responses import RedirectResponse
cleanup_router_config_variables()
filepath = os.path.dirname(os.path.abspath(__file__))
config_fp = f"{filepath}/test_configs/test_config_no_auth.yaml"
# Ensure docs are mounted on a non-root path to trigger redirect logic
monkeypatch.setenv("DOCS_URL", "/docs")
test_redirect_url = "/ui"
monkeypatch.setenv("ROOT_REDIRECT_URL", test_redirect_url)
asyncio.run(initialize(config=config_fp, debug=True))
docs_url = _get_docs_url()
root_redirect_url = os.getenv("ROOT_REDIRECT_URL")
# Remove any existing "/" route that might interfere
routes_to_remove = []
for route in app.routes:
if hasattr(route, "path") and route.path == "/":
if hasattr(route, "methods") and "GET" in route.methods:
routes_to_remove.append(route)
elif not hasattr(route, "methods"): # Catch-all routes
routes_to_remove.append(route)
for route in routes_to_remove:
app.routes.remove(route)
# Add the redirect route if conditions are met (matching the actual implementation)
if docs_url != "/" and root_redirect_url:
@app.get("/", include_in_schema=False)
async def root_redirect():
return RedirectResponse(url=root_redirect_url)
client = TestClient(app)
response = client.get("/", follow_redirects=False)
assert response.status_code == 307
assert response.headers["location"] == test_redirect_url
def test_get_image_non_root_uses_tmp_assets_dir(monkeypatch):
"""
Test that get_image uses /tmp/litellm_assets when LITELLM_NON_ROOT is true.