Skip to content

Commit dc99b23

Browse files
author
sunweiyue
committed
feat: add WYSIWYG edit_tool and fix build for pre-processed data
- Add develop/edit_tool with visual editor for demo page - index.html: editor UI with double-click edit, drag-drop reorder - server.py: local server for data loading/saving and build trigger - README.md: architecture documentation - Fix build.py to support pre-processed data from edit_tool - Detect if case already has turns/system data - Skip source_session lookup for already-processed cases - Update data.js and data_en.js with latest build
1 parent 1b8c972 commit dc99b23

File tree

8 files changed

+3015
-200
lines changed

8 files changed

+3015
-200
lines changed

develop/edit_tool/README.md

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

develop/edit_tool/config/data_en.json

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

develop/edit_tool/index.html

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

develop/edit_tool/server.py

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

develop/minicpm-o-4_5/build.py

Lines changed: 91 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,23 @@
2121
SCRIPT_DIR = Path(__file__).parent
2222
REPO_ROOT = SCRIPT_DIR.parent.parent
2323
CONFIG_PATH = SCRIPT_DIR / "config" / "cases.json"
24+
EDIT_TOOL_CONFIG_ZH = SCRIPT_DIR.parent / "edit_tool" / "config" / "data_zh.json"
25+
EDIT_TOOL_CONFIG_EN = SCRIPT_DIR.parent / "edit_tool" / "config" / "data_en.json"
2426
OUTPUT_DIR = REPO_ROOT / "minicpm-o-4_5"
2527
COLLECTED_DIR = SCRIPT_DIR.parent / "collected"
2628

2729

2830
def load_config() -> dict:
29-
"""加载 cases.json 配置"""
31+
"""加载配置文件
32+
33+
优先从 edit_tool 配置读取(如果存在),否则从 cases.json 读取
34+
"""
35+
if EDIT_TOOL_CONFIG_ZH.exists():
36+
print(f"[INFO] 从 edit_tool 配置加载: {EDIT_TOOL_CONFIG_ZH}")
37+
with open(EDIT_TOOL_CONFIG_ZH, "r", encoding="utf-8") as f:
38+
return json.load(f)
39+
40+
print(f"[INFO] 从默认配置加载: {CONFIG_PATH}")
3041
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
3142
return json.load(f)
3243

