dsh-local-llm-connect
Manifest validBring local llama.cpp manager models into DeepSeek Harness — bring local llama.cpp manager models into DeepSeek Harness
dsh-local-llm-connect
把本地 LLM 管理器(llama.cpp / NInfer 双引擎)里正在运行的模型接入 DeepSeek Harness,启动即出现、停止即消失。
Bring the models currently running in your local LLM manager (llama.cpp + NInfer) into DeepSeek Harness.
它做什么
你在本地 LLM 管理器里维护模型、决定谁在跑。管理器一套界面同时管 Windows 上的 llama.cpp 和 WSL 里的 NInfer,插件对两种引擎一视同仁。这个插件让 DSH 的模型选择列表始终等于你当前正在运行的那几个模型:
- 自动发现本机的 LLM 管理器,读出它维护的模型列表
- 只注册正在运行的模型——每个模型一个独立 provider,在模型选择里单独成组
- 自动跟随——每 15 秒重新评估一次;你在管理器里启动或停止模型,列表自动增删,不需要手动操作
- 配置页可以查看当前状态,也可以点**「立即刷新」**马上同步一次
跑不起来的模型不会出现在列表里:判定不了运行状态时(管理器没开、或版本过旧没有控制接口),插件一个模型也不列,而不是让你选到一个必然报错的条目。
前置条件
| 依赖 | 说明 |
|---|---|
| DeepSeek Harness | 宿主 |
| llm-manager | 本地 LLM 管理器,v1.3.0 或更高(需要其控制接口;v1.3 起支持 NInfer) |
| llama.cpp / NInfer | 由管理器自行管理,本插件不直接调用;WSL 里的 NInfer 需在管理器设置里配好 |
| Node.js | ^22.19.0 或 >=24 |
控制接口说明:判断「谁在运行」依赖管理器的本地控制 API(127.0.0.1:8765,带一次性令牌)。管理器版本低于 v1.2.0、或管理器当前没在运行时,插件无法获知运行状态,此时它不会列出任何模型(配置页会说明原因并给出「立即刷新」按钮)。打开管理器后约 15 秒内自动恢复。
安装
dsh plugin --profile desktop add dsh-local-llm-connect
装好后重启 DSH。
验证是否加载:
dsh --profile desktop --dump-config
应能在插件树里看到 local-llm-connect 一行。
插件是被 profile 装载的。 只有出现在
$DSH_HOME/profiles/<profile>/package.json的dependencies里、并且名字被列进 同一文件的dsh.profile.bundles,它才会被挂载。$DSH_HOME/.local-plugins/<name>只是产物暂存目录,光放在那里不会被加载。
从源码安装(本地开发)
仓库自带 node_modules,所以最省事的装法是让 profile 直接 link: 到仓库本体:
npm install && npm run build
然后编辑 $DSH_HOME/profiles/desktop/package.json:
{
"dependencies": {
"dsh-local-llm-connect": "link:E:/Github工作区/dsh-local-llm-connect"
},
"dsh": {
"profile": {
"bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "…", "dsh-local-llm-connect"]
}
}
}
在该 profile 目录里跑一次 pnpm install,重启 DSH 即可。之后只要 npm run build,重启就是新版本。
也可以改走「暂存 + 链接」的装法(适合只拿到构建产物、没有 node_modules 的场景):
npm run build && npm run deploy
npm run deploy 会把 lib 等产物同步到 $DSH_HOME/.local-plugins/dsh-local-llm-connect,
再补齐只有插件自己才解析得到的 peer,然后真正 import 一次做自检,最后打印挂载 profile 的步骤。
详见下一节。
peer 依赖在 DSH 0.1.7 之后是怎么解析的
launcher 在挂载 profile 之前,会把运行时包表装进 Node 的 ESM/CJS 内部解析器。 表就是安装归档里的
dsh/desktop-runtime.json→sharedPackages(0.1.7-rc.2 共 282 项;0.2.0-rc.2 共 287 项,新增的都是@deepseek-ai/*)。规则是:
- 在每个
D/node_modules位置,包名只要是D/package.json里声明过的 peer 且存在于运行时表, 就用宿主那份 —— peer 位置不需要物理node_modules。- 其余包名照常查物理候选。
所以
@deepseek-ai/dsh-llm、@deepseek-ai/dsh-llm-pi-ai、dsh-settings、cordis、schemastery都由宿主供货,插件不必自备。但
@earendil-works/pi-ai不在表里(它只是安装归档内的普通文件,282 / 287 项里一个@earendil-works都没有)。它必须由插件自己的解析链找到:从.local-plugins/<name>向上找node_modules,既没有自己的、也没有上层能兜住,运行时就会报Cannot find package '@earendil-works/pi-ai' imported from .../.local-plugins/.../lib/index.js这正是
npm run deploy在插件目录下建node_modules/@earendil-works/*的原因, 也是直接 link 到仓库本体能一劳永逸绕开它的原因。0.1.5 时代那个只接管
@deepseek-ai/*的resources/host-module-fallback.mjs已经删了;.dsh-module-fallback目录同样是旧版遗留物,profile 加载时会被清理。DSH 0.2.0-rc.2 起还有一道硬门槛:profile 启动时会校验每个插件的 peer 版本范围, 范围覆盖不到当前运行时版本就直接拒载(不是降级运行),表现为插件路由整片 404:
dsh: warning: Plugin dsh-local-llm-connect@X is incompatible with dsh 0.2.0-rc.2: peerDependencies {"@deepseek-ai/dsh-llm":"^0.1.5-rc.2 || ^0.1.7-rc.2", ...} dsh: it stays installed but profile startup denies it until you grant an exemption for those exact versions.因为 semver 的预发布规则,范围必须逐个枚举(
^0.1.5-rc.2 || ^0.1.7-rc.2 || ^0.2.0-rc.2), 写>=0.1.5-rc.2这类开区间对预发布版本并不生效。临时绕过可以用dsh plugin allow-version(或在插件管理器里授权)给精确版本开豁免,但那只是把风险 显式接受下来,正解仍是补比较符后重装/重启。
使用
首次使用
- 打开 本地 LLM 管理器,确认里面已经配置好模型
- 在管理器里启动你要用的模型
- 打开 DSH 设置 → dsh-本地LLM-connect 可以看到当前有几个在运行(也可直接看模型选择列表)
正在运行的模型会自动出现在 DSH 的模型选择器里,每个模型单独成组。
启动 / 停止模型
在本地 LLM 管理器里操作即可,DSH 侧会自动跟上:
- 启动一个模型 → 约 15 秒内出现在模型选择列表里
- 停止一个模型 → 约 15 秒内从列表里消失
想立刻生效,就到配置页点一次 「立即刷新」。
注意:如果某个模型正被对话使用,而你在管理器里把它停掉了,它会在下一轮刷新时被移除,该对话后续请求会失败。
管理器里改了配置之后
比如改了模型名、别名或端口:插件在下一轮刷新时自动读到,也可以点 「立即刷新」 马上应用。插件只读管理器的配置,不会改写它。
没装管理器也能用:自动发现本机已启动的模型服务
插件还会扫描本机已经启动的 OpenAI 兼容模型服务并自动接入——装不装管理器都工作:
- 判据:
GET http://127.0.0.1:<port>/v1/models能返回data[].id列表,就是一个可直接当 OpenAI 上游用的服务。Ollama(11434)、LM Studio(1234)、llama-server(8080)、vLLM(8000)、KoboldCpp(5001)等都满足 - 端口来源:常见默认端口 + 配置项
scanPorts追加 +netstat -ano枚举全部监听端口(deepScan,仅 Windows) - 扫到的模型在列表里带「OpenAI 兼容 · 自动发现」标记,provider id 形如
local-llm-11434-qwen2-5-7b - 管理器配置过的端口绝不重复接入:同一端口注册两个 provider 只会互相覆盖
- 要求 API key 的服务(401/403)不接:本插件的接入链路免密,接上也发不出请求
- 这类进程是在本插件之外启动的,插件只读不写——不能从这里启停它,停止请回到原工具(Ollama / LM Studio 等)操作
两个来源在同一轮刷新里合并:管理器没运行时扫描来源照常工作,反之亦然。
配置项
| 项 | 默认 | 说明 |
|---|---|---|
managerDir | 自动探测 | 手动指定管理器数据目录;留空则依次探测常见位置 |
scanEnabled | true | 是否扫描本机已启动的 OpenAI 兼容模型服务 |
scanPorts | 空 | 追加扫描端口(逗号分隔),如 '9000, 9001' |
deepScan | true | 是否用 netstat 枚举全部监听端口(关掉后只探常见端口与 scanPorts) |
在 profile 的 cordis.patch.yml 里覆盖:
- id: local-llm-connect
config:
managerDir: 'D:\path\to\llm-manager'
scanPorts: '9000, 9001'
重新评估运行状态(含扫描)的间隔固定为 15 秒;需要立刻生效时用配置页的「立即刷新」按钮。扫描探测结果按端口缓存(命中 20 秒 / 未命中 5 分钟),所以深度扫描不会每 15 秒都把全端口敲一遍。
工作原理
DSH 插件
│
├─ 只读 ──► %APPDATA%\llm-manager\models.json 模型配置来源(有哪些模型)
│
├─ HTTP ──► 127.0.0.1:8765/status 谁在运行(每 15 秒问一次)
│
├─ HTTP ──► 127.0.0.1:<port>/v1/models 扫描本机已启动的 OpenAI 兼容服务
│
└─ HTTP ──► 127.0.0.1:<port>/v1 运行中模型的 OpenAI 兼容端点
插件不启动、不停止任何模型:管理器模型的进程管控完全属于管理器(端口占用检测、就绪轮询、日志缓冲都在那里),扫描来源的进程属于启动它的工具(Ollama、LM Studio 等)。插件只做三件事——读配置、看谁在跑、扫端口——然后把运行中的模型注册给 DSH。这样不会出现两个互不知情的进程管理者(管理器显示「未运行」而进程实际在跑的那种状态)。
只在运行集合变化时才重新注册:否则每 15 秒都会拆装一遍 provider,正在进行的请求会被打断。集合(含端口)没变时刷新是空操作。
模型进入列表的判据
- 管理器来源:管理器控制接口报告
running: true才注册。判定不了运行状态时管理器模型一个也不注册——宁可列表为空,也不让人选到一个必然报错的模型。 - 扫描来源:
/v1/models能应答就是「正在运行」——探测本身就是运行判据。服务停了,下一轮刷新(最多 20 秒缓存延迟)自动撤下。
两种推理引擎
管理器 v1.3 起一个界面管两种引擎,插件对二者一视同仁——都注册成 OpenAI 兼容 provider。差异只在就绪判据上,因为两种服务暴露的端点不同:
| 引擎 | 模型文件 | 运行位置 | 就绪判据 |
|---|---|---|---|
llamacpp | .gguf(+ mmproj) | Windows 原生进程 | /health 返回 {"status":"ok"} |
ninfer | .ninfer(自包含单文件) | WSL 里的 ninfer-serve | /v1/models 能返回 JSON |
NInfer 没有 /health 端点,所以不能用 llama.cpp 的判据去探它。反过来 NInfer 在权重加载完之后还要 prewarm:端口早已 accept,但 /v1/models 还没响应——只探端口会把「还在预热」误判成「已就绪」。插件按 models[].engine 分别探测,与管理器内部 probeReady() 的判据保持一致。
配置页每个模型旁边会标出它跑在哪个引擎上(llama.cpp 或 NInfer · WSL)。
视觉能力也从各引擎自己的声明处读取:llama.cpp 要求 vision: true 且 mmproj 文件非空;NInfer 的 .ninfer 是自包含的、没有独立 mmproj 文件,它的视觉开关记在 ninfer.vision 上。
常见问题
配置页显示「未检测到本地 LLM 管理器」
管理器没装,或数据目录不在探测范围内。装了的话,用 managerDir 手动指定。
配置页显示「运行中 0 / 共 N 个模型」
模型都配置好了,但一个都没在跑。到本地 LLM 管理器里启动你要用的模型,约 15 秒内会自动出现。
显示「无法获知运行状态」
管理器没在运行,或版本过旧(低于 v1.2.0)没有控制接口。只有运行中的管理器才会写出控制接口的端口与令牌。 打开管理器后约 15 秒自动恢复,也可以点「立即刷新」马上重试。
模型列表是空的(配置页说「共 0 个」)
管理器里还没配置模型,或者 models.json 损坏。配置页会显示被跳过的条目及原因。
在管理器里点了停止,DSH 里还看得到那个模型
刷新有最长 15 秒的延迟。点一次「立即刷新」即可立刻消失。
两个模型端口相同
插件的配置解析会跳过端口重复的条目并在配置页指出——端口冲突意味着两个 provider 会指向同一个实例,属配置错误。
开发
pnpm install
pnpm test # 93 个测试
pnpm run build # 产出 lib/
pnpm run check # typecheck + test + build
源码结构
| 文件 | 职责 |
|---|---|
src/discovery.ts | 定位管理器数据目录与控制接口 |
src/config-store.ts | 解析 models.json,容错处理 |
src/control-client.ts | 调用管理器控制 API(含 /status 运行状态) |
src/adapter.ts | 模型 → pi-ai provider 映射 |
src/index.ts | Cordis 插件入口:运行状态过滤、轮询、provider 注册 |
src/client/index.tsx | 设置面板卡片(官方 settings.section 契约) |
测试分层:
- 单元测试(
config-store/discovery/control-client/adapter)不依赖真实 llama.cpp - 引擎测试(
engine)锁定双引擎差异:engine字段解析、按引擎选择就绪端点(/healthvs/v1/models)、NInfer 的视觉声明来自ninfer.vision - 契约测试(
plugin-contract)读源码与构建产物,锁定 DSH 加载器契约(无 default 导出、裸 require react、路由幂等、live代际委派……) - 集成测试(
running-filter/model-catalog)把构建产物装进真实 Cordis 宿主,配一个假的管理器 HTTP 服务,验证「只注册运行中的模型」「集合未变不重注册」「拿不到状态就不列」以及模型确实能被枚举出来
发布前预检
因为插件加载失败会让整个宿主起不来,发布前请确认:
- 对解包后的 npm 产物(不是
lib/目录)跑一遍集成测试 - 产物里没有
exports.default(cordis-plugin-loader的unwrapExports()会把组件当插件本体调用) adapter.listModels()能返回模型(模型选择列表的数据源;只注册成功但枚举为空是曾经的真实故障)- 插件的
apply是箭头函数。普通函数有prototype,会被 cordis 的isConstructor()当成类式插件用new callback(ctx, config)调用,返回值不再被收集为 disposer —— 副作用照常发生所以看起来正常,但卸载时轮询定时器泄漏、适配器不被撤销。 - 手搓的 pi-ai profile 自带官方归一化的字段。我们绕过了
dsh-llm-pi-ai的resolveProfiles()(未导出),而 stream 路径直接读取这些字段:streamIdleTimeoutMs(缺失即抛idleWatchdog timeoutMs must be a positive finite number...)、maxRequestImageBytes/requestImagePixelBudget/requestImageMaxBytes、retryPolicy。见buildAdapterProfile()的注释。 - provider 的 auth 用官方
harnessApiKeyAuth的嵌套形状{ apiKey: { name, resolve } },且resolve必须返回对象(无凭据时返回{ auth: {} })。少嵌一层、或返回undefined,pi-ai 的applyAuth()都会判为未配置,在请求发出前抛Provider is not configured: <provider>。见keylessApiKeyAuth()的注释。 - 端到端测试(
tests/stream-e2e.spec.ts)会真的发一次 stream。注意 pi-ai 把失败作为「流片段」返回而不是抛出 —— 只断言「有没有抛错」会漏判,必须检查片段内容。 - 改了就绪探测相关代码时,故意把它改坏(例如让
modelReady忽略engine一律探/health)确认engine.spec.ts会红——否则那批用例可能只是在复述实现。
致谢
本项目的插件结构参考了 dsh-workbuddy-connect(作者 corrinehu,MIT)。该项目演示了 DSH 插件的组织方式、dsh.bundle 清单写法,以及 host 端与 client 端的分层——本插件的骨架直接受益于它,在此致谢。
设置面板卡片另行按官方 settings.section 契约实现(@deepseek-ai/dsh-client-ui-settings-general 声明的 list slot),client 产物形态对齐官方 client 包。
两者的场景不同:WorkBuddy 需要逆向桌面应用的加密凭据、构造特定 UA、自建代理转发;而本项目上游是标准的 OpenAI 兼容端点,因此适配层简单得多,复杂度集中在服务发现与运行状态判定上。
许可
MIT
Comments
Loading…
From the same category
by awesome-dsh-plugin
A curated list of plugins for DeepSeek Harness (dsh) · DeepSeek Harness 插件精选列表
★ 18.1k
CC0-1.0
Python
Oct 8, 2026
by 0xsline
DeepSeek Harness (DSH) ecosystem: curated plugins, tools, and infrastructure from dsh-external/hub and the public dsh-plugin topic.
★ 1.1k
CC0-1.0
Python
Sep 30, 2026
by pax-beehive
Open-source CLI, schemas, resolver, and DSH agent tools for DSH Plugin Hub
★ 458
MIT
TypeScript
Oct 6, 2026
by yjh051108
推荐组件(非必须):DeepSeek Harness 运行时注入器;已随 dsh-routing-suite 单仓库化保留,本仓库继续维护/发布。
★ 164
TypeScript
Sep 18, 2026
dsh plugin --profile web add @dsh-external/dsh-super-injectorby xiajiajun516
DeepSeek Harness (DSH) backup & restore plugin — export, import, migrate and sync your complete DSH configuration, plugins, MCP servers, skills and workspace. One-click migration to another machine.
★ 164
MIT
TypeScript
Oct 7, 2026
dsh plugin --profile web add dsh-config-managerby jigjoy-ai
A CLI that turns a goal into a pull request - and a sandbox for testing concurrent AI coding agents on the Mozaik runtime.
★ 124
MIT
TypeScript
Oct 2, 2026