dsh-llm-openai-compatible
MIT License Copyright (c) 2026 cqnxnzg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cop
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 19, 2026
- Updated
- Aug 19, 2026
Introduction
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