Back to home

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/zendeepseek-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 生效(详见下文)。

工作原理

  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 防止并发覆盖;改完立刻生效。

架构一览

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.jsondsh.client.inject 声明运行时依赖(dsh-client-runtimedsh-client-ui-settingsdsh-client-localedsh-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 上

构建与安装

# 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:

  1. 创建 junction(DSH 通过 node_modules 解析插件,<profile-dir> 通常是 ~/.dsh/profiles/web):
    New-Item -ItemType Junction -Path "<profile-dir>\node_modules\enhance-provider-config" -Target "<插件目录>"
    
  2. 编辑 profile 的 package.json,在 dependencies 加:
    "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。例如:
      {
        "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 升级会覆盖官方文件,升级后重新执行:

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