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 ff93194c3f1..320db6805fa 100644 --- a/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx +++ b/ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx @@ -3,16 +3,20 @@ import { ApiOutlined, ArrowUpOutlined, + CheckOutlined, + CloseOutlined, ClearOutlined, CodeOutlined, DatabaseOutlined, DeleteOutlined, + EditOutlined, FilePdfOutlined, InfoCircleOutlined, KeyOutlined, LinkOutlined, LoadingOutlined, PictureOutlined, + RedoOutlined, RobotOutlined, SafetyOutlined, SettingOutlined, @@ -222,6 +226,8 @@ const ChatUI: React.FC = ({ const [temperature, setTemperature] = useState(1.0); const [maxTokens, setMaxTokens] = useState(2048); const [useAdvancedParams, setUseAdvancedParams] = useState(false); + const [editingMessageIndex, setEditingMessageIndex] = useState(null); + const [editedMessageContent, setEditedMessageContent] = useState(""); // Code Interpreter state (using custom hook) const codeInterpreter = useCodeInterpreter(); @@ -769,6 +775,68 @@ const ChatUI: React.FC = ({ setUploadedAudio(null); }; + const handleEditMessage = (index: number) => { + const message = chatHistory[index]; + if (message.role !== "user") return; + + setEditingMessageIndex(index); + setEditedMessageContent(typeof message.content === "string" ? message.content : ""); + }; + + const handleCancelEdit = () => { + setEditingMessageIndex(null); + setEditedMessageContent(""); + }; + + const handleSaveEdit = async () => { + if (editingMessageIndex === null || editedMessageContent.trim() === "") return; + + // Update the message content + const updatedHistory = [...chatHistory]; + updatedHistory[editingMessageIndex] = { + ...updatedHistory[editingMessageIndex], + content: editedMessageContent, + }; + + // Remove all messages after the edited message (including assistant response) + const newHistory = updatedHistory.slice(0, editingMessageIndex + 1); + setChatHistory(newHistory); + + // Clear editing state + setEditingMessageIndex(null); + setEditedMessageContent(""); + + // Resend the message + setInputMessage(editedMessageContent); + + // Use setTimeout to ensure state is updated before sending + setTimeout(() => { + handleSendMessage(); + }, 100); + }; + + const handleRetryMessage = async (index: number) => { + // Find the corresponding user message + const userMessageIndex = index - 1; + if (userMessageIndex < 0 || chatHistory[userMessageIndex].role !== "user") return; + + // Remove the assistant message and all subsequent messages + const newHistory = chatHistory.slice(0, userMessageIndex + 1); + setChatHistory(newHistory); + + // Get the user message content + const userMessage = chatHistory[userMessageIndex]; + const messageContent = typeof userMessage.content === "string" ? userMessage.content : ""; + + // Resend the message + setInputMessage(messageContent); + + // Use setTimeout to ensure state is updated before sending + setTimeout(() => { + handleSendMessage(); + }, 100); + }; + const handleSendMessage = async () => { if (inputMessage.trim() === "" && endpointType !== EndpointType.TRANSCRIPTION) return; @@ -1677,9 +1745,9 @@ const ChatUI: React.FC = ({ {chatHistory.map((message, index) => (
-
+
= ({ )}
+ + {/* Action buttons - show on hover */} + {!isLoading && editingMessageIndex !== index && ( +
+ {message.role === "user" && ( + +
+ )} {message.reasoningContent && } {/* Show MCP events at the start of assistant messages */} @@ -1736,16 +1832,45 @@ const ChatUI: React.FC = ({ /> )} -
- {message.isImage ? ( + {/* Edit mode UI */} + {editingMessageIndex === index ? ( +
+