Bundle
enhance-provider-config
enhance-provider-config
- Source
- Adonis511
- Updated
- Updated 6 days ago
Readme
# 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-harness` UA 返回 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 生效(详见[下文](#user-agent-冲突官方保留名需补丁))。
## 工作原理
1. **数据与官方共享**:插件不维护自己的配置命名空间,而是直接读写官方 `llm-pi-ai` 的 settings 命名空间,把增强字段写入 `llm-pi-ai.providers.<route>.headers`。
2. **pi-ai 原生合并**:`dsh-llm-pi-ai` 的 profile schema 原生支持 `headers` 字段,每次请求通过 `requestHeaders()` 合并(并过滤内置 attribution 保留名)。因此插件不需要注册任何 LlmAdapter、不劫持流量——Host 侧是空实现。
3. **同一个页面**:在 Web 设置里注册一个 **Enhanced Provider Config** section(走 `settings.section` slot),下拉框直接列出 `llm.providers` 目录里 `settingsNs === "llm-pi-ai"` 的全部 provider(包括你在官方 Models 页配置的 nvidia/opencode,以及 pi-ai 内置 catalog 路由)。
4. **保存时防冲突**:用 `settings.mutate` 写 `{ op: "set", path: ["providers", route, "headers"] }`,带 `expectedRevision` 防止并发覆盖;改完立刻生效。
### 架构一览
```text
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-editor` slot。本插件走稳定的 `settings.section` slot 做独立页面,兼容性更好。
## 环境要求
- Node.js >= 18
- DSH >= 0.1.0-rc.6
- `pnpm` 在 PATH 上
## 构建与安装
```powershell
# 1. 进入插件目录
cd <enhance-provider-config 所在目录>
# 2. 安装依赖
pnpm install
# 3. 编译 TypeScript 源码到 lib/
pnpm run build
```
编译输出:
```text
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
```powershell
dsh plugin --profile web add ./
dsh web
```
浏览器打开 `http://127.0.0.1:3080`,左侧设置里就能看到 **Enhanced Provider Config**。
### 如果 `dsh plugin add` 因为网络问题失败
可以手动安装,不需要 pnpm:
1. 创建 junction(DSH 通过 node_modules 解析插件,`<profile-dir>` 通常是 `~/.dsh/profiles/web`):
```powershell
New-Item -ItemType Junction -Path "<profile-dir>\node_modules\enhance-provider-config" -Target "<插件目录>"
```
2. 编辑 profile 的 `package.json`,在 `dependencies` 加:
```json
"enhance-provider-config": "link:enhance-provider-config"
```
3. 确保 profile 的 `dsh.profile.bundles` 包含它,然后启动 DSH。
## 使用说明
1. **选择 Provider**:下拉框列出 `llm-pi-ai` 下的全部 provider(官方 Models 页配置的 + 内置 catalog 路由)。
2. **填写字段**:
- **User-Agent**:字符串,写入该 provider profile 的 `headers["user-agent"]`。
- **Header Overrides (JSON)**:对象(字符串值),逐字段合并进 `headers`。例如:
```json
{
"X-Custom-Header": "hello",
"X-Debug": "true"
}
```
3. **点 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 升级会覆盖官方文件,升级后重新执行:
```powershell
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** 才生效;而通过本插件改配置本身无需重启。
## 项目结构
```text
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)里看请求头:
```powershell
$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
MITInstall
dsh plugin --profile web add github:Adonis511/enhance-provider-config#b7d5edf2314eb006b6bc4f594bad168996597a67
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install enhance-provider-config from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.