Add Custom Tooltips to Model Mapping Table (#13294)

* descriptive tooltips

* atomic folder structure

* undo changes

* import fix

* reorder
This commit is contained in:
tanjiro 2025-08-08 12:19:30 +09:00 committed by GitHub
parent 9761ba7c7a
commit 43af255b45
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 118 additions and 11 deletions

View file

@ -1,12 +1,11 @@
import React, { useEffect, useState } from "react";
import { Form, Table, Input } from "antd";
import { Text, TextInput } from "@tremor/react";
import { Row, Col } from "antd";
import { Form, Table } from "antd";
import { TextInput } from "@tremor/react";
import { Tooltip } from "../atoms/index";
const ConditionalPublicModelName: React.FC = () => {
// Access the form instance
const form = Form.useFormInstance();
const [tableKey, setTableKey] = useState(0); // Add a key to force table re-render
const [tableKey, setTableKey] = useState(0);// Add a key to force table re-render
// Watch the 'model' field for changes and ensure it's always an array
const modelValue = Form.useWatch('model', form) || [];
@ -14,7 +13,6 @@ const ConditionalPublicModelName: React.FC = () => {
const customModelName = Form.useWatch('custom_model_name', form);
const showPublicModelName = !selectedModels.includes('all-wildcard');
// Force table to re-render when custom model name changes
useEffect(() => {
if (customModelName && selectedModels.includes('custom')) {
@ -71,9 +69,39 @@ const ConditionalPublicModelName: React.FC = () => {
if (!showPublicModelName) return null;
const publicNameTooltipContent = (
<>
<div className="mb-2 font-normal">
The name you specify in your API calls to LiteLLM Proxy
</div>
<div className="mb-2 font-normal">
<strong>Example:</strong> If you name your public model <code className="bg-gray-700 px-1 py-0.5 rounded text-xs">example-name</code>
, and choose <code className="bg-gray-700 px-1 py-0.5 rounded text-xs">openai/qwen-plus-latest</code> as the LiteLLM model
</div>
<div className="mb-2 font-normal">
<strong>Usage:</strong> You make an API call to the LiteLLM proxy with <code className="bg-gray-700 px-1 py-0.5 rounded text-xs">model = "example-name"</code>
</div>
<div className="font-normal">
<strong>Result:</strong> LiteLLM sends <code className="bg-gray-700 px-1 py-0.5 rounded text-xs">qwen-plus-latest</code> to the provider
</div>
</>
);
const liteLLMModelTooltipContent = (
<div>The model name LiteLLM will send to the LLM API</div>
);
const columns = [
{
title: 'Public Name',
title: (
<span className="flex items-center">
Public Model Name
<Tooltip
content={publicNameTooltipContent}
width="500px"
/>
</span>
),
dataIndex: 'public_name',
key: 'public_name',
render: (text: string, record: any, index: number) => {
@ -90,7 +118,15 @@ const ConditionalPublicModelName: React.FC = () => {
}
},
{
title: 'LiteLLM Model',
title: (
<span className="flex items-center">
LiteLLM Model Name
<Tooltip
content={liteLLMModelTooltipContent}
width="360px"
/>
</span>
),
dataIndex: 'litellm_model',
key: 'litellm_model',
}

View file

@ -70,7 +70,7 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
<>
<Form.Item
label="LiteLLM Model Name(s)"
tooltip="Actual model name used for making litellm.completion() / litellm.embedding() call."
tooltip="The model name LiteLLM will send to the LLM API"
className="mb-0"
>
<Form.Item
@ -145,9 +145,9 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
</Form.Item>
<Row>
<Col span={10}></Col>
<Col span={10}>
<Col span={14}>
<Text className="mb-3 mt-1">
Actual model name used for making litellm.completion() call. We loadbalance models with the same public name
The model name LiteLLM will send to the LLM API
</Text>
</Col>
</Row>

View file

@ -0,0 +1,70 @@
import React, { useState, useRef } from "react"
import { QuestionCircleOutlined } from "@ant-design/icons"
interface TooltipProps {
content: React.ReactNode
children?: React.ReactNode
width?: string
className?: string
}
export const Tooltip: React.FC<TooltipProps> = ({ content, children, width = "auto", className = "" }) => {
const [showTooltip, setShowTooltip] = useState(false)
const [tooltipPosition, setTooltipPosition] = useState<"top" | "bottom">("top")
const tooltipRef = useRef<HTMLDivElement>(null)
// Function to check if tooltip would fit above
const checkTooltipPosition = () => {
if (tooltipRef.current) {
const rect = tooltipRef.current.getBoundingClientRect()
const tooltipHeight = 300 // Approximate height of the tooltip
const spaceAbove = rect.top
const spaceBelow = window.innerHeight - rect.bottom
if (spaceAbove < tooltipHeight && spaceBelow > tooltipHeight) {
setTooltipPosition("bottom")
} else {
setTooltipPosition("top")
}
}
}
return (
<div className="relative inline-block" ref={tooltipRef}>
{children || (
<QuestionCircleOutlined
className="ml-1 text-gray-500 cursor-help"
onMouseEnter={() => {
checkTooltipPosition()
setShowTooltip(true)
}}
onMouseLeave={() => setShowTooltip(false)}
/>
)}
{showTooltip && (
<div
className={`absolute left-1/2 -translate-x-1/2 z-50 bg-black/90 text-white p-2 rounded-md text-sm font-normal shadow-lg ${className}`}
style={{
[tooltipPosition === "top" ? "bottom" : "top"]: "100%",
width: width,
marginBottom: tooltipPosition === "top" ? "8px" : "0",
marginTop: tooltipPosition === "bottom" ? "8px" : "0",
}}
>
{content}
<div
className="absolute left-1/2 -translate-x-1/2 w-0 h-0"
style={{
top: tooltipPosition === "top" ? "100%" : "auto",
bottom: tooltipPosition === "bottom" ? "100%" : "auto",
borderTop: tooltipPosition === "top" ? "6px solid rgba(0, 0, 0, 0.9)" : "6px solid transparent",
borderBottom: tooltipPosition === "bottom" ? "6px solid rgba(0, 0, 0, 0.9)" : "6px solid transparent",
borderLeft: "6px solid transparent",
borderRight: "6px solid transparent",
}}
/>
</div>
)}
</div>
)
}

View file

@ -0,0 +1 @@
export { Tooltip } from './Tooltip';