DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

tappat225 /

tappat225/dsh-provider-hub

Verified

自定义参数provider面板

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: master@8b333514

dsh-provider-hub

DSH LLM provider 中枢插件:通过 DSH 左侧栏设置按钮上方的 Provider Hub 独立面板(卡片式控制台)配置任意多个网关(每个网关是一个 OpenAI / Anthropic 兼容端点,独立协议、独立 UA、独立 key、独立模型目录),启用内置的主流模型目录后,模型直接出现在 DSH 的模型选择器里——不需要手写 settings.yaml 的 llm-pi-ai.providers。

针对按 User-Agent 白名单校验客户端的网关:插件发出的请求携带每个网关完全自定义的 UA,绕开 DSH 强制的 deepseek-harness/<version> 归因头(该头在 llm-pi-ai 配置里是保留字段,无法覆盖)。多网关各自携带各自 UA,可同时接入多个 UA 白名单网关。

开发

TypeScript 源码在 src/,构建产物(esbuild 单文件 bundle)输出到 lib/:

npm run typecheck   # tsc --noEmit
npm run build       # esbuild bundle -> lib/index.js
npm test            # node test/plugin.test.mjs(测试内直连网关时需本机代理)
src/
├── index.ts            # 插件入口(host):Config schema / apply / 凭据解析 / 注册
├── types.ts            # 本地类型(WireConfig / wire 事件形状)
├── catalog.ts          # 内置主流模型目录 + 条目解析(overrides/custom 合并)
├── adapter.ts          # GatewayAdapter(LlmAdapter 实现:三协议请求 + chunk 转换 + 凭据/请求头处理)
├── discovery.ts        # 模型发现(带自定义 UA 拉 GET {baseURL}/models)
├── probe.ts            # 连接测试第二段(/models 不通时经首选模型发 "hi" 实聊验证)
├── url.ts              # endpoint 规范化(/v1 自动补齐 / custom 完整地址,防 /v1/v1)+ 凭据 URL 遮蔽
├── host/
│   ├── contract.ts     # Typert wire 契约(INVOCATIONS / TYPERT_MANIFEST,host+client 共享)
│   └── runtime.ts      # ProviderHubRuntime(Remote 服务:配置读写/模型管理/发现/连接测试)
├── client/
│   ├── static.tsx      # client 入口:挂载 Remote 命名空间 + 注册“Provider Hub”左侧面板与 shell overlay
│   ├── page.tsx        # 「Provider Hub」面板内容(卡片式网格 + cc-switch 风格编辑器:请求头行 / 模型行+目录联想下拉 / 配置 JSON 常驻参数框架双向同步)
│   ├── locales.ts      # 中/英词典
│   ├── page.css.ts     # 卡片式控制台样式(品牌 hero / 分段页签 / 卡片网格 / 双列表单,--dsw-alias-* tokens)
│   └── primitives.d.ts # 宿主 UI 原语类型 shim
├── client-runtime.d.ts # 宿主全局类型 shim
└── wire/
    ├── failure.ts      # 失败分类:HTTP 状态 / 传输抛错 / 上游原生错误码 / 流终局 → DSH 失败码(重试的唯一路由依据)
    ├── sse.ts          # 通用 SSE 解析器 + 终局 chunk 构造(error / aborted / provider-error)
    ├── anthropic.ts    # Anthropic 消息/工具转换 + SSE -> StreamChunk
    ├── openai.ts       # OpenAI 消息转换 + chat.completion.chunk -> StreamChunk
    └── responses.ts    # openai-responses 路径(input/usage/finish 转换)

构建产出两个 bundle:lib/index.js(host,node ESM)+ lib/client.js(client,ModuleLoader CJS)。

基于 DSH 标准插件能力:

