diff --git a/todolist.md b/todolist.md index 81a035e..ab6690b 100644 --- a/todolist.md +++ b/todolist.md @@ -28,4 +28,7 @@ sk-2546da09b6d9471894aeb95278f96c11 4. MCP 功能叠加。 ✅ +5. 优化消息交互。比如标题\内容超长怎么处理❓ + +6. 上下文的问题?上下文由谁维护❓以及如何维护❓ diff --git a/web/components.d.ts b/web/components.d.ts index f38510f..8b5ffde 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -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'] diff --git a/web/src/SimpleApp.vue b/web/src/SimpleApp.vue index 1ccbac7..1789011 100644 --- a/web/src/SimpleApp.vue +++ b/web/src/SimpleApp.vue @@ -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' } } }) diff --git a/web/src/components/Chat/ChatLayout.vue b/web/src/components/Chat/ChatLayout.vue index 930a696..8748467 100644 --- a/web/src/components/Chat/ChatLayout.vue +++ b/web/src/components/Chat/ChatLayout.vue @@ -78,19 +78,51 @@ {{ msg.error }} -
diff --git a/web/src/services/chatService.ts b/web/src/services/chatService.ts index 15f0f9e..8a3068a 100644 --- a/web/src/services/chatService.ts +++ b/web/src/services/chatService.ts @@ -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)