diff --git a/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx b/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx index 70644f12d20..cbdffe46819 100644 --- a/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx +++ b/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx @@ -29,34 +29,33 @@ import { coy } from "react-syntax-highlighter/dist/esm/styles/prism"; import { v4 as uuidv4 } from "uuid"; import { truncateString } from "../../../utils/textUtils"; import GuardrailSelector from "../../guardrails/GuardrailSelector"; +import { MCPServer } from "../../mcp_tools/types"; import NotificationsManager from "../../molecules/notifications_manager"; +import { fetchMCPServers, listMCPTools } from "../../networking"; import TagSelector from "../../tag_management/TagSelector"; import VectorStoreSelector from "../../vector_store_management/VectorStoreSelector"; +import { makeA2ASendMessageRequest } from "../llm_calls/a2a_send_message"; +import { makeAnthropicMessagesRequest } from "../llm_calls/anthropic_messages"; +import { makeOpenAIAudioSpeechRequest } from "../llm_calls/audio_speech"; +import { makeOpenAIAudioTranscriptionRequest } from "../llm_calls/audio_transcriptions"; +import { makeOpenAIChatCompletionRequest } from "../llm_calls/chat_completion"; +import { makeOpenAIEmbeddingsRequest } from "../llm_calls/embeddings_api"; +import { Agent, fetchAvailableAgents } from "../llm_calls/fetch_agents"; +import { fetchAvailableModels, ModelGroup } from "../llm_calls/fetch_models"; +import { makeOpenAIImageEditsRequest } from "../llm_calls/image_edits"; +import { makeOpenAIImageGenerationRequest } from "../llm_calls/image_generation"; +import { makeOpenAIResponsesRequest } from "../llm_calls/responses_api"; +import A2AMetrics from "./A2AMetrics"; import AdditionalModelSettings from "./AdditionalModelSettings"; import AudioRenderer from "./AudioRenderer"; import { OPEN_AI_VOICE_SELECT_OPTIONS, OpenAIVoice } from "./chatConstants"; import ChatImageRenderer from "./ChatImageRenderer"; import ChatImageUpload from "./ChatImageUpload"; import { createChatDisplayMessage, createChatMultimodalMessage } from "./ChatImageUtils"; +import CodeInterpreterOutput from "./CodeInterpreterOutput"; +import CodeInterpreterTool from "./CodeInterpreterTool"; import { generateCodeSnippet } from "./CodeSnippets"; import EndpointSelector from "./EndpointSelector"; -import { makeAnthropicMessagesRequest } from "../llm_calls/anthropic_messages"; -import { makeOpenAIAudioSpeechRequest } from "../llm_calls/audio_speech"; -import { makeOpenAIAudioTranscriptionRequest } from "../llm_calls/audio_transcriptions"; -import { makeOpenAIChatCompletionRequest } from "../llm_calls/chat_completion"; -import { makeOpenAIEmbeddingsRequest } from "../llm_calls/embeddings_api"; -import { listMCPTools, fetchMCPServers } from "../../networking"; -import { MCPServer } from "../../mcp_tools/types"; -import { fetchAvailableModels, ModelGroup } from "../llm_calls/fetch_models"; -import { makeOpenAIImageEditsRequest } from "../llm_calls/image_edits"; -import { makeOpenAIImageGenerationRequest } from "../llm_calls/image_generation"; -import { makeOpenAIResponsesRequest } from "../llm_calls/responses_api"; -import CodeInterpreterOutput from "./CodeInterpreterOutput"; -import { useCodeInterpreter } from "./useCodeInterpreter"; -import { Agent, fetchAvailableAgents } from "../llm_calls/fetch_agents"; -import { makeA2AStreamMessageRequest, makeA2ASendMessageRequest } from "../llm_calls/a2a_send_message"; -import A2AMetrics from "./A2AMetrics"; -import { A2ATaskMetadata } from "./types"; import MCPEventsDisplay, { MCPEvent } from "./MCPEventsDisplay"; import { EndpointType, getEndpointType } from "./mode_endpoint_mapping"; import ReasoningContent from "./ReasoningContent"; @@ -66,8 +65,8 @@ import ResponsesImageUpload from "./ResponsesImageUpload"; import { createDisplayMessage, createMultimodalMessage } from "./ResponsesImageUtils"; import { SearchResultsDisplay } from "./SearchResultsDisplay"; import SessionManagement from "./SessionManagement"; -import { MessageType } from "./types"; -import CodeInterpreterTool from "./CodeInterpreterTool"; +import { A2ATaskMetadata, MessageType } from "./types"; +import { useCodeInterpreter } from "./useCodeInterpreter"; const { TextArea } = Input; const { Dragger } = Upload; diff --git a/ui/litellm-dashboard/src/components/templates/key_edit_view.test.tsx b/ui/litellm-dashboard/src/components/templates/key_edit_view.test.tsx index 85c6192693e..03e1085937d 100644 --- a/ui/litellm-dashboard/src/components/templates/key_edit_view.test.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_edit_view.test.tsx @@ -1,8 +1,8 @@ -import { waitFor } from "@testing-library/react"; -import { renderWithProviders } from "../../../tests/test-utils"; +import { fireEvent, waitFor } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; -import { KeyEditView } from "./key_edit_view"; +import { renderWithProviders } from "../../../tests/test-utils"; import { KeyResponse } from "../key_team_helpers/key_list"; +import { KeyEditView } from "./key_edit_view"; // Mock window.matchMedia Object.defineProperty(window, "matchMedia", { @@ -143,4 +143,28 @@ describe("KeyEditView", () => { expect(metadataTextarea).toHaveValue("{}"); }); }); + + it("should call onCancel when cancel button is clicked", async () => { + const onCancelMock = vi.fn(); + const { getByText } = renderWithProviders( + {}} + accessToken={""} + userID={""} + userRole={""} + premiumUser={false} + />, + ); + + await waitFor(() => { + expect(getByText("Cancel")).toBeInTheDocument(); + }); + + const cancelButton = getByText("Cancel"); + fireEvent.click(cancelButton); + + expect(onCancelMock).toHaveBeenCalledTimes(1); + }); }); diff --git a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx index d655489abf1..836ebb385f1 100644 --- a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx @@ -1,7 +1,9 @@ import GuardrailSelector from "@/components/guardrails/GuardrailSelector"; +import { InfoCircleOutlined } from "@ant-design/icons"; import { TextInput, Button as TremorButton } from "@tremor/react"; import { Form, Input, Select, Switch, Tooltip } from "antd"; import { useEffect, useState } from "react"; +import AgentSelector from "../agent_management/AgentSelector"; import { mapInternalToDisplayNames } from "../callback_info_helpers"; import KeyLifecycleSettings from "../common_components/KeyLifecycleSettings"; import PassThroughRoutesSelector from "../common_components/PassThroughRoutesSelector"; @@ -10,15 +12,13 @@ import { extractLoggingSettings, formatMetadataForDisplay, stripTagsFromMetadata import { KeyResponse } from "../key_team_helpers/key_list"; import MCPServerSelector from "../mcp_server_management/MCPServerSelector"; import MCPToolPermissions from "../mcp_server_management/MCPToolPermissions"; -import AgentSelector from "../agent_management/AgentSelector"; import NotificationsManager from "../molecules/notifications_manager"; -import { fetchMCPAccessGroups, getPromptsList, modelAvailableCall, tagListCall } from "../networking"; +import { getPromptsList, modelAvailableCall, tagListCall } from "../networking"; import { fetchTeamModels } from "../organisms/create_key_button"; import NumericalInput from "../shared/numerical_input"; import { Tag } from "../tag_management/types"; import EditLoggingSettings from "../team/EditLoggingSettings"; import VectorStoreSelector from "../vector_store_management/VectorStoreSelector"; -import { InfoCircleOutlined } from "@ant-design/icons"; interface KeyEditViewProps { keyData: KeyResponse; @@ -82,13 +82,10 @@ export function KeyEditView({ premiumUser = false, }: KeyEditViewProps) { const [form] = Form.useForm(); - const [userModels, setUserModels] = useState([]); const [promptsList, setPromptsList] = useState([]); const [tagsList, setTagsList] = useState>({}); const team = teams?.find((team) => team.team_id === keyData.team_id); const [availableModels, setAvailableModels] = useState([]); - const [mcpAccessGroups, setMcpAccessGroups] = useState([]); - const [mcpAccessGroupsLoaded, setMcpAccessGroupsLoaded] = useState(false); const [disabledCallbacks, setDisabledCallbacks] = useState( Array.isArray(keyData.metadata?.litellm_disabled_callbacks) ? mapInternalToDisplayNames(keyData.metadata.litellm_disabled_callbacks) @@ -98,18 +95,6 @@ export function KeyEditView({ const [rotationInterval, setRotationInterval] = useState(keyData.rotation_interval || ""); const [isKeySaving, setIsKeySaving] = useState(false); - const fetchMcpAccessGroups = async () => { - if (!accessToken) return; - if (mcpAccessGroupsLoaded) return; - try { - const groups = await fetchMCPAccessGroups(accessToken); - setMcpAccessGroups(groups); - setMcpAccessGroupsLoaded(true); - } catch (error) { - console.error("Failed to fetch MCP access groups:", error); - } - }; - useEffect(() => { const fetchModels = async () => { if (!userID || !userRole || !accessToken) return; @@ -526,7 +511,16 @@ export function KeyEditView({ - { + const team = teams?.find((t) => t.team_id === option?.value); + if (!team) return false; + return team.team_alias?.toLowerCase().includes(input.toLowerCase()) ?? false; + }} + > {/* Only show All Team Models if team has models */} {teams?.map((team) => (