Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
address queued message conflicts during retries
  • Loading branch information
icecrasher321 committed Mar 25, 2026
commit a3c9d3b3d4c0603ea9f43df9abb871f8c5039779
64 changes: 57 additions & 7 deletions apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,9 @@ export function useChat(
const [messageQueue, setMessageQueue] = useState<QueuedMessage[]>([])
const messageQueueRef = useRef<QueuedMessage[]>([])
messageQueueRef.current = messageQueue
const [pendingRecoveryMessage, setPendingRecoveryMessage] = useState<QueuedMessage | null>(null)
const pendingRecoveryMessageRef = useRef<QueuedMessage | null>(null)
pendingRecoveryMessageRef.current = pendingRecoveryMessage

const sendMessageRef = useRef<UseChatReturn['sendMessage']>(async () => {})
const processSSEStreamRef = useRef<
Expand Down Expand Up @@ -1530,6 +1533,16 @@ export function useChat(

const messagesRef = useRef(messages)
messagesRef.current = messages
const visibleMessageQueue = useMemo(
() =>
pendingRecoveryMessage
? [
pendingRecoveryMessage,
...messageQueue.filter((msg) => msg.id !== pendingRecoveryMessage.id),
]
: messageQueue,
[messageQueue, pendingRecoveryMessage]
)

const finalize = useCallback(
(options?: { error?: boolean }) => {
Expand All @@ -1550,6 +1563,21 @@ export function useChat(
return
}

const recoveryMessage = pendingRecoveryMessageRef.current
Comment thread
icecrasher321 marked this conversation as resolved.
if (recoveryMessage) {
setPendingRecoveryMessage(null)
const gen = streamGenRef.current
queueMicrotask(() => {
if (streamGenRef.current !== gen) return
sendMessageRef.current(
recoveryMessage.content,
recoveryMessage.fileAttachments,
recoveryMessage.contexts
)
})
return
}

const next = messageQueueRef.current[0]
if (next) {
setMessageQueue((prev) => prev.filter((m) => m.id !== next.id))
Expand Down Expand Up @@ -1758,9 +1786,7 @@ export function useChat(
fileAttachments,
contexts,
}
const nextQueue = [...messageQueueRef.current, queuedMessage]
messageQueueRef.current = nextQueue
setMessageQueue(nextQueue)
setPendingRecoveryMessage(queuedMessage)
Comment thread
icecrasher321 marked this conversation as resolved.

try {
const pendingRecovery = await preparePendingStreamRecovery(requestChatId)
Expand Down Expand Up @@ -1919,24 +1945,47 @@ export function useChat(
}, [invalidateChatQueries, persistPartialResponse, executionStream])

const removeFromQueue = useCallback((id: string) => {
if (pendingRecoveryMessageRef.current?.id === id) {
pendingRecoveryMessageRef.current = null
setPendingRecoveryMessage(null)
return
}
messageQueueRef.current = messageQueueRef.current.filter((m) => m.id !== id)
setMessageQueue((prev) => prev.filter((m) => m.id !== id))
}, [])

const sendNow = useCallback(
async (id: string) => {
const msg = messageQueueRef.current.find((m) => m.id === id)
const recoveryMessage = pendingRecoveryMessageRef.current
const msg =
recoveryMessage?.id === id
? recoveryMessage
: messageQueueRef.current.find((m) => m.id === id)
if (!msg) return
// Eagerly update ref so a rapid second click finds the message already gone
messageQueueRef.current = messageQueueRef.current.filter((m) => m.id !== id)
if (recoveryMessage?.id === id) {
pendingRecoveryMessageRef.current = null
setPendingRecoveryMessage(null)
} else {
messageQueueRef.current = messageQueueRef.current.filter((m) => m.id !== id)
}
await stopGeneration()
setMessageQueue((prev) => prev.filter((m) => m.id !== id))
if (recoveryMessage?.id !== id) {
setMessageQueue((prev) => prev.filter((m) => m.id !== id))
}
await sendMessage(msg.content, msg.fileAttachments, msg.contexts)
},
[stopGeneration, sendMessage]
)

const editQueuedMessage = useCallback((id: string): QueuedMessage | undefined => {
const recoveryMessage = pendingRecoveryMessageRef.current
if (recoveryMessage?.id === id) {
pendingRecoveryMessageRef.current = null
setPendingRecoveryMessage(null)
return recoveryMessage
}

const msg = messageQueueRef.current.find((m) => m.id === id)
if (!msg) return undefined
messageQueueRef.current = messageQueueRef.current.filter((m) => m.id !== id)
Expand All @@ -1952,6 +2001,7 @@ export function useChat(
sendingRef.current = false
lastEventIdRef.current = 0
clientExecutionStartedRef.current.clear()
pendingRecoveryMessageRef.current = null
}
}, [])

Expand All @@ -1969,7 +2019,7 @@ export function useChat(
addResource,
removeResource,
reorderResources,
messageQueue,
messageQueue: visibleMessageQueue,
removeFromQueue,
sendNow,
editQueuedMessage,
Expand Down
Loading