From fd66d87e46f316e424c2a4676311ff197e933e4b Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 10 Aug 2026 16:28:40 -0700 Subject: [PATCH] fix(ui): open the classifier prompt editor above the edit auto-router form (#36438) The prompt editor is a base-ui Dialog at z-index 50. The create form houses it in the same base-ui Dialog, so it stacks on top, but the edit form was an antd Modal whose portal computes to z-index 1000, so the editor opened underneath it and was neither readable nor clickable. Move the edit form onto the Dialog the create form already uses, which puts the whole nesting chain in one overlay layer. A dialog opened from inside another dialog now reads as a drill-down rather than a stack: base-ui stamps data-nested-dialog-open on the parent while a child is open, so the parent steps aside instead of showing its own edges around a differently sized child. --- ui/litellm-dashboard/src/app/globals.css | 7 +++ .../edit_auto_router_modal.test.tsx | 27 +++++++++- .../edit_auto_router_modal.tsx | 54 ++++++++++--------- 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/ui/litellm-dashboard/src/app/globals.css b/ui/litellm-dashboard/src/app/globals.css index 4589d0f528a..97f61a610d9 100644 --- a/ui/litellm-dashboard/src/app/globals.css +++ b/ui/litellm-dashboard/src/app/globals.css @@ -235,3 +235,10 @@ .custom-border { border: 1px solid var(--neutral-border); } + +/* A dialog opened from inside another dialog reads as a drill-down, not a stack: base-ui stamps + this attribute on the parent while a child is open, so the parent steps aside instead of + showing its own edges around a differently sized child. */ +[data-slot="dialog-content"][data-nested-dialog-open] { + visibility: hidden; +} diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.test.tsx b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.test.tsx index 9b36218e17a..29101b38e24 100644 --- a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.test.tsx +++ b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.test.tsx @@ -6,12 +6,19 @@ import { fireEvent, renderWithProviders, screen, waitFor, within } from "@/../te import NotificationsManager from "@/components/molecules/notifications_manager"; import EditAutoRouterModal from "./edit_auto_router_modal"; -const { modelPatchUpdateCall, modelAvailableCall } = vi.hoisted(() => ({ +const { modelPatchUpdateCall, modelAvailableCall, getAutoRouterClassifierDefaultPromptCall } = vi.hoisted(() => ({ modelPatchUpdateCall: vi.fn().mockResolvedValue({}), modelAvailableCall: vi.fn().mockResolvedValue({ data: [] }), + getAutoRouterClassifierDefaultPromptCall: vi.fn().mockResolvedValue("Classify the request into exactly one tier."), })); -vi.mock("../networking", () => ({ modelPatchUpdateCall, modelAvailableCall })); +vi.mock("../networking", () => ({ + modelPatchUpdateCall, + modelAvailableCall, + getAutoRouterClassifierDefaultPromptCall, +})); + +vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({ default: () => ({ accessToken: "sk-test" }) })); vi.mock("@/components/llm_calls/fetch_models", () => ({ fetchAvailableModels: vi.fn().mockResolvedValue([{ model_group: "gpt-4o-mini" }]), @@ -243,6 +250,22 @@ describe("EditAutoRouterModal classifier context window", () => { expect(config.classifier_context_per_turn_chars).toBe(300); }); + // The prompt editor is a base-ui Dialog at z-index 50. Housing this form in an antd Modal put a + // z-index 1000 overlay between the operator and it, so the editor opened underneath and could + // not be read or typed into. jsdom does not paint, so the assertion is the invariant behind the + // stacking: both overlays come from the one Dialog primitive the create form already uses. + it("opens the classifier prompt editor in the same overlay layer as the form", async () => { + const user = userEvent.setup(); + const { baseElement } = renderLlmModal(); + + await user.click(await screen.findByText("Advanced: Classification Method")); + await user.click(await screen.findByRole("button", { name: /prompt/i })); + + expect(await screen.findByLabelText("Classifier system prompt")).toBeInTheDocument(); + expect(baseElement.querySelectorAll('[data-slot="dialog-content"]')).toHaveLength(2); + expect(baseElement.querySelector(".ant-modal")).toBeNull(); + }); + it("persists an edited classifier context window size", async () => { const user = userEvent.setup(); renderLlmModal(); diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx index cf1a5727948..aba0ee9f58a 100644 --- a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx +++ b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -import { Modal, Form, Button, Select as AntdSelect, Tooltip } from "antd"; -import { Text, TextInput } from "@tremor/react"; +import { Form, Button, Select as AntdSelect, Tooltip } from "antd"; +import { TextInput } from "@tremor/react"; import { modelAvailableCall, modelPatchUpdateCall } from "../networking"; import { fetchAvailableModels, ModelGroup } from "@/components/llm_calls/fetch_models"; import RouterConfigBuilder from "../add_model/RouterConfigBuilder"; @@ -24,6 +24,14 @@ import ComplexityRouterConfig, { DEFAULT_TIER_DISTANCE_PENALTY, } from "../add_model/ComplexityRouterConfig"; import NotificationsManager from "../molecules/notifications_manager"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; interface EditAutoRouterModalProps { isVisible: boolean; @@ -432,27 +440,14 @@ const EditAutoRouterModal: React.FC = ({ })); return ( - - Cancel - , - - - , - ]} - width={1000} - destroyOnHidden - > -
- - Edit the auto router configuration including routing logic, default models, and access settings. - + !open && onCancel()}> + + + Edit Auto Router Configuration + + Edit the auto router configuration including routing logic, default models, and access settings. + +
{/* Auto Router Name */} @@ -552,8 +547,17 @@ const EditAutoRouterModal: React.FC = ({ )} -
-
+ + + + + + + + + ); };