mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-10-03 23:54:06 +00:00
## 现象
部署示例后打开 WebUI:侧栏有 Billing 页,但**状态页上没有任何计费组件**。
插件明明声明了 elements,/api/ui-inject 也确实返回了 mount(1570 字节)。
## 根因(不是缺功能)
七个宿主页面的渲染函数都用 `pane.innerHTML = ...` **整体替换**自己的 DOM。
`renderStatus` 在 `injectPluginUI()` 之后由 refresh() 立刻调用,于是刚挂上的
plugin-el 连同整个 pane 一起被下一次赋值销毁。
时序上它**从没有过"显示一帧"的机会**:注入 → refresh("status") → innerHTML 覆盖。
所以症状是"元素从来没出现过",而不是"刷新后消失"——这正是我先前据
/api/ui-inject 返回值判定"注入正常"而漏掉的地方:**载荷到达 ≠ DOM 存活**。
Billing 页不受影响,因为它属于 PLUGIN_PAGES,走插件自有 DOM,不经宿主重建。
于是看起来像"页面注入有效、元素注入无效",把排查引向插件声明本身。
## 修法
- mountPluginElements 改成具名可重入函数,并注册进 PLUGIN_MOUNT_HOOKS
- refresh() 在**唯一出口**统一调 remountPluginElements(),而不是给七个渲染函数
各加一次调用——后者是多一处会忘的地方,而忘记的后果是静默的
- 每个挂载点按 data-idx 幂等:宿主重绘时若该 pane 已有该元素就直接返回,
否则插件的 <script> 会每次重绘都跑一遍,计数器静默翻倍
## ★ 验证方式换了:真实浏览器,而不是 payload
静态测试和 curl 都看不出这个 bug(载荷完全正确)。用 CDP 连本机共享浏览器实测:
修复后:首屏 tile=1,页面重建后=1,连续重建 5 次仍=1,console 无错误
回退后:tile 全程=0
对照二进制(把 done 改成空函数重编译)实测首屏就是 0,
**证明"从未显示过",不是"显示后消失"**。
## 判据与变异
TestPluginElementsSurviveHostRebuild 锁住:remountPluginElements 存在、
PLUGIN_MOUNT_HOOKS 在使用之前声明(const TDZ 会让首屏直接抛错)、refresh 挂了重挂、
挂载按 data-idx 幂等。
三个变异全部被抓住:撤掉 refresh 的重挂 / 去掉幂等守卫 / 把 const 声明移到 push 之后。
★ 第一次跑第三个变异时**判据正确地没报**,因为我的替换脚本命中了注释里的同名文本,
真正的 const 没被移动——是变异无效,不是判据有洞。换按行定位后如期变红。
## 同时补上部署示例
packaging/config.example.yaml 里补 plugin_dir 说明(之前只有 online 部署路径踩过)。
实测升级路径本身是好的:给已有配置加 plugin_dir 后,首次启动会自动 seed 内置
billing 插件,无需手工放置文件。
44 lines
2.1 KiB
YAML
44 lines
2.1 KiB
YAML
# llmsproxy — 统一 OpenAI 兼容网关配置(示例模板)
|
||
#
|
||
# 这个是打包进发行包、供首次安装使用的模板。它故意不含任何可用的凭据:
|
||
# 首次启动时程序会生成一个随机 admin key 并写入实际配置(WebUI「密钥」页可见)。
|
||
#
|
||
# ⚠ 不要在本文件里填一个写死的具体 key。示例文件是公开的,写进去的任何值
|
||
# 都会成为一个全网皆知的 admin key(旧版示例里的 sk-gw-local-0001 就踩过
|
||
# 这个坑:它被直接抄进生产配置并真实可用)。
|
||
|
||
# 网关监听地址(默认 :8080,建议绑内网/回环)
|
||
listen: 127.0.0.1:8080
|
||
|
||
# 客户端访问本网关所需的 API Key(Bearer)。留空数组 = 不鉴权(仅内网)。
|
||
gateway_keys: []
|
||
|
||
# 默认模型选择:具体模型 id 或 AUTO。
|
||
# AUTO = 走 WebUI「优先级页」保存的调度链(存在本文件的 `auto:` 字段)。
|
||
default_model: AUTO
|
||
|
||
# Lua 适配器目录(默认 adapters/,首次启动自动写入内置适配器)
|
||
adapter_dir: adapters
|
||
|
||
# 插件目录(可选)。设置后:
|
||
# - 首次启动会把随核心发布的示例插件(billing:按源/模型/密钥计费 + 仪表盘)
|
||
# 写入本目录,它会在请求流水线上挂钩子,并向 WebUI 注入一个「Billing」页面
|
||
# 和状态页上的一块总开销组件;
|
||
# - 之后 WebUI「插件」页与 Electron 壳的设置面板可安装/禁用/删除/编辑。
|
||
# 目录一旦存在即以目录为准:删除或改写内置插件都是真实生效的操作。
|
||
# 留空 = 插件功能完全关闭(不影响网关其它功能)。
|
||
# plugin_dir: /etc/llmsproxy/plugins
|
||
|
||
# 运行时文件:存放 WebUI 管理的源模板、已删除标记、预置模板名单。
|
||
#
|
||
# 注意:**AUTO 调度链、网关密钥、上游源都存在本 config.yaml 里**,
|
||
# 不在这个文件。runtime.json 只管模板与删除标记。
|
||
runtime_file: runtime.json
|
||
|
||
# 全局并发上限(0 = 不限)
|
||
max_concurrent: 0
|
||
|
||
# ---- 上游 LLM 源列表 ----
|
||
# 留空:安装后到 WebUI 添加源,或从内置模板一键创建。
|
||
sources: []
|