dsh-session-robustness
DSH plugin: after official llm-retry (n/5), keep retrying transient API failures on the same open step until success, cancel, or pause.
- Stars
- 0
- Language
- JavaScript
- Created
- Sep 7, 2026
- Updated
- Sep 7, 2026
Introduction
dsh-session-robustness
工程级会话鲁棒:当一次 turn 的模型请求因 超时 / 传输 / 限流 / 5xx / 空响应 / SSE 断流 失败时,不要在官方默认的「再试五次」之后把会话掐死,而是 继续重试直到成功、你取消、或你暂停。
这不是客户端发「继续」续跑,也不是换模型。它接在官方 dsh-llm-retry 后面,在同一个打开的 step 上重跑同一条请求。
为什么需要它
DSH 已经有官方重试执行器 @deepseek-ai/dsh-llm-retry:
- 挂在 host 组合里(
dsh-base已启用) - 策略写在每个 provider 的
retryPolicy上 - 省略时是
mode: normal,默认maxRetries = 5 - 合格 code:
EMPTY_RESPONSE/RATE_LIMIT/SERVER/TIMEOUT/TRANSPORT(官方);本插件额外无条件覆盖STREAM_CLOSED/MALFORMED_RESPONSE/STREAM/HTTP_408/HTTP_409/HTTP_425/HTTP_429/HTTP_499/HTTP_5xx
所以一次 TIMEOUT 通常是:界面「已重试模型请求 (n/5)」,5/5 之后 agent/request-error 不再返回 { kind: 'retry' },loop 把失败当成终态,turn 结束。
官方也有 retryPolicy.mode: always(无次数上限、连 AUTH/QUOTA 也会一直重)。本插件 不走那条路:永久错误等也没用,无限重试会把会话挂死并烧钱。本插件只覆盖瞬时 API 失败。
行为
adapter 流失败
→ agent/request-error waterfall
→ compaction(只处理 CONTEXT_WINDOW_EXCEEDED)
→ dsh-llm-retry(normal:合格 code 最多 5 次)
→ 本插件(官方预算耗尽后,瞬时失败继续重试)
- 每次重试都在 同一个打开的 turn / 同一个 step 上重跑,失败分片不会进入模型可见历史
- 退避:指数 + 抖动;提供方
Retry-After在上限内优先 - 用户点 Stop / 取消 turn:立即停止
- 设置页可暂停 / 关闭,不必卸插件
- 父会话和 in-process 子 agent 共用 host 上的
agent/request-errorwaterfall,都会接管。进程外 / ACP 子 agent 不会。
永不重试:AUTH、MISSING_CREDENTIAL、INVALID_CREDENTIAL、QUOTA、CONTEXT_WINDOW_EXCEEDED、NO_ADAPTER、ABORTED。上下文溢出仍交给官方 compaction。用户 Stop 不会被重试。
0.1.3 起,能进 agent/request-error 的网络失败一律无条件重试,不再依赖文案:SSE 对端关闭(STREAM_CLOSED,例如 ended without [DONE])、半截 JSON(MALFORMED_RESPONSE)、Responses 未识别流失败(STREAM)、HTTP 408/409/425/429/499/5xx。0.1.5 起还包括适配器原样透传的 stream_read_error,以及网关文案 Upstream request failed(pi-ai 常落成 PI_AI_ERROR)。PI_AI_ERROR / UNKNOWN / 未知码仍用文案启发式(Codex overloaded、Upstream request failed 等)。HTTP_400 / INVALID_REQUEST / CONTENT_FILTER 仍不重试。
进不了 waterfall 的失败本插件也接不到:prepareCall 抛错、标题/摘要走的 ctx.llm.stream()、工具调用失败。
0.1.6 起,只对 openai-codex:流失败后丢掉该会话的 WebSocket 续写(previous_response_id)。pi-ai 的 websocket-cached / auto 会把失败流上的 response.id 当成续写锚点,API 已经恢复也会一直钉在那个不完整的响应上。官方那 5 次重试也会清(只观察 llm/stream,不在流内重试)。成功回合的续写保留;服务端 prompt cache 和会话历史不受影响。其它 provider 不动。
Codex overloaded 为什么也要覆盖
openai-codex 走 pi-ai。上游 SSE 事件 error / response.failed 会变成:
Codex error: Our servers are currently overloaded. Please try again later.
pi-ai 的 classifyPiAiError() 只认 HTTP 状态码和少数关键词,不认 overloaded,于是失败码落成兜底的 PI_AI_ERROR。官方 dsh-llm-retry 只重试那 5 个标准码,本插件原先也只看码表,所以这条瞬时失败会直接变成「本轮运行失败」。
0.1.2 起:PI_AI_ERROR / UNKNOWN 若文案明显是瞬时故障(overloaded、try again later、service unavailable 等),也会继续重试。AUTH / QUOTA 等永久码仍优先,即使文案里写了 try again。
安装
dsh plugin --profile web add dsh-session-robustness
或从 GitHub:
dsh plugin --profile web add github:bainianlaoyao/dsh-session-robustness
包声明了 dsh.bundle.patch + dsh.client。安装后重启 web profile,设置页会出现 会话鲁棒性。
本地开发(本仓库):
dsh plugin --profile web add D:/Data/DEV/dsh/dsh-session-robustness
使用
- 打开 设置 → 会话鲁棒性
- 默认已启用、次数上限 0(无限)
- 官方还在重试时,聊天里是「已重试模型请求 (n/5)」——这时还没轮到本插件
- 5/5 之后 composer 上方会出现「官方 5 次已用完,正在接管第 N 次」,可当场暂停
- 需要硬上限时把「次数上限」改成正整数(这是 官方 5 次之后 的额外次数)
也可直接写 $DSH_HOME/settings.yaml:
session-robustness:
enabled: true
paused: false
maxRetries: 0 # 0 = 无限(官方 5 次之后)
initialDelayMs: 1000
maxDelayMs: 30000
jitterRatio: 0.2
extraRetryableCodes: [] # 例如 INVALID_REQUEST;永久错误仍会被拒绝
不是什么
- 不是
dsh-client-auto-retry:那个在 turn 已经结束 后自动发「继续」 - 不是
retryPolicy.mode: always:那个会重试 AUTH/QUOTA - 不是改官方 npm 包:升级不会被覆盖
- 不包装
ctx.llm.stream():原始流无法持久区分已发出的分片
卸载
dsh plugin --profile web remove dsh-session-robustness
设置里的 session-robustness 段会留在 $DSH_HOME/settings.yaml;不需要了就手动删。
开发与测试
npm test # 静态冒烟:waterfall 接管、永不重试码、HTTP JSON 桥
License
MIT