dsh-llm-openai-compatible(万能插头)
让 DeepSeek Harness 接上任意 OpenAI 兼容端点:本地 vLLM / LM Studio / llama.cpp 服务器、Ollama 的兼容层、或任何远程网关(OpenRouter、Together、Moonshot 等)。装完 + 配好端点就能跑——不需要装 Ollama,也不需要改 dsh 核心。
设计目标:这个插件自己就是一个「万能插头」——一个 provider 路由(
openai-compatible),任何说 OpenAI Chat Completions 协议的服务都能插上来。
特性
- 任意 OpenAI 兼容端点:
baseURL配http://127.0.0.1:8000或http://127.0.0.1:8000/v1都行(自动归一化到/v1)。 - API key 可选:本地服务通常不需要鉴权——没配 key 时请求匿名发出(本地端点忽略多余 Bearer 头);远程网关必须配 key,否则 401。
- 模型目录:
models数组声明端点实际提供的模型(id / contextWindow / maxTokens / vision / thinking / defaultEffort)。 - Web 配置:Settings → Plugins →
llm-openai-compatible,通用表单即可改端点、key、模型目录、重试策略,保存即时生效。 - 模型发现:
discoverModels()读GET <base>/v1/models,返回端点真实提供的模型 id 列表。 - 免 allowlist 安装:仓库提交构建产物
lib/(无prepare脚本),GitHub 安装不需要 pnpm 的 build-script 白名单。
安装
要求 DeepSeek Harness 0.1.0-rc.6+。
# 从 GitHub 安装(推荐,免本地构建)
dsh plugin --profile web add github:cqnxnzg/dsh-llm-openai-compatible
# 本地开发安装(<仓库路径> 替换为克隆下来的插件目录;先 pnpm run build)
dsh plugin --profile web add <仓库路径>/dsh-llm-openai-compatible
dsh web
GitHub 安装无需 build-script allowlist:仓库提交了构建产物
lib/(无prepare脚本),装完即可用。本地开发时改源码后记得pnpm run build再重装。
配置
最小配置(本地 vLLM 等)
默认 baseURL = http://127.0.0.1:8000/v1,默认模型目录里有几个常见本地模型 id。打开 Settings → Plugins → llm-openai-compatible,把 models[].id 改成你本地服务实际提供的模型 id(见下文「UNKNOWN_MODEL 怎么消除」),保存即可在模型选择器里选中聊天。
配置字段(全部可选)
| 字段 | 默认 | 说明 |
|---|---|---|
apiKeyEnv |
OPENAI_API_KEY |
凭据引用(环境变量名);未配置/为空 → 匿名请求(本地端点可用) |
baseURL |
http://127.0.0.1:8000/v1 |
OpenAI 兼容端点;自动归一化到 /v1 |
models |
4 个示例模型 | 端点实际服务的模型目录;未列出则请求报 UNKNOWN_MODEL |
maxTokens |
— | 全局默认输出上限;模型行未声明时兜底 |
defaultContextWindow |
131072 |
模型未声明 contextWindow 时的上下文容量 |
streamIdleTimeoutMs |
300000 |
流式读取空闲超时 |
retryPolicy |
正常默认 | 重试策略(见下) |
models[].* 字段语义:
| 字段 | 说明 |
|---|---|
id |
端点接受的模型 id(必须与端点实际服务的一致,否则 UNKNOWN_MODEL) |
name |
选择器显示名;省略用 id |
description |
选择器里的补充说明(可选) |
contextWindow |
该模型上下文容量(token) |
maxTokens |
该模型专属输出上限,优先于全局 maxTokens;请求级 maxTokens 又优先于它 |
vision |
true = 接受图片输入(请求带图时输入模态含 image) |
thinking |
true = 支持原生思考;选择器可调 thinking 等级(off/low/medium/high/max) |
defaultEffort |
聊天选择器的默认思考等级;需 thinking: true 且等级在支持集合内才生效 |
tools |
遗留能力标志,运行时忽略,仍被解码 |
retryPolicy 可配置值(省略 = 正常默认:最多重试 2 次,重试码 EMPTY_RESPONSE/RATE_LIMIT/SERVER/TIMEOUT/TRANSPORT,退避 initialDelayMs: 500 / maxDelayMs: 10000 / jitterRatio: 0.1):
retryPolicy:
mode: normal # normal | always
maxRetries: 3 # normal 模式:最大重试次数
retryableCodes: [RATE_LIMIT, SERVER, TIMEOUT, TRANSPORT] # normal 模式:可重试错误码
backoff:
initialDelayMs: 500
maxDelayMs: 10000
jitterRatio: 0.1
# mode: always = 无条件重试(只有 backoff 字段),一般用于本地服务
常见本地端点
| 服务 | baseURL | 备注 |
|---|---|---|
| vLLM | http://127.0.0.1:8000/v1 |
默认值;--served-model-name 指定的名字才是请求 id |
| LM Studio | http://127.0.0.1:1234/v1 |
— |
| llama.cpp server | http://127.0.0.1:8080/v1 |
— |
| Ollama(兼容层) | http://127.0.0.1:11434/v1 |
Ollama 原生 API 不是 OpenAI 协议;必须走它的 /v1 兼容层,模型 id 常带 tag,如 qwen2.5:7b |
| OpenRouter | https://openrouter.ai/api/v1 |
网关,必须配 apiKeyEnv,否则 401 |
| Together | https://api.together.xyz/v1 |
网关,必须配 apiKeyEnv,否则 401 |
| 其他远程网关 | https://.../v1 |
网关通常需要 key;见故障排查 |
接 DeepSeek 官方 API 完整示例
DeepSeek 官方端点本身就是 OpenAI 兼容的。在 Settings → Plugins → llm-openai-compatible(或 settings 文档的 llm-openai-compatible 节)里:
llm-openai-compatible:
apiKeyEnv: DEEPSEEK_API_KEY # 环境变量里放你的 DeepSeek API key
baseURL: https://api.deepseek.com # 自动路由到 /v1,不用手写 /v1
models:
- id: deepseek-chat # DeepSeek-V3,通用对话
name: DeepSeek Chat
contextWindow: 65536
maxTokens: 8192
tools: true
- id: deepseek-reasoner # DeepSeek-R1,推理模型,需要 thinking
name: DeepSeek Reasoner
contextWindow: 65536
maxTokens: 8192
thinking: true
要点:
baseURL: https://api.deepseek.com即可——插件会自动补/v1(等价于写https://api.deepseek.com/v1)。deepseek-reasoner是推理模型,目录行要标thinking: true,这样选择器才能正确展示思考等级。- 环境变量
DEEPSEEK_API_KEY由 dsh 的凭据缝读取(apiKeyEnv指的就是环境变量名);配好 key 后请求带Bearer鉴权,不会 401。
无真实模型也能测(装完后的第一课)
node scripts/mock-server.mjs # 起一个 OpenAI 兼容 mock(GET /v1/models + POST /v1/chat/completions)
pnpm run smoke # 用构建产物跑一次 adapter 全链路,打印模型列表 + 流式回复
pnpm run verify # 系统性自验证:52 项断言(纯函数 / schema / adapter / discovery)
smoke 打印 SMOKE OK、verify 打印 VERIFY OK 且退出码 0 = 万能插头端到端打通。
真实 dsh profile 端到端验证
已用一个独立 profile(plugtest)验证过完整链路:dsh-base + dsh-headless + 本插件,patch 层把
agent-default-model 指向 openai-compatible/gpt-oss-120b、插件 baseURL 指向本地 mock,
然后一次 headless 任务直接拿到 mock 的回复:
dsh --profile plugtest "你好,请用一句话自我介绍"
# [mock:gpt-oss-120b] 你好,万能插头已接通!Hello from the OpenAI-compatible mock. auth=Bearer no-key-local
验证要点:插件在真实 profile 中加载、provider/adapter/discovery 注册、agent loop 走通、
settings 指向 profile 专属文件(不触碰全局 ~/.dsh/settings.yaml)。
UNKNOWN_MODEL 怎么消除
UNKNOWN_MODEL 表示请求的模型 id 不在插件的 models 目录里。按下面三步解决:
问端点要真实 id:
curl http://127.0.0.1:8000/v1/models # {"object":"list","data":[{"id":"Qwen/Qwen2.5-7B-Instruct",...}, ...]}把
data[].id原样抄进models[].id(插件也提供discoverModels()做这件事,Web 配置的「fetch models」动作可一键导入)。vLLM 特殊:启动参数
--served-model-name决定请求 id,可能与你下载的模型名不同(比如下载的是Qwen2.5-7B-Instruct,服务名却是qwen-7b)。以curl /v1/models返回的为准。Ollama 特殊:兼容层返回的 id 常带 tag(如
qwen2.5:7b),照抄,别去掉:7b。
故障排查
| 症状 | 原因与解法 |
|---|---|
UNKNOWN_MODEL |
模型 id 不在 models 目录;按上文「UNKNOWN_MODEL 怎么消除」抄真实 id |
401 Unauthorized |
端点需要 key:apiKeyEnv 配了没?环境变量值对吗?本地端点不需要 key 时把 key 清空(匿名请求) |
| 404 / 连不上 | baseURL 写成了完整路径(如 .../v1/chat/completions)——只要 base,插件自动补 /v1;或服务没起 / 端口不对 |
/v1 写两遍 |
baseURL 写 http://host:8000/v1 或 http://host:8000 都行,不要写 http://host:8000/v1/v1 |
| 模型选择器里没有我的模型 | models[].id 与端点返回不一致;或保存后没等配置生效(保存即生效,重开选择器刷新) |
| 匿名请求被本地端点拒绝 | 个别本地服务校验 Bearer 头;给它配一个任意 key(apiKeyEnv 指向一个假值)试试 |
诊断命令(从插件目录跑):
curl http://127.0.0.1:8000/v1/models # 端点到底有哪些模型 id
node scripts/mock-server.mjs && pnpm run smoke # 插件链路是否自洽(不依赖真实端点)
工作原理
- 插件入口
apply(ctx, config):ctx.llm.registerConfigurableProviders()+ctx.llm.registerAdapter()+ctx.llm.registerModelDiscovery()+installSettingsSection()(参考 dsh-llm-ollama 的注册机制)。 - 聊天走 pi-ai 的 OpenAI Chat Completions:
createProvider({ api: openAICompletionsApi(), baseUrl, auth, models }),每次请求通过 harness 的凭据缝解析 key。 - 连接事实(endpoint / key / 模型目录)每次操作重新解析,Settings 里改了立即生效,不用重启。
开发
pnpm install
pnpm run build # tsc(lib/types/*.d.ts)+ tsdown(lib/index.js)
pnpm run smoke
构建产物 lib/ 与声明文件 lib/types/**/*.d.ts 提交进 git(.gitignore 只排除 tsc 中间产物),因此 GitHub 安装无需 build-script allowlist——这是与 dsh-hello-tool(依赖 prepare 脚本)不同的安装策略。
路线图
- Host 端最小可用版(聊天 + 配置 + 发现)
- Settings → Providers 专属卡片(fetch models 一键导入、模型行内编辑)
- 多 provider 路由(同时挂 vLLM + LM Studio)
- 发布到 npm / GitHub Releases
License
MIT
No comments yet. Be the first to write one.