@@ -10,25 +10,25 @@ import './ChatbotInterface.css';
1010// Sample Questions component as a proper React component
1111const SampleQuestions = ( { onSelectQuestion } ) => {
1212 const [ visible , setVisible ] = useState ( true ) ;
13-
13+
1414 // Sample questions that will appear as clickable buttons
1515 const questions = [
1616 "How do I configure MongoDB RAG with Ollama?" ,
1717 "What's the difference between OpenAI and Ollama embeddings?" ,
1818 "Can you fix my config file structure?" ,
1919 "How do I troubleshoot connection issues?"
2020 ] ;
21-
21+
2222 // Function to handle clicking a question
2323 const handleQuestionClick = ( question ) => {
2424 // Call the callback with the selected question
2525 onSelectQuestion ( question ) ;
2626 // Hide suggestions
2727 setVisible ( false ) ;
2828 } ;
29-
29+
3030 if ( ! visible ) return null ;
31-
31+
3232 return (
3333 < div className = "sample-questions" >
3434 < div className = "sample-questions-label" > Suggested questions:</ div >
@@ -58,10 +58,8 @@ export default function ChatbotInterface() {
5858 const [ isLoading , setIsLoading ] = useState ( false ) ;
5959 const [ sessionId , setSessionId ] = useState ( null ) ;
6060 const [ showSampleQuestions , setShowSampleQuestions ] = useState ( true ) ;
61- const [ currentStreamedMessage , setCurrentStreamedMessage ] = useState ( '' ) ;
6261 const messagesEndRef = useRef ( null ) ;
6362 const inputRef = useRef ( null ) ;
64- const eventSourceRef = useRef ( null ) ;
6563
6664 // Auto-scroll to bottom when messages update
6765 useEffect ( ( ) => {
@@ -100,23 +98,11 @@ export default function ChatbotInterface() {
10098 }
10199 } , [ sessionId , messages ] ) ;
102100
103- const cleanupEventSource = ( ) => {
104- if ( eventSourceRef . current ) {
105- eventSourceRef . current . close ( ) ;
106- eventSourceRef . current = null ;
107- }
108- } ;
109-
110- // Cleanup on unmount
111- useEffect ( ( ) => {
112- return ( ) => cleanupEventSource ( ) ;
113- } , [ ] ) ;
114-
115101 // Handle input changes and control sample questions visibility
116102 const handleInputChange = ( e ) => {
117103 const value = e . target . value ;
118104 setInput ( value ) ;
119-
105+
120106 // Show sample questions only when input is empty
121107 setShowSampleQuestions ( value . length === 0 ) ;
122108 } ;
@@ -129,129 +115,42 @@ export default function ChatbotInterface() {
129115 inputRef . current ?. focus ( ) ;
130116 } ;
131117
132- const safeJsonParse = ( text ) => {
133- try {
134- return text ? JSON . parse ( text ) : null ;
135- } catch ( error ) {
136- console . warn ( 'JSON parse error:' , error ) ;
137- return null ;
138- }
139- } ;
140118
141- const handleStreamedChat = async ( input , history ) => {
142- // Clean up any existing EventSource
143- cleanupEventSource ( ) ;
144-
145- try {
146- // Make initial request without expecting JSON response
147- const response = await fetch ( 'https://mongodb-rag-docs-backend.vercel.app/api/chat' , {
148- method : 'POST' ,
149- headers : {
150- 'Content-Type' : 'application/json' ,
151- 'Accept' : 'text/event-stream' // Request SSE response
152- } ,
153- body : JSON . stringify ( {
154- query : input ,
155- sessionId : sessionId ,
156- history : ! sessionId ? history : undefined
157- } )
158- } ) ;
159-
160- if ( ! response . ok ) {
161- throw new Error ( `HTTP error! status: ${ response . status } ` ) ;
162- }
163-
164- if ( response . headers . get ( 'content-type' ) ?. includes ( 'text/event-stream' ) ) {
165- // Handle SSE response directly
166- const reader = response . body . getReader ( ) ;
167- const decoder = new TextDecoder ( ) ;
168- let buffer = '' ;
169-
170- while ( true ) {
171- const { done, value } = await reader . read ( ) ;
172- if ( done ) break ;
173-
174- buffer += decoder . decode ( value , { stream : true } ) ;
175- const lines = buffer . split ( '\n' ) ;
176- buffer = lines . pop ( ) || '' ;
177-
178- for ( const line of lines ) {
179- if ( line . trim ( ) ) {
180- const event = safeJsonParse ( line ) ;
181- if ( event ) {
182- if ( event . type === 'token' ) {
183- setCurrentStreamedMessage ( prev => prev + event . content ) ;
184- } else if ( event . type === 'sources' ) {
185- // Store sources for final message
186- sourcesData = event . sources ;
187- } else if ( event . type === 'done' ) {
188- // Add final message with sources
189- setMessages ( msgs => [ ...msgs , {
190- role : 'assistant' ,
191- content : currentStreamedMessage ,
192- sources : sourcesData
193- } ] ) ;
194- return ;
195- }
196- }
197- }
198- }
199- }
200- } else {
201- // Handle regular JSON response
202- const text = await response . text ( ) ;
203- const data = safeJsonParse ( text ) ;
204-
205- if ( ! data ) {
206- throw new Error ( 'Invalid response format' ) ;
207- }
208-
209- setMessages ( msgs => [ ...msgs , {
210- role : 'assistant' ,
211- content : data . answer ,
212- sources : data . sources
213- } ] ) ;
214- }
215- } catch ( error ) {
216- console . error ( 'Error in streaming chat:' , error ) ;
217- throw error ;
218- } finally {
219- cleanupEventSource ( ) ;
220- }
221- } ;
222-
223119 const handleSubmit = async ( e ) => {
224120 e . preventDefault ( ) ;
225121 if ( ! input . trim ( ) ) return ;
226-
122+
123+ // Add user message
227124 const userMessage = { role : 'user' , content : input } ;
228125 setMessages ( msgs => [ ...msgs , userMessage ] ) ;
229126 setInput ( '' ) ;
230127 setIsLoading ( true ) ;
231-
128+ setShowSampleQuestions ( true ) ;
129+
232130 try {
131+ // Call API with session support
233132 const response = await fetch ( 'https://mongodb-rag-docs-backend.vercel.app/api/chat' , {
234133 method : 'POST' ,
235134 headers : { 'Content-Type' : 'application/json' } ,
236135 body : JSON . stringify ( {
237136 query : input ,
238137 sessionId : sessionId ,
239- history : ! sessionId ? messages : undefined
240- } ) ,
241- // Add request timeout
242- signal : AbortSignal . timeout ( 45000 ) // 45 second timeout
138+ history : ! sessionId ? messages : undefined // Only send history if no sessionId
139+ } )
243140 } ) ;
244-
141+
245142 if ( ! response . ok ) {
246143 throw new Error ( `HTTP error! status: ${ response . status } ` ) ;
247144 }
248-
145+
249146 const data = await response . json ( ) ;
250-
251- if ( data . error ) {
252- throw new Error ( data . error ) ;
147+
148+ // Store the session ID if this is a new session
149+ if ( data . sessionId && ! sessionId ) {
150+ setSessionId ( data . sessionId ) ;
253151 }
254-
152+
153+ // Add bot response with sources
255154 setMessages ( msgs => [
256155 ...msgs ,
257156 {
@@ -261,38 +160,19 @@ export default function ChatbotInterface() {
261160 }
262161 ] ) ;
263162 } catch ( error ) {
264- handleError ( error ) ;
163+ console . error ( 'Chat error:' , error ) ;
164+ setMessages ( msgs => [
165+ ...msgs ,
166+ {
167+ role : 'assistant' ,
168+ content : 'Sorry, I encountered an error. Please try again later.'
169+ }
170+ ] ) ;
265171 } finally {
266172 setIsLoading ( false ) ;
267173 }
268174 } ;
269175
270- const handleError = ( error , fallback = true ) => {
271- console . error ( 'Chat error:' , error ) ;
272-
273- let errorMessage = 'Sorry, I encountered an error. Please try again later.' ;
274-
275- if ( error . response ?. status === 504 || error . message ?. includes ( 'timed out' ) ) {
276- errorMessage = 'The search is taking longer than expected. You might try:' +
277- '\n1. Rephrasing your question to be more specific' +
278- '\n2. Breaking it into smaller parts' +
279- '\n3. Trying again in a moment' ;
280- }
281-
282- setMessages ( msgs => [
283- ...msgs ,
284- {
285- role : 'assistant' ,
286- content : errorMessage
287- }
288- ] ) ;
289-
290- if ( fallback && ! error . message ?. includes ( 'timed out' ) ) {
291- // Optionally try fallback handling for non-timeout errors
292- handleFallbackChat ( input , messages ) ;
293- }
294- } ;
295-
296176 const clearChat = ( ) => {
297177 // Reset the chat
298178 setMessages ( [
@@ -301,17 +181,17 @@ export default function ChatbotInterface() {
301181 content : 'Hi! I\'m the MongoDB-RAG chatbot. Ask me anything about using MongoDB-RAG!'
302182 }
303183 ] ) ;
304-
184+
305185 // Clear the session ID to start a new session
306186 setSessionId ( null ) ;
307-
187+
308188 // Clear localStorage
309189 localStorage . removeItem ( 'mongodbRagChatSession' ) ;
310-
190+
311191 // Clear input and show sample questions
312192 setInput ( '' ) ;
313193 setShowSampleQuestions ( true ) ;
314-
194+
315195 // Focus the input field
316196 inputRef . current ?. focus ( ) ;
317197 } ;
@@ -334,7 +214,7 @@ export default function ChatbotInterface() {
334214 >
335215 { message . content }
336216 </ ReactMarkdown >
337-
217+
338218 { message . sources && message . sources . length > 0 && (
339219 < div className = "sources" >
340220 < details >
@@ -359,24 +239,9 @@ export default function ChatbotInterface() {
359239 </ div >
360240 </ div >
361241 ) ) }
362-
363- { /* Show streaming message */ }
364- { currentStreamedMessage && (
365- < div className = "message assistant" >
366- < div className = "message-avatar" > 🦉</ div >
367- < div className = "message-content" >
368- < ReactMarkdown
369- rehypePlugins = { [ rehypeHighlight , rehypeRaw ] }
370- remarkPlugins = { [ remarkGfm ] }
371- >
372- { currentStreamedMessage }
373- </ ReactMarkdown >
374- </ div >
375- </ div >
376- ) }
377-
242+
378243 { /* Show loading indicator */ }
379- { isLoading && ! currentStreamedMessage && (
244+ { isLoading && (
380245 < div className = "message assistant" >
381246 < div className = "message-avatar" > 🦉</ div >
382247 < div className = "message-content" >
@@ -386,22 +251,22 @@ export default function ChatbotInterface() {
386251 </ div >
387252 </ div >
388253 ) }
389-
254+
390255 < div ref = { messagesEndRef } />
391256 </ div >
392257 </ div >
393-
258+
394259 < div className = "input-area" >
395260 { /* Rest of the input area remains unchanged */ }
396261 < div className = "input-container" >
397262 < button onClick = { clearChat } className = "clear-button" >
398263 Clear Chat
399264 </ button >
400-
265+
401266 { showSampleQuestions && (
402267 < SampleQuestions onSelectQuestion = { handleSelectQuestion } />
403268 ) }
404-
269+
405270 < form onSubmit = { handleSubmit } className = "input-form" >
406271 < input
407272 ref = { inputRef }
@@ -412,8 +277,8 @@ export default function ChatbotInterface() {
412277 className = "chat-input"
413278 disabled = { isLoading }
414279 />
415- < button
416- type = "submit"
280+ < button
281+ type = "submit"
417282 className = "send-button"
418283 disabled = { isLoading || ! input . trim ( ) }
419284 >
0 commit comments