Skip to content

Commit 21368fa

Browse files
committed
fix(conversation): 修复多轮对话历史记录恢复问题并全面优化版本管理
核心修复: - 修复多轮对话模式下 V2 及以上迭代版本无法正确恢复的 bug - 修复消息顺序优化后历史记录恢复时,部分消息错误回溯到原始 (V0) 状态的 bug - 在 iterateMessage 中手动预计算版本号,确保快照中的 appliedVersion 与实际保存的版本号一致 - 重构 useConversationOptimization.ts 中 getMessageAppliedVersion 函数,统一版本语义,使用 record.version 属性而非数组索引进行匹配 - 修正优化快照 (conversationSnapshot) 构建逻辑,确保当前优化消息的 appliedVersion 和 content 始终准确无误 - 改进 conversation optimizer 中的版本匹配机制,使用修剪后的字符串比较防止错误版本不匹配 功能增强: - 新增完整的 iterateMessage 方法,支持多轮对话的消息级迭代优化 - 传递完整上下文数据(messages、selectedMessageId)给底层服务 - 对比测试时分别维护 originalToolCalls 和 optimizedToolCalls,确保工具调用结果互不干扰 - 在 prompt optimizer 历史元数据中添加 conversationSnapshot 以支持准确的状态恢复 - 重构优化流程,optimizeMessage 现在强制重置状态,避免状态污染 UI 优化: - 调整版本标签显示顺序为 v3 v2 v1 v0(最新版本在前,v0 固定在最后) - 优化版本切换交互体验 国际化支持: - 修复 prompt optimizer 中的硬编码中文字符串,替换为 i18n 键 - 为多轮对话优化添加缺失的 i18n 键 代码质量: - 删除所有调试日志(console.log) - 添加模板处理边界情况注释(Mustache 空数组 vs undefined) - 更新 App.vue (Web 和 Extension) 的历史记录恢复逻辑,通过 record.version 精确查找版本,增强了对历史记录删除(导致版本数组稀疏)的健壮性 - 为对话优化功能暂存未跟踪的新文件
1 parent b8cf182 commit 21368fa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+5335
-958
lines changed

docs/workspace/multi-turn-dialogue-mode-design.md

Lines changed: 1462 additions & 0 deletions
Large diffs are not rendered by default.

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export { StaticLoader } from './services/template/static-loader'
2727
export * from './services/template/errors'
2828
export { ElectronTemplateManagerProxy } from './services/template/electron-proxy'
2929
export { ElectronTemplateLanguageServiceProxy } from './services/template/electron-language-proxy'
30+
export { ALL_TEMPLATES } from './services/template/default-templates'
3031

3132
// 导出历史记录相关
3233
export { HistoryManager, createHistoryManager } from './services/history/manager'

packages/core/src/services/favorite/type-mapper.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class TypeMapper {
4747
}
4848

