Skip to content

Commit 4c6bc1f

Browse files
authored
Merge pull request #3747 from liam-hq/fix_human_message
Fix optimistic update not working on reload for first HumanMessage
2 parents 4eb8761 + 08eb4bb commit 4c6bc1f

File tree

2 files changed

+35
-40
lines changed

2 files changed

+35
-40
lines changed

frontend/apps/app/components/SessionDetailPage/SessionDetailPageClient.tsx

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client'
22

33
import {
4+
HumanMessage,
45
mapStoredMessagesToChatMessages,
56
type StoredMessage,
67
} from '@langchain/core/messages'
@@ -128,11 +129,11 @@ export const SessionDetailPageClient: FC<Props> = ({
128129
(artifact !== null || selectedVersion !== null) && activeTab
129130

130131
const chatMessages = mapStoredMessagesToChatMessages(initialMessages)
131-
const { isStreaming, messages, start, replay, error } = useStream({
132-
initialMessages: chatMessages,
133-
designSessionId,
134-
senderName,
135-
})
132+
const { isStreaming, messages, setMessages, start, replay, error } =
133+
useStream({
134+
initialMessages: chatMessages,
135+
designSessionId,
136+
})
136137

137138
const handleLayoutChange = useCallback((sizes: number[]) => {
138139
setCookieJson(PANEL_LAYOUT_COOKIE_NAME, sizes, {
@@ -146,6 +147,31 @@ export const SessionDetailPageClient: FC<Props> = ({
146147
// Track if initial workflow has been triggered to prevent multiple executions
147148
const hasTriggeredInitialWorkflow = useRef(false)
148149

150+
const handleSendMessage = useCallback(
151+
async (content: string) => {
152+
const tempId = `optimistic-${crypto.randomUUID()}`
153+
const optimisticMessage = new HumanMessage({
154+
content,
155+
id: tempId,
156+
additional_kwargs: {
157+
userName: senderName,
158+
},
159+
})
160+
setMessages((prev) => [...prev, optimisticMessage])
161+
162+
const result = await start({
163+
userInput: content,
164+
designSessionId,
165+
isDeepModelingEnabled,
166+
})
167+
168+
if (result.isErr()) {
169+
setMessages((prev) => prev.filter((msg) => msg.id !== tempId))
170+
}
171+
},
172+
[setMessages, start, senderName, designSessionId, isDeepModelingEnabled],
173+
)
174+
149175
// Auto-trigger workflow on page load if there's an unanswered user message
150176
useEffect(() => {
151177
const triggerInitialWorkflow = async () => {
@@ -201,13 +227,7 @@ export const SessionDetailPageClient: FC<Props> = ({
201227
schemaData={displayedSchema}
202228
messages={messages}
203229
isWorkflowRunning={isStreaming}
204-
onSendMessage={(content: string) =>
205-
start({
206-
userInput: content,
207-
designSessionId,
208-
isDeepModelingEnabled,
209-
})
210-
}
230+
onSendMessage={handleSendMessage}
211231
onNavigate={setActiveTab}
212232
error={combinedError}
213233
/>

frontend/apps/app/components/SessionDetailPage/hooks/useStream/useStream.ts

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import {
44
type BaseMessage,
55
coerceMessageLikeToMessage,
6-
HumanMessage,
76
} from '@langchain/core/messages'
87
import { MessageTupleManager, SSE_EVENTS } from '@liam-hq/agent/client'
98
import { err, ok, type Result } from 'neverthrow'
@@ -63,18 +62,11 @@ const extractStreamErrorMessage = (rawData: unknown): string => {
6362
type Props = {
6463
designSessionId: string
6564
initialMessages: BaseMessage[]
66-
senderName: string
6765
}
68-
export const useStream = ({
69-
designSessionId,
70-
initialMessages,
71-
senderName,
72-
}: Props) => {
66+
export const useStream = ({ designSessionId, initialMessages }: Props) => {
7367
const messageManagerRef = useRef(new MessageTupleManager())
7468
const storedMessage = useSessionStorageOnce(designSessionId)
7569

76-
const isFirstMessage = useRef(true)
77-
7870
const processedInitialMessages = useMemo(() => {
7971
if (storedMessage) {
8072
return [storedMessage, ...initialMessages]
@@ -268,30 +260,12 @@ export const useStream = ({
268260
abortRef.current?.abort()
269261
retryCountRef.current = 0
270262

271-
let tempId: string | undefined
272-
if (!isFirstMessage.current) {
273-
tempId = `optimistic-${crypto.randomUUID()}`
274-
const optimisticMessage = new HumanMessage({
275-
content: params.userInput,
276-
id: tempId,
277-
additional_kwargs: {
278-
userName: senderName,
279-
},
280-
})
281-
setMessages((prev) => [...prev, optimisticMessage])
282-
} else {
283-
isFirstMessage.current = false
284-
}
285-
286263
// Set workflow in progress flag
287264
setWorkflowInProgress(params.designSessionId)
288265

289266
const result = await runStreamAttempt('/api/chat/stream', params)
290267

291268
if (result.isErr()) {
292-
if (tempId) {
293-
setMessages((prev) => prev.filter((msg) => msg.id !== tempId))
294-
}
295269
return err(result.error)
296270
}
297271

@@ -304,11 +278,12 @@ export const useStream = ({
304278
isDeepModelingEnabled: params.isDeepModelingEnabled,
305279
})
306280
},
307-
[replay, runStreamAttempt, senderName],
281+
[replay, runStreamAttempt],
308282
)
309283

310284
return {
311285
messages,
286+
setMessages,
312287
isStreaming,
313288
error,
314289
start,

0 commit comments

Comments
 (0)