Skip to content

Commit 8c38283

Browse files
feat(tests): add complex test data generation and insertion scripts
- Introduced a new SQL script for generating complex test data for conversation reviews, including multiple messages and decision points. - Added a JavaScript script to insert the generated test data into the database, ensuring proper setup for testing decision tree functionalities. - Enhanced the review service to accommodate larger input sizes and improved data handling for audio records associated with conversation nodes. - Updated the preload and IPC handlers to support new audio data functionalities, including fetching and deleting audio files.
1 parent 085f3c3 commit 8c38283

11 files changed

Lines changed: 2141 additions & 580 deletions

File tree

desktop/scripts/complex-test-data.sql

Lines changed: 476 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* 脚本:生成超长且复杂的测试对话 SQL
5+
* 生成 200+ 条消息,并带有多条决策路径
6+
*/
7+
8+
import path from 'path';
9+
import fs from 'fs';
10+
import { fileURLToPath } from 'url';
11+
12+
const __filename = fileURLToPath(import.meta.url);
13+
const __dirname = path.dirname(__filename);
14+
15+
// --- 配置 ---
16+
const CHAR_ID = 'complex-test-girl';
17+
const CONV_ID = 'conv-complex-' + Date.now();
18+
const START_TIME = Date.now() - 3600000 * 2; // 2小时前开始
19+
20+
const sqlStatements = [];
21+
sqlStatements.push(`-- Test Data for Conversation Review`);
22+
sqlStatements.push(`PRAGMA foreign_keys = ON;`);
23+
24+
// 1. 创建测试角色
25+
sqlStatements.push(`INSERT OR IGNORE INTO characters (id, name, nickname, relationship_label, avatar_color, affinity, created_at, updated_at)
26+
VALUES ('${CHAR_ID}', '林舒涵', '舒涵', '青梅竹马', '#ff85c0', 65, ${Date.now()}, ${Date.now()});`);
27+
28+
// 2. 创建会话
29+
sqlStatements.push(`INSERT INTO conversations (id, character_id, title, date, created_at, updated_at)
30+
VALUES ('${CONV_ID}', '${CHAR_ID}', '关于未来的深夜长谈', ${START_TIME}, ${Date.now()}, ${Date.now()});`);
31+
32+
// 3. 生成 200+ 条消息
33+
const messages = [];
34+
let currentTs = START_TIME;
35+
36+
const addMsg = (sender, content) => {
37+
currentTs += Math.floor(Math.random() * 15000) + 5000;
38+
const id = `msg-${CONV_ID}-${messages.length}`;
39+
const escContent = content.replace(/'/g, "''");
40+
sqlStatements.push(`INSERT INTO messages (id, conversation_id, sender, content, timestamp, is_ai_generated)
41+
VALUES ('${id}', '${CONV_ID}', '${sender}', '${escContent}', ${currentTs}, ${sender === 'user' ? 0 : 1});`);
42+
messages.push({ id });
43+
return id;
44+
};
45+
46+
// --- 第一阶段:轻松闲聊 (0-40) ---
47+
addMsg('character', '在忙吗?');
48+
addMsg('user', '刚忙完,正打算刷会儿手机,怎么啦?');
49+
addMsg('character', '没什么,今天去吃了你说的那家甜品店,草莓大福真的超好吃!');
50+
addMsg('user', '哈哈我就说吧,那家店是老字号了。');
51+
for (let i = 0; i < 36; i++) {
52+
const contents = [
53+
['character', '明天天气好像不太好呢。'],
54+
['user', '是吗?我看预报说是多云。'],
55+
['character', '但我刚才看又有雷阵雨预警了。'],
56+
['user', '那出门得记得带伞。'],
57+
['character', '嗯我知道啦。']
58+
];
59+
addMsg(...contents[i % contents.length]);
60+
}
61+
62+
// --- 第二阶段:怀旧回忆 (41-80) ---
63+
addMsg('character', '对了,你还记得咱们大二那年去洱海骑行吗?');
64+
addMsg('user', '当然记得,那天我晒得跟黑炭一样。');
65+
addMsg('character', '你还好意思说,我那天可是提醒过你要涂防晒的。');
66+
for (let i = 2; i < 38; i++) {
67+
const contents = [
68+
['character', '那时候咱们真是有精力,骑了大半个洱海。'],
69+
['user', '现在的我估计骑五公里就要求饶了。'],
70+
['character', '其实我偶尔还会翻出那时候的照片看,大家都好青涩。'],
71+
['user', '那时候还没这么多烦心事,每天就想着晚上去哪儿吃。']
72+
];
73+
const item = contents[i % contents.length];
74+
addMsg(item[0], item[1] + ` (${i})`);
75+
}
76+
77+
// 插入决策点 1
78+
const dp1Anchor = addMsg('character', '如果我们现在还能一起再去一次,你觉得会和以前感觉一样吗?');
79+
const dp1Id = `dp-${CONV_ID}-1`;
80+
const batch1Id = `batch-${CONV_ID}-1`;
81+
sqlStatements.push(`INSERT INTO decision_points (id, conversation_id, anchor_message_id, created_at) VALUES ('${dp1Id}', '${CONV_ID}', '${dp1Anchor}', ${currentTs + 100});`);
82+
sqlStatements.push(`INSERT INTO suggestion_batches (id, decision_point_id, trigger, reason, created_at) VALUES ('${batch1Id}', '${dp1Id}', 'manual', 'user_silence', ${currentTs + 200});`);
83+
sqlStatements.push(`INSERT INTO ai_suggestions (id, conversation_id, decision_point_id, batch_id, suggestion_index, title, content, affinity_prediction, created_at) VALUES ('sugg-${CONV_ID}-1A', '${CONV_ID}', '${dp1Id}', '${batch1Id}', 0, '怀旧浪漫', '肯定不一样啊,毕竟现在的我,比那时候更珍惜和你在一起的时间了。', 5, ${currentTs + 300});`);
84+
sqlStatements.push(`INSERT INTO ai_suggestions (id, conversation_id, decision_point_id, batch_id, suggestion_index, title, content, affinity_prediction, created_at) VALUES ('sugg-${CONV_ID}-1B', '${CONV_ID}', '${dp1Id}', '${batch1Id}', 1, '幽默回应', '感觉肯定不一样,这次我得租个电动车,坚决不脚踩了!', 2, ${currentTs + 300});`);
85+
86+
addMsg('user', '肯定不一样啊,毕竟现在的我,比那时候更珍惜和你在一起的时间了。');
87+
88+
// --- 第三阶段:深沉话题 (81-120) ---
89+
addMsg('character', '突然这么感性,我都不知道该怎么接了……');
90+
addMsg('character', '不过说真的,最近我一直在想,现在的这份工作真的适合我吗?');
91+
for (let i = 0; i < 38; i++) {
92+
const contents = [
93+
['user', '怎么突然想这个了?压力太大了吗?'],
94+
['character', '倒也不是压力,就是觉得每天都在重复,找不到成就感。'],
95+
['user', '职场倦怠其实挺普遍的。'],
96+
['character', '但我怕自己一直在这个舒适圈呆下去会废掉。']
97+
];
98+
const item = contents[i % contents.length];
99+
addMsg(item[0], item[1] + ` (${i})`);
100+
}
101+
102+
// 插入决策点 2
103+
const dp2Anchor = addMsg('character', '你说,我是不是该鼓起勇气去试试那个新项目的机会?');
104+
const dp2Id = `dp-${CONV_ID}-2`;
105+
const batch2Id = `batch-${CONV_ID}-2`;
106+
sqlStatements.push(`INSERT INTO decision_points (id, conversation_id, anchor_message_id, created_at) VALUES ('${dp2Id}', '${CONV_ID}', '${dp2Anchor}', ${currentTs + 100});`);
107+
sqlStatements.push(`INSERT INTO suggestion_batches (id, decision_point_id, trigger, reason, created_at) VALUES ('${batch2Id}', '${dp2Id}', 'passive', 'topic_switch', ${currentTs + 200});`);
108+
sqlStatements.push(`INSERT INTO ai_suggestions (id, conversation_id, decision_point_id, batch_id, suggestion_index, title, content, affinity_prediction, created_at) VALUES ('sugg-${CONV_ID}-2A', '${CONV_ID}', '${dp2Id}', '${batch2Id}', 0, '理性支持', '如果那个项目对你的长期规划有帮助,确实值得一试。', 3, ${currentTs + 300});`);
109+
sqlStatements.push(`INSERT INTO ai_suggestions (id, conversation_id, decision_point_id, batch_id, suggestion_index, title, content, affinity_prediction, created_at) VALUES ('sugg-${CONV_ID}-2B', '${CONV_ID}', '${dp2Id}', '${batch2Id}', 1, '共情鼓励', '无论你做什么决定,我都会支持你的。想试就去试吧,别让自己遗憾。', 6, ${currentTs + 300});`);
110+
111+
addMsg('user', '我觉得现在的生活节奏也挺好的,没必要把自己搞得那么累吧?');
112+
113+
// --- 第四阶段:观点冲突 (121-160) ---
114+
addMsg('character', '可是我想变得更好啊,你是在质疑我的上进心吗?');
115+
addMsg('user', '我不是那个意思,只是觉得健康和心情更重要。');
116+
for (let i = 0; i < 38; i++) {
117+
const contents = [
118+
['character', '但你总是试图在我想拼一把的时候泼冷水。'],
119+
['user', '我只是不想看你每天只睡五小时。'],
120+
['character', '那是我的选择,我觉得值得。'],
121+
['user', '好吧,既然你这么坚持,我也没什么好说的了。']
122+
];
123+
const item = contents[i % contents.length];
124+
addMsg(item[0], item[1] + ` (${i})`);
125+
}
126+
127+
const dp3Anchor = addMsg('character', '算了,感觉咱们讨论这个话题只会吵架,早点睡吧。');
128+
const dp3Id = `dp-${CONV_ID}-3`;
129+
const batch3Id = `batch-${CONV_ID}-3`;
130+
sqlStatements.push(`INSERT INTO decision_points (id, conversation_id, anchor_message_id, created_at) VALUES ('${dp3Id}', '${CONV_ID}', '${dp3Anchor}', ${currentTs + 100});`);
131+
sqlStatements.push(`INSERT INTO suggestion_batches (id, decision_point_id, trigger, reason, created_at) VALUES ('${batch3Id}', '${dp3Id}', 'manual', 'conflict_detected', ${currentTs + 200});`);
132+
sqlStatements.push(`INSERT INTO ai_suggestions (id, conversation_id, decision_point_id, batch_id, suggestion_index, title, content, affinity_prediction, created_at) VALUES ('sugg-${CONV_ID}-3A', '${CONV_ID}', '${dp3Id}', '${batch3Id}', 0, '道歉服软', '对不起,我刚才说话语气可能重了点,其实我是担心你。', 8, ${currentTs + 300});`);
133+
sqlStatements.push(`INSERT INTO ai_suggestions (id, conversation_id, decision_point_id, batch_id, suggestion_index, title, content, affinity_prediction, created_at) VALUES ('sugg-${CONV_ID}-3B', '${CONV_ID}', '${dp3Id}', '${batch3Id}', 1, '冷静结束', '行吧,那确实都累了,早点休息。', -2, ${currentTs + 300});`);
134+
135+
addMsg('user', '对不起舒涵,我刚才说话语气太生硬了,其实我只是看你最近压力大,很心疼你。');
136+
137+
// --- 第五阶段:和解 (161-200) ---
138+
addMsg('character', '……我也知道你是关心我,刚才我也有点激动了。');
139+
for (let i = 0; i < 38; i++) {
140+
const contents = [
141+
['user', '抱歉哈,其实你的上进心一直是我最佩服你的地方。'],
142+
['character', '真的吗?我总觉得自己做不到最好。'],
143+
['user', '那是你对自己要求太高了。'],
144+
['character', '听到你这么说,我感觉心情好多了。']
145+
];
146+
const item = contents[i % contents.length];
147+
addMsg(item[0], item[1] + ` (${i})`);
148+
}
149+
150+
const dp4Anchor = addMsg('character', '下个周末你有空吗?我请你吃饭,就当是赔罪啦。');
151+
const dp4Id = `dp-${CONV_ID}-4`;
152+
const batch4Id = `batch-${CONV_ID}-4`;
153+
sqlStatements.push(`INSERT INTO decision_points (id, conversation_id, anchor_message_id, created_at) VALUES ('${dp4Id}', '${CONV_ID}', '${dp4Anchor}', ${currentTs + 100});`);
154+
sqlStatements.push(`INSERT INTO suggestion_batches (id, decision_point_id, trigger, reason, created_at) VALUES ('${batch4Id}', '${dp4Id}', 'passive', 'positive_vibe', ${currentTs + 200});`);
155+
sqlStatements.push(`INSERT INTO ai_suggestions (id, conversation_id, decision_point_id, batch_id, suggestion_index, title, content, affinity_prediction, created_at) VALUES ('sugg-${CONV_ID}-4A', '${CONV_ID}', '${dp4Id}', '${batch4Id}', 0, '欣然接受', '好啊,那我也要吃草莓大福!', 4, ${currentTs + 300});`);
156+
sqlStatements.push(`INSERT INTO ai_suggestions (id, conversation_id, decision_point_id, batch_id, suggestion_index, title, content, affinity_prediction, created_at) VALUES ('sugg-${CONV_ID}-4B', '${CONV_ID}', '${dp4Id}', '${batch4Id}', 1, '调皮调侃', '赔罪可不够,得三顿起步。', 3, ${currentTs + 300});`);
157+
158+
addMsg('user', '好啊,那我也要吃草莓大福!');
159+
160+
addMsg('character', '没问题!管够!');
161+
for (let i = 0; i < 20; i++) {
162+
const contents = [
163+
['user', '那早点睡吧,都快一点了。'],
164+
['character', '嗯确实不早了,晚安。'],
165+
['user', '晚安,好梦。'],
166+
['character', '你也是,梦里见~']
167+
];
168+
const item = contents[i % contents.length];
169+
addMsg(item[0], item[1] + ` (${i})`);
170+
}
171+
172+
const outputPath = path.join(__dirname, 'complex-test-data.sql');
173+
fs.writeFileSync(outputPath, sqlStatements.join('\n'));
174+
console.log(`SQL file generated at: ${outputPath}`);
175+
console.log(`Conversation ID: ${CONV_ID}`);
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* 脚本:插入测试复盘数据到数据库
5+
* 用于测试决策树展示效果
6+
*/
7+
8+
import path from 'path';
9+
import fs from 'fs';
10+
import { fileURLToPath } from 'url';
11+
import Database from 'better-sqlite3';
12+
13+
const __filename = fileURLToPath(import.meta.url);
14+
const __dirname = path.dirname(__filename);
15+
16+
// 数据库路径(与主应用一致)
17+
const dbPath = path.join(
18+
process.env.HOME || process.env.USERPROFILE,
19+
'Library/Application Support/livegalgame-desktop/livegalgame.db'
20+
);
21+
22+
console.log('数据库路径:', dbPath);
23+
24+
if (!fs.existsSync(dbPath)) {
25+
console.error('错误: 数据库文件不存在:', dbPath);
26+
console.error('请先启动应用以创建数据库');
27+
process.exit(1);
28+
}
29+
30+
const db = new Database(dbPath);
31+
db.pragma('foreign_keys = ON');
32+
33+
console.log('连接数据库成功');
34+
35+
// 读取 seed.sql 中的测试数据部分
36+
const seedPath = path.join(__dirname, '../src/db/seed.sql');
37+
const seedSQL = fs.readFileSync(seedPath, 'utf-8');
38+
39+
// 提取测试数据部分(从 "=== 复盘功能测试对话 ===" 开始)
40+
const testDataStart = seedSQL.indexOf('-- === 复盘功能测试对话 ===');
41+
if (testDataStart === -1) {
42+
console.error('错误: 在 seed.sql 中未找到测试数据');
43+
process.exit(1);
44+
}
45+
46+
const testDataSQL = seedSQL.substring(testDataStart);
47+
48+
// 分割 SQL 语句
49+
const statements = testDataSQL
50+
.split(';')
51+
.map(stmt => stmt.trim())
52+
.filter(stmt => {
53+
// 移除注释行
54+
const lines = stmt.split('\n');
55+
const cleanedLines = lines
56+
.map(line => {
57+
const commentIndex = line.indexOf('--');
58+
if (commentIndex >= 0) {
59+
return line.substring(0, commentIndex).trim();
60+
}
61+
return line.trim();
62+
})
63+
.filter(line => line.length > 0);
64+
65+
return cleanedLines.join(' ').length > 0;
66+
})
67+
.map(stmt => {
68+
// 移除行内注释
69+
const lines = stmt.split('\n');
70+
return lines
71+
.map(line => {
72+
const commentIndex = line.indexOf('--');
73+
if (commentIndex >= 0) {
74+
return line.substring(0, commentIndex).trim();
75+
}
76+
return line.trim();
77+
})
78+
.filter(line => line.length > 0)
79+
.join(' ');
80+
})
81+
.filter(stmt => stmt.length > 0);
82+
83+
console.log(`找到 ${statements.length} 条 SQL 语句`);
84+
85+
// 执行插入
86+
const transaction = db.transaction(() => {
87+
let successCount = 0;
88+
let skipCount = 0;
89+
90+
for (const statement of statements) {
91+
try {
92+
const result = db.exec(statement);
93+
successCount++;
94+
console.log(`✓ 执行成功: ${statement.substring(0, 50)}...`);
95+
} catch (err) {
96+
// INSERT OR IGNORE 如果数据已存在会报错,这是正常的
97+
if (err.message.includes('UNIQUE constraint') || err.message.includes('already exists')) {
98+
skipCount++;
99+
console.log(`⊘ 跳过(已存在): ${statement.substring(0, 50)}...`);
100+
} else {
101+
console.error(`✗ 执行失败:`, err.message);
102+
console.error(` SQL: ${statement.substring(0, 100)}...`);
103+
}
104+
}
105+
}
106+
107+
console.log(`\n完成:`);
108+
console.log(` 成功插入: ${successCount} 条`);
109+
console.log(` 跳过(已存在): ${skipCount} 条`);
110+
});
111+
112+
transaction();
113+
114+
db.close();
115+
console.log('\n数据库连接已关闭');
116+

desktop/src/core/modules/ipc-handlers/asr-audio-handlers.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,46 @@ export function registerASRAudioHandlers({
215215
}
216216
});
217217

