diff --git a/llm-fe/src/llm-fe/components/AsyncChat/AsyncChat.tsx b/llm-fe/src/llm-fe/components/AsyncChat/AsyncChat.tsx index e12ca6d..bf993bc 100644 --- a/llm-fe/src/llm-fe/components/AsyncChat/AsyncChat.tsx +++ b/llm-fe/src/llm-fe/components/AsyncChat/AsyncChat.tsx @@ -41,6 +41,7 @@ type AsyncChatProps = { const validationSchema = Yup.object().shape({ prompt: Yup.string() + .trim() .min(1, "Need to have at least one character") .required("This is requried"), }); @@ -206,20 +207,24 @@ const AsyncChat = ({ { resetForm }: any, ): Promise => { // send the prompt to be saved + const trimmedPrompt = (prompt ?? "").trim(); + if (!trimmedPrompt) { + return; + } console.log(fileType); try { const tempConversations: ConversationPrompt[] = [ ...conversationDetails, - new ConversationPrompt({ message: prompt, user_created: true }), + new ConversationPrompt({ message: trimmedPrompt, user_created: true }), new ConversationPrompt({ message: "", user_created: false }), ]; conversationRef.current = tempConversations; setConversationDetails(tempConversations); // TODO: add the file here console.log( - `Sending message. ${prompt} ${selectedConversation} ${fileType}`, + `Sending message. ${trimmedPrompt} ${selectedConversation} ${fileType}`, ); - sendMessage(prompt, selectedConversation, file, fileType); + sendMessage(trimmedPrompt, selectedConversation, file, fileType); resetForm(); } catch (e) { console.log(`error ${e}`); diff --git a/llm-fe/src/llm-fe/contexts/WebSocketContext.js b/llm-fe/src/llm-fe/contexts/WebSocketContext.js index bd6cdb9..cb879cf 100644 --- a/llm-fe/src/llm-fe/contexts/WebSocketContext.js +++ b/llm-fe/src/llm-fe/contexts/WebSocketContext.js @@ -91,6 +91,13 @@ function WebSocketProvider({ children }) { return false; } + const trimmedMessage = + typeof message === "string" ? message.trim() : ""; + if (!trimmedMessage) { + console.log("Error sending message. Message text is empty"); + return false; + } + if (file) { const reader = new FileReader(); reader.onload = () => { @@ -98,7 +105,7 @@ function WebSocketProvider({ children }) { if (base64File && isSocketOpen(ws.current)) { ws.current.send( JSON.stringify({ - message, + message: trimmedMessage, conversation_id, email: accountRef.current?.email, file: base64File, @@ -112,7 +119,7 @@ function WebSocketProvider({ children }) { } else { current.send( JSON.stringify({ - message, + message: trimmedMessage, conversation_id, email: accountRef.current?.email, file: null, diff --git a/llm-fe/src/llm-fe/contexts/WebSocketContext.test.js b/llm-fe/src/llm-fe/contexts/WebSocketContext.test.js index 7cf0705..1e15918 100644 --- a/llm-fe/src/llm-fe/contexts/WebSocketContext.test.js +++ b/llm-fe/src/llm-fe/contexts/WebSocketContext.test.js @@ -246,4 +246,35 @@ describe('WebSocketProvider', () => { expect(payload.message).toBe('hi'); expect(payload.email).toBe('test@example.com'); }); + + it('does not send empty or whitespace-only messages', async () => { + let sendMessage; + function Capture() { + const ctx = useContext(WebSocketContext); + sendMessage = ctx[3]; + return null; + } + + render( + {} }} + > + + + + , + ); + + await waitFor(() => expect(MockWebSocket.instances.length).toBe(1)); + await act(async () => { + MockWebSocket.instances[0].open(); + }); + + const before = MockWebSocket.instances[0].sent.length; + await act(async () => { + expect(sendMessage(' ', 1, null, null, 'FAST')).toBe(false); + expect(sendMessage('', 1, null, null, 'FAST')).toBe(false); + }); + expect(MockWebSocket.instances[0].sent.length).toBe(before); + }); }); diff --git a/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx b/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx index 6aa4fb1..78da260 100644 --- a/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx +++ b/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx @@ -273,6 +273,7 @@ const VisuallyHiddenInput = styled.input` const validationSchema = Yup.object().shape({ prompt: Yup.string() + .trim() .min(1, "Need to have at least one character") .required("This is required"), }); @@ -326,16 +327,20 @@ const AsyncDashboardInner = (): JSX.Element => { { prompt, file, fileType, modelName }: PromptValues, { resetForm }: any, ): Promise => { + const trimmedPrompt = (prompt ?? "").trim(); + if (!trimmedPrompt) { + return; + } try { const tempConversations: ConversationPrompt[] = [ ...conversationDetails, - new ConversationPrompt({ message: prompt, user_created: true }), + new ConversationPrompt({ message: trimmedPrompt, user_created: true }), new ConversationPrompt({ message: "", user_created: false }), ]; conversationRef.current = tempConversations; setConversationDetails(tempConversations); - sendMessage(prompt, selectedConversation, file, fileType, modelName); + sendMessage(trimmedPrompt, selectedConversation, file, fileType, modelName); trackEvent(AnalyticsEvents.MESSAGE_SENT, { hasConversation: Boolean(selectedConversation), hasAttachment: Boolean(file),