dsh-llm-adapters 1.2.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/DEVELOPMENT.md CHANGED
@@ -27,7 +27,6 @@ src/
27
27
  codearts-sign.ts 华为 SDK-HMAC-SHA256 请求签名
28
28
  codearts-endpoints.ts 网关基址、窗口静态表、benefit 与 VL 清单
29
29
  codearts.ts CodeArts 双目录合并 / 签名流式调用 / 额度 / 签到
30
- codearts-window.ts CodeArts 会话预算:每 60 秒 3 次的排队窗口
31
30
  catalog.ts 目录解析、模型缓存、能力来源合并
32
31
  model-name.ts 公共名称规则:括号标签、倍率、连字符归一化、匹配 key
33
32
  openrouter.ts OpenRouter 模型表:原子同步、磁盘缓存、通配匹配档位与模态
@@ -209,8 +208,47 @@ SHA-512 摘要 + JSON。插件从解密结果取 `Cloud-IDE-JWT`,从
209
208
  没有独立的签到端点:积分由服务端在当天首次网页访问时发放,`setting_info` 的
210
209
  `point_grant_popups` 只是通知,`/points/v2/bills` 里 `biz_type=daily_grant` 的记录才是可核对的
211
210
  凭据。插件只把 `grant_at` 属于今天(北京时间)的记录算作今日签到,并把 `created_at` 作为到账时间
212
- 显示;`ensureDailyCheckin()` 先读流水,未到账才以 web 身份调用 `setting_info`。宿主按
213
- `raccoonCheckinMinutes`(0 关闭)定时跑这一遍,卡片上的网页入口始终保留。
211
+ 显示;这条路径是**只读**的:`checkinStatus()` 只读 `/points/v2/bills`,卡片上的按钮是「检测今日积分」,
212
+ 不实现 `claimCheckin()`。
213
+
214
+ 上游确实存在一条能主动触发发放的路径——`POST /office/v3/setting_info` 就是网页当日首次加载时发的那个
215
+ 请求,重放它即可让服务端下发积分。插件**刻意不做**这件事,两个原因:一是那是在用户没要求的那一刻替账号
216
+ 做状态变更;二是 harness 每轮重发上下文的场景下,把「顺带触发一次网页访问」藏进「看一眼到账没有」里,
217
+ 等于让观察动作带副作用。领取交给官网,「打开网页签到」链接始终保留。
218
+
219
+ 自动监测同样不做:按间隔定时跑一遍意味着每个间隔都花掉一次真实请求(无论积分是否迟到),并且会在用户
220
+ 不知情时消耗账号额度。没有 `raccoonCheckinMinutes` 这个配置项,也没有定时器。
221
+
222
+ ### 小浣熊的吞吐墙与会话 id(`raccoon.ts`)
223
+
224
+ 上游的 `user_model_rate_limited`(「您的模型请求频率超限」)不是 RPM,而是**账号级 token 吞吐**。
225
+ 社区在真实账号上的对照测量:1 秒间隔连发 ~320KB body,前 10 条 200、第 11 条 429;50 条小请求
226
+ 连发不撞;直连与走代理同样 429(不是出口 IP);假 token 打同一地址回 401(链路正常,封的是额度);
227
+ 连续 429 达 122 秒后小请求立刻 200、紧接着一条大请求又 429(桶在回血,速率只够小请求)。
228
+ 官方客户端在同一账号上 26 分钟 125 次调用、97 次间隔 ≤10 秒,**0 次 429** —— 频率假说因此被证伪。
229
+
230
+ 恢复时间上游不给:429 报文里没有 `Retry-After`,也没有任何 `x-ratelimit-*`,只有
231
+ `server-timing: inner; dur=20`。所以**没有任何依据去算退避**,插件对这类失败不重试(在已超限的桶上
232
+ 重发会继续吃掉回血额度,公众号召实测里那种「8 次退避白等 38 秒」是负收益)。两条路径统一给出可读
233
+ 错误:HTTP 429 由 `openAiStream` 抛出,流内错误帧则由 `onFrame` 钩子识别——后者原本一律标 502,
234
+ 会把这个限制伪装成插件故障。判定只在 HTTP 路径看状态码,避免把流内已归类的 502 二次包装。
235
+
236
+ 官方与第三方的差异有两处,插件只做了其一:
237
+
238
+ 1. **会话 id 稳定**(已做)。官方日志显示 125 次调用全程一个 `sess-0-53927d74`;每个请求新建会话
239
+ 等于告诉上游「同一账号几分钟内开了上百个会话」。插件按「账号 + 模型」派生
240
+ `sess-0-<8 hex>`,摘要源自 JWT 的 `sub`(`decodeJwtFacts` 新增 `subject`),**纯函数**而非随机值 ——
241
+ 用进程内计数器会让重启后的同一会话变成新会话,正是这个 id 要避免的事。账号身份只以截断摘要
242
+ 出现,不上线;只有 chat 路由带这个头,目录/额度/签到等业务路由保持原样。
243
+ 2. **补官方 `X-Stainless-*` / `X-RACCOON-*` 身份并伪装 UA**(**未做**)。社区方案会把 UA 改成
244
+ `AsyncOpenAI/Python 2.8.0` 并补齐十余个官方头,其作者自己也标注「补头即解限属推断、未复验」。
245
+ 更重要的是这与 DSH 的归因策略冲突:`@deepseek-ai/dsh-llm` 的 `attributionHeaders()` 明确规定
246
+ 每个 provider 请求都必须带产品身份、且「nothing can suppress attribution entirely」,
247
+ 身份如需变更要走 `AppIdentity` 而不是逐请求硬改 UA。插件因此保留 DSH 自身 UA 与
248
+ `x-client-*` 头,不冒充官方客户端。
249
+
250
+ 真正可控的杠杆是单轮上下文大小:harness 每轮全量重发上下文,所以窗口从 1M 降到 200K 会让单轮
251
+ prompt 成本降到约 1/5,会话存活时间相应变长。这是纯本地设置,不依赖上游行为。
214
252
 
