feat(web): 模型设置页复刻官方交互 + 官方推荐插件按 dsh 版本过滤(档案 91/92/93)
档案 91 ——「模型设置」页折行 + 复刻官方交互(插件 0.3.15) · 折行根因不是"窄":原「我已添加的厂家」是 4 列表格(列宽 auto)⇒ 名称/状态/两按钮三处折行同源; 改官方 dsh-client-ui-settings-models 的**卡片行**(rowHead + rowActions margin-left:auto,两侧 nowrap) · 「新增」改官方**两步式**:默认两个虚线按钮 → 点开才出卡片,主字段只剩「API 密钥」 (官方原文:页面从不询问环境变量名);次要字段收进 <details>「自定义设置」 · CSS 数值逐值照抄官方 CSS 模块;顺修「由 admin 配置」「密钥可留空」两处错文案,补删除二次确认 档案 92 —— 官方推荐插件列表按 dsh 版本过滤(插件 0.3.16) · 官方目录不带 dsh 版本字段 ⇒ 逐包查 npm 声明与平台真实版本比,分类 match/none/newer/older/unknown · 默认隐藏「仅兼容更旧版本」,「需更高版本」橙色标注;勾选框为逃生口;说明行给隐藏计数(不静默消失) · ⚠️ 判 satisfies 必须带 includePrerelease(平台是 prerelease,默认语义会把在跑的 dshmarket 之类判成更旧) 档案 93 —— 兼容判定收口(服务器 CI 由红转绿) · 「单测长期红」真相 = 服务器源码落后于 git(本机 48/0 全绿)⇒ 同步即修,非代码问题 · 伞包 @deepseek-ai/dsh 收口到 dsh-install.platformPackageVersion()(它不在自己的 node_modules 里, 原先恒判 null ⇒ 声明在伞包上的"要求更高版本"全被放行);闸门与列表共用同一份版本表 · 闸门判据 A 加 prerelease 兜底:实测抽样 300 里 prerelease-artifact 117 条(≈39% 假阳性, 含在跑的 dshmarket)⇒ 容忍下能满足则只计数(prereleaseOnly)不阻断 校验 · 新增 scripts/verify-dsh-compat.mjs(21 条回归锚点)与 verify-models-dict.mjs / verify-models-render.cjs · verify-platform-admin-section.mjs:断言从旧 UI(表格/下拉「自定义厂家…」)改成断言新 UI (含模拟点击两个新增入口 + 选中目录厂家) · 两个仓库外的动作:服务器 npm run build + ci.sh(CI OK)+ systemctl restart dshs;插件 0.3.16 已铺发 ⚠️ 本提交未包含 src/supervisor/orchestrator.ts —— 那是别人在途的内存配额改动 (BASE 160→448 / MIN 384→512 / MAX 1024→1536 / mcn 128→256),其对应的客户端常量尚未同步, 本机 verify-mem-model 因此有 4 项红;提交后仓库树两边均为旧值,该断言在 HEAD 上一致。
This commit is contained in:
1 parent
1d72e8f9c8
commit
29c57a8207
11 files changed
+1474
-209
No files matched your search
@@ -236,13 +236,41 @@ ok("非 admin:不出现任何功能项", !PORTAL_ITEMS.some(k => tUser.include
|
||||
tMs.includes("内置 DeepSeek") && tMs.includes("my-gw"));
|
||||
ok("模型设置:自定义条目副行显示 endpoint · 协议",
|
||||
tMs.includes("https://api.example.com/v1") && tMs.includes("openai-completions"));
|
||||
// 档案 87 补做:**按官方页的结构** —— 新增先「选厂家」,目录厂家只需填 API 密钥
|
||||
ok("模型设置:新增有厂家选择框(含内置 / 自定义两个端点选项)",
|
||||
tMs.includes("厂家:") && tMs.includes("内置 DeepSeek(平台共享)") && tMs.includes("自定义厂家(OpenAI 兼容网关)…"));
|
||||
ok("模型设置:选择框按「中国大陆 / 国际」分组,并列出目录厂家(含国内厂家)",
|
||||
tMs.includes("中国大陆") && tMs.includes("国际") && tMs.includes("月之暗面 Moonshot(中国)") && tMs.includes("OpenAI"));
|
||||
ok("模型设置:默认选中「内置 DeepSeek」时给出回落说明",
|
||||
tMs.includes("密钥留空则回落到平台共享密钥"));
|
||||
// ── 档案 91(2026-09-14):「新增」改官方**两步式** ──────────────────────────
|
||||
// 官方口径(`dsh-client-ui-settings-models` README.zh 原文):
|
||||
// · 「「新增」流程是一张承载休眠目录提供方选择框的卡片」
|
||||
// · 「编辑卡片上的主字段是单独一个 **API 密钥**输入框——页面从不询问环境变量名」
|
||||
// 所以默认态**只露两个虚线入口**,点开才出卡片(旧版是常开表单 + 下拉里混一项
|
||||
// 「自定义厂家(OpenAI 兼容网关)…」,两者形态不同 ⇒ 断言必须跟着换)。
|
||||
ok("模型设置:新增默认态只有两个虚线入口(添加提供方 / 添加自定义提供方)",
|
||||
tMs.includes("添加提供方") && tMs.includes("添加自定义提供方"));
|
||||
ok("模型设置:默认态**不再**出现厂家下拉与旧「自定义厂家…」选项",
|
||||
!tMs.includes("厂家:") && !tMs.includes("自定义厂家(OpenAI 兼容网关)"));
|
||||
// 点开「添加提供方」:出现「提供方」下拉(中国大陆 / 国际分组 + 目录厂家)+ 单一「API 密钥」主字段
|
||||
const tAdd = text(await renderWith(ms.Comp, () => { slots[5] = "add"; })).join(" | ");
|
||||
ok("模型设置:点开后有「提供方」下拉,按中国大陆 / 国际分组并列出目录厂家",
|
||||
tAdd.includes("提供方") && tAdd.includes("中国大陆") && tAdd.includes("国际") &&
|
||||
tAdd.includes("月之暗面 Moonshot(中国)") && tAdd.includes("OpenAI"));
|
||||
ok("模型设置:新增卡片主字段是「API 密钥」(内置回落占位符)",
|
||||
tAdd.includes("API 密钥") && tAdd.includes("输入你的 DeepSeek API 密钥"));
|
||||
ok("模型设置:次要字段收进「自定义设置」,并说明其余字段在 settings.yaml",
|
||||
tAdd.includes("自定义设置") && tAdd.includes("显示名称") && tAdd.includes("其余字段在 settings.yaml 中"));
|
||||
// 选中一个目录厂家 ⇒ 给出「端点/模型由目录兜底」的说明(档案 87 的核心口径)
|
||||
const tPick = text(await renderWith(ms.Comp, () => { slots[5] = "add"; slots[6] = "moonshotai-cn"; })).join(" | ");
|
||||
ok("模型设置:选目录厂家后提示「端点与 N 个模型由官方目录提供」",
|
||||
tPick.includes("由官方目录提供") && tPick.includes("12 个模型"));
|
||||
// 点开「添加自定义提供方」:官方字段序 + 模型改**行式编辑器**(旧版是 textarea 一行一个)
|
||||
const treeCustom = await renderWith(ms.Comp, () => { slots[5] = "custom"; });
|
||||
const tCustom = text(treeCustom).join(" | ");
|
||||
const cCustom = classes(treeCustom);
|
||||
ok("模型设置:自定义卡片含 Provider ID / API 地址 / API 协议 / 模型目录",
|
||||
tCustom.includes("Provider ID") && tCustom.includes("API 地址") &&
|
||||
tCustom.includes("API 协议") && tCustom.includes("模型目录"));
|
||||
// 行式编辑器:逐行条目(ms-modelEntry/ms-modelRow)+「添加模型」按钮;
|
||||
// 行尾删除按钮的文案只在 `aria-label`(文本收集器不收属性)⇒ 用词典键 + 图标按钮类来断言
|
||||
ok("模型设置:自定义模型目录是**行式编辑器**(逐行条目 + 添加模型 + 行尾删除)",
|
||||
cCustom.includes("ms-modelEntry") && cCustom.includes("ms-modelRow") && cCustom.includes("ms-iconBtn") &&
|
||||
tCustom.includes("添加模型") && !!ZH["ms.removeModel"]);
|
||||
// 2026-09-14 留痕:目录读不到时要**如实说明**,不能静默给个空列表
|
||||
{
|
||||
const saved = DATA["/api/me/model-providers"];
|
||||
@@ -252,20 +280,28 @@ ok("非 admin:不出现任何功能项", !PORTAL_ITEMS.some(k => tUser.include
|
||||
};
|
||||
const tBad = text(await render(ms.Comp)).join(" | ");
|
||||
ok("模型设置:目录不可读时如实提示(含目录路径,不静默空列表)",
|
||||
tBad.includes("未能读取官方厂家目录") && tBad.includes("/opt/nope/pi-ai"), tBad.slice(0, 0) + "命中");
|
||||
tBad.includes("未能读取官方提供方目录") && tBad.includes("/opt/nope/pi-ai"), tBad.slice(0, 0) + "命中");
|
||||
DATA["/api/me/model-providers"] = { providers: [], catalog: { dir: "/x", readable: true, count: 0 } };
|
||||
const tEmpty = text(await render(ms.Comp)).join(" | ");
|
||||
ok("模型设置:目录为空的文案与「读不到」不同",
|
||||
tEmpty.includes("官方厂家目录为空") && !tEmpty.includes("未能读取官方厂家目录"));
|
||||
tEmpty.includes("官方提供方目录为空") && !tEmpty.includes("未能读取官方提供方目录"));
|
||||
DATA["/api/me/model-providers"] = saved;
|
||||
}
|
||||
ok("模型设置:词典备齐「只填密钥」相关文案(目录提示 / 展开模型)",
|
||||
!!ZH["ms.catalogHint"] && !!ZH["ms.showModels"] && !!ZH["ms.hideModels"] && !!ZH["ms.pickProvider"]);
|
||||
ok("模型设置:词典备齐「只填密钥」相关文案(目录提示 / 展开模型 / 两个入口)",
|
||||
!!ZH["ms.keyHintCatalog"] && !!ZH["ms.showModels"] && !!ZH["ms.hideModels"] &&
|
||||
!!ZH["ms.addProvider"] && !!ZH["ms.addCustom"] && !!ZH["ms.customized"]);
|
||||
// 落地在 spawn 时 ⇒ 文案必须说清"重启才生效",否则用户会以为保存即生效
|
||||
ok("模型设置:文案说明「重启实例生效」", tMs.includes("重启实例生效"));
|
||||
// 档案 91:视觉层从「门户表格家族」换成**官方 CSS 模块家族**(`.ms-*`)——
|
||||
// 换掉表格的原因见档案 91:4 列 auto 列宽会把名称/按钮挤到折行。
|
||||
const cMs = classes(await render(ms.Comp));
|
||||
ok("模型设置:复用门户组件类(.pa-box/.pa-tbl/.pa-sm/.pa-badge/.pa-input)",
|
||||
["pa-box", "pa-tbl", "pa-sm", "pa-badge", "pa-input"].every(c => cMs.includes(c)));
|
||||
ok("模型设置:行用官方卡片行类(ms-rows/ms-rowCard/ms-rowHead/ms-rowIdentity/ms-rowActions)",
|
||||
["ms-rows", "ms-rowCard", "ms-rowHead", "ms-rowIdentity", "ms-rowActions"].every(c => cMs.includes(c)));
|
||||
ok("模型设置:新增入口用官方虚线按钮类(ms-addActions/ms-addBtn/ms-btn)",
|
||||
["ms-addActions", "ms-addBtn", "ms-btn"].every(c => cMs.includes(c)));
|
||||
ok("模型设置:**不再**用 4 列表格(pa-tbl / pa-sm / pa-input 已从本分区移除)",
|
||||
!cMs.includes("pa-tbl") && !cMs.includes("pa-sm") && !cMs.includes("pa-input"));
|
||||
ok("模型设置:保留门户 .pa-box 外壳与 .pa-badge 徽章", cMs.includes("pa-box") && cMs.includes("pa-badge"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user