Skip to content

Commit e1d9681

Browse files
author
erangi-ar
committed
foramt markdown of the llm response
1 parent e8acb62 commit e1d9681

1 file changed

Lines changed: 85 additions & 108 deletions

File tree

GUI/src/pages/TestProductionLLM/index.tsx

Lines changed: 85 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { FC, useState, useRef, useEffect } from 'react';
1+
import { FC, useState, useRef, useEffect, useMemo } from 'react';
22
import { useTranslation } from 'react-i18next';
3-
import { Button, FormTextarea, Section } from 'components';
4-
import { productionInference, ProductionInferenceRequest } from 'services/inference';
3+
import { Button, FormTextarea } from 'components';
54
import { useToast } from 'hooks/useToast';
5+
import { useStreamingResponse } from 'hooks/useStreamingResponse';
66
import './TestProductionLLM.scss';
7-
7+
import MessageContent from 'components/MessageContent';
88
interface Message {
99
id: string;
1010
content: string;
@@ -15,139 +15,115 @@ interface Message {
1515
const TestProductionLLM: FC = () => {
1616
const { t } = useTranslation();
1717
const toast = useToast();
18-
const [message, setMessage] = useState<string>('');
18+
const [inputMessage, setInputMessage] = useState<string>('');
1919
const [messages, setMessages] = useState<Message[]>([]);
2020
const [isLoading, setIsLoading] = useState<boolean>(false);
2121
const messagesEndRef = useRef<HTMLDivElement>(null);
2222

23-
const scrollToBottom = () => {
24-
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
25-
};
23+
// Generate a unique channel ID for this session
24+
const channelId = useMemo(() => `channel-${Math.random().toString(36).substring(2, 15)}`, []);
25+
const { startStreaming, stopStreaming, isStreaming } = useStreamingResponse(channelId);
2626

27+
// Auto-scroll to bottom
2728
useEffect(() => {
28-
scrollToBottom();
29+
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
2930
}, [messages]);
3031

3132
const handleSendMessage = async () => {
32-
if (!message.trim()) {
33+
if (!inputMessage.trim()) {
3334
toast.open({
3435
type: 'warning',
35-
title: t('warningTitle'),
36-
message: t('emptyMessageWarning'),
36+
title: 'Warning',
37+
message: 'Please enter a message',
3738
});
3839
return;
3940
}
4041

42+
const userMessageText = inputMessage.trim();
43+
44+
// Add user message
4145
const userMessage: Message = {
4246
id: `user-${Date.now()}`,
43-
content: message.trim(),
47+
content: userMessageText,
4448
isUser: true,
4549
timestamp: new Date().toISOString(),
4650
};
4751

48-
// Add user message to chat
4952
setMessages(prev => [...prev, userMessage]);
50-
setMessage('');
53+
setInputMessage('');
5154
setIsLoading(true);
5255

53-
try {
54-
// Hardcoded values as requested
55-
const request: ProductionInferenceRequest = {
56-
chatId: 'test-chat-001',
57-
message: userMessage.content,
58-
authorId: 'test-author-001',
59-
conversationHistory: messages.map(msg => ({
60-
authorRole: msg.isUser ? 'user' : 'bot',
61-
message: msg.content,
62-
timestamp: msg.timestamp,
63-
})),
64-
url: 'https://test-url.example.com',
65-
};
66-
67-
let response;
68-
let attemptCount = 0;
69-
const maxAttempts = 2;
70-
71-
// Retry logic
72-
while (attemptCount < maxAttempts) {
73-
try {
74-
attemptCount++;
75-
console.log(`Production Inference Attempt ${attemptCount}/${maxAttempts}`);
76-
response = await productionInference(request);
77-
78-
// If we get a successful response, break out of retry loop
79-
if (!response.status || response.status < 400) {
80-
break;
81-
}
82-
83-
// If first attempt failed with error status, retry once more
84-
if (attemptCount < maxAttempts && response.status >= 400) {
85-
console.log('Retrying due to error status...');
86-
continue;
87-
}
88-
} catch (err) {
89-
// If first attempt threw an error, retry once more
90-
if (attemptCount < maxAttempts) {
91-
console.log('Retrying due to exception...');
92-
continue;
93-
}
94-
throw err; // Re-throw on final attempt
95-
}
96-
}
56+
// Create bot message ID
57+
const botMessageId = `bot-${Date.now()}`;
9758

98-
console.log('Production Inference Response:', response);
59+
// Prepare conversation history (exclude the current user message)
60+
const conversationHistory = messages.map(msg => ({
61+
authorRole: msg.isUser ? 'user' : 'bot',
62+
message: msg.content,
63+
timestamp: msg.timestamp,
64+
}));
9965

100-
// Create bot response message
101-
let botContent = '';
102-
let botMessageType: 'success' | 'error' = 'success';
66+
const streamingOptions = {
67+
authorId: 'test-user-456',
68+
conversationHistory,
69+
url: 'opensearch-dashboard-test',
70+
};
10371

104-
if (response.status && response.status >= 400) {
105-
// Error response
106-
botContent = response.content || 'An error occurred while processing your request.';
107-
botMessageType = 'error';
108-
} else {
109-
// Success response
110-
botContent = response?.response?.content || 'Response received successfully.';
72+
// Callbacks for streaming
73+
const onToken = (token: string) => {
74+
console.log('[Component] Received token:', token);
75+
76+
setMessages(prev => {
77+
// Find the bot message
78+
const botMsgIndex = prev.findIndex(msg => msg.id === botMessageId);
11179

112-
if (response.questionOutOfLlmScope) {
113-
botContent += ' (Note: This question appears to be outside the LLM scope)';
80+
if (botMsgIndex === -1) {
81+
// First token - add the bot message
82+
console.log('[Component] Adding bot message with first token');
83+
setIsLoading(false);
84+
return [
85+
...prev,
86+
{
87+
id: botMessageId,
88+
content: token,
89+
isUser: false,
90+
timestamp: new Date().toISOString(),
91+
}
92+
];
93+
} else {
94+
// Append token to existing message
95+
console.log('[Component] Appending token to existing message');
96+
const updated = [...prev];
97+
updated[botMsgIndex] = {
98+
...updated[botMsgIndex],
99+
content: updated[botMsgIndex].content + token,
100+
};
101+
return updated;
114102
}
115-
}
116-
117-
const botMessage: Message = {
118-
id: `bot-${Date.now()}`,
119-
content: botContent,
120-
isUser: false,
121-
timestamp: new Date().toISOString(),
122-
};
123-
124-
setMessages(prev => [...prev, botMessage]);
103+
});
104+
};
125105

126-
// Show toast notification
127-
// toast.open({
128-
// type: botMessageType,
129-
// title: t('errorOccurred'),
130-
// message: t('errorMessage'),
131-
// });
106+
const onComplete = () => {
107+
console.log('[Component] Stream completed');
108+
setIsLoading(false);
109+
};
132110

133-
} catch (error) {
134-
console.error('Error sending message:', error);
111+
const onError = (error: string) => {
112+
console.error('[Component] Stream error:', error);
113+
setIsLoading(false);
135114

136-
const errorMessage: Message = {
137-
id: `error-${Date.now()}`,
138-
content: 'Failed to send message. Please check your connection and try again.',
139-
isUser: false,
140-
timestamp: new Date().toISOString(),
141-
};
142-
143-
setMessages(prev => [...prev, errorMessage]);
144-
145115
toast.open({
146116
type: 'error',
147-
title: 'Connection Error',
148-
message: 'Unable to connect to the production LLM service.',
117+
title: 'Streaming Error',
118+
message: error,
149119
});
150-
} finally {
120+
};
121+
122+
// Start streaming
123+
try {
124+
await startStreaming(userMessageText, streamingOptions, onToken, onComplete, onError);
125+
} catch (error) {
126+
console.error('[Component] Failed to start streaming:', error);
151127
setIsLoading(false);
152128
}
153129
};
@@ -161,6 +137,7 @@ const TestProductionLLM: FC = () => {
161137

162138
const clearChat = () => {
163139
setMessages([]);
140+
stopStreaming();
164141
toast.open({
165142
type: 'info',
166143
title: 'Chat Cleared',
@@ -195,7 +172,7 @@ const TestProductionLLM: FC = () => {
195172
}`}
196173
>
197174
<div className="test-production-llm__message-content">
198-
{msg.content}
175+
<MessageContent content={msg.content} />
199176
</div>
200177
<div className="test-production-llm__message-timestamp">
201178
{new Date(msg.timestamp).toLocaleTimeString()}
@@ -222,20 +199,20 @@ const TestProductionLLM: FC = () => {
222199
<FormTextarea
223200
label="Message"
224201
name="message"
225-
value={message}
226-
onChange={(e) => setMessage(e.target.value)}
202+
value={inputMessage}
203+
onChange={(e) => setInputMessage(e.target.value)}
227204
onKeyDown={handleKeyPress}
228205
placeholder="Type your message here... (Press Enter to send, Shift+Enter for new line)"
229206
hideLabel
230207
maxRows={4}
231-
disabled={isLoading}
208+
disabled={isLoading || isStreaming}
232209
/>
233210
<Button
234211
onClick={handleSendMessage}
235-
disabled={isLoading || !message.trim()}
212+
disabled={isLoading || isStreaming || !inputMessage.trim()}
236213
className="test-production-llm__send-button"
237214
>
238-
{isLoading ? 'Sending...' : 'Send'}
215+
{isLoading || isStreaming ? 'Sending...' : 'Send'}
239216
</Button>
240217
</div>
241218
</div>

0 commit comments

Comments
 (0)