mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
fix(ui): move key tools preview inside Tool Configuration card
This commit is contained in:
parent
f3d8a74cec
commit
9daa70a842
3 changed files with 55 additions and 54 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState } from "react";
|
||||
import { Form, Input, Tooltip } from "antd";
|
||||
import { InfoCircleOutlined, ToolOutlined } from "@ant-design/icons";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { FormInstance } from "antd/es/form";
|
||||
import { AUTH_TYPE } from "./types";
|
||||
import OpenAPIQuickPicker, { OpenAPIRegistryEntry, OpenAPIKeyTool } from "./OpenAPIQuickPicker";
|
||||
|
|
@ -10,25 +10,26 @@ interface OpenAPIFormSectionProps {
|
|||
accessToken: string | null;
|
||||
/** Called when a preset is selected so the parent can sync its formValues state. */
|
||||
onValuesChange: (updates: Record<string, any>) => void;
|
||||
/** Called when key tools change (from registry preset selection). */
|
||||
onKeyToolsChange?: (tools: OpenAPIKeyTool[]) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates all OpenAPI-specific form fields:
|
||||
* - popular API quick-picker (logos)
|
||||
* - spec URL input
|
||||
* - curated key tools preview (8 tools from registry, expandable)
|
||||
*/
|
||||
const OpenAPIFormSection: React.FC<OpenAPIFormSectionProps> = ({
|
||||
form,
|
||||
accessToken,
|
||||
onValuesChange,
|
||||
onKeyToolsChange,
|
||||
}) => {
|
||||
const [selectedPreset, setSelectedPreset] = useState<string | null>(null);
|
||||
const [keyTools, setKeyTools] = useState<OpenAPIKeyTool[]>([]);
|
||||
|
||||
const handlePresetSelect = (entry: OpenAPIRegistryEntry) => {
|
||||
setSelectedPreset(entry.name);
|
||||
setKeyTools(entry.key_tools ?? []);
|
||||
onKeyToolsChange?.(entry.key_tools ?? []);
|
||||
const updates = {
|
||||
spec_path: entry.spec_url,
|
||||
auth_type: AUTH_TYPE.OAUTH2,
|
||||
|
|
@ -66,54 +67,9 @@ const OpenAPIFormSection: React.FC<OpenAPIFormSectionProps> = ({
|
|||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{keyTools.length > 0 && (
|
||||
<KeyToolsPreview tools={keyTools} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface KeyToolsPreviewProps {
|
||||
tools: OpenAPIKeyTool[];
|
||||
}
|
||||
|
||||
const KeyToolsPreview: React.FC<KeyToolsPreviewProps> = ({ tools }) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const shown = expanded ? tools : tools.slice(0, 4);
|
||||
|
||||
return (
|
||||
<div className="mb-4 p-3 bg-blue-50 border border-blue-100 rounded-lg">
|
||||
<div className="flex items-center gap-1.5 mb-2">
|
||||
<ToolOutlined className="text-blue-500 text-xs" />
|
||||
<span className="text-xs font-medium text-blue-700">
|
||||
Key tools from this API
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{shown.map((tool) => (
|
||||
<Tooltip key={tool.name} title={tool.description} placement="top">
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded bg-white border border-blue-200 text-xs text-blue-800 font-mono cursor-default hover:border-blue-400 transition-colors">
|
||||
{tool.name}
|
||||
</span>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{tools.length > 4 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setExpanded((v) => !v)}
|
||||
className="mt-2 text-xs text-blue-600 hover:text-blue-800 cursor-pointer bg-transparent border-none p-0"
|
||||
>
|
||||
{expanded
|
||||
? "Show less"
|
||||
: `+ ${tools.length - 4} more — all tools load from the spec below`}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OpenAPIFormSection;
|
||||
export type { OpenAPIKeyTool };
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import MCPConnectionStatus from "./mcp_connection_status";
|
|||
import MCPToolConfiguration from "./mcp_tool_configuration";
|
||||
import StdioConfiguration from "./StdioConfiguration";
|
||||
import MCPPermissionManagement from "./MCPPermissionManagement";
|
||||
import OpenAPIFormSection from "./OpenAPIFormSection";
|
||||
import OpenAPIFormSection, { OpenAPIKeyTool } from "./OpenAPIFormSection";
|
||||
import { isAdminRole } from "@/utils/roles";
|
||||
import { validateMCPServerUrl, validateMCPServerName } from "./utils";
|
||||
import NotificationsManager from "../molecules/notifications_manager";
|
||||
|
|
@ -58,6 +58,7 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
|
|||
const [toolNameToDisplayName, setToolNameToDisplayName] = useState<Record<string, string>>({});
|
||||
const [toolNameToDescription, setToolNameToDescription] = useState<Record<string, string>>({});
|
||||
const [transportType, setTransportType] = useState<string>("");
|
||||
const [keyTools, setKeyTools] = useState<OpenAPIKeyTool[]>([]);
|
||||
const [searchValue, setSearchValue] = useState<string>("");
|
||||
const [oauthAccessToken, setOauthAccessToken] = useState<string | null>(null);
|
||||
const authType = formValues.auth_type as string | undefined;
|
||||
|
|
@ -612,6 +613,7 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
|
|||
onValuesChange={(updates) =>
|
||||
setFormValues((prev) => ({ ...prev, ...updates }))
|
||||
}
|
||||
onKeyToolsChange={setKeyTools}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
@ -794,6 +796,7 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
|
|||
toolNameToDescription={toolNameToDescription}
|
||||
onToolNameToDisplayNameChange={setToolNameToDisplayName}
|
||||
onToolNameToDescriptionChange={setToolNameToDescription}
|
||||
keyTools={keyTools}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Card, Title, Text } from "@tremor/react";
|
||||
import { ToolOutlined, CheckCircleOutlined, SearchOutlined, EditOutlined } from "@ant-design/icons";
|
||||
import { Badge, Spin, Checkbox, Input } from "antd";
|
||||
import { Badge, Spin, Checkbox, Input, Tooltip } from "antd";
|
||||
import { useTestMCPConnection } from "../../hooks/useTestMCPConnection";
|
||||
|
||||
interface KeyTool {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface MCPToolConfigurationProps {
|
||||
accessToken: string | null;
|
||||
oauthAccessToken?: string | null;
|
||||
|
|
@ -15,6 +20,8 @@ interface MCPToolConfigurationProps {
|
|||
toolNameToDescription: Record<string, string>;
|
||||
onToolNameToDisplayNameChange: (map: Record<string, string>) => void;
|
||||
onToolNameToDescriptionChange: (map: Record<string, string>) => void;
|
||||
/** Curated key tools from the OpenAPI registry preset (shown before spec loads). */
|
||||
keyTools?: KeyTool[];
|
||||
}
|
||||
|
||||
const MCPToolConfiguration: React.FC<MCPToolConfigurationProps> = ({
|
||||
|
|
@ -28,6 +35,7 @@ const MCPToolConfiguration: React.FC<MCPToolConfigurationProps> = ({
|
|||
toolNameToDescription,
|
||||
onToolNameToDisplayNameChange,
|
||||
onToolNameToDescriptionChange,
|
||||
keyTools,
|
||||
}) => {
|
||||
const previousToolsRef = useRef<any[]>([]);
|
||||
const [toolSearchTerm, setToolSearchTerm] = useState("");
|
||||
|
|
@ -173,10 +181,15 @@ const MCPToolConfiguration: React.FC<MCPToolConfigurationProps> = ({
|
|||
{isLoadingTools && (
|
||||
<div className="flex items-center justify-center py-6">
|
||||
<Spin size="large" />
|
||||
<Text className="ml-3">Loading tools...</Text>
|
||||
<Text className="ml-3">Loading tools from spec...</Text>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Key tools preview — shown while loading or when spec hasn't loaded yet */}
|
||||
{keyTools && keyTools.length > 0 && (isLoadingTools || (!isLoadingTools && !toolsError && tools.length === 0)) && (
|
||||
<KeyToolsPreview tools={keyTools} />
|
||||
)}
|
||||
|
||||
{/* Error state */}
|
||||
{toolsError && !isLoadingTools && (
|
||||
<div className="text-center py-6 text-red-500 border rounded-lg border-dashed border-red-300 bg-red-50">
|
||||
|
|
@ -188,7 +201,7 @@ const MCPToolConfiguration: React.FC<MCPToolConfigurationProps> = ({
|
|||
)}
|
||||
|
||||
{/* No tools state */}
|
||||
{!isLoadingTools && !toolsError && tools.length === 0 && canFetchTools && (
|
||||
{!isLoadingTools && !toolsError && tools.length === 0 && canFetchTools && (!keyTools || keyTools.length === 0) && (
|
||||
<div className="text-center py-6 text-gray-400 border rounded-lg border-dashed">
|
||||
<ToolOutlined className="text-2xl mb-2" />
|
||||
<Text>No tools available for configuration</Text>
|
||||
|
|
@ -364,4 +377,33 @@ const MCPToolConfiguration: React.FC<MCPToolConfigurationProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
const KeyToolsPreview: React.FC<{ tools: KeyTool[] }> = ({ tools }) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const shown = expanded ? tools : tools.slice(0, 4);
|
||||
|
||||
return (
|
||||
<div className="p-3 bg-gray-50 border border-gray-200 rounded-lg">
|
||||
<p className="text-xs font-medium text-gray-500 mb-2">Key tools from this API (preview — all tools load from the spec)</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{shown.map((tool) => (
|
||||
<Tooltip key={tool.name} title={tool.description} placement="top">
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded bg-white border border-gray-300 text-xs text-gray-700 font-mono cursor-default hover:border-blue-400 transition-colors">
|
||||
{tool.name}
|
||||
</span>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
{tools.length > 4 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setExpanded((v) => !v)}
|
||||
className="mt-2 text-xs text-blue-600 hover:text-blue-800 cursor-pointer bg-transparent border-none p-0"
|
||||
>
|
||||
{expanded ? "Show less" : `+ ${tools.length - 4} more`}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MCPToolConfiguration;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue