Skip to content

Commit 01edbe5

Browse files
committed
Added voice_id removal. Revised memory review functions. Improved memory browser display.
1 parent 3758401 commit 01edbe5

5 files changed

Lines changed: 241 additions & 10 deletions

File tree

config/prompts_sys.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,17 @@
7070

7171
settings_verifier_prompt = ''
7272

73-
history_review_prompt = """请审阅用户与AI的对话历史记录,识别并修正以下问题:
73+
history_review_prompt = """请审阅%s和%s之间的对话历史记录,识别并修正以下问题:
7474
7575
<问题1> 矛盾的部分:前后不一致的信息或观点 </问题1>
7676
<问题2> 冗余的部分:重复的内容或信息 </问题2>
7777
<问题3> 复读的部分:重复表达相同意思的内容 </问题3>
78-
<问题4> 人称错误的部分:ai以第三人称说话,或擅自生成了多轮对话 </问题4>
78+
<问题4> 人称错误的部分:以第三人称说话,或擅自生成了多轮对话 </问题4>
7979
8080
请注意!
81-
<要点1> AI正在进行角色扮演,她的回答应该是口语化的、自然的、拟人化的,请不要揣测她的人设,只判断逻辑问题。</要点1>
81+
<要点1> 这是一段情景对话,双方的回答应该是口语化的、自然的、拟人化的。</要点1>
8282
<要点2> 请以删除为主,除非不得已、不要直接修改内容。</要点2>
83+
<要点3> 如果对话历史中包含“先前对话的备忘录”,你可以修改它,但不允许删除它。你必须保留这一项。</要点3>
8384
8485
======以下为对话历史======
8586
%s
@@ -89,13 +90,13 @@
8990
{
9091
"修正说明": "简要说明发现的问题和修正内容",
9192
"修正后的对话": [
92-
{"role": "user/ai", "content": "修正后的消息内容"},
93+
{"role": "SYSTEM_MESSAGE/%s/%s", "content": "修正后的消息内容"},
9394
...
9495
]
9596
}
9697
9798
注意:
98-
- 角色扮演AI说话应当是口语化的、自然的、拟人化的
99+
- 对话应当是口语化的、自然的、拟人化的
99100
- 保持对话的核心信息和重要内容
100101
- 确保修正后的对话逻辑清晰、连贯
101102
- 移除冗余和重复内容

main_server.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,13 +616,82 @@ async def rename_catgirl(old_name: str, request: Request):
616616
save_characters(characters)
617617
return {"success": True}
618618

619+
@app.post('/api/characters/catgirl/{name}/unregister_voice')
620+
async def unregister_voice(name: str):
621+
"""解除猫娘的声音注册"""
622+
try:
623+
characters = load_characters()
624+
if name not in characters.get('猫娘', {}):
625+
return JSONResponse({'success': False, 'error': '猫娘不存在'}, status_code=404)
626+
627+
# 检查是否已有voice_id
628+
if not characters['猫娘'][name].get('voice_id'):
629+
return JSONResponse({'success': False, 'error': '该猫娘未注册声音'}, status_code=400)
630+
631+
# 删除voice_id字段
632+
if 'voice_id' in characters['猫娘'][name]:
633+
characters['猫娘'][name].pop('voice_id')
634+
save_characters(characters)
635+
636+
logger.info(f"已解除猫娘 '{name}' 的声音注册")
637+
return {"success": True, "message": "声音注册已解除"}
638+
639+
except Exception as e:
640+
logger.error(f"解除声音注册时出错: {e}")
641+
return JSONResponse({'success': False, 'error': f'解除注册失败: {str(e)}'}, status_code=500)
642+
619643
@app.get('/api/memory/recent_files')
620644
async def get_recent_files():
621645
"""获取 memory/store 下所有 recent*.json 文件名列表"""
622646
files = glob.glob('memory/store/recent*.json')
623647
file_names = [os.path.basename(f) for f in files]
624648
return {"files": file_names}
625649

650+
@app.get('/api/memory/review_config')
651+
async def get_review_config():
652+
"""获取记忆审阅配置"""
653+
try:
654+
config_path = './config/core_config.json'
655+
if os.path.exists(config_path):
656+
with open(config_path, 'r', encoding='utf-8') as f:
657+
config_data = json.load(f)
658+
# 如果配置中没有这个键,默认返回True(开启)
659+
return {"enabled": config_data.get('recent_memory_auto_review', True)}
660+
else:
661+
# 如果配置文件不存在,默认返回True(开启)
662+
return {"enabled": True}
663+
except Exception as e:
664+
logger.error(f"读取记忆审阅配置失败: {e}")
665+
return {"enabled": True}
666+
667+
@app.post('/api/memory/review_config')
668+
async def update_review_config(request: Request):
669+
"""更新记忆审阅配置"""
670+
try:
671+
data = await request.json()
672+
enabled = data.get('enabled', True)
673+
674+
config_path = './config/core_config.json'
675+
config_data = {}
676+
677+
# 读取现有配置
678+
if os.path.exists(config_path):
679+
with open(config_path, 'r', encoding='utf-8') as f:
680+
config_data = json.load(f)
681+
682+
# 更新配置
683+
config_data['recent_memory_auto_review'] = enabled
684+
685+
# 保存配置
686+
with open(config_path, 'w', encoding='utf-8') as f:
687+
json.dump(config_data, f, ensure_ascii=False, indent=2)
688+
689+
logger.info(f"记忆审阅配置已更新: enabled={enabled}")
690+
return {"success": True, "enabled": enabled}
691+
except Exception as e:
692+
logger.error(f"更新记忆审阅配置失败: {e}")
693+
return {"success": False, "error": str(e)}
694+
626695
@app.get('/api/memory/recent_file')
627696
async def get_recent_file(filename: str):
628697
"""获取指定 recent*.json 文件内容"""
@@ -651,7 +720,7 @@ async def save_recent_file(request: Request):
651720
arr.append({
652721
"type": t,
653722
"data": {
654-
"content": text if t == "system" else [{"type": "text", "text": text}],
723+
"content": text,
655724
"additional_kwargs": {},
656725
"response_metadata": {},
657726
"type": t,

memory/recent.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,20 @@ def review_history(self, lanlan_name):
127127
"""
128128
审阅历史记录,寻找并修正矛盾、冗余、逻辑混乱或复读的部分
129129
"""
130+
# 检查配置文件中是否禁用自动审阅
131+
try:
132+
config_path = os.path.join(os.path.dirname(__file__), '..', 'config', 'core_config.json')
133+
if os.path.exists(config_path):
134+
with open(config_path, 'r', encoding='utf-8') as f:
135+
config_data = json.load(f)
136+
if 'recent_memory_auto_review' in config_data and not config_data['recent_memory_auto_review']:
137+
print(f"💡 {lanlan_name} 的自动记忆审阅已禁用,跳过审阅")
138+
return False
139+
except Exception as e:
140+
print(f"⚠️ 读取配置文件失败:{e},继续执行审阅")
141+
130142
# 获取当前历史记录
143+
131144
current_history = self.get_recent_history(lanlan_name)
132145

133146
if not current_history:
@@ -159,8 +172,7 @@ def review_history(self, lanlan_name):
159172

160173
try:
161174
# 使用LLM审阅历史记录
162-
print(f"💡 开始审阅记忆:{history_text}")
163-
prompt = history_review_prompt % history_text
175+
prompt = history_review_prompt % (self.name_mapping['human'], name_mapping['ai'], history_text, self.name_mapping['human'], name_mapping['ai'])
164176
response_content = self.llm.invoke(prompt).content
165177

166178
# 确保response_content是字符串
@@ -183,10 +195,12 @@ def review_history(self, lanlan_name):
183195
role = msg_data.get('role', 'user')
184196
content = msg_data.get('content', '')
185197

186-
if role == 'user':
198+
if role in ['user', 'human', name_mapping['human']]:
187199
corrected_messages.append(HumanMessage(content=content))
188-
elif role == 'ai':
200+
elif role in ['ai', 'assistant', name_mapping['ai']]:
189201
corrected_messages.append(AIMessage(content=content))
202+
elif role in ['system', 'system_message', name_mapping['system']]:
203+
corrected_messages.append(SystemMessage(content=content))
190204
else:
191205
# 默认作为用户消息处理
192206
corrected_messages.append(HumanMessage(content=content))

templates/chara_manager.html

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ <h2 style="margin: 0;">角色管理</h2>
365365
<label>voice_id</label>
366366
<span>${cat['voice_id'] ? cat['voice_id'] : '未注册'}</span>
367367
<button type="button" class="btn sm" onclick="openVoiceClone('${key || ''}')">注册声音</button>
368+
${cat['voice_id'] ? `<button type="button" class="btn sm delete" onclick="unregisterVoice('${key || ''}')">解除注册</button>` : ''}
368369
</div>
369370
<div class="fold">
370371
<hr style="border: none; border-top: 1px solid #e0e0e0; margin: 8px 0;">
@@ -546,6 +547,32 @@ <h2 style="margin: 0;">角色管理</h2>
546547
document.body.appendChild(modal);
547548
}
548549

550+
// 解除声音注册
551+
window.unregisterVoice = async function(catgirlName) {
552+
if (!confirm(`确定要解除猫娘"${catgirlName}"的声音注册吗?`)) {
553+
return;
554+
}
555+
556+
try {
557+
const response = await fetch('/api/characters/catgirl/' + encodeURIComponent(catgirlName) + '/unregister_voice', {
558+
method: 'POST',
559+
headers: {'Content-Type': 'application/json'}
560+
});
561+
562+
const result = await response.json();
563+
564+
if (result.success) {
565+
alert('声音注册已解除');
566+
await loadCharacterData(); // 刷新数据
567+
} else {
568+
alert(result.error || '解除注册失败');
569+
}
570+
} catch (error) {
571+
console.error('解除注册出错:', error);
572+
alert('解除注册时发生错误');
573+
}
574+
}
575+
549576
// Beacon功能 - 页面关闭时发送信号给服务器
550577
let beaconSent = false;
551578

templates/memory_browser.html

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,71 @@
4242
box-shadow: none;
4343
outline: none;
4444
}
45+
46+
.review-toggle {
47+
margin-top: 16px;
48+
padding: 12px;
49+
background: #f8f9fa;
50+
border-radius: 8px;
51+
border: 1px solid #e9ecef;
52+
}
53+
54+
.review-toggle-title {
55+
font-weight: bold;
56+
margin-bottom: 8px;
57+
color: #495057;
58+
}
59+
60+
.toggle-switch {
61+
position: relative;
62+
display: inline-block;
63+
width: 50px;
64+
height: 24px;
65+
}
66+
67+
.toggle-switch input {
68+
opacity: 0;
69+
width: 0;
70+
height: 0;
71+
}
72+
73+
.toggle-slider {
74+
position: absolute;
75+
cursor: pointer;
76+
top: 0;
77+
left: 0;
78+
right: 0;
79+
bottom: 0;
80+
background-color: #ccc;
81+
transition: .4s;
82+
border-radius: 24px;
83+
}
84+
85+
.toggle-slider:before {
86+
position: absolute;
87+
content: "";
88+
height: 18px;
89+
width: 18px;
90+
left: 3px;
91+
bottom: 3px;
92+
background-color: white;
93+
transition: .4s;
94+
border-radius: 50%;
95+
}
96+
97+
input:checked + .toggle-slider {
98+
background-color: #4f8cff;
99+
}
100+
101+
input:checked + .toggle-slider:before {
102+
transform: translateX(26px);
103+
}
104+
105+
.toggle-label {
106+
margin-left: 12px;
107+
font-size: 0.9em;
108+
color: #6c757d;
109+
}
45110
</style>
46111
</head>
47112
<body>
@@ -54,6 +119,16 @@
54119
<div class="file-list">
55120
<div class="file-list-title">猫娘记忆库</div>
56121
<ul id="memory-file-list"></ul>
122+
<div class="review-toggle">
123+
<div class="review-toggle-title">自动记忆审阅</div>
124+
<div style="display: flex; align-items: center;">
125+
<label class="toggle-switch">
126+
<input type="checkbox" id="review-toggle" onchange="toggleReview(this.checked)">
127+
<span class="toggle-slider"></span>
128+
</label>
129+
<span class="toggle-label" id="review-status">加载中...</span>
130+
</div>
131+
</div>
57132
</div>
58133
<div class="editor">
59134
<div class="editor-title">聊天记录</div>
@@ -185,6 +260,8 @@
185260
const content = item.data && item.data.content;
186261
if (Array.isArray(content) && content[0] && content[0].type === 'text') {
187262
text = content[0].text;
263+
} else if (typeof content === 'string') {
264+
text = content;
188265
}
189266
return { role: item.type, text };
190267
} else {
@@ -256,13 +333,56 @@
256333
// 页面加载时隐藏保存按钮
257334
window.onload = function() {
258335
loadMemoryFileList();
336+
loadReviewConfig();
259337
document.getElementById('save-row').style.display = 'none';
260338
// 只有iframe环境下才显示关闭按钮
261339
if (window.parent === window) {
262340
var closeBtn = document.querySelector('.close-btn');
263341
if (closeBtn) closeBtn.style.display = 'none';
264342
}
265343
};
344+
345+
async function loadReviewConfig() {
346+
try {
347+
const resp = await fetch('/api/memory/review_config');
348+
const data = await resp.json();
349+
const toggle = document.getElementById('review-toggle');
350+
const status = document.getElementById('review-status');
351+
352+
toggle.checked = data.enabled;
353+
status.textContent = data.enabled ? '已开启' : '已关闭';
354+
} catch (e) {
355+
console.error('加载审阅配置失败:', e);
356+
document.getElementById('review-status').textContent = '加载失败';
357+
}
358+
}
359+
360+
async function toggleReview(enabled) {
361+
try {
362+
const resp = await fetch('/api/memory/review_config', {
363+
method: 'POST',
364+
headers: { 'Content-Type': 'application/json' },
365+
body: JSON.stringify({ enabled: enabled })
366+
});
367+
const data = await resp.json();
368+
369+
if (data.success) {
370+
const status = document.getElementById('review-status');
371+
status.textContent = enabled ? '已开启' : '已关闭';
372+
} else {
373+
// 如果保存失败,恢复原来的状态
374+
const toggle = document.getElementById('review-toggle');
375+
toggle.checked = !enabled;
376+
document.getElementById('review-status').textContent = '保存失败';
377+
}
378+
} catch (e) {
379+
console.error('更新审阅配置失败:', e);
380+
// 如果请求失败,恢复原来的状态
381+
const toggle = document.getElementById('review-toggle');
382+
toggle.checked = !enabled;
383+
document.getElementById('review-status').textContent = '保存失败';
384+
}
385+
}
266386
</script>
267387
</body>
268388
</html>

0 commit comments

Comments
 (0)