Managed speech and OCR downloads previously used catalog-specific source URLs without a global selection. Platform Features now lets users choose ModelScope by default or Hugging Face, while Main validates and freezes that source for each download. Verified coverage remains explicit: downloads never mix artifacts or silently switch sources, and installed models plus ZIP imports stay source-independent. Release note: 可在“设置 → 平台功能 → 通用设置”中选择 ModelScope 或 Hugging Face 作为后续语音输入与 OCR 模型下载源;缺少完整已验证文件或下载失败时不会静默换源。
1010 lines
33 KiB
Markdown
1010 lines
33 KiB
Markdown
# GoodBuddy 平台功能页签与模型下载源设计
|
||
|
||
## 文档信息
|
||
|
||
| 项目 | 内容 |
|
||
| --- | --- |
|
||
| 文档类型 | 跨功能技术与产品架构 |
|
||
| 状态 | 已实现(语音输入与 OCR) |
|
||
| 版本 | 1.0 |
|
||
| 日期 | 2026-08-19 |
|
||
| 适用产品 | GoodBuddy 桌面端 |
|
||
| 目标平台 | Windows、macOS、Linux,x64 与 arm64 |
|
||
| 相关基线 | [统一界面设计系统](../../UI-DESIGN.md)、[文档解析与本地 OCR](../prd/document-processing/document-extraction-and-local-ocr.md)、[本地文本向量模型与连接设计](./local-text-embedding-model-design.md)、[全双工实时语音交互设计](./full-duplex-voice-design.md) |
|
||
|
||
本文定义“设置 → 平台功能”的二级页签结构,以及所有 GoodBuddy 托管本地模型共同使用的
|
||
“模型下载源”设置。首期下载源为 ModelScope 和 Hugging Face,默认使用 ModelScope。
|
||
|
||
模型下载源是用户明确选择的供应链路径。当前来源不可用或没有所选模型时,GoodBuddy
|
||
必须明确失败并提供设置入口,**不能静默切换到另一个下载源。**
|
||
|
||
---
|
||
|
||
## 1. 摘要与核心决策
|
||
|
||
1. “平台功能”保留现有一级设置分类,在分类内部增加共享 `PageTabs`:
|
||
- **通用设置**
|
||
- **魔法笔记**
|
||
2. “通用设置”首个功能为“模型下载源”。
|
||
3. 首期提供两个互斥选项:
|
||
- `modelscope`,用户文案为 **ModelScope**,默认值。
|
||
- `hugging-face`,用户文案为 **Hugging Face**。
|
||
4. 设置影响 GoodBuddy 管理的所有本地模型下载:
|
||
- 当前本地语音输入模型。
|
||
- 当前本地 OCR 模型。
|
||
- 规划中的应用托管文本向量模型。
|
||
- 后续接入统一模型目录的本地 ASR、TTS、VAD 或其他本地模型。
|
||
5. 设置不影响:
|
||
- 已安装模型及其当前选择。
|
||
- ZIP 导入和导出。
|
||
- Ollama 自行安装或拉取的模型。
|
||
- 云端模型调用。
|
||
- LLM Provider、Runtime、Skills、MCP 或扩展下载。
|
||
- GoodBuddy 应用自身的“检查更新源”。
|
||
6. 每个模型文件继续固定 Revision、字节数和 SHA-256。两个来源只有提供完全相同的预期
|
||
文件时,才表示同一个模型工件。
|
||
7. 一个模型在所选来源缺少任何必需文件时,整个模型在该来源标记“暂不可下载”。不能把
|
||
同一模型包的部分文件从 ModelScope 下载、部分文件从 Hugging Face 下载。
|
||
8. 下载任务启动时冻结来源。任务运行期间切换全局设置,不改变、重启或迁移该任务;新的
|
||
下载使用新来源。
|
||
9. 当前来源失败时,不自动重试另一个来源。错误可以提供“前往通用设置更换下载源”,由
|
||
用户明确选择。
|
||
10. 现有魔法笔记设置只移动到“魔法笔记”页签,不改变保存、启用或评论行为。
|
||
|
||
---
|
||
|
||
## 2. 实现基线
|
||
|
||
### 2.1 平台功能界面
|
||
|
||
“平台功能”现已使用两个二级页签:
|
||
|
||
- “通用设置”承载全局模型下载源。
|
||
- “魔法笔记”承载显示入口、AI 评论方式和 AI 评论形式。
|
||
|
||
页签复用共享 `PageTabs`,默认打开“通用设置”,不会把当前页签写入应用设置。
|
||
|
||
### 2.2 受管模型下载
|
||
|
||
- `SpeechModelManager` 管理语音输入模型。
|
||
- `DocumentOcrModelManager` 管理 OCR 模型。
|
||
|
||
两者均已接入全局模型下载源,并具备:
|
||
|
||
- Main 进程下载。
|
||
- 来源无关的固定文件名、字节数和 SHA-256。
|
||
- ModelScope 或 Hugging Face 的固定 Revision 与下载 Target。
|
||
- 随机暂存目录。
|
||
- 下载进度和取消。
|
||
- 完整校验后原子安装。
|
||
- ZIP 导入、导出和删除。
|
||
- 受管模型目录。
|
||
- 来源冻结、无静默回退和无跨来源文件混合。
|
||
- 只向 Renderer 暴露来源可用性,不暴露下载 URL 或重定向 Host。
|
||
|
||
### 2.3 当前应用设置
|
||
|
||
平台功能与检查更新共用版本化 `ApplicationSettingsStore`。`modelDownloadSource` 已加入
|
||
版本 7 设置,通过共享 Zod Schema、可信 IPC sender 校验和原子 JSON 写入持久化。版本
|
||
1 至 6 惰性迁移为 ModelScope;未知未来版本继续拒绝读取且不会覆盖用户文件。
|
||
|
||
---
|
||
|
||
## 3. 目标
|
||
|
||
### 3.1 用户目标
|
||
|
||
- 在一个固定入口选择后续本地模型从 ModelScope 还是 Hugging Face 下载。
|
||
- 清楚知道当前选择、默认值和影响范围。
|
||
- 切换来源时不影响已安装、已选择或正在使用的模型。
|
||
- 下载失败时知道实际使用了哪个来源。
|
||
- 所选来源没有模型时得到明确说明,并可以主动前往设置切换。
|
||
- 在“平台功能”中快速区分通用设置与魔法笔记。
|
||
|
||
### 3.2 产品目标
|
||
|
||
- 为语音、OCR、向量和后续本地模型建立一个共同的下载来源契约。
|
||
- 保持模型目录中固定 Revision、大小、SHA-256 和许可信息不变。
|
||
- 禁止模型管理器各自实现不同的来源回退和设置读取。
|
||
- 为未来平台功能保留可扩展的二级页签结构。
|
||
- 保持现有安装、ZIP、取消、原子替换和安全边界。
|
||
|
||
### 3.3 质量目标
|
||
|
||
- 同一模型从两个来源下载后,最终安装文件的名称、大小和 SHA-256 完全一致。
|
||
- 来源切换不会产生混合来源模型包。
|
||
- 活动下载可以准确显示其冻结来源。
|
||
- 设置迁移后所有现有用户默认获得 ModelScope,不丢失其他应用设置。
|
||
- ModelScope 或 Hugging Face 单独故障时,测试能证明应用不会请求另一个来源。
|
||
- 页签和来源选择均可通过键盘、屏幕阅读器、浅色、深色和窄窗口使用。
|
||
|
||
---
|
||
|
||
## 4. 非目标
|
||
|
||
首期不包含:
|
||
|
||
- 自定义模型下载源 URL。
|
||
- 根据网络、地域、速度、HTTP 状态或模型可用性自动选择来源。
|
||
- ModelScope 与 Hugging Face 的自动测速或自动排序。
|
||
- 同一下载任务内跨来源续传。
|
||
- 在来源失败后弹窗默认勾选另一个来源并继续。
|
||
- 修改 GoodBuddy 应用更新源。
|
||
- 安装、配置或管理 Ollama。
|
||
- 为云端 Provider 下载模型。
|
||
- 把模型权重打入 GoodBuddy 安装包。
|
||
- 将“通用设置”做成所有设置的重复入口。
|
||
- 创建空的“未来功能”页签。
|
||
- 改变现有魔法笔记的产品行为。
|
||
|
||
---
|
||
|
||
## 5. 术语
|
||
|
||
| 术语 | 定义 |
|
||
| --- | --- |
|
||
| 模型下载源 | 用户选择的 ModelScope 或 Hugging Face |
|
||
| 模型目录 | GoodBuddy 源码中经过验证的模型元数据集合 |
|
||
| 模型包 | 一个模型运行所需的全部必需文件和安装清单 |
|
||
| 下载目标 | 某个来源中一个固定模型文件的 URL 和仓库信息 |
|
||
| 规范工件 | 由文件名、字节数和 SHA-256 定义的来源无关文件 |
|
||
| 来源覆盖 | 一个来源是否为模型包的全部必需文件提供下载目标 |
|
||
| 冻结来源 | 下载操作启动时记录且在操作期间不变化的来源 |
|
||
| 来源回退 | 一个来源失败后由应用自动请求另一个来源 |
|
||
|
||
“模型来源”容易被理解为模型作者或 License 来源,因此用户界面统一使用“模型下载源”。
|
||
模型卡中的 License 和上游模型作者不随下载源改变。
|
||
|
||
---
|
||
|
||
## 6. 平台功能信息架构
|
||
|
||
### 6.1 页面结构
|
||
|
||
```text
|
||
设置
|
||
└─ 平台功能
|
||
├─ 通用设置
|
||
│ └─ 模型下载源
|
||
└─ 魔法笔记
|
||
├─ 显示魔法笔记入口
|
||
├─ AI 评论方式
|
||
└─ AI 评论形式
|
||
```
|
||
|
||
分类页只有一个一级标题“平台功能”。页签标题不重复渲染为第二个页面标题;每个面板内部
|
||
使用区块标题说明设置内容。
|
||
|
||
### 6.2 PageTabs
|
||
|
||
使用共享 `PageTabs`,建议使用 `segmented` 视觉变体:
|
||
|
||
```tsx
|
||
<PageTabs
|
||
ariaLabel="平台功能设置"
|
||
idPrefix="platform-features"
|
||
onChange={setActiveSection}
|
||
tabs={[
|
||
{ id: 'general', label: '通用设置' },
|
||
{ id: 'magic-notes', label: '魔法笔记' }
|
||
]}
|
||
value={activeSection}
|
||
variant="segmented"
|
||
/>
|
||
```
|
||
|
||
语义要求:
|
||
|
||
- 容器使用 `tablist`。
|
||
- 每项使用 `tab` 和 `aria-selected`。
|
||
- 面板使用 `tabpanel`,并由对应 Tab 控制。
|
||
- 左、右方向键切换,支持 Home、End。
|
||
- Tab 键离开页签组进入当前面板。
|
||
- 切换后焦点和可见面板保持一致。
|
||
- 不使用普通按钮组或 `SegmentedControl` 替代页签语义。
|
||
|
||
### 6.3 默认页签和持久化
|
||
|
||
- 每次打开“平台功能”默认进入“通用设置”。
|
||
- 当前页签只在本次设置页面生命周期内保留。
|
||
- 不把当前页签写入 `ApplicationSettingsStore`。
|
||
- 后续支持设置深链接时,可以通过明确的导航参数打开某个页签,不改变默认设置值。
|
||
|
||
### 6.4 未来扩展
|
||
|
||
未来新增页签时:
|
||
|
||
- 必须与“通用设置”和“魔法笔记”处于同级。
|
||
- 页签表示完整、独立的功能设置域。
|
||
- 通用设置只放跨功能的全局行为,不成为任意设置的杂物区。
|
||
- 页签超过可读数量时重新组织信息架构,不允许多行堆叠。
|
||
- 尚未提供的功能不显示空页签或“敬请期待”占位。
|
||
|
||
---
|
||
|
||
## 7. 通用设置界面
|
||
|
||
### 7.1 布局
|
||
|
||
```text
|
||
平台功能
|
||
管理通用平台行为与可选工作区能力
|
||
|
||
[ 通用设置 ] [ 魔法笔记 ]
|
||
|
||
┌ 本地模型 ──────────────────────────────────────────────┐
|
||
│ 模型下载源 │
|
||
│ 选择 GoodBuddy 托管本地模型后续下载使用的平台。 │
|
||
│ 已安装模型和 ZIP 导入不受影响。 │
|
||
│ │
|
||
│ ◉ ModelScope │
|
||
│ 默认,适合优先访问 ModelScope 的网络环境 │
|
||
│ │
|
||
│ ○ Hugging Face │
|
||
│ 适合可以稳定访问 Hugging Face 的网络环境 │
|
||
│ │
|
||
│ 当前选择:ModelScope │
|
||
└────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
页面只保留一个视觉上最突出的任务。模型下载源保存属于即时设置,不增加与来源选项竞争的
|
||
大型“保存全部”按钮。
|
||
|
||
### 7.2 控件
|
||
|
||
来源选项包含说明和未来可能的不可用原因,因此使用语义化 Radio Group,而不是原生
|
||
`select` 或 `SegmentedControl`:
|
||
|
||
- `fieldset` + `legend` 表达“模型下载源”。
|
||
- 每个选项使用 Radio 和整行可点击卡片。
|
||
- 当前项同时显示选中 Radio、强调边框和选中背景。
|
||
- 不能只用站点 Logo 或颜色表达选择。
|
||
- Logo 可作为辅助图形,但站点名称必须始终显示为文字。
|
||
|
||
用户文案固定为:
|
||
|
||
- `ModelScope`
|
||
- `Hugging Face`
|
||
|
||
内部标识不直接显示。
|
||
|
||
### 7.3 说明文案
|
||
|
||
区块说明:
|
||
|
||
> 选择 GoodBuddy 托管本地模型后续下载使用的平台。已安装模型、ZIP 导入、Ollama
|
||
> 模型和应用更新不受影响。
|
||
|
||
来源下方说明:
|
||
|
||
- ModelScope:`默认,适合优先访问 ModelScope 的网络环境。`
|
||
- Hugging Face:`适合可以稳定访问 Hugging Face 的网络环境。`
|
||
|
||
不使用“国内源”“国外源”等绝对地理描述,也不保证任一来源在用户网络中一定更快。
|
||
|
||
### 7.4 保存交互
|
||
|
||
- 用户选择另一个 Radio 后立即调用应用设置更新。
|
||
- 保存期间禁用两个选项,保留最后确认值。
|
||
- 成功后更新当前选择,并通过应用通知提示:
|
||
`模型下载源已切换为 Hugging Face。`
|
||
- 失败时恢复最后确认值,在 Radio Group 附近显示可重试错误。
|
||
- 同一个失败不再同时显示页面横幅和全局错误通知。
|
||
|
||
来源变化不触发:
|
||
|
||
- 模型下载。
|
||
- 已安装模型验证。
|
||
- 模型删除。
|
||
- 当前模型切换。
|
||
- 活动下载取消。
|
||
|
||
### 7.5 活动下载说明
|
||
|
||
来源选项下方持续说明:
|
||
|
||
> 正在进行的模型下载会继续使用启动时的来源;新的下载使用当前选择。
|
||
|
||
首期不为这段说明新增跨语音、OCR 和向量管理器的活动任务聚合 IPC。具体活动操作继续在
|
||
对应模型卡显示冻结来源、进度和取消入口。
|
||
|
||
---
|
||
|
||
## 8. 魔法笔记页签
|
||
|
||
现有魔法笔记卡完整移动到“魔法笔记”面板:
|
||
|
||
- 显示魔法笔记入口。
|
||
- AI 评论方式。
|
||
- AI 评论形式。
|
||
|
||
保持以下行为不变:
|
||
|
||
- 开关继续使用共享 Switch 和 `role="switch"`。
|
||
- 评论方式和形式继续使用 `SegmentedControl`。
|
||
- 每项继续即时保存。
|
||
- 保存失败保留最后确认设置。
|
||
- `onMagicNotesEnabledChange` 继续更新应用导航入口。
|
||
|
||
页签重构不能:
|
||
|
||
- 重置现有设置。
|
||
- 在切换页签时保存或改变值。
|
||
- 因魔法笔记入口关闭而隐藏“魔法笔记”设置页签。
|
||
- 把页签选择误当成启用开关。
|
||
|
||
---
|
||
|
||
## 9. 设置契约与迁移
|
||
|
||
### 9.1 共享契约
|
||
|
||
扩展 `src/shared/application-settings-contracts.ts`:
|
||
|
||
```ts
|
||
export const modelDownloadSourceSchema = z.enum([
|
||
'modelscope',
|
||
'hugging-face'
|
||
])
|
||
|
||
export type ModelDownloadSource = z.infer<
|
||
typeof modelDownloadSourceSchema
|
||
>
|
||
|
||
type ApplicationPreferences = {
|
||
checkUpdatesOnStartup: boolean
|
||
updateSource: 'github' | 'mirror'
|
||
modelDownloadSource: ModelDownloadSource
|
||
magicNotesEnabled: boolean
|
||
magicNoteCommentMode: MagicNoteCommentMode
|
||
magicNoteCommentFormat: MagicNoteCommentFormat
|
||
}
|
||
```
|
||
|
||
`applicationSettingsUpdateSchema` 继续允许有界的 Partial 更新,不允许未知字段或空更新。
|
||
|
||
### 9.2 默认值
|
||
|
||
```ts
|
||
modelDownloadSource: 'modelscope'
|
||
```
|
||
|
||
默认值适用于:
|
||
|
||
- 首次安装。
|
||
- 没有应用设置文件。
|
||
- 从旧设置版本迁移。
|
||
- 设置文件损坏并完成现有隔离恢复流程。
|
||
|
||
恢复损坏设置时继续显示现有应用设置恢复警告,不把来源恢复伪装成用户选择。
|
||
|
||
### 9.3 设置版本
|
||
|
||
`ApplicationSettingsStore` 增加新版本,例如从当前版本 6 升级到 7:
|
||
|
||
```ts
|
||
type StoredApplicationSettingsV7 = {
|
||
version: 7
|
||
checkUpdatesOnStartup: boolean
|
||
updateSource: 'github' | 'mirror'
|
||
modelDownloadSource: 'modelscope' | 'hugging-face'
|
||
magicNotesEnabled: boolean
|
||
magicNoteCommentMode: MagicNoteCommentMode
|
||
magicNoteCommentFormat: MagicNoteCommentFormat
|
||
lastSeenReleaseNotesVersion: string | null
|
||
}
|
||
```
|
||
|
||
迁移要求:
|
||
|
||
- 版本 1 至 6 全部迁移为 `modelDownloadSource: 'modelscope'`。
|
||
- 保留更新源、魔法笔记和已读发布说明版本。
|
||
- 读取不立即写盘,继续沿用设置存储现有的惰性迁移语义。
|
||
- 下一次设置更新或发布说明确认时按新版本原子写入。
|
||
- 更高未知版本继续拒绝读取,不覆盖用户文件。
|
||
|
||
### 9.4 设置服务
|
||
|
||
首期继续复用现有:
|
||
|
||
- `settings:application:get`
|
||
- `settings:application:update`
|
||
- `window.goodbuddy.updates.getSettings()`
|
||
- `window.goodbuddy.updates.updateSettings()`
|
||
|
||
虽然 Preload Namespace 名称为 `updates`,底层契约已经承载应用设置。此功能不要求为了一个
|
||
字段进行无关的桥接重命名。未来如果拆分 `applicationSettings` Namespace,必须保持迁移期
|
||
兼容并避免两套设置源。
|
||
|
||
---
|
||
|
||
## 10. 模型目录契约
|
||
|
||
### 10.1 来源无关的规范工件
|
||
|
||
当前每个文件把 URL、大小和 SHA-256 放在同一个 `download` 对象中。新契约把工件身份与
|
||
下载目标拆开:
|
||
|
||
```ts
|
||
type ModelArtifactTarget = {
|
||
url: string
|
||
repositoryUrl: string
|
||
revision: string
|
||
redirectHosts?: string[]
|
||
}
|
||
|
||
type ModelArtifactFile = {
|
||
name: string
|
||
role: string
|
||
size: number
|
||
sha256: string
|
||
targets: Partial<
|
||
Record<ModelDownloadSource, ModelArtifactTarget>
|
||
>
|
||
}
|
||
|
||
type ManagedModelCatalogEntry = {
|
||
id: string
|
||
displayName: string
|
||
files: ModelArtifactFile[]
|
||
}
|
||
```
|
||
|
||
`size` 和 `sha256` 位于来源外层,表示两个来源必须提供同一规范工件。
|
||
|
||
如果两个站点提供的文件不是完全相同的字节:
|
||
|
||
- 不能把它们放入同一文件的两个 Target。
|
||
- 不能为了“兼容”使用两个 SHA-256。
|
||
- 应创建不同模型工件版本、Variant 或不同模型 ID。
|
||
- 每个 Variant 分别经过运行时和质量验证。
|
||
|
||
Renderer 使用的目录快照不暴露下载 URL、重定向 Host 或文件存储地址,只返回来源可用性:
|
||
|
||
```ts
|
||
type ModelDownloadAvailability = {
|
||
source: ModelDownloadSource
|
||
available: boolean
|
||
totalBytes?: number
|
||
unavailableReason?: string
|
||
}
|
||
|
||
type ManagedModelCatalogView = {
|
||
id: string
|
||
displayName: string
|
||
downloadAvailability: ModelDownloadAvailability[]
|
||
}
|
||
|
||
type ManagedModelSnapshot = {
|
||
selectedDownloadSource: ModelDownloadSource
|
||
catalog: ManagedModelCatalogView[]
|
||
installed: InstalledManagedModel[]
|
||
operations: ManagedModelOperation[]
|
||
}
|
||
```
|
||
|
||
“打开模型仓库”继续通过 Main 中按模型 ID 和当前来源解析的专用 IPC 完成,不让 Renderer
|
||
提交或接收任意仓库 URL。
|
||
|
||
### 10.2 模型来源覆盖
|
||
|
||
一个模型在某个来源可下载,当且仅当:
|
||
|
||
1. 每个必需文件都有该来源的 Target。
|
||
2. 每个 Target 使用固定 Revision,不使用 `main`、`latest` 或可变 Tag。
|
||
3. URL、仓库 URL 和重定向 Host 通过 Schema 和来源策略校验。
|
||
4. 文件大小和 SHA-256 已独立验证。
|
||
|
||
只要缺少一个文件,该模型在该来源整体不可下载。
|
||
|
||
禁止:
|
||
|
||
```text
|
||
detection.onnx ← ModelScope
|
||
recognition.onnx ← Hugging Face
|
||
dictionary.yml ← ModelScope
|
||
```
|
||
|
||
即使最终 SHA-256 正确,也不能在一次任务中混合来源,因为用户选择和审计语义将不再准确。
|
||
|
||
### 10.3 仓库入口
|
||
|
||
“打开模型仓库”使用当前所选下载源对应的 `repositoryUrl`:
|
||
|
||
- 当前来源有完整覆盖时,打开对应来源仓库。
|
||
- 当前来源无覆盖但另一个来源有覆盖时,按钮保持可读但禁用,并说明原因。
|
||
- 不自动打开另一个来源的仓库。
|
||
- 已安装模型可以显示“安装来源”,但打开仓库仍遵守当前选择,避免把历史来源当成全局值。
|
||
|
||
### 10.4 目录校验
|
||
|
||
启动时对整个模型目录执行静态校验:
|
||
|
||
- 模型 ID 唯一。
|
||
- 文件名和角色唯一。
|
||
- 每个来源 Target 的 URL 和 Revision 有效。
|
||
- 至少一个来源完整覆盖可下载模型。
|
||
- 非手动模型不得在两个来源都缺失。
|
||
- `repositoryUrl` 与下载 Target 属于同一声明来源。
|
||
- 重定向 Host Allowlist 有界且不包含通配公网域名。
|
||
|
||
目录错误应在开发和测试阶段阻止启动或测试,不在运行时猜测修复。
|
||
|
||
### 10.5 当前已验证覆盖
|
||
|
||
当前目录只声明已经逐文件核对字节数和 SHA-256 的来源:
|
||
|
||
| 模型 | ModelScope | Hugging Face |
|
||
| --- | --- | --- |
|
||
| PP-OCRv6 Tiny / Small / Medium | 可下载 | 可下载 |
|
||
| SenseVoiceSmall INT8 | 可下载 | 可下载 |
|
||
| Whisper Tiny | 可下载 | 可下载 |
|
||
| Paraformer 中英双语 / 中英日三语 | 暂不可下载 | 可下载 |
|
||
| Whisper Small / Medium | 暂不可下载 | 可下载 |
|
||
|
||
OCR 模型包的检测模型与识别模型来自同一下载源中的不同上游仓库。因此每个 Target 都必须
|
||
符合所声明来源的 Host 策略,而模型卡的主仓库入口必须对应到该来源至少一个实际文件
|
||
Target。语音模型当前每个模型包的 Target 与主仓库入口保持一致。
|
||
|
||
未找到可独立验证且字节完全一致的 Target 时,目录保留部分覆盖,不通过替换模型、
|
||
改写摘要或请求另一个来源来伪造双来源支持。
|
||
|
||
---
|
||
|
||
## 11. 下载任务
|
||
|
||
### 11.1 启动
|
||
|
||
Renderer 发起安装时提交:
|
||
|
||
```ts
|
||
type ManagedModelInstallInput = {
|
||
modelId: string
|
||
expectedDownloadSource: ModelDownloadSource
|
||
}
|
||
```
|
||
|
||
Main 必须:
|
||
|
||
1. 从 `ApplicationSettingsStore` 重新读取已保存来源。
|
||
2. 验证其与 `expectedDownloadSource` 一致。
|
||
3. 在模型目录中解析该来源的完整模型包。
|
||
4. 创建记录冻结来源的操作。
|
||
5. 只使用该解析结果完成全部文件下载。
|
||
|
||
`expectedDownloadSource` 只用于检测 Renderer 快照过期,不能覆盖 Main 中的已保存设置。
|
||
|
||
如果两个值不一致:
|
||
|
||
- 不开始下载。
|
||
- 返回“模型下载源已变化,请刷新后重试”。
|
||
- 不使用 Renderer 提交的来源。
|
||
|
||
### 11.2 操作快照
|
||
|
||
扩展模型操作:
|
||
|
||
```ts
|
||
type ManagedModelOperation = {
|
||
modelId: string
|
||
kind: 'download' | 'import'
|
||
downloadSource?: ModelDownloadSource
|
||
phase: 'preparing' | 'transferring' | 'installing'
|
||
currentFile: string | null
|
||
completedBytes: number
|
||
totalBytes: number | null
|
||
}
|
||
```
|
||
|
||
- 下载操作必须包含 `downloadSource`。
|
||
- ZIP 导入不包含 `downloadSource`。
|
||
- UI 使用操作快照显示“正在从 ModelScope 下载”。
|
||
- 全局设置变化不修改现有操作对象的来源。
|
||
|
||
### 11.3 来源解析
|
||
|
||
语音与 OCR 管理器复用共享的 `resolveModelDownloadPackage` 契约函数:
|
||
|
||
```ts
|
||
resolveModelDownloadPackage(files, source)
|
||
```
|
||
|
||
解析结果是不可变快照,包含:
|
||
|
||
- 来源。
|
||
- 全部文件的 URL、大小、SHA-256。
|
||
- 允许的重定向 Host。
|
||
|
||
模型管理器不直接拼接 ModelScope 或 Hugging Face URL,也不自行尝试第二来源。
|
||
|
||
### 11.4 设置切换期间
|
||
|
||
来源设置更新和模型下载任务相互独立:
|
||
|
||
```text
|
||
10:00 语音模型下载从 ModelScope 启动
|
||
10:01 用户把全局来源切换为 Hugging Face
|
||
10:01 当前语音下载继续使用 ModelScope
|
||
10:02 新 OCR 下载使用 Hugging Face
|
||
```
|
||
|
||
不能:
|
||
|
||
- 取消旧任务后从新来源重新开始。
|
||
- 让当前文件继续从旧来源、下一文件切到新来源。
|
||
- 把进度归零但不告诉用户。
|
||
- 在设置更新完成前启动使用草稿来源的下载。
|
||
|
||
---
|
||
|
||
## 12. 模型管理界面
|
||
|
||
### 12.1 来源状态
|
||
|
||
语音、OCR、向量和后续模型卡统一显示:
|
||
|
||
- 当前全局下载源。
|
||
- 当前模型在该来源是否可下载。
|
||
- 活动操作的冻结来源。
|
||
- 已安装模型的安装和校验状态。
|
||
|
||
未安装且来源可用:
|
||
|
||
```text
|
||
可从 Hugging Face 下载 · 约 91 MB
|
||
[下载]
|
||
```
|
||
|
||
未安装且来源不可用:
|
||
|
||
```text
|
||
Hugging Face 暂不提供此模型的完整已验证文件。
|
||
[前往通用设置]
|
||
```
|
||
|
||
不能显示可点击“下载”后才告诉用户缺少某个文件。
|
||
|
||
### 12.2 已安装模型
|
||
|
||
已安装模型不依赖当前下载源:
|
||
|
||
- 切换来源后继续可用。
|
||
- 不重新验证网络地址。
|
||
- 继续按安装 Manifest 中的大小和 SHA-256 验证。
|
||
- 不自动重新下载。
|
||
- 不改变当前模型选择。
|
||
|
||
当前安装 Manifest 保持来源无关,只记录运行和离线迁移需要的模型 ID、文件名、大小与
|
||
SHA-256。下载来源记录在活动操作快照中,尚未持久化到安装 Manifest。未来如需持久化
|
||
审计来源,可以新增:
|
||
|
||
```ts
|
||
type InstalledModelProvenance = {
|
||
installKind: 'download' | 'archive' | 'local-directory'
|
||
downloadSource?: ModelDownloadSource
|
||
catalogDigest: string
|
||
}
|
||
```
|
||
|
||
该扩展必须向后兼容;不能根据文件路径、模型 ID 或当前全局设置猜测旧安装的来源。
|
||
|
||
### 12.3 ZIP 导入与导出
|
||
|
||
ZIP 是来源无关的离线迁移方式:
|
||
|
||
- 导入始终按模型 ID、文件名、大小和 SHA-256 验证。
|
||
- 当前下载源不参与导入兼容判断。
|
||
- 从 ModelScope 下载的模型可以在选择 Hugging Face 的设备上导入。
|
||
- 导出可以保留原安装来源作为审计元数据,但不能限制另一设备导入。
|
||
- 导入失败不触发网络下载。
|
||
|
||
### 12.4 文案
|
||
|
||
所有硬编码文案改为来源感知:
|
||
|
||
- `正在从 {{source}} 下载`
|
||
- `打开 {{source}} 模型仓库`
|
||
- `当前下载源暂不提供此模型`
|
||
- `前往通用设置更换下载源`
|
||
|
||
不再写死:
|
||
|
||
- `请从 ModelScope 下载`
|
||
- `打开 ModelScope`
|
||
- `正在从 ModelScope 下载`
|
||
|
||
---
|
||
|
||
## 13. 不静默切换契约
|
||
|
||
### 13.1 禁止行为
|
||
|
||
以下行为全部禁止:
|
||
|
||
- ModelScope 网络失败后请求 Hugging Face。
|
||
- Hugging Face 返回 404 后请求 ModelScope。
|
||
- 当前来源缺少一个文件时从另一个来源补齐。
|
||
- 当前来源速度慢时自动测速并切换。
|
||
- 使用环境变量覆盖 UI 已保存来源。
|
||
- 为某一种模型单独保留隐藏的“优先来源”。
|
||
- 同一模型管理器使用与全局设置不同的默认值。
|
||
- 把 CDN 重定向误表示为切换到另一个模型下载源。
|
||
|
||
### 13.2 允许的同来源恢复
|
||
|
||
以下恢复可以自动执行:
|
||
|
||
- 同一固定 Target 的有界网络重试。
|
||
- 同一来源声明的固定镜像 Endpoint。
|
||
- 同一来源明确允许的 CDN 重定向。
|
||
- `.partial` 暂存文件内的安全续传,前提是服务支持且完整文件最终通过 SHA-256。
|
||
|
||
这些恢复必须保持:
|
||
|
||
- 同一用户选择来源。
|
||
- 同一模型 ID 和 Revision。
|
||
- 同一预期大小和 SHA-256。
|
||
- 同一冻结下载任务。
|
||
|
||
### 13.3 用户主动切换
|
||
|
||
失败状态提供:
|
||
|
||
1. 重试当前来源。
|
||
2. 前往“平台功能 → 通用设置”。
|
||
3. 取消。
|
||
|
||
用户切换来源后需要重新点击下载。不能在设置保存后自动恢复上一失败任务。
|
||
|
||
---
|
||
|
||
## 14. 网络与供应链安全
|
||
|
||
### 14.1 URL
|
||
|
||
- 只允许 HTTPS 下载;开发测试 Fixture 可以注入受控 Transport。
|
||
- URL 必须来自源码内置目录,不接受 Renderer 自由输入。
|
||
- 禁止 URL 中的用户名和密码。
|
||
- Fragment 在目录校验时拒绝。
|
||
- 查询参数不写入日志和错误文案。
|
||
- 模型卡只显示来源名称和仓库,不显示带签名的最终 CDN URL。
|
||
|
||
### 14.2 重定向
|
||
|
||
ModelScope 和 Hugging Face 都可能跳转到文件存储或 CDN。每个来源 Adapter 维护明确的
|
||
允许 Host,或者由固定 Target 提供有界 `redirectHosts`:
|
||
|
||
- 重定向次数有上限。
|
||
- 每一步重新校验 HTTPS 和 Host。
|
||
- 不允许跳转到另一下载源的站点。
|
||
- 不允许通配任意公网 Host。
|
||
- 最终内容仍按固定大小和 SHA-256 验证。
|
||
|
||
重定向到已声明 CDN 属于同一下载源内部传输,不构成下载源切换。UI 继续显示用户选择的
|
||
逻辑来源。
|
||
|
||
### 14.3 凭据和隐私
|
||
|
||
- 两个公开模型源首期不要求用户凭据。
|
||
- 不把 LLM、Embedding、Runtime 或渠道 API Key 附加到模型下载请求。
|
||
- 不继承浏览器 Cookie。
|
||
- 不上传已安装模型清单、知识库、对话或设备文件。
|
||
- 常规 User-Agent 可以包含应用名称和版本,不包含用户 ID 或机器标识。
|
||
- 下载错误只记录来源、模型 ID、文件角色、HTTP 状态和有界网络分类。
|
||
|
||
### 14.4 校验
|
||
|
||
无论来源如何,安装成功必须满足:
|
||
|
||
- Content-Length 未超过上限。
|
||
- 实际完整字节数匹配目录。
|
||
- SHA-256 匹配目录。
|
||
- 所有必需文件存在且角色匹配。
|
||
- 安装 Manifest 完整。
|
||
- 正式目录通过原子替换创建。
|
||
|
||
来源可信不替代文件校验,HTTPS 成功也不证明模型工件正确。
|
||
|
||
---
|
||
|
||
## 15. 与其他设置和能力的关系
|
||
|
||
### 15.1 检查更新源
|
||
|
||
“模型下载源”与“检查更新源”是两个独立设置:
|
||
|
||
| 设置 | 位置 | 作用 |
|
||
| --- | --- | --- |
|
||
| 模型下载源 | 平台功能 → 通用设置 | GoodBuddy 托管本地模型 |
|
||
| 检查更新源 | 关于与更新 | GoodBuddy 版本检查和下载页 |
|
||
|
||
切换任一设置不得修改另一个设置。用户选择 ModelScope 不表示应用更新使用镜像节点;选择
|
||
Hugging Face 也不表示应用更新使用 GitHub。
|
||
|
||
### 15.2 Ollama
|
||
|
||
Ollama 的模型下载由用户和 Ollama 管理。GoodBuddy 的模型下载源不:
|
||
|
||
- 改写 Ollama Registry。
|
||
- 影响 `ollama pull`。
|
||
- 安装或更新 Ollama 模型。
|
||
- 改变 Ollama Endpoint。
|
||
|
||
### 15.3 云端模型
|
||
|
||
云端 LLM、Embedding、Rerank、语音和图像 Provider 不下载本地权重,因此不受该设置
|
||
影响。
|
||
|
||
### 15.4 模型运行
|
||
|
||
模型下载源只决定获取规范工件的位置,不进入:
|
||
|
||
- 推理配置。
|
||
- 向量 Provider Fingerprint。
|
||
- 语音识别模型选择。
|
||
- OCR 模型选择。
|
||
- 模型质量或速度标签。
|
||
|
||
只要最终模型文件 SHA-256 相同,从 ModelScope 或 Hugging Face 安装后运行语义必须完全
|
||
一致。
|
||
|
||
---
|
||
|
||
## 16. 失败与恢复
|
||
|
||
| 场景 | 行为 |
|
||
| --- | --- |
|
||
| 读取设置失败 | 显示平台功能阻塞错误,不猜测或写入来源 |
|
||
| 保存来源失败 | 保留上一个确认值,显示就地错误 |
|
||
| 当前来源无完整模型包 | 下载按钮禁用,显示前往通用设置 |
|
||
| 当前来源网络不可用 | 当前下载失败或允许同来源重试,不请求另一来源 |
|
||
| 当前来源返回 404 | 显示来源缺少固定文件,不请求另一来源 |
|
||
| 重定向 Host 不受信任 | 立即失败并删除本次暂存 |
|
||
| 文件大小不匹配 | 立即失败并删除本次暂存 |
|
||
| SHA-256 不匹配 | 立即失败并删除本次暂存 |
|
||
| 设置在下载期间变化 | 当前任务继续冻结来源,新任务使用新来源 |
|
||
| Renderer 来源快照过期 | 拒绝启动,要求刷新 |
|
||
| ZIP 导入期间切换来源 | 导入不受影响 |
|
||
| 已安装模型缺少来源元数据 | 继续可用,显示安装来源未知 |
|
||
| 应用关闭 | 取消下载并清理本次暂存 |
|
||
|
||
错误必须包含可执行下一步,但不能默认执行来源切换。
|
||
|
||
---
|
||
|
||
## 17. 测试
|
||
|
||
### 17.1 设置契约
|
||
|
||
- 接受 `modelscope` 和 `hugging-face`。
|
||
- 拒绝未知来源、空值和多余字段。
|
||
- Partial 更新不覆盖魔法笔记和更新源。
|
||
- 新安装默认 ModelScope。
|
||
- 版本 1 至 6 全部迁移为 ModelScope。
|
||
- 更高未知版本拒绝,不覆盖文件。
|
||
- 并发设置更新保持完整 JSON。
|
||
|
||
### 17.2 平台功能界面
|
||
|
||
- 默认选中“通用设置”Tab。
|
||
- Tab 使用 `tablist`、`tab`、`tabpanel` 和 `aria-selected`。
|
||
- 方向键、Home、End 和焦点恢复正确。
|
||
- 切换到魔法笔记不改变任何设置。
|
||
- Radio Group 具有持久 Label。
|
||
- 选择来源时保存一次,保存期间防止重复提交。
|
||
- 保存失败恢复原值并保留可重试错误。
|
||
- 窄窗口不隐藏页签或来源名称。
|
||
|
||
### 17.3 目录
|
||
|
||
对语音、OCR 和向量目录分别验证:
|
||
|
||
- 两个来源解析到相同文件名、大小和 SHA-256。
|
||
- 缺少任一 Target 时模型在该来源不可下载。
|
||
- 不允许一次任务混合来源。
|
||
- 可变 Revision 被拒绝。
|
||
- 仓库 URL、下载 URL 和重定向 Host 符合来源策略。
|
||
- 重复模型、文件和角色被拒绝。
|
||
|
||
### 17.4 下载管理器
|
||
|
||
使用注入的受控 Fetch 验证:
|
||
|
||
- ModelScope 选择只请求 ModelScope Target。
|
||
- Hugging Face 选择只请求 Hugging Face Target。
|
||
- 一个来源失败时另一个来源零请求。
|
||
- 设置中途变化不改变活动任务。
|
||
- 新任务使用最新已保存来源。
|
||
- Renderer 提交过期来源时零网络请求。
|
||
- 两个来源的安装 Manifest 文件摘要相同。
|
||
- 取消、关闭、错误和摘要不匹配不留下正式模型目录。
|
||
|
||
### 17.5 回归
|
||
|
||
- 已安装语音模型继续可选择和转写。
|
||
- 已安装 OCR 模型继续可解析。
|
||
- ZIP 导入导出在任一来源设置下可用。
|
||
- 魔法笔记入口、评论方式和评论形式保持一致。
|
||
- 检查更新源行为不变。
|
||
- 文档、向量和语音推理不因下载源变化而改变。
|
||
|
||
---
|
||
|
||
## 18. 实施阶段
|
||
|
||
### 阶段 1:应用设置和页签(已完成)
|
||
|
||
- 新增 `ModelDownloadSource` Schema 和默认值。
|
||
- 将应用设置版本升级并迁移旧版本。
|
||
- 在“平台功能”中增加 `PageTabs`。
|
||
- 增加“通用设置”与模型下载源 Radio Group。
|
||
- 把现有魔法笔记卡移动到“魔法笔记”面板。
|
||
|
||
### 阶段 2:共享目录和来源解析(已完成)
|
||
|
||
- 定义来源无关的规范工件与分来源 Target。
|
||
- 实现共享 `ModelDownloadTargetResolver`。
|
||
- 为语音和 OCR 目录补充 ModelScope 与 Hugging Face 固定 Target。
|
||
- 静态验证两个来源的文件大小和 SHA-256。
|
||
- 更正来源感知文案和仓库入口。
|
||
|
||
### 阶段 3:模型管理器(已完成)
|
||
|
||
- 扩展安装输入和操作快照。
|
||
- 让语音与 OCR 下载任务冻结应用设置来源。
|
||
- 保持 ZIP、删除、校验和模型选择行为不变。
|
||
- 接入来源 Host 和重定向策略。
|
||
|
||
### 阶段 4:后续模型
|
||
|
||
- 应用托管文本向量模型接入同一解析器。
|
||
- 实时语音的 ASR、TTS、VAD 模型目录接入同一解析器。
|
||
- 其他本地模型只有满足统一目录契约后才能使用全局下载源。
|
||
|
||
---
|
||
|
||
## 19. 验收标准
|
||
|
||
### 19.1 产品
|
||
|
||
- “平台功能”显示“通用设置”和“魔法笔记”两个可访问页签。
|
||
- 默认打开“通用设置”。
|
||
- “通用设置”提供 ModelScope 和 Hugging Face,默认 ModelScope。
|
||
- 来源选择说明影响范围,并明确已安装模型、ZIP、Ollama 和应用更新不受影响。
|
||
- 切换来源不会下载、删除、切换或重新验证任何已安装模型。
|
||
- 当前来源没有模型时,在点击下载前即可看到不可用原因。
|
||
- 下载失败不会自动请求另一来源。
|
||
- 魔法笔记的现有设置和入口行为保持不变。
|
||
|
||
### 19.2 数据与下载
|
||
|
||
- 旧应用设置完整迁移,并得到 ModelScope 默认值。
|
||
- 每个双来源模型的规范文件名、大小和 SHA-256 相同。
|
||
- 单次下载任务只使用一个冻结来源。
|
||
- 活动下载显示实际冻结来源。
|
||
- 已安装 Manifest 和 ZIP 兼容性不依赖来源。
|
||
- ModelScope 和 Hugging Face 分别通过固定 Target、重定向、取消和摘要校验测试。
|
||
|
||
### 19.3 安全和无障碍
|
||
|
||
- Renderer 不能提交任意下载 URL。
|
||
- Main 重新读取设置并校验 Renderer 的预期来源。
|
||
- 重定向不能跨到未声明来源或任意公网 Host。
|
||
- 下载请求不携带模型 Provider、Runtime 或渠道密钥。
|
||
- 页签、Radio、错误和进度可由键盘及屏幕阅读器操作。
|
||
- 浅色、深色和窄窗口均持续显示当前来源和活动下载来源。
|
||
|
||
### 19.4 工程验证
|
||
|
||
源代码实施后必须通过:
|
||
|
||
```text
|
||
npm test
|
||
npm run typecheck
|
||
npm run lint
|
||
npm run build
|
||
```
|
||
|
||
模型文件真实性和双来源一致性使用门控目录验证命令,不在普通测试中下载模型权重。只有
|
||
显式授权的维护流程可以访问外部模型仓库并更新固定字节数和 SHA-256。
|