refactor: 移除LLM旧兼容入口并统一scene单路由
变更项: 1. LLMRegistry 仅保留 scene 入口,删除 backend_name/backend_ref/scene_ref 等兼容解析分支,未声明 scene 时仅保留直连配置。 2. Dify/GlobalNews/GameTask 插件初始化改为仅传 scene,不再拼接 backend/provider/url 等旧兼容字段。 3. 清理插件配置冗余:dify/global_news/game_task/douyu 的 config.toml 删除 backend 字段,统一由 scene 映射后端。 4. 后台 system API 调整为严格模式:插件依赖扫描仅采集 scene;scene 保存时必须绑定有效 backend。 5. 后台页面去除拓扑中的配置Backend冗余列,并新增前端校验,禁止提交空场景或未绑定后端。
This commit is contained in:
@@ -45,11 +45,11 @@ def _plugins_root_path() -> str:
|
||||
|
||||
|
||||
def _scan_plugin_llm_usage() -> list:
|
||||
"""扫描各插件 config.toml,提取插件与 LLM 后端/场景的引用关系。
|
||||
"""扫描各插件 config.toml,提取插件与 LLM 场景的引用关系。
|
||||
|
||||
说明:
|
||||
1. 该扫描仅用于后台可视化,不会改写插件配置;
|
||||
2. 兼容两种写法:顶层 section 下直接写 backend/scene,或嵌套在 llm/api/report_api 等节点;
|
||||
2. 严格模式只采集 scene:顶层 section 写法,或嵌套在 llm/api/report_api 等节点;
|
||||
3. 返回结果用于“插件 -> scene -> backend”依赖拓扑展示。
|
||||
"""
|
||||
plugins_root = _plugins_root_path()
|
||||
@@ -59,17 +59,15 @@ def _scan_plugin_llm_usage() -> list:
|
||||
usages = []
|
||||
|
||||
def _collect_refs(plugin_name: str, section_name: str, payload: dict) -> None:
|
||||
"""从单个配置节点收集 backend/scene 引用。"""
|
||||
"""从单个配置节点收集 scene 引用。"""
|
||||
if not isinstance(payload, dict):
|
||||
return
|
||||
backend_name = str(payload.get("backend") or "").strip()
|
||||
scene_name = str(payload.get("scene") or payload.get("scene_name") or "").strip()
|
||||
if not backend_name and not scene_name:
|
||||
scene_name = str(payload.get("scene") or "").strip()
|
||||
if not scene_name:
|
||||
return
|
||||
usages.append({
|
||||
"plugin": plugin_name,
|
||||
"section": section_name,
|
||||
"backend": backend_name,
|
||||
"scene": scene_name,
|
||||
})
|
||||
|
||||
@@ -94,13 +92,13 @@ def _scan_plugin_llm_usage() -> list:
|
||||
for nested_name, nested_value in section_value.items():
|
||||
if isinstance(nested_value, dict):
|
||||
_collect_refs(item, f"{section_name}.{nested_name}", nested_value)
|
||||
# 顶层兜底:兼容极少数直接写在根节点的 backend/scene。
|
||||
# 顶层兜底:兼容极少数直接写在根节点的 scene。
|
||||
_collect_refs(item, "__root__", config_obj if isinstance(config_obj, dict) else {})
|
||||
|
||||
# 去重:同插件同 section 仅保留一条记录,避免前后兜底重复。
|
||||
unique = {}
|
||||
for row in usages:
|
||||
key = f"{row.get('plugin')}::{row.get('section')}::{row.get('scene')}::{row.get('backend')}"
|
||||
key = f"{row.get('plugin')}::{row.get('section')}::{row.get('scene')}"
|
||||
unique[key] = row
|
||||
return sorted(unique.values(), key=lambda x: (x.get("plugin", ""), x.get("section", "")))
|
||||
|
||||
@@ -117,12 +115,8 @@ def _build_llm_topology() -> dict:
|
||||
topology_rows = []
|
||||
for usage in plugin_usages:
|
||||
scene_name = str(usage.get("scene") or "").strip()
|
||||
backend_name = str(usage.get("backend") or "").strip()
|
||||
resolved_backend = backend_name
|
||||
# 若插件只写了 scene,则由 scenes 路由解析后端。
|
||||
if not resolved_backend and scene_name:
|
||||
resolved_backend = str(scenes.get(scene_name) or "").strip()
|
||||
# 若既没有 scene 也没有 backend,则兜底 default_backend(仅展示,不改配置)。
|
||||
# 严格模式:插件必须声明 scene,后端统一由 scenes 映射解析。
|
||||
resolved_backend = str(scenes.get(scene_name) or "").strip()
|
||||
if not resolved_backend:
|
||||
resolved_backend = default_backend
|
||||
|
||||
@@ -130,10 +124,9 @@ def _build_llm_topology() -> dict:
|
||||
"plugin": usage.get("plugin", ""),
|
||||
"section": usage.get("section", ""),
|
||||
"scene": scene_name,
|
||||
"backend": backend_name,
|
||||
"resolved_backend": resolved_backend,
|
||||
"provider": str((backends.get(resolved_backend) or {}).get("provider", "") or "").strip(),
|
||||
"valid_scene": bool((not scene_name) or scene_name in scenes),
|
||||
"valid_scene": bool(scene_name in scenes),
|
||||
"valid_backend": bool((not resolved_backend) or resolved_backend in backends),
|
||||
})
|
||||
|
||||
@@ -428,9 +421,11 @@ def update_system_llm_config():
|
||||
backend_name = str(raw.get("backend") or "").strip()
|
||||
if not scene_name:
|
||||
continue
|
||||
if backend_name and backend_name not in normalized_backends:
|
||||
# 严格模式:每个 scene 必须绑定一个有效 backend,避免“空绑定”导致运行时不确定性。
|
||||
if not backend_name:
|
||||
return jsonify({"success": False, "message": f"场景 {scene_name} 未绑定后端"}), 400
|
||||
if backend_name not in normalized_backends:
|
||||
return jsonify({"success": False, "message": f"场景 {scene_name} 绑定的后端不存在"}), 400
|
||||
# 允许场景先占位但暂不绑定后端,方便后台分步配置。
|
||||
normalized_scenes[scene_name] = backend_name
|
||||
|
||||
config_obj = _load_system_yaml()
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<div class="scene-list" v-if="llmForm.scenes.length">
|
||||
<div class="scene-row" v-for="(scene, index) in llmForm.scenes" :key="scene.uid">
|
||||
<el-input v-model="scene.name" placeholder="例如:chat.main"></el-input>
|
||||
<el-select v-model="scene.backend" placeholder="绑定后端" filterable clearable>
|
||||
<el-select v-model="scene.backend" placeholder="绑定后端" filterable>
|
||||
<el-option v-for="item in backendNameOptions" :key="item" :label="item" :value="item"></el-option>
|
||||
</el-select>
|
||||
<el-button type="text" class="danger-text" @click="removeScene(index)">删除</el-button>
|
||||
@@ -172,11 +172,6 @@
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="backend" label="配置Backend" min-width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{% raw %}{{ scope.row.backend || '-' }}{% endraw %}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="resolved_backend" label="实际Backend" min-width="160">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.resolved_backend" size="mini" :type="scope.row.valid_backend ? 'success' : 'danger'">
|
||||
@@ -327,6 +322,14 @@
|
||||
}
|
||||
},
|
||||
async saveLlmConfig() {
|
||||
// 前端先做一次严格校验,避免把空场景名或未绑定后端的记录提交到后端。
|
||||
const invalidScene = (this.llmForm.scenes || []).find(item => {
|
||||
return !String(item.name || '').trim() || !String(item.backend || '').trim();
|
||||
});
|
||||
if (invalidScene) {
|
||||
this.$message.error('场景配置不完整:请确保每一行都填写场景名并绑定后端');
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
default_backend: this.llmForm.default_backend || '',
|
||||
backends: (this.llmForm.backends || []).map(item => {
|
||||
|
||||
Reference in New Issue
Block a user