update at 2025-10-15 20:00:59

This commit is contained in:
douboer
2025-10-15 20:00:59 +08:00
parent 911986d7ee
commit 60b71b294d
5 changed files with 121 additions and 23 deletions

View File

@@ -28,4 +28,7 @@ sk-2546da09b6d9471894aeb95278f96c11
4. MCP 功能叠加。
5. 优化消息交互。比如标题\内容超长怎么处理❓
6. 上下文的问题?上下文由谁维护❓以及如何维护❓

1
web/components.d.ts vendored
View File

@@ -28,6 +28,7 @@ declare module 'vue' {
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSpace: typeof import('naive-ui')['NSpace']
NStatistic: typeof import('naive-ui')['NStatistic']
NTooltip: typeof import('naive-ui')['NTooltip']
ProviderForm: typeof import('./src/components/ProviderForm.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']

View File

@@ -291,6 +291,13 @@ const themeOverrides = computed(() => {
primaryColorHover: primaryColor + 'CC',
primaryColorPressed: primaryColor + '99',
primaryColorSuppl: primaryColor
},
Tooltip: {
color: primaryColor + 'F2', // 95% 透明度
textColor: '#ffffff',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
borderRadius: '6px',
padding: '6px 12px'
}
}
})

View File

@@ -78,19 +78,51 @@
{{ msg.error }}
</div>
</div>
<div v-if="msg.role === 'assistant' && (msg.status === 'success' || msg.status === 'paused')" class="message-actions">
<n-button text size="tiny" @click="handleCopyMessage(msg.content)">
<!-- 用户消息操作按钮 -->
<div v-if="msg.role === 'user'" class="message-actions">
<n-tooltip trigger="hover">
<template #trigger>
<n-button text size="tiny" type="primary" @click="handleCopyMessage(msg.content)">
<n-icon :component="CopyIcon" size="14" />
</n-button>
</template>
复制
</n-button>
<n-button text size="tiny" @click="handleRegenerateMessage(msg.id)">
<n-icon :component="RefreshIcon" size="14" />
重新生成
</n-button>
<n-button text size="tiny" @click="handleDeleteMessage(msg.id)">
</n-tooltip>
<n-tooltip trigger="hover">
<template #trigger>
<n-button text size="tiny" type="error" @click="handleDeleteMessage(msg.id)">
<n-icon :component="TrashIcon" size="14" />
删除
</n-button>
</template>
删除
</n-tooltip>
</div>
<!-- AI 消息操作按钮 -->
<div v-if="msg.role === 'assistant' && (msg.status === 'success' || msg.status === 'paused')" class="message-actions">
<n-tooltip trigger="hover">
<template #trigger>
<n-button text size="tiny" type="primary" @click="handleCopyMessage(msg.content)">
<n-icon :component="CopyIcon" size="14" />
</n-button>
</template>
复制
</n-tooltip>
<n-tooltip trigger="hover">
<template #trigger>
<n-button text size="tiny" type="info" @click="handleRegenerateMessage(msg.id)">
<n-icon :component="RefreshIcon" size="14" />
</n-button>
</template>
重新生成
</n-tooltip>
<n-tooltip trigger="hover">
<template #trigger>
<n-button text size="tiny" type="error" @click="handleDeleteMessage(msg.id)">
<n-icon :component="TrashIcon" size="14" />
</n-button>
</template>
删除
</n-tooltip>
</div>
</div>
</div>

View File

