Repository navigation
Releases: Phoeky/dsh-sensenova-pool
Release list
v0.2.1 — 修复 400(issue #2)
dsh-sensenova-pool v0.2.1
修复 issue #2 的 400。
问题
400 {"message":"inference request is invalid","type":"invalid_request_error"}
400 {"message":"invalid arguments","type":"invalid_request_error","code":"3"}原作者怀疑是 reasoning_effort 取值被上游拒收。实际排查后确认这是一整类问题。
根因
shim 一直把请求体 原样转发给商汤(这是"透明代理"的应有之义),但商汤的 schema
校验比 OpenAI 官方严得多,而 DSH 的 pi-ai 层完全按 OpenAI 规范构造请求。于是
「官方接受、商汤不认」的写法会直接撞 400,而且换 Key 根本解不开——这正是
issue #2 里"换 Key 无用"的原因。
实测(deepseek-v4-flash,逐例对真实上游验证)以下写法全部 400:
| 请求体写法 | 上游返回 |
|---|---|
{"role":"developer","content":"…"} |
inference request is invalid |
{"role":"assistant","content":null}(无 tool_calls) |
inference request is invalid |
{"role":"assistant"}(缺 content 字段) |
inference request is invalid |
{"role":"system","content":null} |
inference request is invalid |
{"role":"tool","content":null} / 缺 content |
inference request is invalid |
tool_calls[*].function.arguments 传对象 |
invalid arguments (code:3) |
content 传数字 |
invalid arguments |
temperature 传字符串 |
invalid arguments |
logprobs / top_logprobs / metadata |
invalid arguments |
reasoning_effort: "off" |
field ReasoningEffort invalid |
关键实测:developer → 400,同一份内容改成 system → 200。
修复内容
新增 src/sanitize.ts,在 shim 转发之前做语义等价规范化:
- role:
developer→system(OpenAI 新规范里替代system的角色) - content:缺失 /
null的assistant·system·tool→""
(带tool_calls的assistant保持null不动,商汤接受,动它有风险) - arguments:传对象 → 序列化成 JSON 字符串
- content 标量:数字/布尔 → 字符串
- 采样参数:
temperature/top_p/frequency_penalty/presence_penalty字符串 → 数字 - reasoning_effort:
off→none - 摘除:
logprobs/top_logprobs/metadata
刻意不代改的三类
改了只会把错误往后推,甚至篡改用户意图:
user.content为null/"":确实是空用户轮,上游报user content required- 数值越界(
temperature=3、frequency_penalty=5):上游报错信息已足够明确 developer之外的其他未知 role
重要修正:max 与 minimal 是合法档位
初版按"off/max/minimal 都不合法"实现,把 max 映射成 xhigh。真机验证时
上游报错原文写得很明确:
should be one of: none, minimal, low, medium, high, xhigh, max
即 max/minimal 本就合法,映射它们会悄悄降低你要求的推理强度。
已修正为只把 off 翻成 none,其余合法值原样保留。
新增排障开关 debugRequestOn400
商汤的 400 不回显请求体、也不说哪个字段有问题,历史上只能靠猜。打开该开关后,
上游返回 400 时日志会直接打出实际发出的请求体,一眼定位。
验证
- 单元测试
tools/test-sanitize.mjs:46 项(每条规则对应一次真实上游实测,
外加幂等性、不修改入参、边界用例) - shim 端到端 新增 3 组:假上游复刻商汤严格校验 →
「脏请求被修好返回 200」/「干净请求逐字节原样转发」/「400 时日志含请求体」 - 全套 9/9 测试套件通过,
typecheck通过 - 真机验证
tools/e2e-issue2-400.mjs(会消耗额度):8/8 用例
清洗前 400 → 清洗后 200
升级
npm i dsh-sensenova-pool@0.2.1然后重启 DSH。不需要改任何配置,更不需要换 Key。
需求版本:DeepSeek Harness 0.2.0-rc.2 或更高。
Closes #2
v0.2.0
新增功能
- 设置页一级分区:DSH 设置侧栏新增
dsh-sensenova-pool,Key 池、对外接口、模型可见性集中在一页。插件页里的卡片保留。 - 对外 OpenAI 兼容接口:设置页展示接口地址、端口与 API Key,复制后填进其他 Agent 或客户端即可使用;请求共用同一个 Key 池,遇到 429 同样自动换 Key。
- 对外 Key 持久化:保存在 DSH 凭据库里,重启后不变;支持切换密钥,切换瞬间旧密钥仍然有效,避免在途请求 401。
- 模型可见性:可勾选哪些模型出现在模型选择器里,取消勾选的模型不再注册给 provider,选择跨重启保留。
升级说明
- 从 0.1.x 直接升级即可,原有 Key 不需要重新填写。
- 对外 Key 在首次启动时生成,随下一次写入保存到凭据库。
v0.1.3 — 修复重启后 Key 池为空(启动竞态)
npm: dsh-sensenova-pool@0.1.3
修复
-
重启电脑后 Key 池为空(「忘记 Key」) —— 启动竞态。插件只
inject: ['llm'],与credentials之间没有顺序保证;cordis 的Service在构造函数里就provide(),而credentials-local的loadInitial()(读.credentials.yaml填充内存快照)要到Service.init才跑完。旧实现在启动块里只读一次凭据库,冷启动磁盘 IO 慢时读到空快照 → 池子变成 0 把,且此后没有任何补载机制(credentials/record-updated只在运行期写入时触发,覆盖不到启动装载),只能重新填 Key。现在改用 cordis 自己的就绪通知
ctx.inject(['credentials'], cb):回调只在凭据服务所属 fiber 变为ACTIVE(loadInitial()已填充快照)时触发。精确、零延迟,且宿主没有凭据服务时回调只是不触发,启动路径照常提示填 Key(不会白等超时)。Key 一直都在,不需要重新填。
-
写入可能抹掉已存的 Key ——
saveKeys()是用pool.keys()整体覆盖写记录。若在首次装载完成前写入(启动还没完就点了「添加 Key」),池子里只有刚加的那把,磁盘上原有的 Key 会被静默抹掉。现在写入前会等待首次权威装载(10s 兜底,且仅在凭据服务确实存在时才等)。 -
卸载时的 libuv 崩溃 —— disposer 原先写
void shim.close(),Promise 无人等待,server 仍在监听时进程就可能退出,Windows 上表现为
Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), src\win\async.c, line 94(退出码0xC0000409)。改为return shim.close(),由 cordis 等待回环服务真正关闭。
测试
- 新增
tools/test-credentials-race.mjs:11 项断言复刻启动竞态(在旧代码上有 6 项失败)。 - 新增
tools/e2e-real-credentials.mjs:真实插件 + 真实 cordis + 真实credentials-local,在真实.credentials.yaml上跑完整启动路径。旧代码复现出与 issue 完全一致的「key 池为空」与/statustotal: 0;修复后装载出 2 把 Key。 - 修掉凭据套件里一个既有的 libuv 崩溃:该测试会真实访问外网,进程在 DNS/TLS 未落地时
process.exit()会触发断言 —— 断言全过却被判失败。测试现已完全离线。 - 离线套件 7/7 通过,typecheck / build 干净。
相关
完整说明见 README
v0.1.2 — 修复凭据记录导致 DSH 无法启动
npm: dsh-sensenova-pool@0.1.2
修复
- Key 池改用
grant记录写入凭据库。credentials-local按标签做字段白名单校验:
api-key只认kind/key/env,grant只认kind/payload。此前以
kind: api-key+payload写入能通过写入路径(不校验未知字段),但下次启动解析失败,
导致credentials插件激活失败并中止整个 DSH 启动
(弹「The application could not start or stopped unexpectedly」)。一条坏记录即可锁死应用。 - 新增回归测试,把写出的记录钉死在
credentials-local真正解析的 schema 上。
改进
@deepseek-ai/*peer 范围补充显式预发布分支,避免用户在npm install时遇到需要手工绕过的ERESOLVE。- README 补充「DSH 完全起不来」的排查与手工修复步骤。
完整说明见 README
v0.1.1 — 截图链接修复
npm: dsh-sensenova-pool@0.1.1
- README 截图改用绝对 URL,npm 页面也能正常显示(
docs/不随包发布)
⚠️ 此版本存在缺陷,请直接使用 v0.1.2。
Key 池以kind: api-key写入凭据库,而该标签只接受kind/key/env;
坏记录会在下次启动解析失败,导致credentials插件激活失败并中止整个 DSH 启动。
已在 v0.1.2 修复。
v0.1.0 — 商汤日日新多 Key 轮换池
dsh-sensenova-pool v0.1.0
DeepSeek Harness 的商汤日日新(SenseNova)多 Key 轮换池插件首个版本。
功能
- 🔑 多 Key 轮换池 —— 一次粘贴任意多把 Key(换行 / 逗号 / 分号分隔),自动清洗
Bearer前缀、引号、重复项与无效串;界面只显示脱敏值 - 🛡 429 限流自动切换 —— 遇 429 立即换下一把 Key 重试;每把 Key 独立冷却,指数退避 + 抖动;本地 RPM 预限流从源头避开限流;池子耗尽时回报
503 + Retry-After而不是 429 - 🧠 一次接入全部模型 ——
deepseek-v4.1-flash、deepseek-v4-flash、deepseek-v4-pro、deepseek-flash、glm-5.2、kimi-k3、sensenova-6.8-flash-lite;内置目录离线可用,拿到有效 Key 后自动从/v1/models刷新 - ⚙️ 零配置 —— Key 存在 DSH 凭据库,不写入任何配置文件,填入即生效
安装
npm install dsh-sensenova-pool然后把 dsh-sensenova-pool 加入 profile package.json 的 dsh.profile.bundles,重启 DeepSeek Harness。
使用
打开 插件 → dsh-sensenova-pool,粘贴 Key,点「添加 Key」;在模型选择器里选「商汤日日新(Key 轮换池)」下的模型即可。
致谢
- corrinehu/dsh-workbuddy-connect —— 将第三方模型接入 DSH 的插件范式
- Phoeky/st-rotator —— 商汤 Key 轮换网关,本插件的轮换策略参考自该项目
完整说明见 README
⚠️ 此版本存在缺陷,请使用 v0.1.2。
Key 池以kind: api-key写入凭据库,会导致下次启动时credentials插件激活失败,
进而中止整个 DSH 启动。已在 v0.1.2 修复。