dsh-openrouter-provider-badge
Manifest validdsh-openrouter-provider-badge
[!IMPORTANT]
已停止维护(2026-09-10)
本次是最后一次维护。
v0.1.2为最终版本,此后不再有任何更新——将来 DSH 升级若导致本插件不兼容,也不会再适配,需要的人请自行 fork 维护。原因:DeepSeek 官方 API 降价后,作者不再使用 OpenRouter,这个徽标也就没有使用场景了。(作者 Windows 端已停用;自己的 WSL 实例保留作测试床,但同样不再更新。)
停更前的最后一次验证(DSH
0.1.5-rc.1,Windows 与 WSL/Linux 两个环境实测):tests/client-smoke.mjs两个环境全绿;tests/test-patch.sh在 Linux/WSL 上 5/5(第 5 项对真实安装 dry-run 干净应用),Windows 上 4/4(第 5 项由等价的git apply --check覆盖,退出 0);补丁在 WSL 安装上实际打入并通过 marker 校验;dsh --profile web --dump-config退出 0、插件进入合成 profile 树、dsh web启动日志零错误(以上三项在 WSL 侧)。代码保持可读、可 fork(MIT)。仍在使用 OpenRouter 的人可以继续用:下面的安装、补丁与兼容性说明全部保留,且对 DSH
0.1.5-rc.1仍然有效。先澄清一个容易误判的点:DSH 官方「本轮用量」面板里的「提供方 / 模型」显示的是你配置的路由串(如
openrouter/deepseek-v4-flash-0731),它不显示 OpenRouter 本次实际选中的上游供应商。官方代码里没有任何地方消费openrouter_metadata——那正是本插件补丁注入并展示的字段(StreamLake / DeepInfra / Novita…)。所以本插件不是官方功能的重复实现;停维护的唯一原因,是作者不再走 OpenRouter 通道。
在 DeepSeek Harness (DSH) 的会话统计行上方显示 OpenRouter 实际供应商(如 StreamLake / DeepInfra)。
⬆ 实际: StreamLake · 通道: openrouter/deepseek-v4-flash-0731
17:08 · 用时 4分25秒 · 首token 23秒 · 78 tok/s
纯客户端插件 + 两个 DSH 补丁。安装分三步:装插件 → 打补丁 → 加配置头,缺一步都不会显示实际供应商。
它解决什么
OpenRouter 是聚合平台,同一个模型(如 deepseek/deepseek-v4-flash-0731)会由多家供应商托管,DSH 请求时 OpenRouter 自动选一家(如 StreamLake、DeepInfra、Novita…)。DSH 原生只显示「走的是 openrouter 通道 + 模型名」,不显示实际是哪家。
本插件让 DSH 请求 OpenRouter 时带上 X-OpenRouter-Metadata: enabled 头,把响应里的 openrouter_metadata(实际供应商)写进会话消息,并在每条消息的统计行上方渲染出来。
与相关插件的区别
DSH 生态里已有几个"显示 provider 信息"的插件,容易混淆,这里明确区分:
| 插件 | 显示什么 | 与本插件的区别 |
|---|---|---|
| dsh-chat-tweaks(前身 dsh-model-provider-label,已归档) | 模型选择器里显示配置的 provider 显示名(如 DeepSeek · DeepSeek V4 Flash) | 显示的是配置文件里的静态 provider 名;本插件显示的是 OpenRouter 为本次请求动态选中的实际上游供应商(StreamLake / DeepInfra / Novita…) |
| dsh-plugin-model-provider-readout | 模型按钮左侧显示 DeepSeek | 标签 | 同上,显示配置的 provider 名;不消费 openrouter_metadata,看不到实际服务商 |
| landtml/openrouter-dsh | 钉选 OpenRouter 供应商、关闭 reasoning 的控制工具 | 定位是控制路由(pin 供应商);本插件是观测展示(显示实际服务商)。两者都补丁支持 openRouterRouting,功能互补而非重复 |
一句话:别的插件告诉你"配置的是谁",本插件告诉你"这次请求实际是谁在服务"。
安装
1. 安装插件
dsh plugin --profile web add /path/to/dsh-openrouter-provider-badge
(或使用你现有的插件管理器/注入器。)
2. 打补丁(必须)
DSH 默认配置层不接受 OpenRouter 路由参数,且会丢弃响应里的供应商元数据。运行脚本自动打两个补丁:
bash patch.sh
# 或指定 DSH 安装目录:
# bash patch.sh /usr/local/lib/node_modules/@deepseek-ai/dsh
补丁内容(脚本按 dsh-llm-pi-ai 版本选对应目录):
| DSH 版本 | 补丁目录 | 改动 |
|---|---|---|
| 0.1.1-rc.2 / 0.1.2-* | patch/ | @earendil-works/pi-ai/dist/api/openai-completions.js:流式 chunk 解析 openrouter_metadata → output.openrouterMetadata;@deepseek-ai/dsh-llm-pi-ai/lib/index.js:① compat gate 开放 openRouterRouting(withhold→offer)② schema 增加该字段 ③ toPiReplayState 透传 openrouterMetadata ④ replay 还原时带回 |
| 0.1.5-rc.1+ | patch/0.1.5-rc.1/ | 同样四处,按 0.1.5 的真实代码重新取锚点(0.1.5 的 replay 信封升到 version: 2,多了 providerThinkingLevel,toPiReplayState 多了 requestedModel 参数,所以旧补丁的两个 hunk 打不上) |
⚠️ 升级 DSH 会覆盖补丁(npm 是整包替换,不只是改文件),升级后重跑
bash patch.sh即可。 ⚠️ patch.sh 会检测dsh-llm-pi-ai版本并选对应补丁目录;不认识的版本直接拒绝,避免把补丁打到结构已变的文件上。 ⚠️ 补丁只改node_modules里的产物,patch -b会在旁边留.orig备份便于回退。
3. 配置请求头
编辑 ~/.dsh/settings.yaml(Windows: C:\Users\<你>\.dsh\settings.yaml),给 openrouter provider 加:
llm-pi-ai:
providers:
openrouter:
apiKeyEnv: OPENROUTER_API_KEY
headers:
X-OpenRouter-Metadata: enabled
models:
- id: deepseek/deepseek-v4-flash-0731
name: "DeepSeek: DeepSeek V4 Flash 0731"
contextWindow: 1048576
maxTokens: 384000
reasoningEfforts:
off: null
low: low
high: high
max: max
compat:
openRouterRouting:
quantizations: [bf16, fp8]
sort: price
allow_fallbacks: true
compat.openRouterRouting 是可选项(配合补丁生效):
quantizations: [bf16, fp8]—— 排除 fp4 量化供应商sort: price—— 在允许的供应商里选最便宜allow_fallbacks: true—— 首选失败时回退
4. 重启 DSH
重启 DSH 服务后刷新网页,发一轮 OpenRouter 模型对话即可看到供应商徽标。
版本兼容(客户端)
| DSH 0.1.1-rc.2 | DSH 0.1.5-rc.1+ | |
|---|---|---|
| 定义注册表服务 | ctx.conversationEvents | ctx.uiConversation.events |
| 携带 replayState 的事件 | assistant/chunk(chunk.type === "finish") | assistant/message(message.source.replayState) |
slots 服务提供方 | @deepseek-ai/dsh-client-runtime(0.1.5 已删除该包) | @deepseek-ai/dsh-client-ui-renderer |
| 元数据读取路径 | chunk.replayState.response.openrouterMetadata | message.source.replayState.response.openrouterMetadata |
同一份 lib/client.js 两个版本都能跑:
- 注册表用
ctx.get(name)运行时探测。不能写ctx.uiConversation这种属性访问——cordis 对没写进inject的服务会直接抛cannot get property "uiConversation" without inject;而服务名随版本变化,写死进inject会让另一个版本永远 pending。服务晚挂上来时用有界轮询兜底。 - 两个事件名都匹配(
assistant/message与assistant/chunkfinish),所以不用分版本发包。 - 插槽
conversation.chat.turnTail的注册方式(name/select/locale)两个版本一致,无需改动。
改完先跑冒烟测试,不用开浏览器(Node 里用假 ctx 驱动 bundle,覆盖两个版本的服务形态、两种事件、轮询兜底):
node tests/client-smoke.mjs
效果与降级
- 有元数据 → 显示
⬆ 实际: <供应商>+ 通道/模型名 - 无元数据(没打补丁 / 没加 header / Key 限额被拒) → 降级只显示
通道: openrouter/<模型> - 插件静默失败,不影响会话本身
目录结构
dsh-openrouter-provider-badge/
├── lib/
│ ├── index.js # 服务端入口(空骨架)
│ └── client.js # 客户端插件(turnTail slot)
├── patch/
│ ├── 0.1.5-rc.1/ # 适配 DSH 0.1.5-rc.1+ 的补丁(锚点按 0.1.5 代码重取)
│ ├── pi-ai-openai-completions.patch # 适配 0.1.1-rc.2 / 0.1.2-*
│ └── dsh-llm-pi-ai-index.patch
├── patch.sh # 一键打补丁(按 dsh-llm-pi-ai 版本选补丁目录)
├── src/client/index.ts # 客户端源码(lib/client.js 的来源)
├── tests/
│ ├── test-patch.sh # patch.sh 行为测试(red/green)
│ └── client-smoke.mjs # 客户端 bundle 冒烟测试(免浏览器)
└── package.json
兼容性
- DSH 0.1.1-rc.2+(patch.sh 会检测版本,旧版 0.1.0-rc.x 会拒绝并提示升级)
- 仅 OpenRouter 通道
- 服务端补丁对 WSL / Windows 安装均适用(DSH
0.1.5-rc.1上两个环境均实测通过) - 补丁分别针对 0.1.1-rc.2 / 0.1.2-* 与 0.1.5-rc.1+ 的代码结构编写(compat gate 表驱动机制),
patch.sh按dsh-llm-pi-ai版本自动选目录 - 已停止维护:DSH 更高版本若改动了相关代码,
patch.sh会拒绝不认识的版本(避免把补丁打到结构已变的文件上);此时不再提供适配
停用 / 卸载
三步,改完重启 DSH 生效:
-
从 profile 移除插件:编辑
~/.dsh/profiles/<profile>/package.json,把@dsh-external/dsh-openrouter-provider-badge从dependencies和dsh.profile.bundles里都删掉(bundles才是决定加载的那一项)。 -
回滚 DSH 补丁:
patch -b会在目标文件旁留下.orig备份,直接覆盖回去即可;或用git apply -R反打本仓库的补丁:cd <DSH 安装目录> git apply -p1 -R /path/to/dsh-openrouter-provider-badge/patch/0.1.5-rc.1/pi-ai-openai-completions.patch git apply -p1 -R /path/to/dsh-openrouter-provider-badge/patch/0.1.5-rc.1/dsh-llm-pi-ai-index.patchWindows 上用
git apply -R会按core.autocrlf把整个文件改成 CRLF(语义等价,但不再是字节级原版);要字节级还原就改用.orig覆盖。 -
清理
~/.dsh/settings.yaml:删掉compat.openRouterRouting(必删,不是可选项)。回滚补丁后openRouterRouting的 compat gate 回到withhold,而resolveModelCompat()对模型级 compat 字段会直接抛PiAiCatalogError——settings 加载(deferred)时该模型会被剔除并留下诊断错误,Models 设置页保存(strict)时会直接报错。headers.X-OpenRouter-Metadata只是普通请求头,留着无害。
License
MIT
Comments
Loading…