@@ -491,10 +491,10 @@ class ChatService {
throw new Error('消息不存在')
}
// 删除该消息之后的所有消息
// 删除该消息之后的所有消息(包括当前要重新生成的助手消息)
conversation.messages.splice(messageIndex)
// 获取最后一条用户消息
// 获取最后一条用户消息(应该就是刚才删除的助手消息的前一条)
let lastUserMessage: Message | undefined
for (let i = conversation.messages.length - 1; i >= 0; i--) {
if (conversation.messages[i].role === 'user') {
@@ -507,12 +507,53 @@ class ChatService {
throw new Error('没有找到用户消息')
}
// 重新发送
return await this.sendMessage({
topicId,
content: lastUserMessage.content,
// 创建新的助手消息(不要重复添加用户消息!)
const assistantMessage: Message = {
id: this.generateId(),
role: 'assistant',
content: '',
status: 'sending',
timestamp: new Date(),
model: conversation.metadata?.model
})
}
conversation.messages.push(assistantMessage)
conversation.updatedAt = new Date()
this.conversations.set(conversation.id, conversation)
this.saveConversations()
try {
// 调用 AI 模型
const response = await this.callModel(conversation, conversation.metadata?.model)
// 更新助手消息
assistantMessage.content = response.content
assistantMessage.status = 'success'
assistantMessage.tokens = response.tokens
conversation.updatedAt = new Date()
this.conversations.set(conversation.id, conversation)
this.saveConversations()
// 更新话题
const topic = this.topics.get(topicId)
if (topic) {
topic.messageCount = conversation.messages.length
topic.lastMessage = this.getMessagePreview(response.content)
topic.updatedAt = new Date()
this.topics.set(topicId, topic)
this.saveTopics()
}
return assistantMessage
} catch (error) {
assistantMessage.status = 'error'
assistantMessage.error = error instanceof Error ? error.message : '重新生成失败'
conversation.updatedAt = new Date()
this.conversations.set(conversation.id, conversation)
this.saveConversations()
throw error
}
}
// ==================== 私有方法 ====================
@@ -527,15 +568,22 @@ class ChatService {
const callModelStartTime = performance.now()
console.log('⏱️ [callModel] 开始处理', { model, 对话消息数: conversation.messages.length })
// 准备消息历史
// 准备消息历史(包含 success 和 paused 状态的消息paused 的消息也包含有效内容)
const beforePrepare = performance.now()
const messages = conversation.messages
.filter(m => m.status === 'success')
let messages = conversation.messages
.filter(m => m.status === 'success' || m.status === 'paused')
.map(m => ({
role: m.role,
content: m.content
}))
// 限制上下文:只保留最近 20 条消息(约 10 轮对话)
const MAX_CONTEXT_MESSAGES = 20
if (messages.length > MAX_CONTEXT_MESSAGES) {
console.log(`📊 [callModel] 限制上下文: ${messages.length} 条 → ${MAX_CONTEXT_MESSAGES}`)
messages = messages.slice(-MAX_CONTEXT_MESSAGES)
}
const afterPrepare = performance.now()
console.log('⏱️ [callModel] 准备消息耗时:', (afterPrepare - beforePrepare).toFixed(2), 'ms', '处理后消息数:', messages.length)
@@ -634,14 +682,21 @@ class ChatService {
console.log('⚠️ [callModelStream] 未选择 MCP 服务器,不注入工具')
}
// 准备消息历史
// 准备消息历史(包含 success 和 paused 状态的消息paused 的消息也包含有效内容)
let messages = conversation.messages
.filter(m => m.status === 'success')
.filter(m => m.status === 'success' || m.status === 'paused')
.map(m => ({
role: m.role,
content: m.content
}))
// 限制上下文:只保留最近 20 条消息(约 10 轮对话)
const MAX_CONTEXT_MESSAGES = 20
if (messages.length > MAX_CONTEXT_MESSAGES) {
console.log(`📊 [callModelStream] 限制上下文: ${messages.length} 条 → ${MAX_CONTEXT_MESSAGES}`)
messages = messages.slice(-MAX_CONTEXT_MESSAGES)
}
// 如果有工具,添加系统提示词指导 AI 使用工具
if (tools.length > 0 && messages.length > 0 && messages[0].role !== 'system') {
const systemPrompt = this.createSystemPromptWithTools(tools, mcpServerName)