@fivetu53/soul-chat 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/api.js +13 -0
- package/bin/index.js +92 -22
- package/package.json +1 -1
package/bin/api.js
CHANGED
|
@@ -237,6 +237,19 @@ export async function deleteLastMessages(conversationId) {
|
|
|
237
237
|
return data;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
export async function regenerateMessage(conversationId) {
|
|
241
|
+
const response = await request(`/api/chat/conversations/${conversationId}/regenerate`, {
|
|
242
|
+
method: 'POST'
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (!response.ok) {
|
|
246
|
+
const data = await response.json();
|
|
247
|
+
throw new Error(data.error);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return response;
|
|
251
|
+
}
|
|
252
|
+
|
|
240
253
|
// Memory APIs
|
|
241
254
|
export async function getMemories(characterId) {
|
|
242
255
|
const response = await request(`/api/memories/${characterId}`);
|
package/bin/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { loadConfig, saveConfig, getConfigPath } from './config.js';
|
|
|
10
10
|
import { checkAuth, showAuthScreen } from './auth.js';
|
|
11
11
|
import {
|
|
12
12
|
isLoggedIn, getCurrentUser, logout, updateProfile,
|
|
13
|
-
sendMessage, getCharacters, createCharacter, getConversations, getMessages, clearConversation, deleteLastMessages, getMemories, pinMemory, deleteMemory
|
|
13
|
+
sendMessage, getCharacters, createCharacter, getConversations, getMessages, clearConversation, deleteLastMessages, regenerateMessage, getMemories, pinMemory, deleteMemory
|
|
14
14
|
} from './api.js';
|
|
15
15
|
|
|
16
16
|
// Setup marked with terminal renderer
|
|
@@ -462,7 +462,12 @@ async function showChat() {
|
|
|
462
462
|
try {
|
|
463
463
|
const history = await getMessages(currentConversationId);
|
|
464
464
|
history.forEach(m => {
|
|
465
|
-
messages.push({
|
|
465
|
+
messages.push({
|
|
466
|
+
role: m.role === 'user' ? 'user' : 'bot',
|
|
467
|
+
text: m.content,
|
|
468
|
+
id: m.id,
|
|
469
|
+
timestamp: m.created_at ? new Date(m.created_at) : null
|
|
470
|
+
});
|
|
466
471
|
});
|
|
467
472
|
} catch (err) {
|
|
468
473
|
// 忽略
|
|
@@ -485,6 +490,9 @@ async function showChat() {
|
|
|
485
490
|
: '';
|
|
486
491
|
if (msg.role === 'user') {
|
|
487
492
|
console.log(c('user', ` ${currentUser.username}${timeStr}: ${msg.text}`));
|
|
493
|
+
} else if (msg.role === 'system') {
|
|
494
|
+
// 系统消息(导出、搜索等)
|
|
495
|
+
console.log(c('cyan', ` ${msg.text}`));
|
|
488
496
|
} else {
|
|
489
497
|
// 渲染 markdown
|
|
490
498
|
const rendered = marked.parse(msg.text).trim();
|
|
@@ -545,23 +553,84 @@ async function showChat() {
|
|
|
545
553
|
|
|
546
554
|
if (input === '/regen') {
|
|
547
555
|
// 重新生成最后一条回复
|
|
548
|
-
if (
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
continue;
|
|
556
|
+
if (!currentConversationId) {
|
|
557
|
+
console.log(c('yellow', ' 没有可重新生成的对话'));
|
|
558
|
+
await sleep(1000);
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// 找到最后一条 AI 回复的索引
|
|
563
|
+
let lastBotIdx = -1;
|
|
564
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
565
|
+
if (messages[i].role === 'bot') {
|
|
566
|
+
lastBotIdx = i;
|
|
567
|
+
break;
|
|
561
568
|
}
|
|
562
|
-
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (lastBotIdx === -1) {
|
|
572
|
+
console.log(c('yellow', ' 没有可重新生成的回复'));
|
|
573
|
+
await sleep(1000);
|
|
563
574
|
continue;
|
|
564
575
|
}
|
|
576
|
+
|
|
577
|
+
// 删除客户端的 AI 回复(以及之后的 system 消息)
|
|
578
|
+
messages.splice(lastBotIdx);
|
|
579
|
+
drawMessages();
|
|
580
|
+
|
|
581
|
+
// 显示思考中
|
|
582
|
+
process.stdout.write(c('bot', ` ${currentCharacter.name}: `) + c('hint', '重新生成中...'));
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
const response = await regenerateMessage(currentConversationId);
|
|
586
|
+
process.stdout.write('\r\x1b[K');
|
|
587
|
+
process.stdout.write(c('bot', ` ${currentCharacter.name}: `));
|
|
588
|
+
|
|
589
|
+
let fullResponse = '';
|
|
590
|
+
const reader = response.body.getReader();
|
|
591
|
+
const decoder = new TextDecoder();
|
|
592
|
+
let buffer = '';
|
|
593
|
+
|
|
594
|
+
while (true) {
|
|
595
|
+
const { done, value } = await reader.read();
|
|
596
|
+
if (done) break;
|
|
597
|
+
buffer += decoder.decode(value, { stream: true });
|
|
598
|
+
const lines = buffer.split('\n');
|
|
599
|
+
buffer = lines.pop() || '';
|
|
600
|
+
|
|
601
|
+
for (const line of lines) {
|
|
602
|
+
if (line.startsWith('data: ')) {
|
|
603
|
+
const data = line.slice(6);
|
|
604
|
+
if (data === '[DONE]') continue;
|
|
605
|
+
try {
|
|
606
|
+
const json = JSON.parse(data);
|
|
607
|
+
if (json.type === 'tool_call') {
|
|
608
|
+
process.stdout.write('\r\x1b[K');
|
|
609
|
+
process.stdout.write(c('cyan', ` 🔍 正在搜索: ${json.query}...`));
|
|
610
|
+
messages.push({ role: 'system', text: `🔍 正在搜索: ${json.query}` });
|
|
611
|
+
} else if (json.type === 'tool_result') {
|
|
612
|
+
process.stdout.write('\r\x1b[K');
|
|
613
|
+
process.stdout.write(c('green', ` 📋 找到 ${json.count} 条结果\n`));
|
|
614
|
+
process.stdout.write(c('bot', ` ${currentCharacter.name}: `));
|
|
615
|
+
messages.push({ role: 'system', text: `📋 找到 ${json.count} 条结果` });
|
|
616
|
+
} else if (json.type === 'content') {
|
|
617
|
+
fullResponse += json.content;
|
|
618
|
+
process.stdout.write(json.content);
|
|
619
|
+
}
|
|
620
|
+
} catch {}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
console.log();
|
|
626
|
+
messages.push({ role: 'bot', text: fullResponse || '...', timestamp: new Date() });
|
|
627
|
+
drawMessages();
|
|
628
|
+
} catch (err) {
|
|
629
|
+
process.stdout.write('\r\x1b[K');
|
|
630
|
+
console.log(c('red', ` 错误: ${err.message}`));
|
|
631
|
+
await sleep(1500);
|
|
632
|
+
}
|
|
633
|
+
continue;
|
|
565
634
|
}
|
|
566
635
|
|
|
567
636
|
if (input === '/export') {
|
|
@@ -571,18 +640,17 @@ async function showChat() {
|
|
|
571
640
|
let content = `# 与 ${currentCharacter.name} 的对话\n\n`;
|
|
572
641
|
content += `导出时间: ${new Date().toLocaleString('zh-CN')}\n\n---\n\n`;
|
|
573
642
|
|
|
574
|
-
messages.forEach(msg => {
|
|
643
|
+
messages.filter(m => m.role !== 'system').forEach(msg => {
|
|
575
644
|
const role = msg.role === 'user' ? currentUser.username : currentCharacter.name;
|
|
576
645
|
content += `**${role}**:\n${msg.text}\n\n`;
|
|
577
646
|
});
|
|
578
647
|
|
|
579
648
|
try {
|
|
580
649
|
fs.writeFileSync(filepath, content);
|
|
581
|
-
|
|
650
|
+
messages.push({ role: 'system', text: `✓ 已导出到: ${filepath}` });
|
|
582
651
|
} catch (err) {
|
|
583
|
-
|
|
652
|
+
messages.push({ role: 'system', text: `✗ 导出失败: ${err.message}` });
|
|
584
653
|
}
|
|
585
|
-
await sleep(1500);
|
|
586
654
|
continue;
|
|
587
655
|
}
|
|
588
656
|
|
|
@@ -629,14 +697,16 @@ async function showChat() {
|
|
|
629
697
|
if (json.type === 'info') {
|
|
630
698
|
currentConversationId = json.conversationId;
|
|
631
699
|
} else if (json.type === 'tool_call') {
|
|
632
|
-
//
|
|
700
|
+
// 显示搜索状态并添加到消息
|
|
633
701
|
process.stdout.write('\r\x1b[K');
|
|
634
702
|
process.stdout.write(c('cyan', ` 🔍 正在搜索: ${json.query}...`));
|
|
703
|
+
messages.push({ role: 'system', text: `🔍 正在搜索: ${json.query}` });
|
|
635
704
|
} else if (json.type === 'tool_result') {
|
|
636
|
-
//
|
|
705
|
+
// 显示搜索结果数量并添加到消息
|
|
637
706
|
process.stdout.write('\r\x1b[K');
|
|
638
707
|
process.stdout.write(c('green', ` 📋 找到 ${json.count} 条结果\n`));
|
|
639
708
|
process.stdout.write(c('bot', ` ${currentCharacter.name}: `));
|
|
709
|
+
messages.push({ role: 'system', text: `📋 找到 ${json.count} 条结果` });
|
|
640
710
|
} else if (json.type === 'content') {
|
|
641
711
|
fullResponse += json.content;
|
|
642
712
|
// 打字机效果:直接输出内容
|