dsh-request-manager
中文 | English
管理 DeepSeek Harness 的 LLM API 请求策略,修改默认的请求行为策略。
插件在会话区顶部加入了「请求管理」按钮,点击打开「请求管理面板」。所有设置全局生效、跨会话共享,无需独立开关——每个功能由其配置值决定是否介入。
请求管理面板
功能
请求额外间隔
在每个 API 请求之间插入额外的延时(毫秒)。各会话的首个请求不等待,自动与手动重试同样生效;0 = 不介入。
重试次数
修改默认 API 请求失败后的自动重试次数(DSH 当前版本默认 5 次),留空 = 不覆盖。某些类型的 API 请求失败 DSH 默认不会重试(例如 SSE stream ended without [DONE]、NETWORK ERROR 等),本选项不影响这类失败的重试策略。
拦截「请求失败」
API 请求失败后,请求管理面板自动展开,显示拦截的失败请求处理方式卡片:
拦截「请求失败」
- 🔁 重试:原地重发同一请求(仍受额外间隔约束);若再次失败会再次显示,可反复重试。
- 🚩 放弃:按 DSH 原生路径终结该轮。
本选项对 DSH 默认不会重试的失败也统一生效。超时时长(秒)与超时动作可配置:超过设定时长后按所选动作自动重试或自动放弃,0 = 不启用超时;点击停止或销毁会话会立即放行。选择「自动重试」时,持续失败的请求会按超时周期反复重发。
安装方法
dsh plugin --profile web add github:tree-func/dsh-request-manager
安装后重启 DSH 进程生效。
卸载方法
dsh plugin --profile web remove dsh-request-manager
架构
单包 bundle 插件,host 与 client 两半:
dsh-request-manager/
├── package.json # dsh.bundle.patch + dsh.client.platform: web 声明
├── cordis.patch.yml # 在 profile root 层插入本 bundle
├── index.js # host 半边
├── client.js # 浏览器半边
└── test/
├─ smoke.mjs # 冒烟回归:在真实 cordis 应用里跑通三条主线
└─ persistence.mjs # 持久化回归:面板 ↔ localStorage ↔ 宿主 三方一致性
- host(index.js):以 bundle 形式挂在 web profile 根层,在 root 层监听所有会话的
agent/request(插入额外间隔)与 agent/request-error(重试次数覆盖、拦截挂起)事件;并注册 /reqmgr-api 常驻路由,供浏览器读写全局配置、提交拦截决议。
- client(client.js):经官方 ModuleLoader 加载,向
conversation.session.header.utilities 槽位注册「请求管理」按钮与下拉面板;每秒轮询拦截挂起状态,样式全部使用 --dsw-* 语义令牌以适配浅色/深色主题;配置经浏览器 localStorage 持久化,页面加载时自动恢复到宿主。
兼容性
当前适配 DeepSeek Harness 0.1.6-alpha.2。harness 升级可能「静默」破坏插件机制(事件名/负载或读取接口变化不会报错,只是功能失效),因此仓库内附冒烟测试:
npm test
会输出两类结果:
全部通过(ALL PASS):三条主线(额外间隔 / 重试覆盖 / 拦截挂起)在真实 cordis 应用中均正常;
SKIP 未找到可解析的 DeepSeek Harness 运行时:当前机器没有可解析的 harness 包,测试整体跳过(退出码仍为 0)。
历史回归点:Session.events getter 已被 harness 移除(commit 5660f44d29)。插件早期靠遍历它统计「自动重试 N 次」,升级后会直接抛 TypeError: agent.session.events is not iterable,整条 agent/request-error 瀑布崩掉、拦截卡片不再出现。现改为插件自维护计数(凡下游交回 {kind:'retry'} 即计一次),不依赖会话事件日志。冒烟测试专门覆盖这一点。
设置的持久化
配置是宿主内存里的全局单例(DSH 一重启就回默认),浏览器 localStorage 是唯一持久副本。因此面板、本地存储、宿主三者必须始终一致,客户端半边按下面三个状态管理:
| 状态 | 含义 |
|---|
dirty | 用户改动过、待推送宿主的字段。写入只推这些字段,不做全字段覆盖——面板的旧值因此不会盖掉别的标签页刚写入的字段 |
pinned | 显示值以本地为准:编辑中,或启动恢复尚未被宿主接受。宿主轮询一律不回填 pinned 字段,所以迟到的响应不会抹掉正在输入的值 |
seeded | 是否已有「配置事实源」。首次读到宿主配置且本地无存储时顺手落盘,使纯只读的浏览器也能持久化 |
配套行为:
- 防抖保存用
ctx.timeout(插件 fiber 作用域)——切会话/卸载组件不再丢弃已输入的内容,只有插件真正卸载才取消;
- 写入只推改动字段,且逐字段校验:某个字段处于编辑中间态(如
abc)只跳过它,不吞掉同批其它字段;
- 写入失败保留用户输入并保持
dirty,每秒轮询自动重试,同时弹一次可见提示;
- 启动恢复被拒同样提示并重试,直到宿主接受为止;
- 读取 localStorage 只接受真正的
number/boolean/null——Number(null) === 0 会把「超时时长」静默变成 0(含义是"不启用超时"),这类强转全部去掉。
test/persistence.mjs 覆盖以上全部行为(共 34 项断言),覆盖的都是曾经真实复现过的缺陷。
已知局限
- 会话之间相互隔离:失败拦截的提示(按钮警示圆点、自动展开的面板)只出现在发生失败的那个会话里;停留在其他会话时不会收到任何提示。若长时间不切回,该挂起会按超时设置被自动重试或放弃。
- 自动重试计数包含非 provider 重试:
compaction-basic / compaction-image-offload 也会在 agent/request-error 上交回 {kind:'retry'}(修复上下文/图片后原地重发,不消耗 provider 重试预算)。这些同样计入卡片上的次数,因为对用户而言都是「这一次请求被自动重发」。
本地化
界面文案经官方 locale 服务注册词典(命名空间 requestManager),跟随 DSH 语言设置在中英文间自动切换。
构建方法
本插件为纯 JavaScript,源码即产物,无需构建步骤:
npm test # 语法校验 + 持久化回归(34 项)+ 冒烟回归(真实 cordis 应用)
开发期迭代:修改 client.js 后刷新浏览器页面即可;修改 index.js 需重启 DSH 进程。
许可证
MIT
本插件使用 AI 辅助制作。