@omdp/dsh-key-fallback 3.1.0 → 3.1.1

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/README.md CHANGED
@@ -13,9 +13,10 @@ Current version is **v6** (`3.1.0`).
13
13
 
14
14
  - **Pool cards** with status dots, badges, enable switch, per-pool cooldown display and a "重置冷却" reset button.
15
15
  - **轮转触发码 (rotation triggers) are now configurable and actually enforced**: click chips to select which error codes
16
- cause rotation (`QUOTA` / `AUTH` / `RATE_LIMIT` / `TIMEOUT` / `TRANSPORT` / `SERVER`), or add a custom code. The default set is a
17
- superset of the old hard-coded behavior, so existing pools behave identically unless you change it.
18
- - **真正当前使用的 key 显示**: the page shows which key is actually being used right now (derived from the last value written to the
16
+ cause rotation — the preset chips cover the full DSH `LlmError` standard code set (`QUOTA` / `AUTH` / `RATE_LIMIT` / `TIMEOUT` /
17
+ `TRANSPORT` / `SERVER` / `EMPTY_RESPONSE` / `INVALID_CREDENTIAL`), or add a non-standard/custom code (matched exactly against the
18
+ provider's `failure.code`). The default set is the full standard set.
19
+ - **真实当前使用 key 显示**: the page shows which key is actually being used right now (derived from the last value written to the
19
20
  provider's env), instead of a truncated hash.
20
21
  - **短 ref 命名**: new keys are named `key_fallback_<provider>_key1`, `key_fallback_<provider>_key2`, … so the UI shows clean short
21
22
  names (`key1`, `key2`…) instead of long ref strings. Existing long refs are migrated once, automatically and idempotently.
package/lib/client.js CHANGED
@@ -34,9 +34,11 @@ window.__ModuleLoader__.load({
34
34
  var ce = React.createElement
35
35
 
36
36
  // ── 常用代码 / 名称 ──
37
- var ROTATE_CODES = ['QUOTA', 'AUTH', 'RATE_LIMIT', 'TIMEOUT', 'TRANSPORT', 'SERVER']
37
+ // 预设 chips = DSH LlmError 标准码(换 key 有意义的全集)。这些码决定「失败后是否换 key」,
38
+ // 与 DSH 的重试机制(provider 的 retryPolicy.retryableCodes,决定同一把 key 是否重发)独立互补。
39
+ var ROTATE_CODES = ['QUOTA', 'AUTH', 'RATE_LIMIT', 'TIMEOUT', 'TRANSPORT', 'SERVER', 'EMPTY_RESPONSE', 'INVALID_CREDENTIAL']
38
40
  function codeLabel(c) {
39
- return { QUOTA: '配额用尽', AUTH: '认证失败', RATE_LIMIT: '限流', TIMEOUT: '超时', TRANSPORT: '传输错误', SERVER: '服务端' }[c] || c
41
+ return { QUOTA: '配额用尽', AUTH: '认证失败', RATE_LIMIT: '限流', TIMEOUT: '超时', TRANSPORT: '传输错误', SERVER: '服务端', EMPTY_RESPONSE: '空响应', INVALID_CREDENTIAL: '凭据无效' }[c] || c
40
42
  }
41
43
 
42
44
  // ── CSS(一次注入)──
@@ -94,7 +96,7 @@ window.__ModuleLoader__.load({
94
96
  '.kf_statusDot.recovered{background:#38bdf8}',
95
97
  '.kf_keyName{font-weight:600;font-size:12.5px;color:var(--dsw-alias-label-primary,#e8e8e8);min-width:56px;display:flex;align-items:center;gap:6px}',
96
98
  '.kf_keyRef{font-size:10.5px;color:var(--dsw-alias-label-secondary,#9aa);font-family:ui-monospace,Menlo,Consolas,monospace;opacity:.75;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
97
- '.kf_keyVal{flex:1;min-width:170px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;padding:6px 10px;border-radius:8px;border:1px dashed var(--dsw-alias-border-l2,rgba(128,128,128,.3));background:var(--dsw-alias-bg-base,rgba(0,0,0,.22));color:inherit}',
99
+ '.kf_keyVal{flex:1;min-width:170px;max-width:100%;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;padding:6px 10px;border-radius:8px;border:1px dashed var(--dsw-alias-border-l2,rgba(128,128,128,.3));background:var(--dsw-alias-bg-base,rgba(0,0,0,.22));color:inherit;overflow-wrap:anywhere;word-break:break-all;line-height:1.5}',
98
100
  '.kf_keyActions{display:flex;gap:6px;align-items:center;flex-wrap:wrap}',
99
101
  '.kf_err{color:var(--dsw-alias-state-error-primary,#f87171);font-size:12px;line-height:1.5}',
100
102
  '.kf_ok{color:var(--dsw-alias-state-success-primary,#4ade80);font-size:12px;line-height:1.5}',
@@ -106,7 +108,7 @@ window.__ModuleLoader__.load({
106
108
  '.kf_iconBtn{cursor:pointer;font:inherit;font-size:12px;width:28px;height:28px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.3));background:transparent;color:var(--dsw-alias-label-secondary,#9aa);display:inline-flex;align-items:center;justify-content:center;transition:all .12s;flex:none;line-height:1}',
107
109
  '.kf_iconBtn:hover:not(:disabled){color:var(--dsw-alias-label-primary,#eee);border-color:var(--dsw-alias-brand-primary,#5b8cff);background:var(--dsw-alias-bg-layer-2,rgba(128,128,128,.12))}',
108
110
  '.kf_iconBtn:disabled{opacity:.4;cursor:default}',
109
- '.kf_envNote{font-size:11.5px;color:var(--dsw-alias-label-secondary,#9aa);border:1px dashed var(--dsw-alias-border-l2,rgba(128,128,128,.28));border-radius:8px;padding:6px 10px;flex:1;min-width:150px}',
111
+ '.kf_envNote{font-size:11.5px;color:var(--dsw-alias-label-secondary,#9aa);border:1px dashed var(--dsw-alias-border-l2,rgba(128,128,128,.28));border-radius:8px;padding:6px 10px;flex:1;min-width:150px;overflow-wrap:anywhere}',
110
112
  ].join('')
111
113
  if (typeof document !== 'undefined' && !document.querySelector('style[data-plugin-css="@omdp/dsh-key-fallback"]')) {
112
114
  var tag = document.createElement('style')
@@ -201,7 +203,7 @@ window.__ModuleLoader__.load({
201
203
  k.active ? ce('span', { className: 'kf_badge ok', style: { padding: '1px 7px', fontSize: 10.5 } }, '当前') : null,
202
204
  ),
203
205
  ce('span', { className: 'kf_keyRef', title: k.ref }, k.ref),
204
- isEnv
206
+ isEnv && !isRevealed
205
207
  ? ce('span', { className: 'kf_envNote' },
206
208
  props.pool.envSource === 'env' && props.pool.envWritable === false
207
209
  ? '由启动环境提供(只读)'
@@ -389,7 +391,12 @@ window.__ModuleLoader__.load({
389
391
  (pool.rotateOn || []).filter(function (c) { return ROTATE_CODES.indexOf(c) < 0 }).map(function (c) {
390
392
  return ce('span', { key: c, className: 'kf_chip on' }, c)
391
393
  }),
392
- ce('span', { style: { fontSize: 11, color: 'var(--dsw-alias-label-secondary,#9aa)' } }, '勾选=失败后轮换;取消=不轮换'),
394
+ ce('span', { style: { fontSize: 11, color: 'var(--dsw-alias-label-secondary,#9aa)' } }, '勾选=该错误发生后换下一把 key;换 key 与 DSH 的重试再发相互独立'),
395
+ ),
396
+ ce('div', { className: 'kf_customRow', style: { fontSize: 11, color: 'var(--dsw-alias-label-tertiary,#778)', lineHeight: 1.6 } },
397
+ '补充:DSH 的重试插件负责「同一把 key 重发几次」,由各模型 provider 的 retryPolicy 决定;',
398
+ ce('br'),
399
+ '这里只决定「失败后是否切换到下一把 key」。预设 chips 已含 LlmError 标准码,下方输入框用于添加非标准/自定义错误码(与 provider 返回的 failure.code 精确匹配)。',
393
400
  ),
394
401
  ce('div', { className: 'kf_section' }, '密钥链'),
395
402
  keys.length === 0
package/lib/index.js CHANGED
@@ -95,10 +95,12 @@ function sendJson(res, code, data) {
95
95
  }
96
96
 
97
97
  // ── 轮转触发语义(v6:rotateOn 真正生效)──
98
- // 默认集 = 旧 RETRYABLE_CODES 行为超集(QUOTA_EXCEEDED 规范化成规范码 QUOTA);
99
- // 用户保存什么就严格按什么判断——不做「等于旧三元组就提升成超集」的魔法,
100
- // 否则用户精确配置的三个码保存后刷新会变回六个,配置不生效。
101
- const DEFAULT_ROTATE_ON = ['QUOTA', 'AUTH', 'RATE_LIMIT', 'TIMEOUT', 'TRANSPORT', 'SERVER']
98
+ // 注意:rotateOn 决定「失败后是否换下一把 key」,与 DSH 的重试机制(dsh-llm-retry 按
99
+ // provider 的 retryPolicy.retryableCodes 决定是否用同一把 key 重发、重试几次)相互独立、互补:
100
+ // 同一 failure.code,重试插件决定重发,本插件决定换 key。所以这是两套开关,不存在谁覆盖谁。
101
+ // 预设 = LlmError 标准码(换 key 有意义的全集):QUOTA/AUTH/RATE_LIMIT/TIMEOUT/TRANSPORT/SERVER
102
+ // + EMPTY_RESPONSE/INVALID_CREDENTIAL。ABORTED(用户取消)永远不轮换。
103
+ const DEFAULT_ROTATE_ON = ['QUOTA', 'AUTH', 'RATE_LIMIT', 'TIMEOUT', 'TRANSPORT', 'SERVER', 'EMPTY_RESPONSE', 'INVALID_CREDENTIAL']
102
104
  function canonicalCode(c) {
103
105
  const s = String(c || '').toUpperCase()
104
106
  if (s === 'QUOTA_EXCEEDED' || s === 'QUOTA') return 'QUOTA'
@@ -118,6 +120,7 @@ const CODE_KEYWORDS = {
118
120
  TRANSPORT: /transport|network|econn|socket|eai|dns|fetch failed|connection|closed|reset|unreachable/i,
119
121
  SERVER: /5\d\d|server error|internal|unavailable|502|503|504|bad gateway|overload/i,
120
122
  EMPTY_RESPONSE: /empty response|no content|empty reply/i,
123
+ INVALID_CREDENTIAL: /invalid credential|malformed|bad credential|invalid api key/i,
121
124
  }
122
125
  function shouldRotate(pool, code, rawMsg, status) {
123
126
  const triggers = effectiveRotateOn(pool.cfg.rotateOn)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@omdp/dsh-key-fallback",
3
- "description": "API Key 回退轮换插件:多 key 池 + 自动轮换(触发码可配置)+ 环境密钥编辑 + 明文揭示 + 短 ref 迁移。Settings → API Key 回退 UI. v6.",
4
- "version": "3.1.0",
3
+ "description": "API Key 回退轮换插件:多 key 池 + 自动轮换(触发码可配置,含 LlmError 标准码全集)+ 环境密钥编辑 + 明文揭示 + 短 ref 迁移。Settings → API Key 回退 UI. v6.1.",
4
+ "version": "3.1.1",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {