Files
dsh_ai1net_server/dsh-server-docs/archive/交接单-已完成/T06-模型设置页-多厂家条目可开关.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
   保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
   工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
   必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
   + ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
   ⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
   验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

8.4 KiB
Raw Blame History

T06 · 平台自建「模型设置」页 —— 多厂家条目自助配置(各自开关、可同时启用)

  • 日期:2026-09-13
  • 状态:✅ 已完成并归档(后端已部署 + 插件 0.3.11 已铺发两实例 + 角色补丁已刷新;验收四件事见 §6)
  • 执行会话:craft-session-T06(原 lane 会话 rebuild-my-keys 失联,经用户明确授权解锁后接手)
  • 触发(用户 09-13 22:5x 三轮口径):官方「设置 → 模型」页在平台环境必然报错 ⇒ 改走平台自建页;条目要能各自开关、平台共享模型也要列入且可开关;具体用哪个模型在 dsh 对话框里选。
  • 档案:04-调整方案/87-模型设置页-用户自配厂家与共享开关.md(本轮全部技术结论的单一来源)

1. 目标(已达成)

让每个用户在实例内「设置 → 模型设置」自助管理多条模型条目(内置 DeepSeek + 自定义 OpenAI 兼容厂家),每条可独立开关、可同时启用;平台按「已启用」把配置写进实例($DSH_HOME/.credentials.yaml 的 refs: + $DSH_HOME/settings.yaml 的 llm-pi-ai.providers.<route>),用户再到 dsh 对话框的模型选择器里挑具体模型。

2. 只读前置(执行时的核实结果)

