更新
This commit is contained in:
+80
-26
@@ -193,36 +193,44 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function sendMessage(content, attachments = [], agentId = selectedAgentId.value) {
|
||||
if (!currentId.value) {
|
||||
await createConversation(selectedModelId.value)
|
||||
} else if (selectedModelId.value != null) {
|
||||
// 发送前确保当前会话已绑定侧边栏所选模型
|
||||
const conv = conversations.value.find(c => c.id === currentId.value)
|
||||
if (conv && Number(conv.model_id) !== Number(selectedModelId.value)) {
|
||||
await setSelectedModel(selectedModelId.value)
|
||||
}
|
||||
}
|
||||
async function sendMessage(content, attachments = [], agentId = selectedAgentId.value, options = {}) {
|
||||
if (sending.value) return
|
||||
|
||||
const userMsg = {
|
||||
id: Date.now(),
|
||||
role: 'user',
|
||||
content,
|
||||
attachments,
|
||||
created_at: new Date().toISOString()
|
||||
}
|
||||
messages.value.push(userMsg)
|
||||
sending.value = true
|
||||
streamingContent.value = ''
|
||||
streamingAttachments.value = []
|
||||
let userMsg = null
|
||||
|
||||
try {
|
||||
await streamChat(content, attachments, agentId)
|
||||
if (!currentId.value) {
|
||||
await createConversation(selectedModelId.value)
|
||||
} else if (selectedModelId.value != null) {
|
||||
// 发送前确保当前会话已绑定侧边栏所选模型
|
||||
const conv = conversations.value.find(c => c.id === currentId.value)
|
||||
if (conv && Number(conv.model_id) !== Number(selectedModelId.value)) {
|
||||
await setSelectedModel(selectedModelId.value)
|
||||
}
|
||||
}
|
||||
|
||||
userMsg = {
|
||||
id: Date.now(),
|
||||
role: 'user',
|
||||
content,
|
||||
attachments,
|
||||
created_at: new Date().toISOString()
|
||||
}
|
||||
messages.value.push(userMsg)
|
||||
|
||||
await streamChat(content, attachments, agentId, options)
|
||||
await loadMessages()
|
||||
} catch (e) {
|
||||
// 连接断开/代理超时:任务可能已落库为 pending,先同步再决定是否抛错
|
||||
messages.value = messages.value.filter(m => m.id !== userMsg.id)
|
||||
const cancelled = e?.name === 'AbortError' || options.signal?.aborted
|
||||
if (userMsg) {
|
||||
messages.value = messages.value.filter(m => m.id !== userMsg.id)
|
||||
}
|
||||
await loadMessages().catch(() => {})
|
||||
if (cancelled) throw e
|
||||
if (hasPendingJobs(messages.value)) {
|
||||
startPendingJobPolling()
|
||||
return
|
||||
@@ -235,7 +243,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function streamChat(content, attachments, agentId = null) {
|
||||
async function streamChat(content, attachments, agentId = null, options = {}) {
|
||||
const token = localStorage.getItem('token')
|
||||
const response = await fetch('/api/chat/completions', {
|
||||
method: 'POST',
|
||||
@@ -243,11 +251,13 @@ export const useChatStore = defineStore('chat', () => {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
signal: options.signal,
|
||||
body: JSON.stringify({
|
||||
conversation_id: currentId.value,
|
||||
content,
|
||||
attachments,
|
||||
agent_id: agentId || null,
|
||||
image_tool: options.imageTool || null,
|
||||
stream: true
|
||||
})
|
||||
})
|
||||
@@ -268,8 +278,47 @@ export const useChatStore = defineStore('chat', () => {
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ''
|
||||
let fullContent = ''
|
||||
let receivedContent = ''
|
||||
let gotAttachments = false
|
||||
let pendingDone = false
|
||||
let agentRenderVersion = 0
|
||||
let agentRenderQueue = Promise.resolve()
|
||||
const isAgentStream = !!agentId
|
||||
|
||||
const appendVisibleContent = (content) => {
|
||||
if (!content) return
|
||||
receivedContent += content
|
||||
|
||||
if (!isAgentStream) {
|
||||
fullContent += content
|
||||
streamingContent.value = fullContent
|
||||
return
|
||||
}
|
||||
|
||||
const version = agentRenderVersion
|
||||
const characters = Array.from(content)
|
||||
const frameCount = Math.min(10, Math.max(2, Math.ceil(characters.length / 8)))
|
||||
const frameSize = Math.ceil(characters.length / frameCount)
|
||||
|
||||
agentRenderQueue = agentRenderQueue.then(async () => {
|
||||
for (let index = 0; index < characters.length; index += frameSize) {
|
||||
if (version !== agentRenderVersion) return
|
||||
fullContent += characters.slice(index, index + frameSize).join('')
|
||||
streamingContent.value = fullContent
|
||||
if (index + frameSize < characters.length) {
|
||||
await new Promise(resolve => setTimeout(resolve, 14))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const replaceVisibleContent = (content) => {
|
||||
agentRenderVersion++
|
||||
agentRenderQueue = Promise.resolve()
|
||||
receivedContent = content
|
||||
fullContent = content
|
||||
streamingContent.value = content
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
@@ -285,6 +334,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
const { event, data } = parseSseBlock(block)
|
||||
|
||||
if (event === 'error') {
|
||||
agentRenderVersion++
|
||||
throw new Error(data?.message || 'AI 请求失败')
|
||||
}
|
||||
|
||||
@@ -298,8 +348,11 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
|
||||
if (event === 'message' && data?.content) {
|
||||
fullContent += data.content
|
||||
streamingContent.value = fullContent
|
||||
appendVisibleContent(data.content)
|
||||
}
|
||||
|
||||
if (event === 'replace' && typeof data?.content === 'string') {
|
||||
replaceVisibleContent(data.content)
|
||||
}
|
||||
|
||||
if (event === 'done') {
|
||||
@@ -309,9 +362,8 @@ export const useChatStore = defineStore('chat', () => {
|
||||
streamingContent.value = data.content
|
||||
}
|
||||
}
|
||||
if (data?.content && !fullContent) {
|
||||
fullContent = data.content
|
||||
streamingContent.value = fullContent
|
||||
if (data?.content && !receivedContent) {
|
||||
appendVisibleContent(data.content)
|
||||
}
|
||||
if (Array.isArray(data?.attachments) && data.attachments.length) {
|
||||
streamingAttachments.value = data.attachments
|
||||
@@ -321,6 +373,8 @@ export const useChatStore = defineStore('chat', () => {
|
||||
}
|
||||
}
|
||||
|
||||
await agentRenderQueue
|
||||
|
||||
if (pendingDone) {
|
||||
await loadMessages().catch(() => {})
|
||||
startPendingJobPolling()
|
||||
|
||||
Reference in New Issue
Block a user