Skip to content

Commit 0d1c3e5

Browse files
authored
frontend: fix sluggish conversion (#158)
Assisted-by: Claude Signed-off-by: Yuval Turgeman <[email protected]>
1 parent cad2c98 commit 0d1c3e5

File tree

2 files changed

+45
-46
lines changed

2 files changed

+45
-46
lines changed

frontend/src/components/chat.tsx

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ export function Chat({ preSelectedAgentId }: ChatProps = {}) {
137137
// Session list will be refreshed when user opens the drawer (on-demand)
138138
},
139139
});
140-
const contentToText = (content: SimpleContentItem): string => {
140+
// Memoized content conversion to prevent recreation on every render
141+
const contentToText = React.useCallback((content: SimpleContentItem): string => {
141142
if (content.type === 'input_text' || content.type === 'output_text') {
142143
return content.text;
143144
}
@@ -151,44 +152,33 @@ export function Chat({ preSelectedAgentId }: ChatProps = {}) {
151152
}
152153

153154
return '';
154-
};
155-
156-
const multipleContentToText = React.useCallback((content: SimpleContentItem[]): string => {
157-
return content.map((m) => contentToText(m)).join('\n');
158155
}, []);
159156

160-
// Convert our chat messages to PatternFly format
161-
const messages = React.useMemo(
162-
() =>
163-
chatMessages.map(
164-
(msg): MessageProps => ({
165-
id: msg.id,
166-
role: msg.role === 'user' ? 'user' : 'bot',
167-
content: multipleContentToText(msg.content),
168-
name: msg.role === 'user' ? 'You' : 'Agent',
169-
timestamp: msg.timestamp.toLocaleString(),
170-
avatar: msg.role === 'user' ? userAvatar : botAvatar,
171-
avatarProps: { isBordered: true },
172-
isLoading:
173-
msg.role === 'assistant' &&
174-
isLoading &&
175-
msg.id === chatMessages[chatMessages.length - 1]?.id,
176-
})
177-
),
178-
[chatMessages, isLoading, multipleContentToText]
157+
const multipleContentToText = React.useCallback(
158+
(content: SimpleContentItem[]): string => {
159+
return content.map((m) => contentToText(m)).join('\n');
160+
},
161+
[contentToText]
179162
);
180163

181-
const displayMode = ChatbotDisplayMode.embedded;
164+
// Convert our chat messages to PatternFly format
165+
const messages = React.useMemo(() => {
166+
const lastMessageId = chatMessages[chatMessages.length - 1]?.id;
167+
return chatMessages.map(
168+
(msg): MessageProps => ({
169+
id: msg.id,
170+
role: msg.role === 'user' ? 'user' : 'bot',
171+
content: multipleContentToText(msg.content),
172+
name: msg.role === 'user' ? 'You' : 'Agent',
173+
timestamp: msg.timestamp.toLocaleString(),
174+
avatar: msg.role === 'user' ? userAvatar : botAvatar,
175+
avatarProps: { isBordered: true },
176+
isLoading: msg.role === 'assistant' && isLoading && msg.id === lastMessageId,
177+
})
178+
);
179+
}, [chatMessages, isLoading, multipleContentToText]);
182180

183-
// Track message composer width to align suggestions with it
184-
useEffect(() => {
185-
// Keeping observer attached in case of future width-based logic
186-
if (!composerRef.current) return;
187-
const el = composerRef.current;
188-
const ro = new ResizeObserver(() => {});
189-
ro.observe(el);
190-
return () => ro.disconnect();
191-
}, []);
181+
const displayMode = ChatbotDisplayMode.embedded;
192182

193183
// Load demo questions for the selected agent (if template-backed)
194184
useEffect(() => {

frontend/src/hooks/useChat.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -249,19 +249,28 @@ export function useChat(agentId: string, options?: UseLlamaChatOptions) {
249249
}
250250

251251
setMessages((prev) => {
252-
const updated = [...prev];
253-
const lastMsg = updated[updated.length - 1];
254-
if (lastMsg && lastMsg.role === 'assistant') {
255-
const c: SimpleContentItem[] = [...lastMsg.content];
256-
if (c[0].type === 'output_text') {
257-
// Replace content (backend sends complete response, not chunks)
258-
c[0].text = parsed;
259-
lastMsg.content = c;
260-
// Update timestamp when response arrives
261-
lastMsg.timestamp = new Date();
262-
}
252+
const lastMsg = prev[prev.length - 1];
253+
if (
254+
lastMsg &&
255+
lastMsg.role === 'assistant' &&
256+
lastMsg.content[0]?.type === 'output_text'
257+
) {
258+
// Create new message object and content array to trigger React update
259+
const newContent: SimpleContentItem[] = [
260+
{
261+
type: 'output_text',
262+
text: parsed,
263+
},
264+
];
265+
const newLastMsg: ChatMessage = {
266+
...lastMsg,
267+
content: newContent,
268+
timestamp: new Date(),
269+
};
270+
// Only spread array once, replace last message
271+
return [...prev.slice(0, -1), newLastMsg];
263272
}
264-
return updated;
273+
return prev;
265274
});
266275
}
267276

0 commit comments

Comments
 (0)