本地模型 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 复制到 .managednode_modules(已绑定当前 runtime ID 则跳过),设置里会出现 dsh.fish 一栏。package.json 的 bundle 顺序固定包含 dsh-basedsh-web-app、本插件和商店,同时保留用户添加的其他 bundle。受管插件的依赖写成 file:./.managed/@local-dsh/local-dshfile:./.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-ailocal-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 创建目录链接需要启用“开发人员模式”或以具备符号链接权限 的账户运行。

项目级开发流程属于根 MakefileMakefile.local 只放代理、镜像、目标平台等未提交的机器本地 差异,不再重复定义插件 stamp 或构建规则。发行构建不会传入上述 HMR overlay 或开发目录。

真实启动验证还应请求 /api/local-models/state 并确认插件 client.js 返回 200。模型文件体积较大, 普通代码检查不会自动触发模型下载。