dsh-undici-timeout:为 DSH LLM 出口配置可调的 undici 直连通道

前言

在 DeepSeek Harness(DSH)里,智能体调用大模型通常走 Node 内置的 fetch,底层是 undici。维护者 mqnu00 在 README 里记录过一个很具体的问题:connectTimeout 固定为 10 秒(DNS、TCP、TLS 一并计入),没有环境变量或参数可调;在 WSL 直连 opencode.ai 时,新建握手曾测到约 11.6 秒,容易被截断。pi-ai 每次重试会 new OpenAI(...),当场通过 Shims.getDefaultFetch() 读取全局 fetch,于是每次尝试都是新的 TCP+TLS,超时后重试又重连。对比之下,OpenCode 的 Go 客户端可以复用已建立的 HTTP/2 连接,握手只做一次,同类网络下往往更稳。

dsh-undici-timeout 针对的是这条 LLM 出口链路:用可配置超时和 keep-alive 的 undici Agent 接管白名单域名的直连流量,尽量做到「长超时 + 保活复用」,同时不与代理插件抢 setGlobalDispatcher。下文按定位、能力、安装与排障说明用法;事实以 GitHub 仓库 README 为准。

这是什么

  • 名称:dsh-undici-timeout(GitHub:mqnu00/dsh-undici-timeout)
  • 维护者:mqnu00
  • 分类:联网工具(社区目录 SkillHub 条目)
  • 定位:给 DSH 的 LLM 出口(globalThis.fetch → Node 内置 undici)换一条可配置超时、可保活复用的直连通道;单文件补丁、零 npm 依赖,只从 GitHub 分发,不发 npm、不进插件市场。

README 明确把它写成「补丁」而非长期功能块:一旦上游解决固定 10s 连接超时、或 DSH LLM 层可自行指定 dispatcher/超时,或换掉内置 fetch,即可卸载。

核心功能

包装全局 fetch,只接管白名单直连

请求路径大致为:agent 回合 → dsh-llm-retry → dsh-llm-pi-ai → pi-ai → OpenAI 客户端每次尝试读取全局 fetch → 本插件包装后的 globalThis.fetch。

  • 命中 hosts 白名单且未检测到代理环境变量时:使用自建的 undici Agent 调用 undici.fetch(url, { dispatcher })。
  • 代理模式、非白名单域名或 Request 输入等:透传原来的 fetch。

插件不调用 setGlobalDispatcher,避免与 @deepseek-ai/dsh-http-proxy 冲突;当存在 http_proxy / https_proxy / ALL_PROXY(大小写均可)且 delegateOnProxyEnv 为默认 true 时自动让路,可与 dsh-net-proxy、dsh-net-autoswitch 共存。

可调超时与连接复用

通过 cordis.patch.yml 中本插件的 config 配置(节选 README 表格中的核心项):

键 默认 说明
connectTimeoutMs 30000 DNS+TCP+TLS 总预算(对比内置固定 10s)
headersTimeoutMs 300000 等待响应头
bodyTimeoutMs 0 相邻 body 块间隔;0 表示关闭
keepAliveTimeoutMs 60000 空闲连接保留,便于复用
keepAliveMaxTimeoutMs 600000 保活上限
pipelining 1 每连接并发请求数
family 0 0 跟随系统;4/6 强制 IP 族
allowH2 false 默认关闭 HTTP/2(见下文注意)
hosts ["opencode.ai"] 含子域;"*" 表示全部直连流量

HTTP/2 默认关闭

undici 8 将 allowH2 默认改为 true。README 记录:用 harness 解析到的 undici 8 建 Agent 时,ALPN 可能变成 h2,而 opencode.ai 经 Cloudflare 时,h2 上的 SSE 长流可能出现 NGHTTP2_ENHANCE_YOUR_CALM 等流错误。插件默认 allowH2: false,在保持 keep-alive 复用的前提下继续走 h1。

状态路由与探针

  • 只读状态:routePath 默认为 /_dsh/undici-timeout。
  • 启动探针:probeOnStart 默认 true,默认打 LLM 网关 models 端点(不消耗 token)。
  • 日志:默认写入 $DSH_HOME/logs/dsh-undici-timeout/host.log;logFailures 默认记录失败及 cause。

安装与启用

官方安装命令(README 原文):

dsh plugin --profile web add github:mqnu00/dsh-undici-timeout

装完后重启 DSH 生效。需要钉版本时:

dsh plugin --profile web add github:mqnu00/dsh-undici-timeout#v0.1.1

package.json 要求 Node >=22.19.0、DSH >=0.2.0,许可证 MIT。零 npm 依赖、无构建步骤;勿与 file:// 直插 profile 的同名 insert id undici-timeout 同时使用,否则会冲突。

卸载示例(README):

dsh plugin --profile web remove dsh-undici-timeout

典型用法

1、用 GitHub 源安装后按需改配置

在 profile 的 cordis.patch.yml 里找到本插件的 config,按网络环境调整。WSL 或 IPv6 无出口时,README 路线 B 示例会设 family: 4 并保持 allowH2: false:

config:
  enabled: true
  hosts: [opencode.ai]
  connectTimeoutMs: 30000
  family: 4
  allowH2: false
  bodyTimeoutMs: 0
  keepAliveTimeoutMs: 60000

2、查看运行状态

在本机回环访问状态路由(Host 白名单仅放行回环/本机网卡):

curl -s http://127.0.0.1:3080/_dsh/undici-timeout | python3 -m json.tool

关注 stats.matched / ok / failed / byError 等字段,判断接管次数与失败类型。

3、手动触发探针

curl -s -X POST http://127.0.0.1:3080/_dsh/undici-timeout \
  -H 'x-dsh-undici-timeout: 1' -H 'content-type: application/json' \
  -d '{"action":"probeNow"}'

适用场景与注意

适合谁

  • DSH web profile 下频繁调用 LLM API,直连握手经常顶到内置 10s connectTimeout 的用户。
  • 需要拉长连接阶段预算、并通过 keep-alive 减少重复握手的环境。
  • 与 net-proxy / autoswitch 同机部署,希望代理开启时插件自动让路、关闭时代理直连的用户。

使用前请知悉

  • 插件以当前 DSH 进程权限运行,安装前应在 GitHub 阅读源码与 MIT 许可证,自行评估是否纳入环境。
  • 它是针对 Node/undici 上游行为的补丁;DSH 或 Node 侧一旦提供等价能力,应按 README 建议卸载,避免长期叠补丁。
  • connectTimeoutMs 调大不能单独解决「双栈 happy eyeballs 在无效 IPv6 上耗光整轮」类问题;README 实测建议在无 IPv6 出口时考虑 family: 4。
  • 社区目录 SkillHub 与 DeepSeek / 幻方无官方从属关系;DSH 生态强调「一切皆插件」,本文介绍的是社区维护的联网工具插件。

结尾

dsh-undici-timeout 用最小改动接管 LLM 出口的直连 fetch:可调连接/头/body/保活超时、默认关 h2、代理环境自动退让,并提供状态路由与探针便于排障。需要细节配置表、h2 问题分析与卸载条件时,以仓库 README 为准。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye