diff --git a/docs/my-website/docs/proxy/config_settings.md b/docs/my-website/docs/proxy/config_settings.md index c52b5d571b6..52757b846d3 100644 --- a/docs/my-website/docs/proxy/config_settings.md +++ b/docs/my-website/docs/proxy/config_settings.md @@ -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) diff --git a/docs/my-website/docs/proxy/ui.md b/docs/my-website/docs/proxy/ui.md index f6fa02fb69b..33033b06f85 100644 --- a/docs/my-website/docs/proxy/ui.md +++ b/docs/my-website/docs/proxy/ui.md @@ -6,32 +6,31 @@ import TabItem from '@theme/TabItem'; Create keys, track spend, add models without worrying about the config / CRUD endpoints. - - - - + ## 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 # /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/` @@ -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** - \ No newline at end of file + diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 88eb1cec6e7..4d1a34f49c4 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -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 diff --git a/tests/test_litellm/proxy/test_proxy_server.py b/tests/test_litellm/proxy/test_proxy_server.py index 7b99cd0b920..1b7d285bf33 100644 --- a/tests/test_litellm/proxy/test_proxy_server.py +++ b/tests/test_litellm/proxy/test_proxy_server.py @@ -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.