@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 +4 -3
- package/lib/client.js +13 -6
- package/lib/index.js +7 -4
- package/package.json +2 -2
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` /
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
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 池 +
|
|
4
|
-
"version": "3.1.
|
|
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": {
|