From ec7405f616517e9b3ea7cf88a09e6b658b0acfdb Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Fri, 14 Aug 2026 17:22:12 -0700 Subject: [PATCH] fix(ui): make every playground model-settings help control a click popover The playground Model Settings menu had two different help affordances. Stream responses, Temperature and Max Tokens used a Tooltip, while Simulate failure to test fallbacks used a Popover. Before the shadcn migration both were antd and both opened on hover after 100ms, so the split was only cosmetic. Base UI's Popover is click-triggered and its Tooltip waits for 600ms of motionless hover, so the migration turned that cosmetic split into two interaction models: the Simulate icon answered a click and the other three appeared inert. Move all four onto Popover so each one opens on click, and share the content class between them. --- .../chat_ui/AdditionalModelSettings.test.tsx | 20 +++++++ .../chat_ui/AdditionalModelSettings.tsx | 57 ++++++++++--------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.test.tsx index 1c2cb426f23..a594ee8efff 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.test.tsx @@ -129,6 +129,26 @@ describe("AdditionalModelSettings", () => { }); }); + it.each([ + ["Help: Stream responses", /Streams the answer token by token/], + ["Help: Simulate failure to test fallbacks", /Causes the first request to fail/], + ["Help: Temperature", /Controls randomness/], + ["Help: Max Tokens", /Maximum number of tokens to generate/], + ])("should open the %s help on click and not on hover", async (triggerLabel, helpText) => { + const user = userEvent.setup(); + + render(); + + const trigger = screen.getByLabelText(triggerLabel); + + await user.hover(trigger); + await new Promise((resolve) => setTimeout(resolve, 900)); + expect(screen.queryByText(helpText)).not.toBeInTheDocument(); + + await user.click(trigger); + await waitFor(() => expect(screen.getByText(helpText)).toBeInTheDocument()); + }); + it("should keep a half-typed decimal temperature instead of rewriting it", async () => { const onTemperatureChange = vi.fn(); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.tsx index af306e0eb2e..1ff05fdc457 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AdditionalModelSettings.tsx @@ -3,7 +3,6 @@ import React, { useEffect, useId, useState } from "react"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { cn } from "@/lib/cva.config"; interface AdditionalModelSettingsProps { @@ -20,6 +19,8 @@ interface AdditionalModelSettingsProps { showAdvancedParams?: boolean; } +const helpPopoverClassName = "max-w-[340px] gap-2 p-3 text-sm"; + function clamp(value: number, min: number, max: number): number { return Math.min(max, Math.max(min, value)); } @@ -116,15 +117,17 @@ const AdditionalModelSettings: React.FC = ({ - - + + - - - Streams the answer token by token. Uncheck to send a non-streaming request and render the full response at - once. - - + + +

+ Streams the answer token by token. Uncheck to send a non-streaming request and render the full response + at once. +

+
+ )} @@ -157,7 +160,7 @@ const AdditionalModelSettings: React.FC = ({ - +

Causes the first request to fail so the router tries fallbacks (if configured). Use this to verify your fallback setup. @@ -188,14 +191,16 @@ const AdditionalModelSettings: React.FC = ({ - - - - - - Controls randomness. Lower values make output more deterministic, higher values more creative. - - + + + + + +

+ Controls randomness. Lower values make output more deterministic, higher values more creative. +

+
+ = ({ - - - - - - Maximum number of tokens to generate in the response. - - + + + + + +

Maximum number of tokens to generate in the response.

+
+