Skip to content

Commit 79d0487

Browse files
authored
feat: add template_list config type to support multiple repeated core/plugin config sets (#4208)
* feat: 添加模板列表配置支持,包含验证和编辑功能 * refactor(dashboard): extract ConfigItemRenderer to eliminate code duplication - Create ConfigItemRenderer.vue to centralize rendering logic for various config types (string, int, bool, selectors, etc.) - Refactor TemplateListEditor.vue to use the new renderer for entry fields - Refactor AstrBotConfig.vue and AstrBotConfigV4.vue to simplify metadata-driven rendering - Resolve circular dependency by decoupling TemplateListEditor from the base renderer * ruff format * refactor: improve config validation and fix unidirection data flow - Frontend: Fix one-way data flow in TemplateListEditor.vue by cloning entries before applying defaults and emitting updates instead of in-place modification. - Frontend: Remove unused TemplateListEditor import in ConfigItemRenderer.vue. - Backend: Refactor validate_config in config.py by extracting _expect_type and _validate_template_list helpers to reduce nesting and complexity.
1 parent 4f15102 commit 79d0487

9 files changed

Lines changed: 898 additions & 399 deletions

File tree

astrbot/core/config/astrbot_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ def _parse_schema(schema: dict, conf: dict):
8080
if v["type"] == "object":
8181
conf[k] = {}
8282
_parse_schema(v["items"], conf[k])
83+
elif v["type"] == "template_list":
84+
conf[k] = default
8385
else:
8486
conf[k] = default
8587

astrbot/core/config/default.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3064,4 +3064,5 @@ class ChatProviderTemplate(TypedDict):
30643064
"text": "",
30653065
"list": [],
30663066
"object": {},
3067+
"template_list": [],
30673068
}

astrbot/dashboard/routes/config.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,46 @@ def try_cast(value: Any, type_: str):
4646
return None
4747

4848

49+
def _expect_type(value, expected_type, path_key, errors, expected_name=None):
50+
if not isinstance(value, expected_type):
51+
errors.append(
52+
f"错误的类型 {path_key}: 期望是 {expected_name or expected_type.__name__}, "
53+
f"得到了 {type(value).__name__}"
54+
)
55+
return False
56+
return True
57+
58+
59+
def _validate_template_list(value, meta, path_key, errors, validate_fn):
60+
if not _expect_type(value, list, path_key, errors, "list"):
61+
return
62+
63+
templates = meta.get("templates")
64+
if not isinstance(templates, dict):
65+
templates = {}
66+
67+
for idx, item in enumerate(value):
68+
item_path = f"{path_key}[{idx}]"
69+
if not _expect_type(item, dict, item_path, errors, "dict"):
70+
continue
71+
72+
template_key = item.get("__template_key") or item.get("template")
73+
if not template_key:
74+
errors.append(f"缺少模板选择 {item_path}: 需要 __template_key")
75+
continue
76+
77+
template_meta = templates.get(template_key)
78+
if not template_meta:
79+
errors.append(f"未知模板 {item_path}: {template_key}")
80+
continue
81+
82+
validate_fn(
83+
item,
84+
template_meta.get("items", {}),
85+
path=f"{item_path}.",
86+
)
87+
88+
4989
def validate_config(data, schema: dict, is_core: bool) -> tuple[list[str], dict]:
5090
errors = []
5191

@@ -61,6 +101,11 @@ def validate(data: dict, metadata: dict = schema, path=""):
61101
if value is None:
62102
data[key] = DEFAULT_VALUE_MAP[meta["type"]]
63103
continue
104+
105+
if meta["type"] == "template_list":
106+
_validate_template_list(value, meta, f"{path}{key}", errors, validate)
107+
continue
108+
64109
if meta["type"] == "list" and not isinstance(value, list):
65110
errors.append(
66111
f"错误的类型 {path}{key}: 期望是 list, 得到了 {type(value).__name__}",

0 commit comments

Comments
 (0)