能力 机制
Provider Hub 面板 参考内置 dsh-community-market:sidebar.footer.action 注册设置按钮上方入口,shell.overlay 注册独立模态面板;面板为卡片式控制台(品牌 hero 状态卡、提供方/模型目录分段页签、响应式卡片网格、双列表单内联编辑器);网关数据仍通过 providerHub Remote 读写
配置持久化/热更新 llm-provider-hub settings 命名空间 → Host Remote 与 LLM 路由实时同步
Models 页卡片 ctx.llm.registerConfigurableProviders([...])(每网关一张卡片)
模型进选择器 ctx.llm.registerAdapter(providers, adapter) 一次注册全部网关路由(adapter 按 options.provider 选路)
一键发现模型 ctx.llm.registerModelDiscovery(NS, discover):带自定义 UA 请求 GET {baseURL}/models,解析 context_window/max_output_tokens 等(按网关)
内置模型参数 MODEL_CATALOG:主流模型(GLM / Claude / GPT / Qwen / DeepSeek / Kimi / Gemini)的 contextWindow、maxTokens、输入模态、reasoningEfforts
失败自动重试 适配器把每种上游/传输失败分类到宿主重试执行器认识的规范码(瞬态条件重发,确定性失败如实上报),语义见「失败分类与自动重试」

安装

方式一(官方,推荐):dsh plugin add

# 本地路径(开发/测试)
dsh plugin --profile desktop add /path/to/dsh-provider-hub

# 推送 GitHub 后(仓库须打 dsh-plugin topic)
dsh plugin --profile web add github:tappat225/dsh-provider-hub
# 或指定版本 tarball
dsh plugin --profile web add https://github.com/tappat225/dsh-provider-hub/archive/refs/tags/v0.7.0.tar.gz

dsh plugin add 会把参数转发给 profile 目录里的 pnpm(需先 npm install -g pnpm)。 包的 dsh.bundle.patch + cordis.patch.yml 会让加载器自动挂载插件;重启并刷新 DSH Web 后,左侧栏的设置按钮上方会出现入口 Provider Hub。

npm 包名为 @tappat225/dsh-provider-hub(用户名 scope,避免与其他同名项目冲突;npm pack 产物相应为 tappat225-dsh-provider-hub-<版本>.tgz)。正式发布渠道为 GitHub tag;若发布 npm,需 scope 所有者账号 + --access public。

从旧包名 dsh-provider-hub 升级:包名已 scoped,旧安装不会自动迁移——重新执行 dsh plugin --profile <profile> add(或把 profile package.json 里 bundles/dependencies 的旧条目替换为 @tappat225/dsh-provider-hub 后 pnpm install),再重启。后台配置(命名空间 llm-provider-hub)与网关配置不受影响。

方式二:手动 bundle(pnpm workspace)

  1. ~/.dsh/profiles/desktop/package.json:

    {
      "dsh": { "profile": { "bundles": [ "...", "@tappat225/dsh-provider-hub" ] } },
      "dependencies": { "...", "@tappat225/dsh-provider-hub": "file:/path/to/dsh-provider-hub" }
    }
    

    在 ~/.dsh/profiles/desktop 下 pnpm install,重启 DSH Desktop。

  2. 打开左侧栏设置按钮上方的 Provider Hub 面板:hero 卡的“添加提供方”(或网格末尾的虚线卡)新建提供方,点卡片上的“编辑”展开配置页。配置页为 cc-switch 风格:基础字段(提供方 ID / 显示名 / Base URL / 协议 / 端点模式 / User-Agent / API Key / Key 环境变量;显示名默认跟随提供方 ID,自己填写显示名后才以填写值为准,清空则回落提供方 ID)→ 请求头 键值行(添加请求头 / 行内删除)→ 模型配置 简表(每行 模型 ID + 显示名称;“拉取模型列表”从上游 /models 拉取后点选即加入列表,“添加模型”手动加行)→ 配置 JSON 详细参数页 → 页尾唯一的 保存。 模型 ID 联想(点选才套用):在模型 ID 输入框打字,从第一个字符起出现内置目录联想下拉(前缀匹配优先、包含匹配次之,同时匹配显示名;↑↓ 高亮、Enter 选中、Esc 关闭)。只有点选下拉条目(或 ↑↓+Enter)才写入完整的目录参数(contextWindow / maxTokens / input / reasoningEfforts 显式展开,可直接修改)——哪怕手动输完整个目录模型名,也不会自动填参,仅预留全字段参数框架。 配置 JSON = 常驻完整参数框架 + 独立编辑面:每个模型组始终保留完整字段框架 name / contextWindow / maxTokens / input / reasoningEfforts,null = 未设置(内置模型保存时继承目录值;自定义模型需填 contextWindow 与 maxTokens,除非网关配置了 defaultContextWindow / defaultMaxTokens)。列表与 JSON 双向同步:列表增删改行会迁移 JSON 组;在 JSON 中手写新组即新增模型(组内 name 即显示名)、删除组即删除模型——完全可以不碰模型列表,直接在 JSON 里逐项填参。JSON 文本无效时锁定列表编辑(不覆盖正在编辑的文本),修正后自动解锁。一次保存同时提交基础字段(save-config)与整份模型列表+参数(save-models,写前经模型解析校验)。

  3. (可选)点提供方卡片或配置页的“测试连接”,两段式判定:先查上游 /models——通了即判定可用(横幅显示延迟/模型数,并自动填充下方模型下拉,不再发对话请求);/models 不通才回退为经当前首选模型(配置页取模型列表第一个非空行——未保存也可测;卡片取已保存配置的第一个已解析模型)直接发送一条 "hi" 对话请求,按真实回应判定——横幅显示延迟/所用模型/回复片段与 token 用量。两段都失败才判定 provider 不可用(错误信息同时列出两段原因)。

  4. 在模型选择器里切到该 provider 下的模型即可使用。

配置项(后台 settings namespace)

配置是一个网关列表(gateways),每个网关独立一套下列字段:

字段 默认 说明
provider hub-gateway(自动去重为 hub-gateway-1…) 提供方 ID(跨网关唯一,改名实时生效、无需重启;重名或空名在保存时拒绝。前缀 hub- 标识本插件,避免与其他 provider 插件路由名冲突)
displayName 提供方 ID(空/未填回落提供方 ID) 选择器显示名(默认使用提供方 ID;填写后以填写值为准)
baseURL 空(必填) 上游地址
api anthropic-messages 或 openai-completions(后者按模型 reasoningEfforts 映射派发 reasoning_effort,语义见「思考强度派发」)
userAgent claude-cli/2.0.1 (external, cli) wire UA(可完全自定义,每网关独立;留空自动回退默认值,面板内置常见客户端 UA 预设一键填写)
apiKeyEnv GATEWAY_API_KEY credential-ref 环境变量名
apiKey 空 字面量 key,优先于 apiKeyEnv(面板默认掩码显示,可切换明文)
extraHeaders {} 附加请求头
systemRole system OpenAI 路径系统提示词角色;developer 可修复只认 developer 角色的严格网关(GPT 系)
enabledModels ["glm-5.3"] 从内置目录勾选
modelOverrides {} 按模型 id 对内置目录做字段级参数覆盖(contextWindow/maxTokens/input/reasoningEfforts/name)
customModels [] 自定义模型(id/name/contextWindow/maxTokens/input/reasoningEfforts;映射语义见下方「思考强度派发」)

内置模型目录(MODEL_CATALOG)

GLM-5.3 / GLM-5.3-Flash / Claude Opus 4.8·4.6 / Sonnet 5·4.6 / Haiku 4.5 / GPT-5.6 Sol·Luna·Terra / GPT-4o·4o-mini / Qwen3.8-Max·27B / DeepSeek V4 Flash·Pro·Flash-Vision-Exp / V3·R1 / Kimi K2 / Gemini 3.8 Flash·3.7 Flash·3.1 Pro ——含 contextWindow、maxTokens、模态与 reasoningEfforts(GLM-5.3 为纯文本、GLM-5.3-Flash 支持图片;DeepSeek V4 Flash·Pro 为纯文本、图片输入在 Flash-Vision-Exp 变体;GLM-5.3·Flash 按官方规格为 1M 上下文 / 128K 输出,思考始终启用故仅提供 low/high/max;DeepSeek V4 家族为 1M 上下文 / 384K 输出,推理档仅 Non-think/High/Max;其余推理模型 off 到 max;GPT-4o·4o-mini·DeepSeek V3 为非推理模型,无映射)。参数为公开规格,网关实际能力以"发现模型"结果为准。

思考强度派发(reasoningEfforts)

映射语义(对照 llm-pi-ai 参考实现):键 = 选择器提供的档位,值 = 线上发送的拼写。

  • 非 off 档位必须带 wire 值;选中的档位按其值派发——如 high: 'ultra' 在 openai 路径发送 reasoning_effort: "ultra"(网关方言拼写)。
  • off 留空(null)= 支持"关闭",省略参数(保留 provider 默认行为);off 带值(如 off: 'none')= 显式发送该关闭拼写。
  • 未声明的档位(以及无映射模型收到任何档位)在发请求前以 UNSUPPORTED_REASONING_EFFORT 拒绝,不透传给网关。
  • 仅含 off 的映射会被解析拒绝——非推理模型不要声明映射(内置 GPT-4o / GPT-4o mini / DeepSeek V3 已无映射);reasoningEfforts: {} 与缺省等价(无推理能力)。
  • 校验在模型解析期进行(fail-loud):非 off 空值 / 空字符串 / 仅含 off 的映射都以带网关+模型+档位名的诊断拒绝。
  • Anthropic 路径:选中非 off 档位时,适配器自动按原生 Anthropic thinking 预算派发;off 不发送 thinking。
  • modelOverrides 的 reasoningEfforts 整体替换内置映射(字典无删除语义;留空 {} 视为未设置,保留内置映射)。
  • 模型配置面板可在配置 JSON 中按模型 id 编辑 reasoningEfforts(组内写该字段即生效,未写保留原值);删除该字段则回落内置映射。

失败分类与自动重试

DSH 的请求重试是三段式的:适配器声明策略 → 一次 stream() 只尝试一次,把结果作为终局 finish 上报(成功,或携带 failure)→ 宿主的重试执行器按 failure.code 决定是否重发。也就是说失败码是重试的唯一路由依据:码不在策略的可重试集合里,执行器直接把失败交给下游,本轮就此结束——表现为要用户手动输入「继续」才能推进对话。

本插件沿用宿主默认 normal 策略(最多 5 次、500ms→10s 指数退避带抖动、尊重上游 Retry-After),可重试集合为 EMPTY_RESPONSE / RATE_LIMIT / SERVER / TIMEOUT / TRANSPORT;三协议与 HTTP 边界上能观察到的每种失败都分类到规范码:

观察到的情况 失败码 重试
HTTP 429、上游 rate_limit* / too_many_requests RATE_LIMIT(带上游 Retry-After) ✅
HTTP 5xx、上游 overloaded / server_error / api_error,以及流内无法归类的中继错误 SERVER ✅
HTTP 408/425、上游 timeout*、请求超时 TIMEOUT ✅
连接被拒/重置、DNS 失败、流被掐断 TRANSPORT ✅
有终局标记但零输出(退化补全,空 assistant 消息会静默结束本轮) EMPTY_RESPONSE ✅
已投递内容后终局标记缺失(半截回答不能当完整) TRANSPORT ✅
HTTP 401/403、上游 authentication_error / invalid_api_key AUTH ❌
HTTP 400/404/413/422、上游 invalid_request*、内容策略拒绝 INVALID_REQUEST ❌
上下文超限 CONTEXT_WINDOW_EXCEEDED ❌
配额/余额/欠费耗尽 QUOTA ❌
配置错误(未知模型、未声明的思考档位、坏 baseURL/UA/请求头、缺凭据) UNKNOWN_MODEL / UNSUPPORTED_REASONING_EFFORT / UPSTREAM_ERROR ❌
调用方取消 aborted 终局(不是失败,任何策略都不得介入) ❌

