Skip to content
dsh.fish
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

MIT

Install

dsh plugin --profile web add github:Adonis511/enhance-provider-config#b7d5edf2314eb006b6bc4f594bad168996597a67

Profile: web

  • 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.
Source