diff --git a/Dockerfile b/Dockerfile index 7bda32acf27..28a157b3333 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,19 @@ COPY . . # Convert Windows line endings to Unix and make executable RUN sed -i 's/\r$//' docker/build_admin_ui.sh && chmod +x docker/build_admin_ui.sh && ./docker/build_admin_ui.sh +# Pre-restructure UI: move root-level {page}.html → {page}/index.html so +# Starlette StaticFiles can serve extensionless routes (e.g. /ui/chat). +# Must run before building the wheel since the out/ dir is included in the package. +RUN cd litellm/proxy/_experimental/out && \ + for html_file in *.html; do \ + if [ "$html_file" != "index.html" ] && [ "$html_file" != "404.html" ] && [ -f "$html_file" ]; then \ + folder_name="${html_file%.html}" && \ + mkdir -p "$folder_name" && \ + mv "$html_file" "$folder_name/index.html"; \ + fi; \ + done && \ + touch .litellm_ui_ready + # Build the package RUN rm -rf dist/* && python -m build diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 9c29927c5cb..723a2032610 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1203,7 +1203,27 @@ try: if entry.is_dir() and not entry.name.startswith("_"): index_path = os.path.join(entry.path, "index.html") if os.path.exists(index_path): - # Found at least one restructured route - this proves the pattern + # Found at least one restructured route. + # Also verify no root-level .html files still need restructuring. + # Next.js static export may generate both pre-restructured pages + # (e.g. login/index.html) and new pages as root-level .html files + # (e.g. chat.html), causing the heuristic to fire prematurely. + try: + orphaned = [ + e.name + for e in os.scandir(ui_dir) + if e.is_file() + and e.name.endswith(".html") + and e.name not in ("index.html", "404.html") + ] + except (PermissionError, OSError): + orphaned = [] + if orphaned: + verbose_proxy_logger.debug( + f"Found un-restructured HTML files at root: {orphaned}. " + f"Restructuring needed despite existing {entry.name}/index.html." + ) + return False verbose_proxy_logger.debug( f"Detected restructured UI via pattern: found {entry.name}/index.html" )