dsh-deepseek-relay
DeepSeek relay station adapter for deepseek-harness with reasoning-effort control
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 25, 2026
- Updated
- Aug 25, 2026
Introduction
dsh-deepseek-relay — DeepSeek 中转站思考强度适配插件
让 deepseek-harness(dsh)里通过中转站(第三方 OpenAI 兼容网关,如
OneAPI / new-api / one-hub 或各种"API 转发"服务)接入的 DeepSeek 模型,
也能像官方 API 一样在 Web UI 里设置推理等级(思考强度):Off / Low /
High / Max 四档,并把思考参数按中转站认识的格式发出去。
问题
官方 API 走内置 llm-deepseek 适配器,resolveModel 会返回
reasoning.efforts = [Off, Low, High, Max],所以 UI 有推理等级选项。
中转站通常走 llm-pi-ai 的"自定义提供方"(openai-completions)。但:
- Web UI 的表单没有
reasoningEfforts字段,手写模型没有推理元数据, UI 里根本不出现推理等级选项; - 即使声明了,发往中转站的思考参数格式也不一定对——DeepSeek 官方格式
(
thinking: {type: "enabled"}+reasoning_effort)和 OpenAI 格式 (只有reasoning_effort)在中转站上不通用。
解决方式
本插件注册独立的 OpenAI 兼容适配器路由(每条中转站一条路由),并且:
- 始终向 UI 暴露 Off / Low / High / Max 四档推理等级(与官方
llm-deepseek完全一致,见src/adapter.ts的REASONING_EFFORTS); - 按
thinkingFormat把思考参数写成中转站认识的方言(见src/serialize.ts):
| 档位 | openai(默认) | deepseek |
|---|---|---|
| off | 不发送任何思考字段(网关默认) | thinking: {type:"disabled"} |
| low | reasoning_effort: "low" | thinking:{type:"enabled"} + reasoning_effort:"low" |
| high | reasoning_effort: "high" | thinking:{type:"enabled"} + reasoning_effort:"high" |
| max | reasoning_effort: "max" | thinking:{type:"enabled"} + reasoning_effort:"max" |
thinkingFormat: auto时按 baseURL 域名猜:含deepseek.com/ai/cn用deepseek,其余默认openai(多数中转站)。- 支持按模型覆盖 wire 值(
reasoningEfforts),应对"网关要求ultra之类 自定义取值"的场景。
使用
两种方式都符合官方插件规范(见下文「官方规范符合性」):
方式 A:从源码运行,--patch 本地加载(开发/自用)
在 deepseek-harness 仓库根目录(已 pnpm install 过):
pnpm dsh web --patch ./dsh-deepseek-relay/cordis.yml
启动前先编辑 cordis.yml,把 providers.relay 换成你的中转站信息:
- insert:
- id: dsh-deepseek-relay
name: '/绝对路径/deepseek-harness/dsh-deepseek-relay/src/index.ts'
config:
providers:
my-relay:
baseURL: https://your-relay.example.com/v1
apiKeyEnv: RELAY_API_KEY
thinkingFormat: auto
models:
- id: deepseek-v4-flash
- API key:设置环境变量
RELAY_API_KEY,或启动后在 Web UI 设置 → 模型 里选中该提供方填入密钥(密钥存$DSH_HOME/.credentials.yaml)。 - 模型:
models列表即模型选择器里出现的模型;contextWindow/maxTokens会作为上下文/输出上限信息。 - 保存后,在模型选择器选到该模型的会话里,输入框旁就会出现 Off / Low / High / Max 推理等级下拉(和官方 API 一样)。
方式 B:作为组合包(bundle)安装(可分发)
# 本地目录 / git / tarball 均可;首次会初始化 profile
dsh plugin --profile demo add ./dsh-deepseek-relay
# 或 dsh plugin --profile demo add github:you/dsh-deepseek-relay
包内 cordis.patch.yml 声明了插件行(name: dsh-deepseek-relay 按包名解析,
加载 lib/index.js),prepare 脚本(esbuild)会在安装时自动构建
lib/。安装后插件处于 dormant(空配置可安全装载,不注册任何路由/目录,
适配器与 configurable-provider 目录两侧都有空数组守卫,见 src/index.ts 的
ensureDirectory/ensureRegistrationFacts),在 profile 的
cordis.patch.yml 或 $DSH_HOME/cordis.patch.yml 里覆盖同 id 行填入中转站
配置(参考 cordis.patch.yml 顶部注释),或随后在 Web UI 设置中热更新生效。
配置项
| 字段 | 说明 | 默认 |
|---|---|---|
baseURL | 中转站 OpenAI 兼容地址,/chat/completions 自动拼接 | 必填 |
apiKeyEnv | API key 环境变量名 | RELAY_API_KEY |
displayName | 模型选择器显示名 | 路由 key |
thinkingFormat | auto / openai / deepseek | auto |
reasoningEffort | 默认推理等级 off/low/high/max | high |
maxTokensField | 输出上限字段 max_tokens/max_completion_tokens | max_tokens |
maxTokens | 路由级默认输出上限 | 256000 |
models[].id | 发往网关的模型 id | 必填 |
models[].name | 选择器显示名 | id |
models[].contextWindow | 上下文容量 | 262144 |
models[].maxTokens | 该模型输出上限 | 路由级值 |
models[].reasoningEfforts | 按档位覆盖 wire 值(null=支持但不发送) | 方言默认 |
官方规范符合性
对照仓库 docs/user/develop/basic/{index,config,publish}.zh.md 与
docs/user/develop/practice/llm-adapter.zh.md:
| 官方要求 | 本插件 |
|---|---|
插件模块导出 name + apply(ctx, config) | ✅ src/index.ts |
声明 inject(本插件依赖 llm 服务) | ✅ inject = ['llm'] |
导出 Config 类型 + 同名 Schemastery schema,默认值写在 schema 中 | ✅ src/index.ts |
--patch overlay 加载本地插件(源码路径 .ts) | ✅ cordis.yml(方式 A) |
组合包 dsh.bundle manifest + cordis.patch.yml(按包名引用) | ✅ package.json + cordis.patch.yml(方式 B) |
git 安装的 TS 包必须自带 prepare 构建(产出 lib/) | ✅ scripts/build.mjs + tsconfig.build.json |
LlmAdapter 契约:实现 stream()、resolveModel 返回 reasoning 元数据、attributionHeaders()、LlmError 稳定 code、透传 options.signal | ✅ src/adapter.ts |
注:--patch 从源码路径加载 .ts 是官方教程支持的开发方式;正式分发
(npm/git/tarball)走方式 B,prepare 构建出 lib/index.js 后与源码运行
等价。两种方式共用同一份 src/。
备选:不装插件,直接用官方 llm-pi-ai 手工配置
官方 llm-pi-ai 本身支持同样的能力,只是要手写 $DSH_HOME/settings.yaml。
如果不想装插件,可以这样写(等效于 thinkingFormat: deepseek):
llm-pi-ai:
providers:
my-relay:
apiKeyEnv: RELAY_API_KEY
api: openai-completions
baseURL: https://your-relay.example.com/v1
compat:
thinkingFormat: deepseek
models:
- id: deepseek-v4-flash
reasoningEfforts:
off:
low: low
high: high
max: max
注意:
reasoningEfforts里off:冒号后留空表示"支持该档、不发送"; 其余档必须给 wire 值,否则配置被拒。多数网关(OneAPI 等)应使用thinkingFormat: openai而不是deepseek,除非网关原样转发官方 API。
代码结构
dsh-deepseek-relay/
├── cordis.yml # --patch 本地加载配置示例(方式 A)
├── cordis.patch.yml # 组合包配置层(方式 B,dsh.bundle.patch 指向它)
├── package.json # dsh.bundle manifest + scripts(prepare 构建)
├── scripts/build.mjs # esbuild 构建单文件 lib/index.js(external @deepseek-ai/*)
├── tsconfig.json # 类型检查(tsc --noEmit)
└── src/
├── index.ts # 插件入口:Config schema、路由注册、settings 热更新
├── adapter.ts # RelayAdapter:四档推理等级 + fetch/SSE stream
├── serialize.ts # 消息 + 思考参数序列化(openai/deepseek 方言)
├── translate.ts # SSE chunk → harness StreamChunk
├── sse.ts # SSE 解析(eventsource-parser)
└── types.ts # wire 类型
验证
tsc --noEmit完整类型检查:0 错误(对@deepseek-ai/dsh-llm等 0.1.1-rc.2 官方 npm 包)。- 序列化逻辑测试(
resolveThinking/resolveThinkingFormat/serializeRequest,openai/deepseek 方言 × off/low/high/max × 模型级覆盖 × session-title × maxTokensField):全部通过。 - SSE 转换测试(
reasoning_content流、tool_calls 增量拼接、usage 去重、[DONE]收尾、空响应):全部通过。 - 验证依赖通过 npm 平铺安装(
C:/Users/29261/Downloads/relay-verify,测试脚本可复用)。
注意事项
- 中转站若在
/chat/completions之外还有/v1后缀,baseURL写完整路径, 例如https://relay.example.com(若网关恰好要求不带/v1)。 - 插件只支持文本输入(与官方
llm-deepseek一致);图片输入会以UNSUPPORTED_CONTENT拒绝。 - 一个插件实例可配多条中转站路由(
providersdict 里加即可)。