# 核实项 结果
1 工作树里哪些是本轮改动 src/db/*、src/web/{server.ts,routes/auth.ts,model-landing.ts}、ensure-role-profile-patch.cjs、poc/business-plugins/** = 本轮;admin-user-ops.ts / dsh.ts / portal.html / client.js 的改名段 = 档案 86
2 npx tsc --noEmit 应报「类未实现接口」 ✅ 如预期(sqlite.ts/pg.ts 各缺 5 项)→ 已补齐归零
3 「官方页为何不可用」的结论在哪 ⚠️ 不在档案 86(86 = admin 跨用户实例管理 + 两处改名,全文仅 §一–§七);真结论原只在代码注释里。本轮已挖到权威出处(官方 dsh-client-ui-settings/README.md:97)并落进档案 87 §一 —— 同时辨清了与 03-路线图 决策 3 的「两层」关系(见档案 87 §1.1)
4 线上状态 后端未构建未部署、插件 0.3.10 ✅(但角色补丁其实已被上一会话刷过——见 §7 教训)
5 0.3.11 这个版本号没被用过 ✅(本机 / /opt/dsh/artifacts / 两实例 profile 三方核对)

3. 范围

改了(详见档案 87 §四):src/db/{schema,types,repo,adapter,sqlite,pg}.ts · 新文件 src/web/model-landing.ts · src/web/server.ts · src/web/routes/auth.ts · poc/business-plugins/lib/client.js + package.json(→0.3.11) · ensure-role-profile-patch.cjs · test/db.test.mjs · scripts/verify-{model-landing,platform-admin-section,mem-model}.mjs · package.json(注册新验证脚本)

没动(守住了边界):官方 dsh 主程序与缓存 · web/portal.html 的「模型管理」页 · 04-调整方案/86 · /var/lib/** 之外的系统面 · systemd 单元 · nginx/nft · 内存配额档位。

4. 决策点

  • 用户已定三条口径(各自开关可同时启用 / 共享模型也列入且可开关 / 模型在 dsh 对话框选)—— 全程未再上抛。
  • 技术自决(可推翻):共享开关走单列查询不动 USER_COLS;getEnabledCredentialKeyRef 重定义为「已启用的内置 DeepSeek 条目」;selectCredentialKey 降级为非互斥同义实现;落地用成对标记夹住自己写的 route 块。
  • 开跑前唯一的未取证项(自定义厂家的 settings 字段名)= 已取证,且救回一个必须纠正的字段名:协议字段是 api,不是 protocol(§5)。

5. 步骤与验证(全部执行完毕)

  1. 抢锁 + 占号 → ✅(原 lane 会话失联;用户明确授权解锁,随后自持 craft-session-T06)
  2. 补 repo.ts 两个函数 + 重定义 getEnabledCredentialKeyRef → ✅
  3. 取证官方 schema → ✅ 读 [email protected]:分区名 llm-pi-ai、凭据字段 apiKeyEnv、endpoint baseURL、协议 api(三枚举)、ref 名须匹配 ^[A-Za-z_][A-Za-z0-9_]*$、provider/maxRetries 会直接抛错
  4. 补 sqlite.ts / pg.ts 各 5 项 → ✅ tsc 全绿
  5. 写落地层 model-landing.ts + 13 组回归 → ✅(回归当场抓出一个真 bug:providers: 被写重复 —— 第一版按"只看顶层行"找链,而 providers: 本身就是缩进 2 的子键)
  6. 改 server.ts(逐条落地 + 托管清单 + 一次性交接)→ ✅ 见 §6③
  7. 改 auth.ts 4 条路由 → ✅ 含 4 类非法入参被拒(400/409)
  8. 前端「模型设置」分区(插件 0.3.11)→ ✅ 无浏览器 harness 全绿(新增 8 条断言)
  9. 角色补丁 --force --restart → ✅ 先留回滚点再跑,跑完 diff 验证
  10. 收尾四件套 + 台账 → ✅

6. 验收(用户点名的四件事 —— 全部有证据)

# 验收项 证据
① 官方「模型」栏两账号都不再出现 两账号 cordis.patch.yml 各 1 处 - id: ui-settings-models + disabled: true;--restart 已生效(客户端 bundle 于实例启动时重打)。页面实看留用户确认(本机 agent-browser daemon 起不来,沿用档案 86 §七-3 的做法)
② 新页能配 / 能开关 / 能删 HTTP 实测(临时 session 直插,R4):新增内置条目 → effective: shared→own;新增自定义厂家 → route/base_url/api/models 全部落库;toggle 关/开 → enabled 0/1;非法入参:非 http baseUrl→400、缺 models→400、route 撞车→409、非法协议→400;前端 harness 全绿
③ 共享模型开关真的影响实例落地 关共享 + 重启 → 实例 .credentials.yaml 里 DEEPSEEK_API_KEY 消失(老实现写下的那行被"一次性交接"正确认领并撤掉),实例进程 env 里该变量 = 0 条;再打开 → 重启 → 该行回来且与自定义厂家的 ref 并存
④ 自定义厂家被 dsh 模型选择器认到 实例 settings.yaml 落地为官方 schema 原文(llm-pi-ai.providers.acctest-gw: {apiKeyEnv, baseURL, api: openai-completions, models: [...]});实例启动成功、journalctl 无 llm-pi-ai / settings-rejected 报错;删除该条目后重启 → 整块消失

本地全套(Node 22):单测 49 项 0 失败 + verify-inject / verify-static / verify-platform-admin-section / verify-mem-model / verify-model-landing 全绿。

7. 回滚

见档案 87 §七。要点:后端 npm run build && systemctl restart dshs;插件回落 0.3.10;角色补丁回滚件 = /opt/dsh/backups/patch-20260913-234511/(⚠️ 回滚它 = 把必然报错的官方页放回给用户,除非接受该副作用)。

8. 回报格式(已回填)

  • 每步命令与输出:见档案 87 §五/§六;
  • 取证结论:档案 87 §三(api 不是 protocol,是本轮最重要的一条外部事实);
  • 产物:插件 0.3.11 / 48,590 B / md5 15c8083f5db85b3fce6a8a15db133319,两实例 bundles=6 且含 @dsh-local/business-plugins;
  • 台账:本表 + 交接单/README.md §一 + INDEX.md;
  • commit:未提交(用户未要求;且本机代码仓含别人在途的档案 86 改动,不能一起提)。

附:本轮暴露的三个「顺便修 / 已上报」项(都属本 lane)

  1. 🔴 ensure-role-profile-patch.cjs 的整文件覆盖是颗雷(已修):它的 --force 升级分支原本 writeFileSync(patchPath, block),而 admin 的 cordis.patch.yml 里同时住着三个平台块(disable-hmr / workspace-scoped-picker / role patch)⇒ 一旦触发升级就会静默抹掉另两个(HMR 重新连、目录选择器退回无限制版)。修法 = 新增 stripManagedBlock() 只替换自己那段;实机 diff 验证另两块完整保留。
  2. ⚠️ verify-mem-model.mjs 陈旧断言(已修):档案 86 重构后 request.user!.id → user.id,该断言长期红着。
  3. ⚠️ 上一会话自述与线上不符:它称「未构建未部署」,但角色补丁实际已被它刷到线上(两账号的 patch 里已有 ADMIN_MODELS_BLOCK 的内容)。⚠️ 教训:别拿会话自述当线上事实,一律看服务器实际文件 —— 这正是本次「先看服务器再动手」救回两颗雷的原因。