@@ -160,6 +171,17 @@ def process_case(case: dict, lang: str, output_audio_dir: Path) -> Optional[dict
160171
Returns:
161172
处理后的 case 数据(用于 data.js),处理失败返回 None
162173
"""
174+
# 如果已经有完整数据(从 edit_tool 保存的),直接使用
175+
if "turns" in case and case["turns"] and "system" in case:
176+
print(f" 使用已处理的 case: {case['id']}")
177+
return {
178+
"id": case["id"],
179+
"summary": case.get("summary", ""),
180+
"system": case["system"],
181+
"turns": case["turns"]
182+
}
183+
184+
# 否则从 source_session 读取
163185
source_session = case.get("source_session")
164186
if not source_session:
165187
print(f" [WARN] Case {case.get('id', '?')} 缺少 source_session,跳过")
@@ -255,9 +277,9 @@ def build_data_js(config: dict, output_dir: Path) -> dict:
255277
return output_data
256278

257279

258-
def write_data_js(data: dict, output_dir: Path):
280+
def write_data_js(data: dict, output_dir: Path, filename: str = "data.js"):
259281
"""将数据写入 data.js"""
260-
data_js_path = output_dir / "data.js"
282+
data_js_path = output_dir / filename
261283

262284
# 格式化 JSON,便于阅读
263285
json_str = json.dumps(data, ensure_ascii=False, indent=2)
@@ -268,41 +290,83 @@ def write_data_js(data: dict, output_dir: Path):
268290
print(f"写入 {data_js_path}")
269291

270292

293+
def load_config_for_lang(lang: str) -> Optional[dict]:
294+
"""加载指定语言的配置
295+
296+
Args:
297+
lang: 语言代码,zh 或 en
298+
299+
Returns:
300+
配置字典,如果不存在则返回 None
301+
"""
302+
edit_tool_config = EDIT_TOOL_CONFIG_ZH if lang == "zh" else EDIT_TOOL_CONFIG_EN
303+
304+
if edit_tool_config.exists():
305+
print(f"[INFO] 从 edit_tool 配置加载 ({lang}): {edit_tool_config}")
306+
with open(edit_tool_config, "r", encoding="utf-8") as f:
307+
return json.load(f)
308+
309+
# 回退到默认配置
310+
if CONFIG_PATH.exists():
311+
print(f"[INFO] 从默认配置加载 ({lang}): {CONFIG_PATH}")
312+
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
313+
return json.load(f)
314+
315+
return None
316+
317+
271318
def main():
272319
print("=" * 60)
273320
print("MiniCPM-o 4.5 Demo Page Builder")
274321
print("=" * 60)
275322

276-
# 检查配置文件
277-
if not CONFIG_PATH.exists():
278-
print(f"[ERROR] 配置文件不存在: {CONFIG_PATH}")
279-
return 1
280-
281-
# 加载配置
282-
print(f"加载配置: {CONFIG_PATH}")
283-
config = load_config()
284-
285323
# 清理输出目录中的音频(保留其他文件)
286324
output_audio_dir = OUTPUT_DIR / "audio"
287325
if output_audio_dir.exists():
288326
print(f"清理音频目录: {output_audio_dir}")
289327
shutil.rmtree(output_audio_dir)
290328

291-
# 构建数据
292-
print("\n处理 cases...")
293-
output_data = build_data_js(config, OUTPUT_DIR)
294-
295-
# 写入 data.js
296-
print("\n生成输出...")
297-
write_data_js(output_data, OUTPUT_DIR)
298-
299-
# 统计
300-
total_cases = sum(
301-
len(sub["cases"])
302-
for ability in output_data["abilities"]
303-
for sub in ability["sub_abilities"]
304-
)
305-
print(f"\n完成!共处理 {total_cases} 个 cases")
329+
total_cases_all = 0
330+
331+
# 处理中文版本
332+
print("\n" + "=" * 40)
333+
print("处理中文版本")
334+
print("=" * 40)
335+
config_zh = load_config_for_lang("zh")
336+
if config_zh:
337+
output_data_zh = build_data_js(config_zh, OUTPUT_DIR)
338+
write_data_js(output_data_zh, OUTPUT_DIR, "data.js")
339+
total_cases_zh = sum(
340+
len(sub["cases"])
341+
for ability in output_data_zh["abilities"]
342+
for sub in ability["sub_abilities"]
343+
)
344+
print(f"中文版本完成:{total_cases_zh} 个 cases")
345+
total_cases_all += total_cases_zh
346+
else:
347+
print("[WARN] 未找到中文配置文件")
348+
349+
# 处理英文版本
350+
print("\n" + "=" * 40)
351+
print("处理英文版本")
352+
print("=" * 40)
353+
config_en = load_config_for_lang("en")
354+
if config_en:
355+
output_data_en = build_data_js(config_en, OUTPUT_DIR)
356+
write_data_js(output_data_en, OUTPUT_DIR, "data_en.js")
357+
total_cases_en = sum(
358+
len(sub["cases"])
359+
for ability in output_data_en["abilities"]
360+
for sub in ability["sub_abilities"]
361+
)
362+
print(f"英文版本完成:{total_cases_en} 个 cases")
363+
total_cases_all += total_cases_en
364+
else:
365+
print("[WARN] 未找到英文配置文件")
366+
367+
print("\n" + "=" * 60)
368+
print(f"构建完成!共处理 {total_cases_all} 个 cases")
369+
print("=" * 60)
306370

307371
return 0
308372

minicpm-o-4_5/data.js

Lines changed: 65 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -11,71 +11,6 @@ const DEMO_DATA = {
1111
"name": "MiniCPM 助手",
1212
"description": "高表现力语音合成,专业声优音色,自然韵律",
1313
"sub_abilities": [
14-
{
15-
"id": "story",
16-
"name": "故事创作",
17-
"description": "",
18-
"cases": [
19-
{
20-
"id": "haitian_story_001",
21-
"summary": "睡前故事(轻柔语气)",
22-
"system": {
23-
"prefix": "模仿音频样本的音色并生成新的内容。",
24-
"ref_audio": "audio/haitian_story_001/ref.mp3",
25-
"suffix": "你的任务是用这种声音模式来当一个助手。请认真、高质量地回复用户的问题。请用高自然度的方式和用户聊天。你是由面壁智能开发的人工智能助手:面壁小钢炮。"
26-
},
27-
"turns": [
28-
{
29-
"user_text": "用轻柔的语气讲一个睡前故事,伴我入睡。",
30-
"assistant_text": "今天的故事讲的是一个叫小熊的朋友,它在森林里迷路了。小熊很害怕,但它没有哭,而是决定勇敢地往前走。它遇到了一只老猫头鹰,老猫头鹰告诉小熊:“如果你迷路了,就停下来听一听周围的声音。”小熊听了,闭上眼睛,仔细地听着。它听到了树叶沙沙的声音,听到了小溪潺潺的声音,还听到了远处鸟儿的歌声。小熊觉得这些声音好美啊,它不再害怕了。它继续往前走,终于找到了回家的路。现在,你也可以像小熊一样,闭上眼睛,听一听周围的声音。森林里的动物们在轻声地唱歌,小溪在轻轻地流淌,月亮在温柔地照耀着你。慢慢地,你的身体也会变得很放松,准备进入甜美的梦乡啦。晚安,愿你有一个美好的梦境。",
31-
"assistant_audio": "audio/haitian_story_001/000_assistant.mp3"
32-
},
33-
{
34-
"user_text": "嗯。",
35-
"assistant_text": "你好呀,今天过得怎么样?",
36-
"assistant_audio": "audio/haitian_story_001/001_assistant.mp3"
37-
}
38-
]
39-
},
40-
{
41-
"id": "haitian_story_002",
42-
"summary": "童话故事(小鹿)",
43-
"system": {
44-
"prefix": "模仿音频样本的音色并生成新的内容。",
45-
"ref_audio": "audio/haitian_story_002/ref.mp3",
46-
"suffix": "你的任务是用这种声音模式来当一个助手。请认真、高质量地回复用户的问题。请用高自然度的方式和用户聊天。你是由面壁智能开发的人工智能助手:面壁小钢炮。"
47-
},
48-
"turns": [
49-
{
50-
"user_text": "讲一个,呃,讲一个森林里的小鹿的一个童话故事。",
51-
"assistant_text": "从前,森林里住着一只小鹿。它喜欢在清晨的阳光下漫步,穿过青翠的草地,跳过清澈的小溪。有一天,它发现了一片从未见过的花海,五颜六色的花朵随风摇曳,仿佛在向它招手。小鹿好奇地走过去,不小心踩到了一朵会发光的蘑菇。突然,一道柔和的光晕笼罩了它,它听见一个温柔的声音说:‘你的心很纯净,愿意帮助迷路的小动物吗?’小鹿点点头,从那天起,它成了森林里的小帮手,用智慧和善良守护着每一个朋友。",
52-
"assistant_audio": "audio/haitian_story_002/000_assistant.mp3"
53-
},
54-
{
55-
"user_text": "继续。",
56-
"assistant_text": "有一天,一只迷路的小刺猬来到森林深处,它找不到回家的路。小鹿听见它的哭声,立刻跑过去问:“你怎么了?”小刺猬抽噎着说:“我走丢了,妈妈叫我快回家。”小鹿想了想,用树叶和藤蔓为小刺猬搭了个临时窝,还给它找来野果充饥。它又在大树上做了一个记号,指引小刺猬回家的方向。小刺猬感激地说:“谢谢你,小鹿哥哥!”从那以后,每当有动物需要帮助,小鹿总是第一个出现,它的故事在森林里传开了。",
57-
"assistant_audio": "audio/haitian_story_002/001_assistant.mp3"
58-
}
59-
]
60-
},
61-
{
62-
"id": "haitian_story_003",
63-
"summary": "诗歌朗读(忧伤语气)",
64-
"system": {
65-
"prefix": "模仿音频样本的音色并生成新的内容。",
66-
"ref_audio": "audio/haitian_story_003/ref.mp3",
67-
"suffix": "你的任务是用这种声音模式来当一个助手。请认真、高质量地回复用户的问题。请用高自然度的方式和用户聊天。你是由面壁智能开发的人工智能助手:面壁小钢炮。"
68-
},
69-
"turns": [
70-
{
71-
"user_text": "请创作一首关于北京的冬天的诗,并用忧伤的语气朗读。",
72-
"assistant_text": "北京的冬天,冷得让人发抖。北风呼啸着,穿过古老的城墙,吹散了街角的炊烟。胡同里,雪落无声,覆盖了石板路,也盖住了人们的脚印。红墙黄瓦在寒光中显得格外孤寂,仿佛在诉说着往日的繁华已成追忆。人们缩着脖子匆匆走过,眼神里藏着一丝无奈。这城市,像一座沉默的碑,立在岁月的尽头。",
73-
"assistant_audio": "audio/haitian_story_003/000_assistant.mp3"
74-
}
75-
]
76-
}
77-
]
78-
},
7914
{
8015
"id": "qa",
8116
"name": "知识问答",
@@ -149,6 +84,71 @@ const DEMO_DATA = {
14984
}
15085
]
15186
},
87+
{
88+
"id": "story",
89+
"name": "故事创作",
90+
"description": "",
91+
"cases": [
92+
{
93+
"id": "haitian_story_001",
94+
"summary": "睡前故事(轻柔语气)",
95+
"system": {
96+
"prefix": "模仿音频样本的音色并生成新的内容。",
97+
"ref_audio": "audio/haitian_story_001/ref.mp3",
98+
"suffix": "你的任务是用这种声音模式来当一个助手。请认真、高质量地回复用户的问题。请用高自然度的方式和用户聊天。你是由面壁智能开发的人工智能助手:面壁小钢炮。"
99+
},
100+
"turns": [
101+
{
102+
"user_text": "用轻柔的语气讲一个睡前故事,伴我入睡。",
103+
"assistant_text": "今天的故事讲的是一个叫小熊的朋友,它在森林里迷路了。小熊很害怕,但它没有哭,而是决定勇敢地往前走。它遇到了一只老猫头鹰,老猫头鹰告诉小熊:“如果你迷路了,就停下来听一听周围的声音。”小熊听了,闭上眼睛,仔细地听着。它听到了树叶沙沙的声音,听到了小溪潺潺的声音,还听到了远处鸟儿的歌声。小熊觉得这些声音好美啊,它不再害怕了。它继续往前走,终于找到了回家的路。现在,你也可以像小熊一样,闭上眼睛,听一听周围的声音。森林里的动物们在轻声地唱歌,小溪在轻轻地流淌,月亮在温柔地照耀着你。慢慢地,你的身体也会变得很放松,准备进入甜美的梦乡啦。晚安,愿你有一个美好的梦境。",
104+
"assistant_audio": "audio/haitian_story_001/000_assistant.mp3"
105+
},
106+
{
107+
"user_text": "嗯。",
108+
"assistant_text": "你好呀,今天过得怎么样?",
109+
"assistant_audio": "audio/haitian_story_001/001_assistant.mp3"
110+
}
111+
]
112+
},
113+
{
114+
"id": "haitian_story_002",
115+
"summary": "童话故事(小鹿)",
116+
"system": {
117+
"prefix": "模仿音频样本的音色并生成新的内容。",
118+
"ref_audio": "audio/haitian_story_002/ref.mp3",
119+
"suffix": "你的任务是用这种声音模式来当一个助手。请认真、高质量地回复用户的问题。请用高自然度的方式和用户聊天。你是由面壁智能开发的人工智能助手:面壁小钢炮。"
120+
},
121+
"turns": [
122+
{
123+
"user_text": "讲一个,呃,讲一个森林里的小鹿的一个童话故事。",
124+
"assistant_text": "从前,森林里住着一只小鹿。它喜欢在清晨的阳光下漫步,穿过青翠的草地,跳过清澈的小溪。有一天,它发现了一片从未见过的花海,五颜六色的花朵随风摇曳,仿佛在向它招手。小鹿好奇地走过去,不小心踩到了一朵会发光的蘑菇。突然,一道柔和的光晕笼罩了它,它听见一个温柔的声音说:‘你的心很纯净,愿意帮助迷路的小动物吗?’小鹿点点头,从那天起,它成了森林里的小帮手,用智慧和善良守护着每一个朋友。",
125+
"assistant_audio": "audio/haitian_story_002/000_assistant.mp3"
126+
},
127+
{
128+
"user_text": "继续。",
129+
"assistant_text": "有一天,一只迷路的小刺猬来到森林深处,它找不到回家的路。小鹿听见它的哭声,立刻跑过去问:“你怎么了?”小刺猬抽噎着说:“我走丢了,妈妈叫我快回家。”小鹿想了想,用树叶和藤蔓为小刺猬搭了个临时窝,还给它找来野果充饥。它又在大树上做了一个记号,指引小刺猬回家的方向。小刺猬感激地说:“谢谢你,小鹿哥哥!”从那以后,每当有动物需要帮助,小鹿总是第一个出现,它的故事在森林里传开了。",
130+
"assistant_audio": "audio/haitian_story_002/001_assistant.mp3"
131+
}
132+
]
133+
},
134+
{
135+
"id": "haitian_story_003",
136+
"summary": "诗歌朗读(忧伤语气)",
137+
"system": {
138+
"prefix": "模仿音频样本的音色并生成新的内容。",
139+
"ref_audio": "audio/haitian_story_003/ref.mp3",
140+
"suffix": "你的任务是用这种声音模式来当一个助手。请认真、高质量地回复用户的问题。请用高自然度的方式和用户聊天。你是由面壁智能开发的人工智能助手:面壁小钢炮。"
141+
},
142+
"turns": [
143+
{
144+
"user_text": "请创作一首关于北京的冬天的诗,并用忧伤的语气朗读。",
145+
"assistant_text": "北京的冬天,冷得让人发抖。北风呼啸着,穿过古老的城墙,吹散了街角的炊烟。胡同里,雪落无声,覆盖了石板路,也盖住了人们的脚印。红墙黄瓦在寒光中显得格外孤寂,仿佛在诉说着往日的繁华已成追忆。人们缩着脖子匆匆走过,眼神里藏着一丝无奈。这城市,像一座沉默的碑,立在岁月的尽头。",
146+
"assistant_audio": "audio/haitian_story_003/000_assistant.mp3"
147+
}
148+
]
149+
}
150+
]
151+
}
152152
]
153153
},
154154
{

0 commit comments

Comments
 (0)