Skip to content

Commit 2d2f91a

Browse files
committed
Ensure continuous generating state during chat/tool call transitions
1 parent a3b03f1 commit 2d2f91a

File tree

2 files changed

+38
-20
lines changed

2 files changed

+38
-20
lines changed

src/renderer/composables/chatCompletions.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ interface ChatRequestBody {
2828
tools?: Tool[]
2929
}
3030

31+
export type ChatProcessResult = 'aborted' | 'error' | 'done'
32+
3133
type RequestMessageType = ChatCompletionRequestMessage | McpSamplingMessage
3234

3335
const THINK_OPEN = '<think>'
@@ -113,7 +115,7 @@ export const createCompletion = async (
113115
rawconversation: RequestMessageType[],
114116
sessionId: string,
115117
sampling: any = null
116-
) => {
118+
): Promise<ChatProcessResult> => {
117119
const snackbarStore = useSnackbarStore()
118120

119121
const messageStore = useMessageStore()
@@ -250,7 +252,7 @@ export const createCompletion = async (
250252
}
251253
} finally {
252254
snackbarStore.showErrorMessage(errMessage)
253-
return
255+
return 'error'
254256
}
255257
}
256258

@@ -262,7 +264,7 @@ export const createCompletion = async (
262264
const reader = completion.body?.getReader()
263265
if (!reader) {
264266
snackbarStore.showErrorMessage('snackbar.parse-stream-fail')
265-
return
267+
return 'error'
266268
}
267269

268270
// Add the bot message
@@ -284,10 +286,8 @@ export const createCompletion = async (
284286
} catch (error: any) {
285287
snackbarStore.showErrorMessage(error?.message)
286288
} finally {
287-
if (sessionId in messageStore.generating) {
288-
messageStore.generating[sessionId].abort()
289-
delete messageStore.generating[sessionId]
290-
}
289+
const result = messageStore.delete(sessionId) ? 'done' : 'aborted'
290+
return result
291291
}
292292
}
293293

@@ -310,10 +310,10 @@ const read = async (
310310

311311
// If the stream is done reading, release the lock on the reader
312312
if (done) {
313-
if (sessionId in messageStore.generating) {
314-
messageStore.generating[sessionId].abort()
315-
delete messageStore.generating[sessionId]
316-
}
313+
// if (sessionId in messageStore.generating) {
314+
// messageStore.generating[sessionId].abort()
315+
// delete messageStore.generating[sessionId]
316+
// }
317317
return reader.releaseLock()
318318
}
319319

src/renderer/store/message.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import { defineStore } from 'pinia'
22
import { useSnackbarStore } from '@/renderer/store/snackbar'
33
import { useMcpStore } from '@/renderer/store/mcp'
44
import { useHistoryStore } from '@/renderer/store/history'
5-
import { createCompletion, isEmptyTools } from '@/renderer/composables/chatCompletions'
5+
import {
6+
createCompletion,
7+
isEmptyTools,
8+
ChatProcessResult
9+
} from '@/renderer/composables/chatCompletions'
610

711
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'
812

@@ -15,7 +19,7 @@ interface MessageStoreState {
1519
conversation: ChatConversationMessage[]
1620
historyId: string
1721
base64: string
18-
generating: Record<string, AbortController>
22+
generating: Record<string, 'prepare' | AbortController | 'toolcall'>
1923
}
2024

2125
export const useMessageStore = defineStore('messageStore', {
@@ -41,12 +45,21 @@ export const useMessageStore = defineStore('messageStore', {
4145
this.historyId = ''
4246
},
4347
stop() {
44-
const id = this.historyId
48+
this.delete(this.historyId)
4549
const snackbarStore = useSnackbarStore()
46-
this.generating[id].abort()
47-
delete this.generating[id]
4850
snackbarStore.showInfoMessage('snackbar.stopped')
4951
},
52+
delete(id: string) {
53+
if (id in this.generating) {
54+
if (this.generating[id] instanceof AbortController) {
55+
this.generating[id].abort()
56+
}
57+
delete this.generating[id]
58+
return true
59+
} else {
60+
return false
61+
}
62+
},
5063
clear() {
5164
this.userMessage = ''
5265
},
@@ -120,13 +133,17 @@ export const useMessageStore = defineStore('messageStore', {
120133
const historyId = this.syncHistory()
121134
this.clear()
122135

123-
createCompletion(this.conversation, historyId).then(() => {
124-
this.postToolCall()
136+
this.generating[historyId] = 'prepare'
137+
138+
createCompletion(this.conversation, historyId).then((reason: ChatProcessResult) => {
139+
if (reason === 'done') {
140+
this.postToolCall(historyId)
141+
}
125142
})
126143

127144
return historyId
128145
},
129-
postToolCall: async function () {
146+
postToolCall: async function (historyId: string) {
130147
const mcpStore = useMcpStore()
131148
const last = this.conversation.at(-1)
132149

@@ -141,6 +158,7 @@ export const useMessageStore = defineStore('messageStore', {
141158

142159
let toolCalled = false
143160
console.log(last.tool_calls)
161+
this.generating[historyId] = 'toolcall'
144162

145163
for (const toolCall of last.tool_calls) {
146164
let result: CallToolResult
@@ -160,7 +178,7 @@ export const useMessageStore = defineStore('messageStore', {
160178
}
161179
}
162180

163-
if (toolCalled) {
181+
if (this.delete(historyId) && toolCalled) {
164182
this.startInference()
165183
}
166184
},

0 commit comments

Comments
 (0)