215
253
  ### CodeArts(`codearts-login.ts` + `codearts.ts`)
216
254
 
@@ -258,8 +296,8 @@ IDE 自身 provider 配置的 `limit.context`(`codearts-endpoints.ts` 的静
258
296
  4. 转述结果按 `sha256(data URL)` 缓存到 `$DSH_HOME/llm-adapters/codearts-image-cache.json`(最多
259
297
  64 条,`tmp → rename` 原子写)。同一张图在后续轮次、甚至重启后都直接复用,不再占会话额度。
260
298
 
261
- 每张**新**图是一次独立的 chat 请求,和主请求共用 `CodeArtsSessionWindow` 的 3 次/分钟额度,所以
262
- `describeImage` 也排队、也会在 `TM.00001041` 时退名额重试。`hasImageParts` 先做一次纯内存检查,
299
+ 每张**新**图是一次独立的 chat 请求,但它和主请求共用同一个会话(同一个 `x-ot-session-id`),所以
300
+ 不再额外占一个名额;`describeImage` 只在 `TM.00001041` 时退避重试。`hasImageParts` 先做一次纯内存检查,
263
301
  没有图片的请求完全不碰缓存文件。
264
302
 
265
303
  思考只有两档:`off` / `on`。网关切的是**顶层** `thinking: { type: 'disabled' }`,不是
@@ -294,18 +332,31 @@ IDE 自身 provider 配置的 `limit.context`(`codearts-endpoints.ts` 的静
294
332
  `wire.ts` 的 SSE 消费被拆成 `consumeOpenAiStream`:CodeArts 要自己签名 fetch,但帧处理与
295
333
  `openAiStream` 完全相同;`onFrame` 钩子用来抓「HTTP 200 + SSE 内嵌 `error_code`」的排队/额度错误。
296
334
 
297
- ### CodeArts 的会话预算(`codearts-window.ts`)
335
+ ### CodeArts 的会话计数键(`x-ot-session-id`)
298
336
 
299
- 体验版按账号限制会话:**每滚动 60 秒 3 次**,全模型共用,与并发无关,换 `session-id` 不加额度。
300
- 实网连续 6 次串行请求的结果是 `OK OK OK` 后整批被 `TM.00001041` 拒绝,约 61 秒后一起恢复;被拒的
301
- 请求**不**延长锁定(第 4 次在 72.9s 被拒、93.0s 与 113.2s 再被拒,133.4s 就恢复了,而不是
302
- 113.2 + 60)。反过来说,`InferHub.4004.200`(缺 benefit 包)是**会**占额度的,所以那条路径靠
303
- `noBenefit` 记住结论,而不是每次重猜。
337
+ 体验版按账号限制的是**并发会话**,不是每分钟请求数:3 个名额,一个会话在请求结束后仍占位约 60 秒。
338
+ 决定占哪个名额的是 `x-ot-session-id`,不是 `chat-id` / `session-id` / `prompt_cache_key`(后三者只管
339
+ 前缀缓存亲和)。实网对照,同一账号、同一个 `glm-5.3-flash`、每条 `max_tokens: 16`,臂间排空 78 秒:
304
340
 
305
- `CodeArtsSessionWindow` 因此做两件事:发请求前排队拿名额(满了就等最老的滑出窗口,`MARGIN_MS`
306
- 留 1.5 秒吸收「本地开始 → 网关计数」之间的网络时延),以及在被 `TM.00001041` 明确拒绝时把名额
307
- **退回**(`slot.release()`)——占着拒绝请求的名额,会把下一分钟拖成每 20 秒一次,而网关根本没记这
308
- 一笔。窗口是进程内单例:热重载重置计数只会让下一次请求撞上网关还记得的额度。
341
+ | 会话头 | 6 条串行 | 3 条并发 | 12 条串行 |
342
+ | --- | --- | --- | --- |
343
+ | 不发(每请求一个新会话) | 3 成功 + 3 × `TM.00001041` | — | — |
344
+ | 账号级稳定值 | 6/6,共 15.8 秒 | 3/3,1.5–1.7 秒 | 12/12,一条不排队 |
345
+
346
+ 「复用 id 挡不住撞线」的旧结论测的是 `session-id`,与这里不冲突;早期那个 3 次/60 秒的排队窗口
347
+ (`codearts-window.ts`)也因此删掉了——留着它,插件自己就还在按分钟限自己。
348
+
349
+ 这个值是**派生**的,不是随机生成的:对 refresh token 里的 `principal_id`(缺身份时退到
350
+ `account_name`)加固定前缀做 SHA-256,取 32 位 hex。好处是同一个账号在任何进程、任何一次续期之后
351
+ 都是同一个值,老记录不用重新登录也能补上;解析出来的那份同时存进 `accounts.json`(`otSessionId`,
352
+ 续期写回时保留)。令牌里解不出身份时退到本进程的 id——仍然是一个会话,不会退回每请求一个会话。
353
+
354
+ 这不是绕过计费:稳定会话与逐请求新会话两条臂的计量完全一致(每条 944 token、0.12 积分,SSE 长度也
355
+ 一样),上游的 token 计数器同步增长。这一点和「非流式请求返回 token 却不扣分」不同——后者是计费漏洞,
356
+ 不采用。上游也没有释放会话的接口(`/api/v2/chat/stop`、`/session/close`、`/sessions` 实测全部 404),
357
+ 所以被 `TM.00001041` 拒绝时只有退避重试(`QUEUE_ATTEMPTS` × `QUEUE_BACKOFF_MS`),用尽后给可读错误。
358
+ 带 `maas_type: benefit` 的失败(`InferHub.4004.200`)在过去每请求一会话的口径下确实各占一个名额,
359
+ 现在共用一个会话,但结论仍由 `noBenefit` 记住:没人该为一场注定失败的请求付一整轮时间。
309
360
 
310
361
  ### 公共的名称与能力规则
311
362
 
package/FAQ.md CHANGED
@@ -78,6 +78,25 @@ Trae 共用这套规则。默认选**最强**的一档(强度顺序
78
78
  插件不缓存 SSE,收到一块就转发一块。实测同一账号直连小浣熊的首字在 1–50 秒间波动,主要是
79
79
  上游排队;关闭思考可以减少生成阶段的等待,但不能消除排队时间。
80
80
 
81
+ **小浣熊聊久了报 `user_model_rate_limited`(「您的模型请求频率超限」)?**
82
+
83
+ 这条文案有误导:它说的不是每分钟请求数,而是**按账号的 token 吞吐**。社区实测可以佐证:
84
+ 1 秒间隔连发大 body 的请求,前 10 条全 200、第 11 条被拒;而 50 条小请求连发不撞。同一账号在
85
+ 官方客户端上 26 分钟跑了 125 次调用、其中 97 次间隔不超过 10 秒,一次都没被限流。所以
86
+ 「频率」不是瓶颈,**单位时间灌进去的 token 量**才是。
87
+
88
+ 撞上之后桶按吞吐缓慢回血,恢复是分钟级的;上游既不给 `Retry-After`,也不给任何
89
+ `x-ratelimit-*` 头,所以插件算不出「等多久」。插件也**不会**拿这事重试——在已经超限的桶上重发只会在
90
+ 回血额度里继续加压、把恢复时间往后推。你看到的是一条说明了原因的错误,而不是裸的 429。
91
+
92
+ 能自己动手的地方只有一个:**把该模型的上下文窗口调小**。一轮对话会把整段上下文全量重发,
93
+ 窗口从 100 万降到 20 万,单轮成本降到约 1/5,同一会话的存活时间就相应变长。这是纯本地设置,
94
+ 不依赖上游任何行为。
95
+
96
+ 插件另外给每个「账号 + 模型」发一个稳定的会话 id(`x-raccoon-session-id`,形态对齐官方客户端的
97
+ `sess-0-xxxxxxxx`,由账号身份摘要而来、不含账号本身):官方客户端全程复用一个会话,而每请求新建
98
+ 会话会被上游看成「同一账号在几分钟内开了上百个会话」。这一项有依据但尚未在本机实测到撞墙量级验证。
99
+
81
100
  **额度读取失败会影响对话吗?**
82
101
  不会。额度与模型目录是独立的读取;额度失败时卡片保留账号信息并显示原因,对话仍可用。
83
102
 
@@ -257,6 +276,17 @@ STS 令牌本来就只有两小时,插件在到期前 5 分钟用续期令牌
257
276
  (可用 / 永久 / 每日)和小浣熊(剩余积分)只报剩余额度,画进度条就得凭空补一个分母,因此仍然
258
277
  只显示数字。
259
278
 
279
+ **小浣熊的每日积分怎么领?**
280
+
281
+ 插件**只检测、不领取**。上游没有独立的签到端点:积分由服务端在当天首次打开网页时发放,插件读
282
+ `/points/v2/bills` 里 `biz_type=daily_grant`、`grant_at` 属于今天的记录来判断到账没有,并在到账时
283
+ 显示具体时间。卡片上的按钮因此是「检测今日积分」,不是「签到领积分」。
284
+
285
+ 要领取就点「打开网页签到」到官网(该链接始终保留)。上游确实有一条能主动触发发放的路径——网页当日
286
+ 首次加载发的 `setting_info`——但插件刻意不重放它:那是在你没要求的那一刻替账号做状态变更,而且会把
287
+ 「看一眼到账没有」变成带副作用的动作。插件也**没有**自动监测:定时按间隔跑一遍意味着每个间隔都花掉
288
+ 一次真实请求,无论积分是否迟到。
289
+
260
290
  **CodeArts 有签到吗?**
261
291
 
262
292
  有,但只对**积分计费账户**开放:插件先读账户类型,再查活动列表里的 `USER_LOGIN`,可领取时才
@@ -272,15 +302,16 @@ STS 令牌本来就只有两小时,插件在到期前 5 分钟用续期令牌
272
302
 
273
303
  **CodeArts 报 `TM.00001041 并发会话数已达上限(3个)`?**
274
304
 
275
- 这是体验版按**账号**计的会话预算:**每滚动 60 秒只允许发起 3 次对话**,所有模型共用一份额度,
276
- 换 `session-id` 或开新窗口都不加额度;并发与否无关,串行连发第 4 次一样会被拒。实测一次连续
277
- 请求是 `OK OK OK` 然后连续拒绝,约 61 秒后整批恢复。
305
+ 体验版按**账号**限制并发会话:3 个名额,一个会话在请求结束后还占位约 60 秒。网关是按
306
+ `x-ot-session-id` 这个头区分会话的,不是 `chat-id` / `session-id`,所以插件用一个从账号身份派生的
307
+ 固定值——所有轮次共用一个会话,不会再被自己的历史请求挤掉名额。实网对照:同一个账号同一个模型,
308
+ 不发这个头时串行第 4 条起全部被拒、约 60 秒后整批恢复;发稳定值后 6 连发、12 连发、6 条真并发
309
+ 全部成功。计量不受影响(两条路径都是每条 944 token / 0.12 积分)。
278
310
 
279
- 插件为此内置了一个滚动窗口(`CodeArtsSessionWindow`,3 次 / 60 秒,进程内共享,热重载不重置):
280
- 发请求前先排队拿名额,满了就等到最老的那次滑出窗口再发,而不是撞上去反复重试。被网关明确拒绝
281
- (`TM.00001041`)的那次会把名额退回来——网关并不为拒绝的请求计数,占着名额只会把下一分钟拖成
282
- 每 20 秒一次。所以现在连续对话只会变慢,不会失败。若名额一直拿不到(例如别处还有 CodeArts 会话
283
- 在抢同一份额度),3 次重试后会给出可读的错误而不是原始错误码。
311
+ 如果还在报这一条,说明 3 个名额被**别的东西**占着:同一账号的码道 IDE 或网页版、另一台设备,或另一个
312
+ 用同一份凭据的插件。关掉其它会话,或等约一分钟让名额自己过期。插件会先退避重试 3 次,再给出这条
313
+ 可读提示,而不是原始错误码。上游没有「关闭会话」的接口(`/api/v2/chat/stop`、`/session/close`、
314
+ `/sessions` 实测全部 404),所以没有主动释放名额这条路。
284
315
 
285
316
  **Trae 模型列表为什么和客户端对不上?**
286
317
  插件只列出 Trae 客户端当前聊天选择器的主通道模型(`solo_work_remote` / `solo_work_lite`),
package/README.md CHANGED
@@ -80,11 +80,10 @@ CodeArts 走华为云 OAuth:浏览器里授权后,宿主拿到 AK/SK 与 STS
80
80
 
81
81
  - **登录卡**:显示账号、到期时间、登录方式与额度;未登录时可发起登录,登录中可取消,也可退出登录。
82
82
  Trae 的卡片顶部是区域切换(国内版 / 国际版),并提供网页登录与复用本机客户端两种方式;
83
- Trae 国内版(仅客户端登录态)、CodeArts 与小浣熊另有「签到领积分」按钮;签到状态按
84
- 「今日已签到」(账号)与「本机今日已签到」(设备)分别显示。小浣熊没有独立签到接口:插件会
85
- 核对积分流水里的 `daily_grant`(只认今天的记录),并在到账时显示具体到账时间;未到账时调用
86
- 网页当日首次加载用的 `setting_info` 尝试触发。「打开网页签到」链接无论是否已到账都始终保留。
87
- 插件页还可设置自动监测间隔(关闭 / 15 分钟 / 30 分钟 / 1 小时 / 2 小时),关闭后只保留手动入口。
83
+ Trae 国内版(仅客户端登录态)与 CodeArts 有「签到领积分」按钮;签到状态按
84
+ 「今日已签到」(账号)与「本机今日已签到」(设备)分别显示。小浣熊**只检测、不领取**:插件读积分
85
+ 流水里的 `daily_grant`(只认今天的记录),到账时显示具体到账时间,按钮是「检测今日积分」;积分由
86
+ 服务端在当天首次打开网页时发放,领取请走「打开网页签到」(该链接无论是否到账都始终保留)。
88
87
  - **Trae 本机客户端登录**:检测到本机安装了 Trae CN / 国际版客户端时,登录卡会显示客户端账号与
89
88
  「复用客户端登录态」。插件只读 `User/globalStorage/storage.json` 里的加密登录态并在本机解密,
90
89
  不修改客户端,也不把令牌返回给页面;网页登录与客户端登录可以随时切换,退出插件登录不会退出
@@ -111,16 +110,18 @@ CodeArts 走华为云 OAuth:浏览器里授权后,宿主拿到 AK/SK 与 STS
111
110
  也可点「立即同步」。快照保存在本机,下载并解析完整成功后才原子替换;失败时继续使用上次成功
112
111
  的数据,刷新模型列表不会清空它。一次网络抖动或上游 429/5xx 会自动重试一次,客户端标识也随
113
112
  请求发送,避免被 CDN 当作匿名脚本限流。
114
- - **小浣熊签到监测**:同一页可设置监测间隔(关闭 / 15 分钟 / 30 分钟 / 1 小时 / 2 小时)。开启时
115
- 宿主按间隔核对今日流水,未到账再尝试网页触发;关闭后不再自动请求,卡片上的「打开网页签到」
116
- 仍然可用。
113
+ - **小浣熊每日积分(只检测)**:卡片上的按钮是「检测今日积分」,只读今日流水并把到账时间显示出来。
114
+ 插件不替你领取——积分由服务端在当天首次打开网页时发放,要领取请点「打开网页签到」。既没有自动
115
+ 监测项(定时轮询每个间隔都要花一次真实请求),也不会把网页访问调用(`setting_info`)当成副作用发出去。
117
116
  - **CodeArts 的模型清单**:合并两个端点(免费额度模型与常规模型),窗口表里没有的模型回退到
118
117
  当前目录最小的真实窗口;签到只在积分计费账户上可用。所有模型都接收图片:`kimi-k2.6-vl` 与
119
118
  `Qwen3-VL-235B` 直接读图,其余模型(含 DeepSeek、GLM、openpangu)先用 VL 模型转述成文字再回答;
120
119
  转述结果按图片内容缓存,同一张图只在第一次多花一次对话。模型清单里的「读图」只标注这两个**原生**
121
120
  多模态模型,转述能力写在列表下方的一句提示里。
122
- - **CodeArts 的会话预算**:体验版每滚动 60 秒只允许发起 3 次对话(全账号、全模型共用)。插件内置
123
- 滚动窗口排队,名额满了就等最老的一次滑出窗口再发,连续对话只会变慢而不会撞上 `TM.00001041`。
121
+ - **CodeArts 的会话预算**:体验版按账号只允许 3 个**并发会话**,一个会话在请求结束后仍占位约 60 秒。
122
+ 网关按 `x-ot-session-id` 这个头区分会话(不是 `chat-id` / `session-id`),插件因此用一个从账号身份
123
+ 派生的固定值:所有轮次共用一个会话,连续对话不会被自己的历史请求挤掉名额。真被别处占满时(例如同时
124
+ 开着码道 IDE),3 次退避重试后给出可读提示。
124
125
  - **高级设置**:分组名称、强制识图 / 纯文本模型、模型别名、默认输出上限。
125
126
  - **供应商开关**:在插件管理页关闭某个供应商后,它的设置页、模型分组与 provider 路由一起移除。
126
127