handle abort properly + finally clause to resolve (#6227)
This commit is contained in:
parent
55390de070
commit
9ea9b7d87d
@ -309,7 +309,7 @@ export const useChat = () => {
|
|||||||
let pendingDeltaCount = 0
|
let pendingDeltaCount = 0
|
||||||
const reasoningProcessor = new ReasoningProcessor()
|
const reasoningProcessor = new ReasoningProcessor()
|
||||||
const scheduleFlush = () => {
|
const scheduleFlush = () => {
|
||||||
if (rafScheduled) return
|
if (rafScheduled || abortController.signal.aborted) return
|
||||||
rafScheduled = true
|
rafScheduled = true
|
||||||
const doSchedule = (cb: () => void) => {
|
const doSchedule = (cb: () => void) => {
|
||||||
if (typeof requestAnimationFrame !== 'undefined') {
|
if (typeof requestAnimationFrame !== 'undefined') {
|
||||||
@ -321,6 +321,12 @@ export const useChat = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
doSchedule(() => {
|
doSchedule(() => {
|
||||||
|
// Check abort status before executing the scheduled callback
|
||||||
|
if (abortController.signal.aborted) {
|
||||||
|
rafScheduled = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const currentContent = newAssistantThreadContent(
|
const currentContent = newAssistantThreadContent(
|
||||||
activeThread.id,
|
activeThread.id,
|
||||||
accumulatedText,
|
accumulatedText,
|
||||||
@ -367,41 +373,63 @@ export const useChat = () => {
|
|||||||
pendingDeltaCount = 0
|
pendingDeltaCount = 0
|
||||||
rafScheduled = false
|
rafScheduled = false
|
||||||
}
|
}
|
||||||
for await (const part of completion) {
|
try {
|
||||||
// Error message
|
for await (const part of completion) {
|
||||||
if (!part.choices) {
|
// Check if aborted before processing each part
|
||||||
throw new Error(
|
if (abortController.signal.aborted) {
|
||||||
'message' in part
|
break
|
||||||
? (part.message as string)
|
}
|
||||||
: (JSON.stringify(part) ?? '')
|
|
||||||
)
|
// Error message
|
||||||
|
if (!part.choices) {
|
||||||
|
throw new Error(
|
||||||
|
'message' in part
|
||||||
|
? (part.message as string)
|
||||||
|
: (JSON.stringify(part) ?? '')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part.choices[0]?.delta?.tool_calls) {
|
||||||
|
extractToolCall(part, currentCall, toolCalls)
|
||||||
|
// Schedule a flush to reflect tool update
|
||||||
|
scheduleFlush()
|
||||||
|
}
|
||||||
|
const deltaReasoning =
|
||||||
|
reasoningProcessor.processReasoningChunk(part)
|
||||||
|
if (deltaReasoning) {
|
||||||
|
accumulatedText += deltaReasoning
|
||||||
|
pendingDeltaCount += 1
|
||||||
|
// Schedule flush for reasoning updates
|
||||||
|
scheduleFlush()
|
||||||
|
}
|
||||||
|
const deltaContent = part.choices[0]?.delta?.content || ''
|
||||||
|
if (deltaContent) {
|
||||||
|
accumulatedText += deltaContent
|
||||||
|
pendingDeltaCount += 1
|
||||||
|
// Batch UI update on next animation frame
|
||||||
|
scheduleFlush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// Always clean up scheduled RAF when stream ends (either normally or via abort)
|
||||||
|
if (rafHandle !== undefined) {
|
||||||
|
if (typeof cancelAnimationFrame !== 'undefined') {
|
||||||
|
cancelAnimationFrame(rafHandle)
|
||||||
|
} else {
|
||||||
|
clearTimeout(rafHandle)
|
||||||
|
}
|
||||||
|
rafHandle = undefined
|
||||||
|
rafScheduled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (part.choices[0]?.delta?.tool_calls) {
|
// Only finalize and flush if not aborted
|
||||||
extractToolCall(part, currentCall, toolCalls)
|
if (!abortController.signal.aborted) {
|
||||||
// Schedule a flush to reflect tool update
|
// Finalize reasoning (close any open think tags)
|
||||||
scheduleFlush()
|
accumulatedText += reasoningProcessor.finalize()
|
||||||
}
|
// Ensure any pending buffered content is rendered at the end
|
||||||
const deltaReasoning =
|
flushIfPending()
|
||||||
reasoningProcessor.processReasoningChunk(part)
|
|
||||||
if (deltaReasoning) {
|
|
||||||
accumulatedText += deltaReasoning
|
|
||||||
pendingDeltaCount += 1
|
|
||||||
// Schedule flush for reasoning updates
|
|
||||||
scheduleFlush()
|
|
||||||
}
|
|
||||||
const deltaContent = part.choices[0]?.delta?.content || ''
|
|
||||||
if (deltaContent) {
|
|
||||||
accumulatedText += deltaContent
|
|
||||||
pendingDeltaCount += 1
|
|
||||||
// Batch UI update on next animation frame
|
|
||||||
scheduleFlush()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Finalize reasoning (close any open think tags)
|
|
||||||
accumulatedText += reasoningProcessor.finalize()
|
|
||||||
// Ensure any pending buffered content is rendered at the end
|
|
||||||
flushIfPending()
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user