@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.
Files changed (3) hide show
  1. package/bin/api.js +13 -0
  2. package/bin/index.js +92 -22
  3. 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({ role: m.role === 'user' ? 'user' : 'bot', text: m.content, id: m.id });
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 (messages.length >= 2) {
549
- const lastUserMsg = [...messages].reverse().find(m => m.role === 'user');
550
- if (lastUserMsg) {
551
- // 删除服务端最后两条
552
- if (currentConversationId) {
553
- try { await deleteLastMessages(currentConversationId); } catch {}
554
- }
555
- messages.splice(-2);
556
- // 重新发送用户消息
557
- input = lastUserMsg.text;
558
- // continue,让后续代码处理发送
559
- } else {
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
- } else {
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
- console.log(c('green', ` ✓ 已导出到: ${filepath}`));
650
+ messages.push({ role: 'system', text: `✓ 已导出到: ${filepath}` });
582
651
  } catch (err) {
583
- console.log(c('red', ` 导出失败: ${err.message}`));
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
  // 打字机效果:直接输出内容
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fivetu53/soul-chat",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Soul Chat - 智能 AI 伴侣命令行客户端",
5
5
  "type": "module",
6
6
  "bin": {