218+
ipcMain.handle('asr-get-audio-data-url', async (event, filePath) => {
219+
try {
220+
if (!filePath) return null;
221+
const fs = await import('fs/promises');
222+
const buffer = await fs.readFile(filePath);
223+
const base64 = buffer.toString('base64');
224+
// Assume WAV for simplicity, or detect from extension
225+
const ext = filePath.split('.').pop().toLowerCase();
226+
const mimeType = ext === 'webm' ? 'audio/webm' : 'audio/wav';
227+
return `data:${mimeType};base64,${base64}`;
228+
} catch (error) {
229+
console.error('Error reading audio file:', error);
230+
return null;
231+
}
232+
});
233+
234+
ipcMain.handle('asr-delete-audio-file', async (event, { recordId, filePath }) => {
235+
try {
236+
const asrManager = getOrCreateASRManager();
237+
238+
// Delete physical file
239+
if (filePath) {
240+
const fs = await import('fs/promises');
241+
await fs.unlink(filePath).catch(err => {
242+
console.warn('Physical file already gone or could not be deleted:', err);
243+
});
244+
}
245+
246+
// Update database
247+
if (recordId) {
248+
db.deleteSpeechRecordAudio(recordId);
249+
}
250+
251+
return { success: true };
252+
} catch (error) {
253+
console.error('Error deleting audio file:', error);
254+
return { success: false, error: error.message };
255+
}
256+
});
257+
218258
console.log('ASR Audio IPC handlers registered');
219259
}
220260

desktop/src/core/modules/llm-suggestion-service.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ export default class LLMSuggestionService {
136136
model: modelName,
137137
temperature: trigger === 'manual' ? 0.8 : 0.6,
138138
max_tokens: 4096,
139+
reasoning_effort: "disabled", // 禁用推理(建议生成不需要推理)
139140
stream: true,
140141
messages: [
141142
{
@@ -765,6 +766,7 @@ export default class LLMSuggestionService {
765766
model: modelName,
766767
temperature: 0,
767768
max_tokens: 120,
769+
reasoning_effort: "disabled", // 禁用推理(情境检测不需要推理)
768770
stream: true,
769771
messages: [
770772
{

0 commit comments

Comments
 (0)