不重试的类别都是同一请求必然同样失败的情况,重试只是白烧时间。三点实现细节:

  • Retry-After 超过策略上限时丢弃而非转发:执行器把超过 maxDelayMs 的 providerRetryAfterMs 当作「放弃重试」(normal 模式直接委托下游),而不是「等更久」,所以一个 Retry-After: 600 若原样转发反而会取消掉整轮重试。上限读自宿主默认策略本身,两处不会漂移。
  • 上游原生错误码保留在 message 里:映射后的码是给执行器读的路由身份,网关自己的拼写以 (原生码) 括注进失败消息,映射后仍可诊断。
  • 分类不丢弃已投递的内容:三个转换器统一按「先关闭未闭合的块 → usage → 终局分类」的顺序收尾,半截回答的 delta 不会被失败覆盖掉。

验证

  • 单测(test/plugin.test.mjs,全过):Config schema(多网关)、多网关注册(registerConfigurableProviders/registerAdapter 各 2 路由)、网关隔离(listModels A 不含 B 的模型)、内置+自定义模型解析、listModels/resolveModel(含 UNKNOWN_MODEL 拒绝)、prepareCall、两种协议的 SSE→chunk 转换(文本、流式 tool_use、流式 tool_calls、reasoning_content)、模型发现字段映射、思考强度派发(wire 拼写、off 显式关闭、未声明/未映射档位请求前拒绝、map 校验、lone-off 清理)、runtime 按网关 index 的增删改/provider 改名实时生效(重名/空名拒绝)/自定义、两段式连接测试(/models 优先且成功即止——仅一次请求;/models 不通回退三协议 "hi" 实聊探测、首选模型选取、草稿 model 原样派发、/models 不通且无模型的引导失败、两段皆败合并报错、坏 URL/CRLF 请求头拒绝、上游 401 回显脱敏、凭据 URL 掩码、custom 模式两段各自 verbatim 端点)、失败分类回归(429→RATE_LIMIT 且带 status/providerRetryAfterMs、超上限 Retry-After 被丢弃、503→SERVER、401→AUTH、拒连→TRANSPORT、半截流→TRANSPORT 且不丢已投递 delta、零输出→EMPTY_RESPONSE)。
  • wire 单测(test/wire.test.mjs / test/responses.test.mjs,全过):三协议各自的终局分类——[DONE]+finish_reason / message_stop / response.completed 的正常三态(stop / max-tokens / tool-calls)、流内 error 事件与 in-band error 对象的码映射、传输抛错、截断与零输出,以及分类函数本身的单元断言(HTTP 状态、Retry-After 两种拼写、上游原生码映射表、文本兜底顺序、凭据 URL 遮蔽)。
  • 渲染冒烟(test/client-page.test.mjs,全过):面板结构(hero/页签/卡片/编辑器)与模型列表 ⇄ 配置 JSON 双向契约——常驻完整参数框架(null=未设置、目录值不泄漏)、手动输完整目录 id 不自动填参、点选下拉/↑↓+Enter 才套用预设、JSON 手写组重建列表行、无效 JSON 锁定列表编辑。
  • live(假 key):对按 UA 白名单校验的网关,自定义 UA 生效(UA 关通过、key 关拒绝);配真 key 即可用。

限制

  • anthropic-messages 路径会根据模型选择的 reasoning 档位自动处理原生 thinking;无需单独配置开关。
  • openai-completions 路径按 reasoningEfforts 映射派发 reasoning_effort(语义见「思考强度派发」;未声明档位请求前拒绝,不做网关侧猜测)。
  • 模型发现拉取的 /models 若网关也做 UA 校验,插件已带自定义 UA,可正常访问。
  • 网关路由名改动实时生效(面板保存时立即重新注册路由);多网关路由名不能重复(面板会自动生成去重名,重名保存被拒绝)。
—/ 5

No ratings yet

Verified DSH bundle

Commit 8b3335143c6c

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout