feat: 对话缓存机制

This commit is contained in:
2026-04-02 14:50:20 +08:00
parent 59a9046333
commit 04611c60d8
6 changed files with 67 additions and 211 deletions

View File

@@ -5,6 +5,7 @@ import { ref, computed } from 'vue'
import { removeLocal, setLocal } from '@/utils/local'
import MyEvent from '@/utils/myEvent'
// Agent 项目初始数据 store
type InitialProjectData = {
text: string
@@ -12,13 +13,13 @@ type InitialProjectData = {
type: string
area: string
style: string
useReport: boolean
needSuggestion: boolean
useReport:boolean
needSuggestion:boolean
quoteList: Array<string>
tempImages: any[]
}
export const useAgentStore = defineStore('agent', () => {
const initialProjectData = ref<InitialProjectData | null>(null)
const initialProjectData = ref<InitialProjectData | null>(null)
// 保存项目初始数据
const setInitialProjectData = (data: InitialProjectData) => {
@@ -33,94 +34,10 @@ export const useAgentStore = defineStore('agent', () => {
initialProjectData.value = null
}
// 会话缓存管理,最多保存 10 条会话
const conversationCache = new Map<string, any>()
const MAX_CACHE_ENTRIES = 10
const SESSION_KEY_PREFIX = 'agent_session_'
const pruneCache = () => {
const keys = Array.from(conversationCache.keys())
const total = conversationCache.size
if (total <= MAX_CACHE_ENTRIES) return
for (let i = 0; i < total - MAX_CACHE_ENTRIES; i++) {
conversationCache.delete(keys[i])
sessionStorage.removeItem(SESSION_KEY_PREFIX + keys[i])
}
}
const saveCacheConversation = (projectId: string, data: any) => {
if (!projectId) return
const finalData = {
...data,
updatedAt: Date.now()
}
if (conversationCache.has(projectId)) {
conversationCache.delete(projectId)
}
conversationCache.set(projectId, finalData)
try {
sessionStorage.setItem(SESSION_KEY_PREFIX + projectId, JSON.stringify(finalData))
} catch (e) {
console.warn('saveCacheConversation sessionStorage failed', e)
}
pruneCache()
}
const getCacheConversation = (projectId: string) => {
if (!projectId) return null
let data = conversationCache.get(projectId)
if (data) return data
try {
const raw = sessionStorage.getItem(SESSION_KEY_PREFIX + projectId)
if (!raw) return null
data = JSON.parse(raw)
conversationCache.set(projectId, data)
return data
} catch (e) {
console.warn('getCacheConversation failed', e)
return null
}
}
const clearAllCacheConversations = () => {
conversationCache.value.clear()
for (const key in window.sessionStorage) {
if (key.startsWith(SESSION_KEY_PREFIX)) {
sessionStorage.removeItem(key)
}
}
}
const removeCacheConversation = (projectId: string) => {
if (!projectId) return
conversationCache.value.delete(projectId)
sessionStorage.removeItem(SESSION_KEY_PREFIX + projectId)
}
const getCacheStats = () => {
return {
total: conversationCache.value.size,
keys: Array.from(conversationCache.value.keys())
}
}
return {
initialProjectData,
setInitialProjectData,
getInitialProjectData,
clearInitialProjectData,
conversationCache,
saveCacheConversation,
getCacheConversation,
removeCacheConversation,
clearAllCacheConversations,
getCacheStats
clearInitialProjectData
}
})

View File

@@ -155,13 +155,13 @@ function addPending(config: any) {
* @param {*} config
*/
function removePending(config: any) {
const pendingKey = getPendingKey(config)
if (pendingMap.has(pendingKey)) {
const cancelToken = pendingMap.get(pendingKey)
cancelToken(pendingKey)
pendingMap.delete(pendingKey)
return true
}
// const pendingKey = getPendingKey(config)
// if (pendingMap.has(pendingKey)) {
// const cancelToken = pendingMap.get(pendingKey)
// cancelToken(pendingKey)
// pendingMap.delete(pendingKey)
// return true
// }
}
// ----------------------------------loading的函数-------------------------------
const LoadingInstance: { _count: number } = {

View File

@@ -8,7 +8,7 @@
<!-- <SvgIcon name="equal" color="#0d0d0d" size="24" /> -->
</div>
<div class="agent-body flex-1 flex flex-col">
<List ref="listRef" :message-list="messageList" @regenerate="handleRegenerate" />
<List ref="listRef" :message-list="messageList" />
<Input
ref="inputRef"
is-agent-mode
@@ -80,55 +80,7 @@
{ deep: true }
)
const saveSession = (projectId: string) => {
if (!projectId) return
agentStore.saveCacheConversation(projectId, {
messageList: messageList.value,
sketchList: sketchList.value,
params: {
...params,
projectID: projectId
},
isGenerating: isGenerating.value,
isPaused: isPaused.value
})
}
const restoreSession = (projectId: string) => {
if (!projectId) return false
const payload = agentStore.getCacheConversation(projectId)
if (!payload) return false
messageList.value = payload.messageList || []
sketchList.value = payload.sketchList || []
if (payload.params) {
params.projectID = payload.params.projectID || projectId
params.message = payload.params.message || ''
params.versionID = payload.params.versionID || ''
params.configParams = payload.params.configParams || params.configParams
params.needSuggestion = payload.params.needSuggestion || false
params.useReport = payload.params.useReport || false
params.imageUrlList = payload.params.imageUrlList || []
params.quotaUrl = payload.params.quotaUrl || []
}
isGenerating.value = payload.isGenerating || false
isPaused.value = payload.isPaused || false
return true
}
const clearSession = (projectId: string) => {
if (!projectId) return
agentStore.removeCacheConversation(projectId)
}
const handleReset = (force = false) => {
if (!force && params.projectID) {
saveSession(params.projectID)
}
if (force && params.projectID) {
clearSession(params.projectID)
}
const handleReset = () => {
messageList.value = []
sketchList.value = []
params.versionID = ''
@@ -142,7 +94,6 @@
style: ''
}
isGenerating.value = false
isPaused.value = false
}
// 每次请求时创建新的 AbortController
@@ -158,10 +109,7 @@
}
onUnmounted(() => {
if (params.projectID) {
saveSession(params.projectID)
}
abort?.abort()
// abort?.abort()
MyEvent.remove('resetAgent', handleReset)
})
@@ -410,7 +358,14 @@
}
if (jsonData.title) {
emits('setTitle', jsonData.title)
MyEvent.emit('newTitle', jsonData.title)
console.log('发送title', {
title: jsonData.title,
id: params.projectID
})
MyEvent.emit('newTitle', {
title: jsonData.title,
id: params.projectID
})
}
if (hasSketch) {
@@ -505,39 +460,36 @@
isPaused.value = true
isGenerating.value = false
abort?.abort()
if (params.projectID) {
saveSession(params.projectID)
}
MyEvent.emit('stopChat')
}
const handleRegenerate = async (aiMessage: any) => {
// 找到当前 AI 消息在列表中的索引
const aiIndex = messageList.value.findIndex((msg) => msg.id === aiMessage.id)
if (aiIndex === -1) return
// const handleRegenerate = async (aiMessage: any) => {
// // 找到当前 AI 消息在列表中的索引
// const aiIndex = messageList.value.findIndex((msg) => msg.id === aiMessage.id)
// if (aiIndex === -1) return
// 找到对应的用户消息AI 消息前面的最近一条用户消息)
let userMessage = null
for (let i = aiIndex - 1; i >= 0; i--) {
if (messageList.value[i].isUser) {
userMessage = messageList.value[i]
break
}
}
if (!userMessage) return
// // 找到对应的用户消息AI 消息前面的最近一条用户消息)
// let userMessage = null
// for (let i = aiIndex - 1; i >= 0; i--) {
// if (messageList.value[i].isUser) {
// userMessage = messageList.value[i]
// break
// }
// }
// if (!userMessage) return
// 删除当前的 AI 回复消息
messageList.value.splice(aiIndex, 1)
// // 删除当前的 AI 回复消息
// messageList.value.splice(aiIndex, 1)
// 重新调用 API跳过用户消息添加因为用户消息已存在
await handleSendMessage(
{
text: userMessage.text,
images: userMessage.images || []
},
true
)
}
// // 重新调用 API跳过用户消息添加因为用户消息已存在
// await handleSendMessage(
// {
// text: userMessage.text,
// images: userMessage.images || []
// },
// true
// )
// }
// 处理对话列表,将连续的 assistant 消息合并为一条
const processDialogue = (dialogue, startIndex, existingImgList, sessionId) => {
@@ -598,7 +550,7 @@
thinkingText: combinedThinkingText,
text: combinedContent,
image_url: combinedImageUrl,
webAddress: webAddress ? JSON.parse(webAddress) : null,
webAddress: !!webAddress ? JSON.parse(webAddress) : null,
isUser: false,
id: result.length + 1,
sessionId: sessionId
@@ -674,7 +626,7 @@
item.text += `<slot slot-name="sketch"></slot>`
}
})
console.log('ancestorslist', ancestorsList)
// console.log('ancestorslist', ancestorsList)
messageList.value = [...ancestorsList]
params.versionID = current?.id
sketchList.value = imgList
@@ -682,10 +634,7 @@
}
defineExpose({
setChatInfo,
saveSession,
restoreSession,
clearSession
setChatInfo
})
</script>

View File

@@ -4,12 +4,15 @@
<div class="btn" @click="versionTreeData.drawer = true">Version Tree</div>
</div>
<div class="content-wrapper">
<Agent
ref="agentRef"
:title="agentTitle"
@update:sketchList="updateSketchList"
@setTitle="handleSetTitle"
/>
<KeepAlive :max="10">
<Agent
:key="proJectId"
ref="agentRef"
:title="agentTitle"
@update:sketchList="updateSketchList"
@setTitle="handleSetTitle"
/>
</KeepAlive>
<div class="preview-wrapper">
<Preview
ref="previewRef"
@@ -131,31 +134,18 @@
watch(
() => proJectId.value,
async (newVal, oldVal) => {
if (oldVal && agentRef.value && typeof agentRef.value.saveSession === 'function') {
agentRef.value.saveSession(oldVal as string)
}
(newVal, oldVal) => {
projectStore.clearProject()
if (newVal) {
let restored = false
if (agentRef.value && typeof agentRef.value.restoreSession === 'function') {
restored = await agentRef.value.restoreSession(newVal as string)
}
if (!restored) {
handleGetProjectInfoAndHistory()
}
MyEvent.emit('projectChange')
} else {
handleGetProjectInfoAndHistory()
MyEvent.emit('projectChange')
// MyEvent.emit('resetAgent')
}
}
)
onMounted(() => {
console.log('11111agentindex')
MyEvent.add('openReport', handleOpenReport)
MyEvent.add('openUrls', handleOpenUrls)
MyEvent.add('openSketch', handleOpenSketch)

View File

@@ -13,7 +13,7 @@
<div class="right-main">
<top-nav />
<div class="bottom-view"><router-view></router-view></div>
</div>
</div>¬
</div>
<setting />
<flow-canvas ref="flowCanvasRef" />

View File

@@ -207,10 +207,10 @@
MyEvent.add('updateProjectList', GetProjectList)
GetProjectList()
const replaceTitle = (title: string) => {
const replaceTitle = (data: { title: string; id: string | number }) => {
list.value.forEach((item: any) => {
if (String(item.id) === String(projectStore.state.id)) {
item.name = title
if (String(item.id) === String(data.id)) {
item.name = data.title
}
})
}