refactor: 统一 system/_meta.json 管理分类;修复前端 TypeScript 报错

This commit is contained in:
小鱼开发
2026-04-22 11:10:33 +08:00
parent 13c5c18dcc
commit 1057727fc5
8 changed files with 45 additions and 27 deletions
+27 -21
View File
@@ -14,6 +14,7 @@ Prompt 简单加载器
└── ...
"""
import json
import json
import random
from pathlib import Path
@@ -60,12 +61,25 @@ def render_template(template: str, **kwargs) -> str:
# ====================== 新分类体系:动态扫描 ======================
SYSTEM_PROMPTS_DIR = _PROMPTS_DIR / "system"
_SYSTEM_META_PATH = SYSTEM_PROMPTS_DIR / "_meta.json"
def _load_system_meta() -> dict:
"""读取 system/_meta.json"""
if _SYSTEM_META_PATH.exists():
try:
return json.loads(_SYSTEM_META_PATH.read_text(encoding="utf-8"))
except (json.JSONDecodeError, OSError):
pass
return {}
def list_categories() -> list[dict]:
"""
扫描 system/ 目录,返回所有分类结构
名称从 system/_meta.json 读取,count 动态扫描目录统计。
Returns:
[
{
@@ -79,7 +93,10 @@ def list_categories() -> list[dict]:
...
]
"""
meta = _load_system_meta()
meta_categories = {c["code"]: c for c in meta.get("categories", [])}
categories = []
if not SYSTEM_PROMPTS_DIR.exists():
return categories
@@ -87,34 +104,34 @@ def list_categories() -> list[dict]:
if not cat_dir.is_dir():
continue
# 读取大类元数据
cat_meta = _load_meta(cat_dir)
cat_name = cat_meta.get("name", cat_dir.name)
cat_code = cat_dir.name
cat_meta = meta_categories.get(cat_code, {})
meta_subs = {s["code"]: s for s in cat_meta.get("subcategories", [])}
cat_name = cat_meta.get("name", cat_code)
subcategories = []
for sub_dir in sorted(cat_dir.iterdir()):
if not sub_dir.is_dir():
continue
# 读取小类元数据
sub_meta = _load_meta(sub_dir)
sub_name = sub_meta.get("name", sub_dir.name)
sub_code = sub_dir.name
sub_name = meta_subs.get(sub_code, {}).get("name", sub_code)
# 统计提示词文件数量(排除 _meta.json)
prompt_files = [
f for f in sub_dir.iterdir()
if f.is_file() and f.name != "_meta.json"
if f.is_file() and f.suffix == ".txt"
]
subcategories.append({
"code": sub_dir.name,
"code": sub_code,
"name": sub_name,
"count": len(prompt_files),
})
if subcategories:
categories.append({
"code": cat_dir.name,
"code": cat_code,
"name": cat_name,
"subcategories": subcategories,
})
@@ -122,17 +139,6 @@ def list_categories() -> list[dict]:
return categories
def _load_meta(directory: Path) -> dict:
"""读取目录下的 _meta.json"""
meta_path = directory / "_meta.json"
if meta_path.exists():
try:
return json.loads(meta_path.read_text(encoding="utf-8"))
except (json.JSONDecodeError, OSError):
pass
return {}
def load_system_prompt(category: str, subcategory: str) -> str:
"""
根据大类+小类随机加载一个 System Prompt
@@ -148,7 +154,7 @@ def load_system_prompt(category: str, subcategory: str) -> str:
if not sub_dir.exists():
return ""
# 收集所有提示词文件(排除 _meta.json
# 收集所有提示词文件
prompt_files = [
f for f in sub_dir.iterdir()
if f.is_file() and f.suffix == ".txt"
@@ -0,0 +1,13 @@
{
"categories": [
{
"code": "bk",
"name": "装修避坑",
"subcategories": [
{ "code": "ht", "name": "装修合同避坑" },
{ "code": "lc", "name": "装修全流程避坑" },
{ "code": "qw", "name": "全屋定制避坑" }
]
}
]
}
@@ -1 +0,0 @@
{"name": "装修合同避坑"}
@@ -1 +0,0 @@
{"name": "装修全流程避坑"}
@@ -1 +0,0 @@
{"name": "全屋定制避坑"}
@@ -20,7 +20,6 @@ export default function ScriptCreation() {
const segments = useProjectStore(state => state.segments);
const setSegments = useProjectStore(state => state.setSegments);
const updateSegment = useProjectStore(state => state.updateSegment);
const topic = useProjectStore(state => state.topic) || '';
const setTopic = useProjectStore(state => state.setTopic);
const scriptDuration = useProjectStore(state => state.scriptDuration) || 45;
const setScriptDuration = useProjectStore(state => state.setScriptDuration);
@@ -218,7 +218,7 @@ export default function VoiceDubbing() {
<span className="voice-row-desc-inline">{v.description}</span>
</div>
</div>
<button className="preview-icon" onClick={e => handlePlayPause(v.voiceId, v.previewUrl, e)}>
<button className="preview-icon" onClick={e => handlePlayPause(v.voiceId, v.previewUrl ?? null, e)}>
{playingVoiceId === v.voiceId ? '⏸' : '▶'}
</button>
</div>
@@ -243,7 +243,7 @@ export default function VoiceDubbing() {
</span>
</div>
</div>
<button className="preview-icon" onClick={e => handlePlayPause(m.voiceId, m.trialUrl, e)}>
<button className="preview-icon" onClick={e => handlePlayPause(m.voiceId, m.trialUrl ?? null, e)}>
{playingVoiceId === m.voiceId ? '⏸' : '▶'}
</button>
</div>
+3
View File
@@ -88,6 +88,9 @@ interface SaveState {
selectedHumanId?: string;
selectedElementId?: number;
selectedVoiceId?: string;
dubbingAudioUrl?: string;
dubbingAudioPath?: string;
dubbingVoiceId?: string;
scriptDuration?: number;
scriptType?: string;
currentStep?: number;