本地模型 DSH 插件
August 24, 2026 · View on GitHub
状态:已实现 更新时间:2026-08-24
本地模型能力以一个双面 DSH 插件 @local-dsh/local-dsh 接入,源码位于
packages/local-dsh。它与桌面壳放在同一个 pnpm workspace 中发布,不拆成第二个仓库,
也不创建另一套模型配置。
组件边界
DSH React Client
└─ /api/local-models(同源)
└─ DSH Host 插件
└─ 随机端口 + Bearer token
└─ Tauri Rust 控制桥
├─ catalog / 断点续传 / SHA-256
└─ llama-server 进程与健康检查
- Client 插件注册 DSH
settings.section,显示模型下载与 SHA-256 校验进度,以及当前使用状态。 - Host 插件只开放固定的 model ID 操作,把同源请求转给桌面壳的 loopback 控制桥。
- Rust 是本机能力的唯一所有者;浏览器不能传入下载 URL、目标路径、可执行文件或任意参数。
- 控制桥和 llama.cpp 每次应用启动分别使用随机端口和独立的 256-bit token。地址与 token 只注入 DSH Host 子进程,不暴露给浏览器 bundle,也不要求用户保存或输入 API Key。
DSH 标准 profile
桌面端运行 dsh --profile local-dsh,并设置 DSH_PROFILE=local-dsh,这样
@dsh-fish/hub 这类把 current 解析成环境变量的插件会写回本 profile,而不是落到
默认的 web。profile 位于:
${DSH_HOME:-~/.dsh}/profiles/local-dsh/
├─ package.json
├─ cordis.patch.yml
├─ pnpm-workspace.yaml
├─ .managed/
│ ├─ @local-dsh/local-dsh/
│ └─ @dsh-fish/hub/
└─ node_modules/
├─ @local-dsh/local-dsh/
└─ @dsh-fish/hub/
启动时从内置 runtime 把 @local-dsh/local-dsh 和 @dsh-fish/hub 复制到 .managed 与
node_modules(已绑定当前 runtime ID 则跳过),设置里会出现 dsh.fish 一栏。package.json 的 bundle 顺序固定包含 dsh-base、dsh-web-app、本插件和商店,同时保留用户添加的其他 bundle。受管插件的依赖写成
file:./.managed/@local-dsh/local-dsh 和 file:./.managed/@dsh-fish/hub,而不是 npm 版本号——前者
包不发布到 registry,写成 0.1.0 会让随后的 dsh plugin add 在解析 profile 依赖时 404;后者钉在
runtime lockfile 里,启动时不再访问 registry。
插件在构建时进入内置 runtime,启动时按 runtime ID 原子部署到 .managed 和标准 node_modules。
这是 Node ESM 的正常包解析结构,Windows 不需要创建符号链接,也不会把整棵 DSH 依赖复制到
profile。
配置、凭据、会话和模型仍全部使用标准 DSH Home。桌面应用不设置私有 DSH_HOME。
API
浏览器可访问的同源端点为:
| 方法 | 路径 | 行为 |
|---|---|---|
| GET | /api/local-models/state | 返回 catalog、安装/下载进度和 llama.cpp 状态 |
| POST | /api/local-models/download | 按固定 model ID 开始或续传下载 |
| POST | /api/local-models/download/cancel | 取消指定下载 |
| POST | /api/local-models/llama/ensure | 确保指定 model ID 正在运行;相同模型复用,不同模型自动切换 |
启动成功后,Host 插件把 loopback endpoint 和内部 LOCAL_DSH_LLAMA_API_KEY 凭据引用写入 DSH
llm-pi-ai 的 local-llama provider,并通过 agentDefaultModel 保存选中的 alias。对应的随机值只在
本轮进程环境中存在,同时通过 llama.cpp 标准的 LLAMA_API_KEY 环境变量启用认证;安装记录只描述
GGUF 文件,不承担 provider 配置职责。
当前 DSH 客户端没有图片输入链路,Provider 只声明 text 输入。模型目录不下载 mmproj,Rust Host
也不向 llama.cpp 传入多模态 projector;以后启用视觉能力时需要三层同时恢复,不能只改 Provider 文案。
模型的理论上下文不作为列表属性展示。catalog 中的硬件矩阵按精确 GGUF 制品和平台后端保存运行
profile:macOS/Metal 以统一内存容量选择模型专属的 contextWindow;Windows/CUDA 和
Linux/CUDA 保留独立矩阵,完成实测前保持 unknown。Rust Host 使用 profile 启动单槽
llama.cpp;缺少 profile 时不传 --ctx-size,回退到 --fit on。服务就绪后仍从 /props 读取
实际生效的 n_ctx,作为 DSH provider 的 contextWindow,插件模型列表不把这个内部运行参数
呈现为模型固有属性。
构建与验证
make plugin # TypeScript 检查并构建 Host/Client bundle
make runtime-check # 在隔离 DSH_HOME 中验证 profile 能解析插件
make check # 插件、runtime 和 Rust 的完整静态/smoke 检查
make dev # 使用 staged runtime 启动,并监听插件源码热更新 Host/Client
make dev 启动后会监听插件的 src/ 和 scripts/。debug 启动会让标准 local-dsh profile 中这个
受管插件的 lib/ 临时链接到仓库生成目录;每次构建成功,Host 由一个只观察该目录的 DSH HMR
overlay 重载,Client 由 DSH 自带的 client HMR 重载。这样既保留裸包名及标准 Node 包解析,又让
Host 的真实模块路径处于 node_modules 之外——DSH Host HMR 会主动排除 node_modules,简单复制
文件并不能使 Host 热更新。这个循环不重新组装约 100 MiB 的 runtime,也不重启 Tauri 或 DSH;
构建失败时保留上一次可工作的 bundle,并继续等待下一次修改。
开发链接带有 .local-dsh-dev 标记。未设置开发环境的应用下次启动会从内置 runtime 恢复普通目录,
所以开发状态不会进入发行行为。Windows 创建目录链接需要启用“开发人员模式”或以具备符号链接权限
的账户运行。
项目级开发流程属于根 Makefile。Makefile.local 只放代理、镜像、目标平台等未提交的机器本地
差异,不再重复定义插件 stamp 或构建规则。发行构建不会传入上述 HMR overlay 或开发目录。
真实启动验证还应请求 /api/local-models/state 并确认插件 client.js 返回 200。模型文件体积较大,
普通代码检查不会自动触发模型下载。