feat(models): 「模型设置」接官方厂家目录 + 按官方页结构重做界面(档案 87 补做)

用户实测反馈:新增条目看不懂、厂家选择太少且国内一家都没有。根因是**第一版只做了手填表单**,没接官方 pi-ai 自带目录(实测 38 家,国内 11 家:蚂蚁 / 通义千问 / 小米 / 月之暗面 / 智谱 / MiniMax)。
- 新文件 src/web/model-catalog.ts:只读官方 data/*.json(10 分钟缓存,不导入 pi-ai 运行时);中文名走显示用静态表,选取范围以目录为准
- 新增 GET /api/me/model-providers(38 家 + cn 分组 + modelCount + 模型名;排除 deepseek —— 平台已有内置入口)
- POST /api/me/keys 新增 provider 参数:**目录厂家只需 API 密钥**(端点/协议/模型全由官方目录兜底 —— 官方 config.d.ts 明示 route 命中即继承默认)
- refForEntry 判据由「baseUrl 为空」改为「**没有 route**」:否则目录厂家的 key 会被写进 DEEPSEEK_API_KEY
- SettingsEntry 的 baseURL/api/models 改为可选:目录厂家只写 apiKeyEnv(写得越少越不容易漂)
- 前端按官方页结构重写:提供方行(厂家/说明/状态/操作)+ 新增**先选厂家**(中国大陆 / 国际分组 / 自定义)+ 保留并强化 admin 的「平台共享模型」区块
- 真环境验收:目录端点 38 家 / 国内 11 家;加 moonshotai-cn 只给 key → settings.yaml 仅 apiKeyEnv;删后整块消失
This commit is contained in:
admin committed 2026-09-14 00:15:17 +08:00
1 parent 918f1d3a51
commit 022b1f770c
7 files changed
+489 -121

No files matched your search

+22 -5
View File
@@ -65,6 +65,13 @@ const DATA = {
sharedModelEnabled: true,
protocols: ["openai-completions", "openai-responses", "anthropic-messages"],
},
// 档案 87 补做:官方 pi-ai 厂家目录(「新增」选择框的数据源)—— 国内 + 国际各一家代表
"/api/me/model-providers": {
providers: [
{ id: "moonshotai-cn", label: "月之暗面 Moonshot(中国)", api: "openai-completions", baseURL: "https://api.moonshot.cn/v1", cn: true, modelCount: 12, models: ["kimi-k2", "moonshot-v1-8k"] },
{ id: "openai", label: "OpenAI", api: "openai-responses", baseURL: null, cn: false, modelCount: 20, models: ["gpt-4o"] },
],
},
};
const sandbox = {
console, setTimeout, clearTimeout,
@@ -112,6 +119,8 @@ function text(n, out = [], depth = 0) {
// 表单控件的占位符也是**用户可见文案**(输入框没有 children)⇒ 一并收集,
// 否则「新增表单有哪些栏位」这类断言只能查到可见标签,漏掉真正的引导文案。
if (typeof n.props.placeholder === "string") out.push(n.props.placeholder);
// `<optgroup label="…">` 同理:分组名是 prop,不收就会漏掉「中国大陆 / 国际」这类分组断言。
if (n.type === "optgroup" && typeof n.props.label === "string") out.push(n.props.label);
text(n.props.children, out, depth + 1);
}
}
@@ -222,11 +231,19 @@ ok("非 admin:不出现任何功能项", !PORTAL_ITEMS.some(k => tUser.include
ok("模型设置:平台共享模型区块 + 开关按钮", tMs.includes("平台共享模型") && tMs.includes("停用共享模型"));
// 口径①:条目各自开关 ⇒ 要有「启用/停用」而不是「设为当前」
ok("模型设置:条目按各自状态渲染徽章(已启用 / 已停用)", tMs.includes("已启用") && tMs.includes("已停用"));
ok("模型设置:内置与自定义两类条目都在列表里", tMs.includes("内置 DeepSeek") && tMs.includes("我的中转网关"));
ok("模型设置:自定义条目显示厂家标识 / endpoint / 协议",
tMs.includes("my-gw") && tMs.includes("https://api.example.com/v1") && tMs.includes("openai-completions"));
ok("模型设置:新增表单(名称 / API Key / Endpoint / 模型清单栏位齐备)",
tMs.includes("新增模型条目") && tMs.includes("模型 id,一行一个") && tMs.includes("留空 = 内置 DeepSeek"));
ok("模型设置:内置条目显示为「内置 DeepSeek」、自定义条目显示其 route",
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("密钥留空则回落到平台共享密钥"));
ok("模型设置:词典备齐「只填密钥」相关文案(目录提示 / 展开模型)",
!!ZH["ms.catalogHint"] && !!ZH["ms.showModels"] && !!ZH["ms.hideModels"] && !!ZH["ms.pickProvider"]);
// 落地在 spawn 时 ⇒ 文案必须说清"重启才生效",否则用户会以为保存即生效
ok("模型设置:文案说明「重启实例生效」", tMs.includes("重启实例生效"));
const cMs = classes(await render(ms.Comp));