4949
// 上下文模式映射 (context)
50-
if (recordType === 'contextSystemOptimize' || recordType === 'contextIterate') {
50+
if (recordType === 'conversationMessageOptimize' || recordType === 'contextIterate') {
5151
return {
5252
functionMode: 'context',
5353
optimizationMode: 'system'
@@ -161,7 +161,7 @@ export class TypeMapper {
161161
// 上下文模式
162162
if (functionMode === 'context') {
163163
if (optimizationMode === 'system') {
164-
return ['contextSystemOptimize', 'contextIterate'];
164+
return ['conversationMessageOptimize', 'contextIterate'];
165165
}
166166
if (optimizationMode === 'user') {
167167
return ['contextUserOptimize'];

packages/core/src/services/history/types.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,19 @@ import type { OptimizationMode } from '../prompt/types';
33
/**
44
* 提示词记录类型
55
*/
6-
export type PromptRecordType = 'optimize' | 'userOptimize' | 'iterate' | 'test' | 'contextSystemOptimize' | 'contextUserOptimize' | 'contextIterate' | 'imageOptimize' | 'contextImageOptimize' | 'imageIterate' | 'text2imageOptimize' | 'image2imageOptimize';
6+
export type PromptRecordType =
7+
| 'optimize'
8+
| 'userOptimize'
9+
| 'iterate'
10+
| 'test'
11+
| 'contextUserOptimize'
12+
| 'contextIterate'
13+
| 'imageOptimize'
14+
| 'contextImageOptimize'
15+
| 'imageIterate'
16+
| 'text2imageOptimize'
17+
| 'image2imageOptimize'
18+
| 'conversationMessageOptimize';
719

820
/**
921
* 提示词记录接口
@@ -40,6 +52,16 @@ export interface PromptRecord {
4052
/** 元数据 */
4153
metadata?: {
4254
optimizationMode?: OptimizationMode; // 优化模式
55+
messageId?: string; // 被优化消息的 ID
56+
messageRole?: string; // 消息角色
57+
conversationSnapshot?: Array<{ // 会话快照(用于多轮对话优化)
58+
id: string; // 消息 ID
59+
role: string; // 消息角色
60+
content: string; // 消息内容
61+
originalContent?: string; // 原始内容
62+
chainId?: string; // 🆕 该消息使用的优化链 ID
63+
appliedVersion?: number; // 🆕 应用的版本号 (0=v0原始, 1=v1, 2=v2...)
64+
}>;
4365
[key: string]: any; // 保持扩展性
4466
};
4567
}

packages/core/src/services/prompt/electron-proxy.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
IPromptService,
33
OptimizationRequest,
4+
MessageOptimizationRequest,
45
StreamHandlers,
56
CustomConversationRequest,
67
} from './types';
@@ -32,14 +33,27 @@ export class ElectronPromptServiceProxy implements IPromptService {
3233
return this.api.optimizePrompt(safeRequest);
3334
}
3435

36+
async optimizeMessage(request: MessageOptimizationRequest): Promise<string> {
37+
// 自动序列化,防止Vue响应式对象IPC传递错误
38+
const safeRequest = safeSerializeForIPC(request);
39+
return this.api.optimizeMessage(safeRequest);
40+
}
41+
3542
async iteratePrompt(
3643
originalPrompt: string,
3744
lastOptimizedPrompt: string,
3845
iterateInput: string,
3946
modelKey: string,
40-
templateId?: string
47+
templateId?: string,
48+
contextData?: {
49+
messages?: any[];
50+
selectedMessageId?: string;
51+
variables?: Record<string, string>;
52+
tools?: any[];
53+
}
4154
): Promise<string> {
42-
return this.api.iteratePrompt(originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId);
55+
const safeContextData = contextData ? safeSerializeForIPC(contextData) : undefined;
56+
return this.api.iteratePrompt(originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, safeContextData);
4357
}
4458

4559
async testPrompt(
@@ -66,15 +80,28 @@ export class ElectronPromptServiceProxy implements IPromptService {
6680
await this.api.optimizePromptStream(safeRequest, callbacks);
6781
}
6882

83+
async optimizeMessageStream(request: MessageOptimizationRequest, callbacks: StreamHandlers): Promise<void> {
84+
// 自动序列化,防止Vue响应式对象IPC传递错误
85+
const safeRequest = safeSerializeForIPC(request);
86+
await this.api.optimizeMessageStream(safeRequest, callbacks);
87+
}
88+
6989
async iteratePromptStream(
7090
originalPrompt: string,
7191
lastOptimizedPrompt: string,
7292
iterateInput: string,
7393
modelKey: string,
7494
callbacks: StreamHandlers,
75-
templateId?: string
95+
templateId?: string,
96+
contextData?: {
97+
messages?: any[];
98+
selectedMessageId?: string;
99+
variables?: Record<string, string>;
100+
tools?: any[];
101+
}
76102
): Promise<void> {
77-
await this.api.iteratePromptStream(originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, callbacks);
103+
const safeContextData = contextData ? safeSerializeForIPC(contextData) : undefined;
104+
await this.api.iteratePromptStream(originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, callbacks, safeContextData);
78105
}
79106

80107
async testPromptStream(

0 commit comments

Comments
 (0)