nnbw-liu/deepseek-ai-dsh-llm-local
OpenAI-compatible local model adapter for the harness LLM seam: direct fetch + SSE (framed by eventsource-parser ) against any server that speaks the OpenAI chat-completions dialect — Ollama (/v1), the llama.cpp server (/v1), Microsoft Foundry Local, LM Studio, vLLM, and OpenAI-compatible gateways, local or remote.
이것은 DeepSeek Harness(DSH) 플러그인입니다. 이 사이트는 GitHub README, 설치 정보, 유지보수 상태, 공개 보안 시그널을 모아 보여줍니다.
@deepseek-ai/dsh-llm-local
English | 中文
面向 Harness LLM 接缝的 OpenAI 兼容本地模型适配器:基于 fetch + SSE(由 eventsource-parser 分帧),对接任何讲 OpenAI chat-completions 方言的服务器——Ollama(/v1)、llama.cpp server(/v1)、微软 Foundry Local、LM Studio、vLLM,以及本地或远程的 OpenAI 兼容网关。请求不带任何 DeepSeek 专属字段,因此同一个适配器即可服务所有端点;支持推理的服务器若输出 reasoning_content,仍会以 Harness 的 reasoning 块流入会话。
本包内置 ollama、llamacpp、foundry-local 三个 Provider 路由预设,也支持你手工声明的任意路由。插件以 dormant(休眠)方式挂载:它会在 Models 页显示,但在 llm-local: 设置段(由 Web 的 Models 页或手工写入)提供 Provider 配置之前,不会真正服务任何请求。
关于本仓库 —— 这是
@deepseek-ai/dsh-llm-local插件的独立源码快照,抽取自 DeepSeek Harness 单体仓库(packages/llm/llm-local)。它是面向 Harness 的 Cordis 插件,需在该单体仓库内构建与测试;package.json依赖与tsconfig.json引用因此指向 Harness 工作区。上游主仓库为 https://github.com/deepseek-ai/deepseek-harness。
安装与使用
dsh-llm-local 是 DeepSeek Harness 的一个插件。它已接入 harness 的 base bundle,并以 dormant(休眠)方式挂载:ollama、llamacpp、foundry-local 路由会出现在 Models 页,但只有在 llm-local: 设置段提供 profile 后才会服务请求。
作为最终用户 —— 安装 harness,然后配置插件(无需单独安装本仓库):
npx @deepseek-ai/dsh web
打开 http://127.0.0.1:3080 的 Web 界面,进入 设置 → Models 编辑某个本地 provider 行;或在 $DSH_HOME/settings.yaml 写入 llm-local: 段(见配置)。
npx拉取的是已发布的 harness 构建,因此该构建必须包含本包(及其 base bundle 条目)插件才会存在。在对应版本发布前,请改为从源码构建。
作为开发者 —— 在 harness 单体仓库内构建。本包使用 workspace:^ 依赖和单体仓库的 tsconfig.json 引用,因此无法独立构建:
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
# place this repository's src/ and tests/ under packages/llm/llm-local/
pnpm install
pnpm run build
pnpm dsh web
然后像最终用户一样配置插件。
快速开始
- 重启 dsh 服务(Web GUI 进程)。插件在启动时加载;在插件存在之前启动的服务会静默忽略
llm-local:设置——没有任何报错,重启前该段就是不生效。 - 配置 Provider:二选一——在 Web 界面操作(设置 → Models → 对应 Provider 行 → 编辑 → 填写 API 地址 → 获取可用模型 → 保存),或写入
$DSH_HOME/settings.yaml(见下文)。 - 新建会话后切换模型:打开模型选择器(
/model),选择本地 Provider 与模型 ID。新会话的默认 Provider 仍是deepseek-official,直到你主动切换。
settings.yaml 在哪里
设置文档是 harness home 目录下的 settings.yaml:
- 默认 home:
~/.dsh——Windows 上是C:\Users\<你的用户名>\.dsh,即文件为C:\Users\<你的用户名>\.dsh\settings.yaml。 - 若设置了
DSH_HOME,则为$DSH_HOME/settings.yaml。 - Web 设置页展示的路径、或组合
cordis.yml中settings-file.config.path指定的路径优先于以上两者。
该文件是热加载的:外部编辑约 100ms 内被感知,下一次模型请求即用新值——插件加载过一次之后,后续编辑无需重启。
配置
插件注册了 llm-local 设置命名空间。providers 字典的键就是 Provider 路由:ollama、llamacpp、foundry-local 是内置预设(继承端点与显示名),其余键是手工声明的端点,必须填写 baseURL。
最小可用示例(Ollama)
llm-local:
providers:
ollama:
models:
- id: qwen3:8b
contextWindow: 32768
这样就够了:预设端点 http://127.0.0.1:11434/v1 被自动继承,无需任何 API 密钥,文件保存的瞬间路由即注册。请求时用 provider: ollama、model: qwen3:8b。
全字段示例
llm-local:
providers:
# Shipped preset: endpoint defaults to http://127.0.0.1:11434/v1.
ollama:
models: # optional; discovery can fill these
- id: qwen3:8b
contextWindow: 32768
# defaultContextWindow: 128000 # fallback capacity for unlisted models
# maxTokens: 32768 # default per-request output cap
# usageInStream: true # stream_options.include_usage
# vision: false # image input for vision models
# streamIdleTimeoutMs: 300000 # per-read idle budget
# Shipped preset for the llama.cpp server.
llamacpp:
baseURL: http://127.0.0.1:8080/v1
models:
- id: qwen2.5-coder-14b-instruct-q4_K_M
contextWindow: 32768
# Shipped preset for Microsoft Foundry Local.
foundry-local:
baseURL: http://127.0.0.1:53415/v1 # confirm the port in `foundry local` output
models: []
# Hand-declared route: any OpenAI-compatible endpoint.
my-gateway:
displayName: My Gateway
baseURL: http://127.0.0.1:1234/v1
apiKeyEnv: MY_GATEWAY_KEY # optional; local servers usually need none
vision: true # optional; image input for vision models
usageInStream: true # optional; stream_options.include_usage
maxTokens: 32768 # optional default per-request output cap
defaultContextWindow: 128000 # optional fallback context capacity
streamIdleTimeoutMs: 300000 # optional per-read idle budget
retryPolicy: # optional; normal defaults when omitted
mode: normal
maxRetries: 2
models:
- id: gpt-oss-120b
contextWindow: 131072
maxTokens: 32768
各字段说明:
baseURL— OpenAI 兼容根地址(会自动追加/chat/completions与/models)。预设默认值:Ollamahttp://127.0.0.1:11434/v1、llama.cpphttp://127.0.0.1:8080/v1、Foundry Localhttp://127.0.0.1:53415/v1(具体端口以foundry local输出为准)。手工声明路由必填。apiKeyEnv— 凭据引用,每次请求经ctx.credentials、再退回到环境变量解析。省略即免认证——本地默认;Ollama、llama.cpp、Foundry Local 开箱即用无需密钥。一旦填写,解析不到就报MISSING_CREDENTIAL。models— 提供给选择器与发现的建议目录;未列出的模型 id 也能直接请求。每项:id,可选name/description/contextWindow/maxTokens/vision。models: []表示不列出任何模型。vision(profile 级或模型级)— 声明接受图片输入。图片块会经持久化附件服务序列化为image_url的 data-URL;纯文本模型收到图片会以UNSUPPORTED_CONTENT拒绝。usageInStream— 默认true(发送stream_options.include_usage)。遇到不认该字段的旧版 llama.cpp 构建可关掉。maxTokens、defaultContextWindow、streamIdleTimeoutMs、retryPolicy— 语义与 DeepSeek 适配器一致。
在 Models 页配置
Web 的 Models 设置页已认识本适配器家族:每个 Provider 行(三个预设无论是否休眠都会列出)展开的编辑器包含 API 密钥(可选——留空即免认证访问本地端点)、API 地址(预设已预填)、以及带 获取可用模型 按钮的模型目录——该按钮会请求 GET {baseURL}/models,对预设路由即使未填 URL 也能直接询问。"添加提供方"下拉包含三个休眠预设。手工声明的路由仍需写 settings.yaml(本地路由没有协议下拉,因为所有本地端点讲同一种方言)。
动态配置(settings + credentials)
连接事实不在加载时冻结。resolveAdapterOptions 是唯一显式解析步骤,适配器每次操作通过 thunk 重新读取事实:baseURL、目录、usage 上报、vision、输出上限、空闲预算都会在下一个请求生效,而进行中的流保持其开始时的事实。
ctx.settings— 插件以同一Configschema 注册llm-local命名空间,以组合条目为base;用户设置文档中的llm-local:段可覆盖任意字段且无需重启。ctx.credentials— 密钥每次流调用从同一个快照解析,与端点绑定。配置只携带apiKeyEnv,从不携带字面密钥;未填写的路由直接免认证请求。
唯一在注册时捕获的事实是重试策略:某路由解析值变化时,插件会原地重注册整个路由集(同一适配器实例、同一同步区间)。路由集变化(增删路由)同样原地重注册。每次路由变化都会输出一行日志(llm-local: serving ollama (http://127.0.0.1:11434/v1), ...),休眠挂载时输出 llm-local: dormant; ...——这是排查"段写了没反应"时第一件要确认的事。
排查:settings.yaml 写了不生效
- 插件加入之后服务重启过吗? 文档中的
llm-local:段在插件于启动时注册命名空间之前,会被静默保留但不生效。重启 dsh 服务,然后找llm-local:开头的日志行。 - 文件位置对吗? 该段必须是
$DSH_HOME/settings.yaml(默认~/.dsh/settings.yaml)的顶层键,不能嵌套在其他键下,也不能写进cordis.yml。 - YAML 有效且缩进正确吗?
providers是字典:Provider 键缩进在它下面,profile 字段缩进在 Provider 键下面,与示例一致。 - 路由注册了吗? Models 页上对应 Provider 行会从休眠的"添加"状态变成已配置行;服务日志打印
llm-local: serving <route> (<baseURL>)。 - 会话真的在用这个 Provider 吗? 配置 Provider 只是让它可用;会话仍使用创建时的 Provider/模型。在新会话里打开模型选择器(
/model)选择本地路由。 - 看服务日志里的错误。 schema 不合法的段会记
settings: keeping last good "llm-local" after invalid stored section并附原因;路由解析失败会保留旧路由并记llm-local: keeping the previously registered routes after a refused update。
应用归属头
每个请求携带 dsh-llm attributionHeaders() 的共享归属头(强制 User-Agent 基线)。请求身份以 x-deepseek-harness-user-id(稳定匿名 id)随 wire 发送;带会话的请求还发送 x-deepseek-harness-session-id。不添加任何 Provider 专属头——本地端点不需要。
Wire 格式说明
- 仅流式;默认发送
stream_options.include_usage(除非usageInStream: false)。usage可能挂在 finish 块上,也可能作为尾部仅 usage 块到达——翻译器把两者都推迟到[DONE],因此usage永远先于finish。 - 历史中的 assistant 消息不回放
reasoning_content(OpenAI 方言没有 CoT 回传;本地服务器要么忽略要么拒绝)。收到的reasoning_content增量仍会变成 reasoning 块。 - 缓存统计:
cacheReadTokens←prompt_tokens_details.cached_tokens(llama.cpp)或prompt_cache_hit_tokens(DeepSeek 风格拼写);该值会从inputTokens中扣除以保持互斥口径。 - 思考模式首块常带
reasoning_content: ""——已处理(不会误开 reasoning 块)。
错误
非 2xx 响应抛出带稳定码的 LlmError:AUTH(401/403)、QUOTA(Provider 详情表明额度耗尽)、RATE_LIMIT(429)、CONTEXT_WINDOW_EXCEEDED(400 且详情表明超出上下文)、INVALID_REQUEST(其他 400)、SERVER(5xx),其余为 HTTP_<status>。可序列化的 failure 保留 HTTP 状态、有效的正数 Retry-After 延迟及 x-request-id(如有)。响应前的传输失败(DNS、拒连、TLS、代理)抛 TRANSPORT 并指名端点;调用方中止抛 ABORTED;协议违规抛 STREAM_CLOSED(缺 [DONE])或 MALFORMED_RESPONSE(JSON 损坏)。未知的 wire finish_reason 变成 finish {kind: 'error'} 块;以 stop 结束但未产出任何内容块的流变成 EMPTY_RESPONSE(默认策略会重试)。
已知限制与待办
- 设置中的
models列表整体替换组合列表——设置层按字段合并,数组算一个字段。 - 未映射
tool_choice——不属于核心词汇表(与 DeepSeek、pi-ai 适配器一致)。 - 请求使用原生
fetch,未走@cordisjs/plugin-http——没有共享代理/拦截配置。 - 无 reasoning effort 选择器——本地 OpenAI 兼容服务器没有统一的
reasoning_effort方言;推理模型能流式输出 CoT,但 Harness 无法为它们选择思考档位。 - 除非模型声明 vision,否则用户与工具结果内容会扁平化为纯文本;空工具输出以字面量
(no output)过线。
보안 및 설치 증거
이 점수는 공개 저장소 메타데이터와 이 사이트에 등록된 설치 증거에만 기반하며, 코드 보안 감사와 다릅니다.
공개 플러그인 카탈로그에서 왔으며, 공개 GitHub 저장소로 연결됩니다.
저장소가 MIT 라이선스를 선언했습니다.
최근 180일 내 코드 업데이트가 있습니다.
재현 가능한 정확한 설치 메타데이터가 아직 등록되지 않았습니다. 저장소 설명에 따라 직접 확인하세요.
검사한 패키지 메타데이터에 설치 라이프사이클 스크립트가 선언되지 않았습니다.