Adonis511
enhance-provider-config
No description
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 15, 2026
- Updated
- Aug 15, 2026
Introduction
enhance-provider-config
一个为 DeepSeek Harness (DSH) 开发的插件:在 Web 设置界面新增一个 Enhanced Provider Config(服务商配置) 面板,为每个 LLM provider 单独配置自定义请求头(User-Agent + 任意 Header 覆盖)。
配置不保存在插件自己的命名空间里,而是直接写入官方 @deepseek-ai/dsh-llm-pi-ai 的 settings 命名空间(llm-pi-ai.providers.<route>.headers),由 pi-ai 在每次模型请求时自动合并进 HTTP 头。插件不注册自定义 adapter、不劫持流量,Host 侧是空实现——所有工作都发生在官方配置文件这条"最短路径"上。
典型场景
- 网关按 User-Agent 限流或要求特定 UA(如
opencode.ai/zen对deepseek-harnessUA 返回 429)。 - 需要在请求里附加自定义头:调试信息、认证令牌、追踪 ID 等。
核心特性
- 按 provider 配置请求头:下拉框列出
llm-pi-ai下全部 provider(官方 Models 页配置的 + pi-ai 内置 catalog 路由),可为每个 route 单独设置 User-Agent 和任意 header 覆盖。 - 与官方配置完全共享:读写的是同一个 profile,在官方 Models 页能看到同一份数据,不存在两边割裂。
- 保存即生效,无需重启:配置写入 DSH user settings,pi-ai 每次请求都会重新读取当前 profile,下一次请求就带上新头。
- 零流量劫持:不注册 LlmAdapter、不拦截请求,纯配置注入。
- User-Agent 冲突修复(附
re-patch脚本):官方requestHeaders()把user-agent当 attribution 保留名强制覆盖,本仓库的补丁脚本反转优先级,让显式配置的 UA 生效(详见下文)。
工作原理
- 数据与官方共享:插件不维护自己的配置命名空间,而是直接读写官方
llm-pi-ai的 settings 命名空间,把增强字段写入llm-pi-ai.providers.<route>.headers。 - pi-ai 原生合并:
dsh-llm-pi-ai的 profile schema 原生支持headers字段,每次请求通过requestHeaders()合并(并过滤内置 attribution 保留名)。因此插件不需要注册任何 LlmAdapter、不劫持流量——Host 侧是空实现。 - 同一个页面:在 Web 设置里注册一个 Enhanced Provider Config section(走
settings.sectionslot),下拉框直接列出llm.providers目录里settingsNs === "llm-pi-ai"的全部 provider(包括你在官方 Models 页配置的 nvidia/opencode,以及 pi-ai 内置 catalog 路由)。 - 保存时防冲突:用
settings.mutate写{ op: "set", path: ["providers", route, "headers"] },带expectedRevision防止并发覆盖;改完立刻生效。
架构一览
Web 设置页(Enhanced Provider Config section)
│ settings.section slot
▼
src/client/index.ts ── settings.mutate ──► llm-pi-ai.providers.<route>.headers
│ (官方 settings 命名空间)
▼
(Host 侧无逻辑:src/host/index.ts 是 no-op)
│
▼
@deepseek-ai/dsh-llm-pi-ai 每次模型请求经 requestHeaders() 合并 headers
Client 侧通过 package.json 的 dsh.client.inject 声明运行时依赖(dsh-client-runtime、dsh-client-ui-settings、dsh-client-locale、dsh-api-remotes),构建时被包装成 window.__ModuleLoader__.load({ id, factory }) bundle,与官方 client bundle 走同一套浏览器模块加载机制。
设计说明(为什么这么做)
为什么不做独立 adapter / 独立命名空间? 最初版本自己注册了 adapter 并劫持流量,但 provider 数据与官方 Models 页割裂(你在 Models 页配的 nvidia/opencode 看不到)。pi-ai 原生支持 headers 合并,直接把增强配置写进它的 profile 是最短路径。
为什么不直接改
settings-models内置卡片? 当前 DSH 版本(rc.6 / 0.1.0)的ProviderEditor是手写的,没有声明可注入的provider-editorslot。本插件走稳定的settings.sectionslot 做独立页面,兼容性更好。
环境要求
- Node.js >= 18
- DSH >= 0.1.0-rc.6
pnpm在 PATH 上
构建与安装
# 1. 进入插件目录
cd <enhance-provider-config 所在目录>
# 2. 安装依赖
pnpm install
# 3. 编译 TypeScript 源码到 lib/
pnpm run build
编译输出:
lib/
├── index.js # 根入口(package.json main 指向,re-export host apply)
├── index.d.ts # 根入口类型
├── host/
│ └── index.js # 空实现(no-op,配置由 pi-ai 原生处理)
├── client.js # 预编译 React 设置页(__ModuleLoader__ bundle)
└── client.d.ts # client 入口类型
安装到 DSH
dsh plugin --profile web add ./
dsh web
浏览器打开 http://127.0.0.1:3080,左侧设置里就能看到 Enhanced Provider Config。
如果 dsh plugin add 因为网络问题失败
可以手动安装,不需要 pnpm:
- 创建 junction(DSH 通过 node_modules 解析插件,
<profile-dir>通常是~/.dsh/profiles/web):New-Item -ItemType Junction -Path "<profile-dir>\node_modules\enhance-provider-config" -Target "<插件目录>" - 编辑 profile 的
package.json,在dependencies加:"enhance-provider-config": "link:enhance-provider-config" - 确保 profile 的
dsh.profile.bundles包含它,然后启动 DSH。
使用说明
- 选择 Provider:下拉框列出
llm-pi-ai下的全部 provider(官方 Models 页配置的 + 内置 catalog 路由)。 - 填写字段:
- User-Agent:字符串,写入该 provider profile 的
headers["user-agent"]。 - Header Overrides (JSON):对象(字符串值),逐字段合并进
headers。例如:{ "X-Custom-Header": "hello", "X-Debug": "true" }
- User-Agent:字符串,写入该 provider profile 的
- 点 Save:配置写入
llm-pi-ai.providers.<route>.headers,与官方 Models 页完全共享——你可以在 Models 页看到同一 profile,pi-ai 每次请求都会带上这些头。
字段优先级
| 位置 | 优先级 |
|---|---|
headers 中显式配置的 user-agent | 最高(需先应用 pnpm re-patch,见下方"User-Agent 冲突") |
| pi-ai 内置 attribution headers(除 user-agent 外的保留名) | 次高,headers 中同名字段会被过滤掉 |
headers 中的自定义字段 | 再次,其余保留名全部合入 |
注意:
headers的值必须是字符串。pi-ai 的 profile schema 是z.dict(z.string()),非字符串值会在settings.mutate时被拒绝(settings-rejected)。当前版本不提供 body 覆盖(pi-ai profile 没有 body 字段)。
User-Agent 冲突(官方保留名,需补丁)
DSH 官方 requestHeaders()(@deepseek-ai/dsh-llm-pi-ai/lib/index.js)把 user-agent 列为 attribution 保留名:任何配置的 user-agent 都会被过滤,并强制覆盖为 deepseek-harness/<version> (+https://github.com/deepseek-ai/...)。
实测后果:opencode.ai/zen 网关按 UA 限流——deepseek-harness UA 连续返回 429 Too Many Requests(表现为请求"超时"),而 opencode/1.17.9 ... UA 返回 200。也就是说,不 patch 的情况下,你在本插件里配的 User-Agent 是静默失效的。
本仓库通过 scripts/re-patch.mjs 给安装目录里的官方文件打补丁,反转优先级:配置了 user-agent 就用你的,没配置才用 attribution 默认值(其他 provider 不受影响)。DSH 升级会覆盖官方文件,升级后重新执行:
cd <enhance-provider-config 所在目录>
pnpm re-patch
# 然后重启 dsh
脚本特性:
- 幂等:已 patch 时直接跳过(exit 0)。
- 安全:官方代码形状变化时拒绝操作并提示(exit 2),避免误改。
- 可还原:补丁前的原始文件备份在
backups/dsh-llm-pi-ai.index.js.bak。 - 可指定目标:通过环境变量
REPATCH_TARGET指向dsh-llm-pi-ai/lib/index.js。
提醒:patch 修改的是官方模块文件,DSH 在进程启动时加载,应用 patch 后需要重启 DSH 才生效;而通过本插件改配置本身无需重启。
项目结构
enhance-provider-config/
├── src/ # TypeScript 源码
│ ├── types.d.ts # 外部 DSH 包的迷你类型声明(替代 @types 全家桶)
│ ├── index.ts # 根入口,re-export host 的 apply
│ ├── host/
│ │ └── index.ts # Host 侧:空实现(配置由 pi-ai 原生处理)
│ └── client/
│ └── index.ts # Client 侧:React 设置页 + settings.section slot 注入
├── lib/ # tsc 编译产物(package.json main/exports 指向这里)
├── scripts/
│ ├── build.mjs # 构建脚本:tsc 编译 host + client,包装 __ModuleLoader__ bundle
│ └── re-patch.mjs # 给官方 dsh-llm-pi-ai 打 user-agent 优先补丁(升级后重跑)
├── backups/ # 官方文件补丁前的原始备份(可还原)
├── cordis.patch.yml # DSH bundle patch layer
├── package.json # 声明 main/exports、dsh.client.inject、构建脚本
├── tsconfig.build.json # host 编译配置(ESM → lib/)
├── tsconfig.client.json # client 编译配置(CommonJS → __ModuleLoader__ bundle)
└── README.md
常见问题
Q: 编译为什么需要 src/types.d.ts?
A: TypeScript 编译时需要知道外部包(如 react、@deepseek-ai/dsh-*)的类型。本插件用极小的 ambient declarations 替代完整的 @types/* 包,避免安装几十个 type 包。运行时这些声明不影响行为。
Q: 编辑后立刻生效吗?需要重启 DSH 吗? A: 不需要重启。配置写入 DSH user settings,pi-ai 每次请求都会重新读取当前 profile,下一次请求就带上新头。
Q: 配置了 User-Agent 但请求还是显示 deepseek-harness?
A: 官方 requestHeaders() 把 user-agent 当 attribution 保留名强制覆盖,这是 DSH 0.1.0-rc.6 的既定行为(nothing can suppress attribution entirely)。运行 pnpm re-patch 反转优先级即可(见上方"User-Agent 冲突"一节),然后重启 dsh。
Q: 如何抓包验证请求头? A: 因为 pi-ai 用原生 undici fetch、不读系统代理,抓包需要显式给 DSH 进程设置代理环境变量,再在代理工具(如 127.0.0.1:8888)里看请求头:
$env:HTTP_PROXY = "http://127.0.0.1:8888"
$env:HTTPS_PROXY = "http://127.0.0.1:8888"
dsh web
确认请求的 user-agent 是你配置的值(而不是 deepseek-harness/...)。
Q: 下拉框为什么是空的?
A: 那是旧版本的 bug——connection.api 的响应包了 { result: { ok, value } } 信封,旧代码直接读 resp.providers 拿不到数据。现已修复为解包 resp.result.value.providers。
Q: dsh plugin add 和手改 cordis.patch.yml 有什么区别?
A: dsh plugin add 是 DSH 官方推荐、可逆向的操作。它会自动把 bundle 加到 profile 的 dsh.profile.bundles 里,dsh plugin --profile web remove enhance-provider-config 就能干净卸载。
License
MIT