dsh-codearts 1.0.1 → 1.1.0
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/CHANGELOG.md +13 -0
- package/lib/bin.mjs +2 -2
- package/lib/client/index.d.cts +2 -0
- package/lib/client/index.js +90 -55
- package/lib/index.d.mts +105 -3
- package/lib/index.mjs +231 -3
- package/lib/{usage-BEoMNRwo.mjs → usage-Cie0IlC4.mjs} +158 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
本文件记录本插件的重要改动。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
4
4
|
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
5
5
|
|
|
6
|
+
## [1.1.0] - 2026-10-21
|
|
7
|
+
|
|
8
|
+
### 新增
|
|
9
|
+
|
|
10
|
+
- 思考强度:模型发现时拉 `thinking-variants`,只给上游声明过档位的模型(GLM-5.2 /
|
|
11
|
+
GLM-5.2-ArkTS-SPARK:high / max)露出推理选择器,请求以 `reasoning_effort` 透传;
|
|
12
|
+
这路失败不影响目录。
|
|
13
|
+
- 上游排队:发请求前先向 `/api/v1/queue/status` 挂号并轮询(每 5 秒一次,最多替
|
|
14
|
+
用户等 20 秒),轮到才开流;等待期间对话底部积分条显示「排队第 N 位」。20 秒
|
|
15
|
+
还没轮到就不挂请求,直接回一句上游自己的排队文案(第几位 / 共多少条)加
|
|
16
|
+
「请稍后重试」;结束、取消、放弃都会 `DELETE` 清号,不留幽灵排队。
|
|
17
|
+
- 新接口 `GET /api/codearts/queue`,给卡片读排队快照。
|
|
18
|
+
|
|
6
19
|
## [1.0.1] - 2026-10-21
|
|
7
20
|
|
|
8
21
|
### 文档
|
package/lib/bin.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { F as codeartsAccountsPath, M as isBenefitModel, N as CodeArtsAccountStore, d as fetchCallerIdentity, n as codeartsUsagePath, r as discoverModels, t as CodeArtsUsageTracker } from "./usage-Cie0IlC4.mjs";
|
|
3
3
|
import { existsSync, statSync } from "node:fs";
|
|
4
4
|
//#region src/version.ts
|
|
5
|
-
const CODEARTS_VERSION = "1.0
|
|
5
|
+
const CODEARTS_VERSION = "1.1.0";
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/bin.ts
|
|
8
8
|
/**
|
package/lib/client/index.d.cts
CHANGED
package/lib/client/index.js
CHANGED
|
@@ -23,7 +23,7 @@ function stripModelSuffixes(name) {
|
|
|
23
23
|
}
|
|
24
24
|
//#endregion
|
|
25
25
|
//#region \0dsh-css:/home/runner/work/dsh-codearts/dsh-codearts/src/client/CodeArtsPluginCard.module.css.mjs
|
|
26
|
-
const css = ".xoziZG_card{border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-settings-card-fill);max-width:720px}.xoziZG_header{align-items:flex-start;gap:12px;padding:14px 16px;display:flex}.xoziZG_headText{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.xoziZG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px}.xoziZG_desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.xoziZG_statusLine{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.xoziZG_meta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}.xoziZG_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 16px}.xoziZG_tabs{margin-top:12px}.xoziZG_tabPanel{padding-top:2px}.xoziZG_accountToolbar{gap:8px;margin-top:12px;display:flex}.xoziZG_accountToolbar>*{flex:1}.xoziZG_manualForm{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:12px}.xoziZG_accountCard{flex-direction:column;gap:10px;margin-top:12px;display:flex}.xoziZG_accountEntry{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-2)}.xoziZG_accountRow{align-items:center;gap:10px;padding:12px;display:flex}.xoziZG_claimAllRow{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;padding:10px 12px 0;display:flex}.xoziZG_avatar{background:var(--dsw-alias-bg-layer-3);width:32px;height:32px;color:var(--dsw-alias-label-primary);border-radius:50%;flex:none;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:inline-flex}.xoziZG_accountInfo{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.xoziZG_accountNameRow{align-items:center;gap:6px;min-width:0;display:flex}.xoziZG_accountName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:600;line-height:20px;overflow:hidden}.xoziZG_accountRowActions{flex:none;gap:6px;display:flex}.xoziZG_accountHint{color:var(--dsw-alias-label-tertiary);margin:0;padding:0 12px 12px;font-size:12px;line-height:18px}.xoziZG_creditBlock{border-top:.5px solid var(--dsw-alias-border-l2);padding:12px}.xoziZG_balanceRow{flex-wrap:wrap;align-items:baseline;gap:6px;display:flex}.xoziZG_balance{color:var(--dsw-alias-label-primary);letter-spacing:-.02em;font-variant-numeric:tabular-nums;font-size:28px;font-weight:600;line-height:34px}.xoziZG_balanceUnit{color:var(--dsw-alias-label-secondary);font-size:12px}.xoziZG_balanceMeta{color:var(--dsw-alias-label-tertiary);margin-left:2px;font-size:12px}.xoziZG_updated{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;margin-left:auto;font-size:12px}.xoziZG_creditRows{flex-direction:column;gap:4px;margin-top:8px;display:flex}.xoziZG_creditRow{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;justify-content:space-between;align-items:baseline;gap:12px;font-size:12px;line-height:18px;display:flex}.xoziZG_usageBar{background:var(--dsw-alias-border-l2);border-radius:2px;height:4px;margin-top:4px;display:block;overflow:hidden}.xoziZG_usageFill{background:var(--dsw-alias-brand-primary);border-radius:2px;height:100%;transition:width .2s;display:block}.xoziZG_expiryRow{margin-top:2px}.xoziZG_expiryLabel{color:var(--dsw-alias-state-warn-label)}.xoziZG_bar{background:var(--dsw-alias-bg-layer-3);border-radius:2px;height:4px;margin-top:6px;display:block;overflow:hidden}.xoziZG_barFill{background:var(--dsw-alias-state-warn-primary);border-radius:2px;height:100%;display:block}.xoziZG_creditLink{margin-top:8px;display:inline-block}.xoziZG_creditFooter{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:8px;margin-top:12px;padding-top:10px;display:flex}.xoziZG_creditHint{color:var(--dsw-alias-label-tertiary);margin:8px 0 0;font-size:12px;line-height:18px}.xoziZG_section{margin-top:16px}.xoziZG_sectionHead{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.xoziZG_sectionLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:500}.xoziZG_count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:12px}.xoziZG_hint{color:var(--dsw-alias-label-secondary);margin:8px 0 0;font-size:13px;line-height:20px}.xoziZG_error{color:var(--dsw-alias-state-error-primary);margin:10px 0 0;font-size:13px;line-height:20px}.xoziZG_actions{flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px;display:flex}.xoziZG_danger{color:var(--dsw-alias-state-error-primary)}.xoziZG_link{appearance:none;color:var(--dsw-alias-brand-text);font:inherit;text-align:left;word-break:break-all;cursor:pointer;background:0 0;border:none;padding:0;font-size:12px}.xoziZG_link:hover{text-decoration:underline}.xoziZG_choices{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_choiceRow{border-radius:var(--dsw-radius-sm);align-items:center;gap:8px;margin:0 -6px;padding:5px 6px;display:flex}.xoziZG_choiceRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.xoziZG_modelChart{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_modelRow{font-variant-numeric:tabular-nums;grid-template-columns:minmax(0,1.2fr) minmax(48px,1fr)
|
|
26
|
+
const css = ".xoziZG_card{border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-settings-card-fill);max-width:720px}.xoziZG_header{align-items:flex-start;gap:12px;padding:14px 16px;display:flex}.xoziZG_headText{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.xoziZG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px}.xoziZG_desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.xoziZG_statusLine{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.xoziZG_meta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}.xoziZG_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 16px}.xoziZG_tabs{margin-top:12px}.xoziZG_tabPanel{padding-top:2px}.xoziZG_accountToolbar{gap:8px;margin-top:12px;display:flex}.xoziZG_accountToolbar>*{flex:1}.xoziZG_manualForm{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:12px}.xoziZG_accountCard{flex-direction:column;gap:10px;margin-top:12px;display:flex}.xoziZG_accountEntry{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-2)}.xoziZG_accountRow{align-items:center;gap:10px;padding:12px;display:flex}.xoziZG_claimAllRow{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;padding:10px 12px 0;display:flex}.xoziZG_avatar{background:var(--dsw-alias-bg-layer-3);width:32px;height:32px;color:var(--dsw-alias-label-primary);border-radius:50%;flex:none;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:inline-flex}.xoziZG_accountInfo{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.xoziZG_accountNameRow{align-items:center;gap:6px;min-width:0;display:flex}.xoziZG_accountName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:600;line-height:20px;overflow:hidden}.xoziZG_accountRowActions{flex:none;gap:6px;display:flex}.xoziZG_accountHint{color:var(--dsw-alias-label-tertiary);margin:0;padding:0 12px 12px;font-size:12px;line-height:18px}.xoziZG_creditBlock{border-top:.5px solid var(--dsw-alias-border-l2);padding:12px}.xoziZG_balanceRow{flex-wrap:wrap;align-items:baseline;gap:6px;display:flex}.xoziZG_balance{color:var(--dsw-alias-label-primary);letter-spacing:-.02em;font-variant-numeric:tabular-nums;font-size:28px;font-weight:600;line-height:34px}.xoziZG_balanceUnit{color:var(--dsw-alias-label-secondary);font-size:12px}.xoziZG_balanceMeta{color:var(--dsw-alias-label-tertiary);margin-left:2px;font-size:12px}.xoziZG_updated{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;margin-left:auto;font-size:12px}.xoziZG_creditRows{flex-direction:column;gap:4px;margin-top:8px;display:flex}.xoziZG_creditRow{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;justify-content:space-between;align-items:baseline;gap:12px;font-size:12px;line-height:18px;display:flex}.xoziZG_usageBar{background:var(--dsw-alias-border-l2);border-radius:2px;height:4px;margin-top:4px;display:block;overflow:hidden}.xoziZG_usageFill{background:var(--dsw-alias-brand-primary);border-radius:2px;height:100%;transition:width .2s;display:block}.xoziZG_expiryRow{margin-top:2px}.xoziZG_expiryLabel{color:var(--dsw-alias-state-warn-label)}.xoziZG_bar{background:var(--dsw-alias-bg-layer-3);border-radius:2px;height:4px;margin-top:6px;display:block;overflow:hidden}.xoziZG_barFill{background:var(--dsw-alias-state-warn-primary);border-radius:2px;height:100%;display:block}.xoziZG_creditLink{margin-top:8px;display:inline-block}.xoziZG_creditFooter{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:8px;margin-top:12px;padding-top:10px;display:flex}.xoziZG_creditHint{color:var(--dsw-alias-label-tertiary);margin:8px 0 0;font-size:12px;line-height:18px}.xoziZG_section{margin-top:16px}.xoziZG_sectionHead{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.xoziZG_sectionLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:500}.xoziZG_count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:12px}.xoziZG_hint{color:var(--dsw-alias-label-secondary);margin:8px 0 0;font-size:13px;line-height:20px}.xoziZG_error{color:var(--dsw-alias-state-error-primary);margin:10px 0 0;font-size:13px;line-height:20px}.xoziZG_actions{flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px;display:flex}.xoziZG_danger{color:var(--dsw-alias-state-error-primary)}.xoziZG_link{appearance:none;color:var(--dsw-alias-brand-text);font:inherit;text-align:left;word-break:break-all;cursor:pointer;background:0 0;border:none;padding:0;font-size:12px}.xoziZG_link:hover{text-decoration:underline}.xoziZG_choices{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_choiceRow{border-radius:var(--dsw-radius-sm);align-items:center;gap:8px;margin:0 -6px;padding:5px 6px;display:flex}.xoziZG_choiceRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.xoziZG_modelChart{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_modelRow{font-variant-numeric:tabular-nums;grid-template-columns:minmax(0,1.2fr) minmax(48px,1fr) 56px 52px 64px;align-items:center;gap:8px;padding:4px 0;display:grid}.xoziZG_modelName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;overflow:hidden}.xoziZG_modelTrack{background:var(--dsw-alias-border-l2);border-radius:4px;height:8px;display:block;overflow:hidden}.xoziZG_modelFill{background:var(--dsw-alias-brand-primary);border-radius:4px;height:100%;transition:width .16s;display:block}.xoziZG_modelTokens,.xoziZG_modelShare,.xoziZG_modelRequests{color:var(--dsw-alias-label-secondary);text-align:right;white-space:nowrap;font-size:11px;line-height:16px}.xoziZG_modelRequests{color:var(--dsw-alias-label-tertiary)}.xoziZG_check{flex:1;min-width:0}.xoziZG_check>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.xoziZG_manual{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:0 12px}.xoziZG_manual[open]{padding-bottom:12px}.xoziZG_manualSummary{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;padding:9px 0;font-size:13px;list-style:none;display:flex}.xoziZG_manualSummary::-webkit-details-marker{display:none}.xoziZG_field{flex-direction:column;gap:5px;margin-top:10px;display:flex}.xoziZG_fieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px}.xoziZG_area{box-sizing:border-box;resize:vertical;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:20px}.xoziZG_area::placeholder{color:var(--dsw-alias-label-dimmed)}.xoziZG_area:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}";
|
|
27
27
|
const tagId = "dsh-codearts/CodeArtsPluginCard.module.css";
|
|
28
28
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
29
29
|
const tag = document.createElement("style");
|
|
@@ -33,70 +33,70 @@ if (typeof document !== "undefined" && document.querySelector("style[data-plugin
|
|
|
33
33
|
document.head.appendChild(tag);
|
|
34
34
|
}
|
|
35
35
|
var CodeArtsPluginCard_module_css_default = {
|
|
36
|
-
"accountCard": "xoziZG_accountCard",
|
|
37
|
-
"accountRow": "xoziZG_accountRow",
|
|
38
|
-
"creditBlock": "xoziZG_creditBlock",
|
|
39
|
-
"section": "xoziZG_section",
|
|
40
36
|
"accountRowActions": "xoziZG_accountRowActions",
|
|
41
|
-
"
|
|
37
|
+
"accountHint": "xoziZG_accountHint",
|
|
42
38
|
"choiceRow": "xoziZG_choiceRow",
|
|
43
|
-
"
|
|
39
|
+
"manualSummary": "xoziZG_manualSummary",
|
|
40
|
+
"accountCard": "xoziZG_accountCard",
|
|
44
41
|
"modelName": "xoziZG_modelName",
|
|
42
|
+
"name": "xoziZG_name",
|
|
43
|
+
"tabs": "xoziZG_tabs",
|
|
44
|
+
"actions": "xoziZG_actions",
|
|
45
|
+
"accountToolbar": "xoziZG_accountToolbar",
|
|
46
|
+
"creditRow": "xoziZG_creditRow",
|
|
47
|
+
"modelRequests": "xoziZG_modelRequests",
|
|
48
|
+
"usageBar": "xoziZG_usageBar",
|
|
45
49
|
"manual": "xoziZG_manual",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
50
|
+
"error": "xoziZG_error",
|
|
51
|
+
"meta": "xoziZG_meta",
|
|
52
|
+
"accountEntry": "xoziZG_accountEntry",
|
|
53
|
+
"area": "xoziZG_area",
|
|
54
|
+
"tabPanel": "xoziZG_tabPanel",
|
|
49
55
|
"card": "xoziZG_card",
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
56
|
+
"field": "xoziZG_field",
|
|
57
|
+
"check": "xoziZG_check",
|
|
58
|
+
"usageFill": "xoziZG_usageFill",
|
|
59
|
+
"creditHint": "xoziZG_creditHint",
|
|
60
|
+
"modelShare": "xoziZG_modelShare",
|
|
53
61
|
"barFill": "xoziZG_barFill",
|
|
54
|
-
"expiryLabel": "xoziZG_expiryLabel",
|
|
55
|
-
"area": "xoziZG_area",
|
|
56
|
-
"manualForm": "xoziZG_manualForm",
|
|
57
|
-
"sectionLabel": "xoziZG_sectionLabel",
|
|
58
|
-
"choices": "xoziZG_choices",
|
|
59
62
|
"body": "xoziZG_body",
|
|
60
|
-
"
|
|
63
|
+
"headText": "xoziZG_headText",
|
|
64
|
+
"creditLink": "xoziZG_creditLink",
|
|
65
|
+
"desc": "xoziZG_desc",
|
|
61
66
|
"balanceRow": "xoziZG_balanceRow",
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
67
|
+
"choices": "xoziZG_choices",
|
|
68
|
+
"sectionHead": "xoziZG_sectionHead",
|
|
69
|
+
"manualForm": "xoziZG_manualForm",
|
|
70
|
+
"avatar": "xoziZG_avatar",
|
|
71
|
+
"expiryLabel": "xoziZG_expiryLabel",
|
|
72
|
+
"updated": "xoziZG_updated",
|
|
73
|
+
"balanceUnit": "xoziZG_balanceUnit",
|
|
74
|
+
"accountInfo": "xoziZG_accountInfo",
|
|
75
|
+
"sectionLabel": "xoziZG_sectionLabel",
|
|
76
|
+
"danger": "xoziZG_danger",
|
|
77
|
+
"expiryRow": "xoziZG_expiryRow",
|
|
78
|
+
"statusLine": "xoziZG_statusLine",
|
|
79
|
+
"section": "xoziZG_section",
|
|
66
80
|
"link": "xoziZG_link",
|
|
67
81
|
"modelFill": "xoziZG_modelFill",
|
|
82
|
+
"accountName": "xoziZG_accountName",
|
|
83
|
+
"modelRow": "xoziZG_modelRow",
|
|
68
84
|
"modelTrack": "xoziZG_modelTrack",
|
|
69
|
-
"desc": "xoziZG_desc",
|
|
70
|
-
"header": "xoziZG_header",
|
|
71
85
|
"hint": "xoziZG_hint",
|
|
72
|
-
"
|
|
73
|
-
"
|
|
86
|
+
"balanceMeta": "xoziZG_balanceMeta",
|
|
87
|
+
"count": "xoziZG_count",
|
|
88
|
+
"modelChart": "xoziZG_modelChart",
|
|
89
|
+
"accountRow": "xoziZG_accountRow",
|
|
90
|
+
"creditRows": "xoziZG_creditRows",
|
|
91
|
+
"header": "xoziZG_header",
|
|
74
92
|
"claimAllRow": "xoziZG_claimAllRow",
|
|
75
|
-
"
|
|
93
|
+
"creditBlock": "xoziZG_creditBlock",
|
|
76
94
|
"modelTokens": "xoziZG_modelTokens",
|
|
77
|
-
"bar": "xoziZG_bar",
|
|
78
|
-
"expiryRow": "xoziZG_expiryRow",
|
|
79
|
-
"statusLine": "xoziZG_statusLine",
|
|
80
|
-
"accountHint": "xoziZG_accountHint",
|
|
81
|
-
"sectionHead": "xoziZG_sectionHead",
|
|
82
|
-
"tabs": "xoziZG_tabs",
|
|
83
|
-
"balanceMeta": "xoziZG_balanceMeta",
|
|
84
|
-
"meta": "xoziZG_meta",
|
|
85
|
-
"usageBar": "xoziZG_usageBar",
|
|
86
|
-
"error": "xoziZG_error",
|
|
87
|
-
"field": "xoziZG_field",
|
|
88
95
|
"balance": "xoziZG_balance",
|
|
89
|
-
"creditFooter": "xoziZG_creditFooter",
|
|
90
|
-
"creditRows": "xoziZG_creditRows",
|
|
91
|
-
"headText": "xoziZG_headText",
|
|
92
|
-
"actions": "xoziZG_actions",
|
|
93
|
-
"count": "xoziZG_count",
|
|
94
96
|
"accountNameRow": "xoziZG_accountNameRow",
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"modelRequests": "xoziZG_modelRequests",
|
|
99
|
-
"manualSummary": "xoziZG_manualSummary"
|
|
97
|
+
"bar": "xoziZG_bar",
|
|
98
|
+
"creditFooter": "xoziZG_creditFooter",
|
|
99
|
+
"fieldLabel": "xoziZG_fieldLabel"
|
|
100
100
|
};
|
|
101
101
|
//#endregion
|
|
102
102
|
//#region src/client/CodeArtsPluginCard.tsx
|
|
@@ -1203,8 +1203,11 @@ function CodeArtsPluginCard({ t }) {
|
|
|
1203
1203
|
/** 与 src/adapter.ts 的 CODEARTS_PROVIDER 对齐 —— client 不能 import host 模块。 */
|
|
1204
1204
|
const CODEARTS_PROVIDER = "codearts";
|
|
1205
1205
|
const STATUS_PATH = "/api/codearts/status";
|
|
1206
|
+
const QUEUE_PATH = "/api/codearts/queue";
|
|
1206
1207
|
/** 轮询间隔:与缓存 TTL(60s)对齐,再短也只是反复命中缓存。 */
|
|
1207
1208
|
const REFRESH_INTERVAL_MS = 6e4;
|
|
1209
|
+
/** 排队位置变得快:官方客户端也是 5 秒一次;这是本机路由,打不疼上游。 */
|
|
1210
|
+
const QUEUE_INTERVAL_MS = 5e3;
|
|
1208
1211
|
function readSelection(useProjection) {
|
|
1209
1212
|
if (useProjection === void 0) return void 0;
|
|
1210
1213
|
try {
|
|
@@ -1220,6 +1223,7 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1220
1223
|
const active = isCodeArts(readSelection(useProjection));
|
|
1221
1224
|
const [remain, setRemain] = (0, react.useState)(null);
|
|
1222
1225
|
const [total, setTotal] = (0, react.useState)(0);
|
|
1226
|
+
const [queued, setQueued] = (0, react.useState)(null);
|
|
1223
1227
|
const mounted = (0, react.useRef)(true);
|
|
1224
1228
|
(0, react.useEffect)(() => {
|
|
1225
1229
|
mounted.current = true;
|
|
@@ -1257,13 +1261,40 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1257
1261
|
if (timer !== void 0) window.clearInterval(timer);
|
|
1258
1262
|
};
|
|
1259
1263
|
}, [active]);
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
+
(0, react.useEffect)(() => {
|
|
1265
|
+
if (!active) {
|
|
1266
|
+
setQueued(null);
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
let timer;
|
|
1270
|
+
const refresh = async () => {
|
|
1271
|
+
try {
|
|
1272
|
+
const res = await fetch(QUEUE_PATH, {
|
|
1273
|
+
headers: { Accept: "application/json" },
|
|
1274
|
+
credentials: "same-origin"
|
|
1275
|
+
});
|
|
1276
|
+
if (!res.ok) return;
|
|
1277
|
+
const data = await res.json().catch(() => void 0);
|
|
1278
|
+
if (mounted.current) setQueued(data?.queue?.[0] ?? null);
|
|
1279
|
+
} catch {}
|
|
1280
|
+
};
|
|
1281
|
+
refresh();
|
|
1282
|
+
timer = window.setInterval(refresh, QUEUE_INTERVAL_MS);
|
|
1283
|
+
return () => {
|
|
1284
|
+
if (timer !== void 0) window.clearInterval(timer);
|
|
1285
|
+
};
|
|
1286
|
+
}, [active]);
|
|
1287
|
+
if (!active || remain === null && queued === null) return null;
|
|
1288
|
+
const text = remain === null ? void 0 : t("creditsDock", { remain: new Intl.NumberFormat().format(remain) });
|
|
1289
|
+
const title = queued !== null ? t("queueDockTitle", {
|
|
1290
|
+
model: queued.model,
|
|
1291
|
+
position: queued.position,
|
|
1292
|
+
length: queued.length
|
|
1293
|
+
}) : t("creditsDockTitle", {
|
|
1294
|
+
remain: remain ?? 0,
|
|
1264
1295
|
total
|
|
1265
1296
|
});
|
|
1266
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1297
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1267
1298
|
style: {
|
|
1268
1299
|
display: "inline-flex",
|
|
1269
1300
|
alignItems: "center",
|
|
@@ -1277,7 +1308,7 @@ function CodeArtsCreditDock({ useProjection, t }) {
|
|
|
1277
1308
|
minWidth: "max-content"
|
|
1278
1309
|
},
|
|
1279
1310
|
title,
|
|
1280
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text })
|
|
1311
|
+
children: [queued !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("queueDock", { position: queued.position }) }), text !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text })]
|
|
1281
1312
|
});
|
|
1282
1313
|
}
|
|
1283
1314
|
//#endregion
|
|
@@ -1367,6 +1398,8 @@ const zh = {
|
|
|
1367
1398
|
creditsNeedSignIn: "这个账号需要重新登录",
|
|
1368
1399
|
creditsDock: "积分 {remain}",
|
|
1369
1400
|
creditsDockTitle: "CodeArts 剩余积分 {remain} / {total}",
|
|
1401
|
+
queueDock: "排队第 {position} 位",
|
|
1402
|
+
queueDockTitle: "上游排队:{model} · 第 {position} 位 / 共 {length} 条",
|
|
1370
1403
|
claimButton: "签到领积分",
|
|
1371
1404
|
claimButtonDone: "今日已领",
|
|
1372
1405
|
claimPending: "领取中…",
|
|
@@ -1462,6 +1495,8 @@ const en = {
|
|
|
1462
1495
|
creditsNeedSignIn: "This account needs signing in again",
|
|
1463
1496
|
creditsDock: "Credits {remain}",
|
|
1464
1497
|
creditsDockTitle: "CodeArts credits left {remain} / {total}",
|
|
1498
|
+
queueDock: "Queued #{position}",
|
|
1499
|
+
queueDockTitle: "Upstream queue: {model} · position {position} of {length}",
|
|
1465
1500
|
claimButton: "Claim daily credits",
|
|
1466
1501
|
claimButtonDone: "Claimed today",
|
|
1467
1502
|
claimPending: "Claiming…",
|
package/lib/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
+
import "@earendil-works/pi-ai";
|
|
2
3
|
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
3
4
|
import { SettingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
4
5
|
import { Context } from "@deepseek-ai/cordis";
|
|
@@ -23,12 +24,25 @@ import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
|
|
|
23
24
|
* @module dsh-codearts/catalog
|
|
24
25
|
*/
|
|
25
26
|
/** One model the adapter exposes to DSH. */
|
|
27
|
+
interface CodeArtsModelThinking {
|
|
28
|
+
/** 上游提供的思考强度(`thinking-variants` 的 `strengths`,如 `['high','max']`)。 */
|
|
29
|
+
strengths: readonly string[];
|
|
30
|
+
/** 上游默认强度(`default_strength`)。宿主不强制使用,只作诊断。 */
|
|
31
|
+
default?: string;
|
|
32
|
+
}
|
|
26
33
|
interface CodeArtsModelInfo {
|
|
27
34
|
id: string;
|
|
28
35
|
name: string;
|
|
29
36
|
contextWindow: number;
|
|
30
37
|
maxTokens: number;
|
|
31
38
|
supportsImages: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* 该模型可选的思考强度。
|
|
41
|
+
*
|
|
42
|
+
* 由动态发现填入(`POST /v1/model/thinking-variants`,按账号授予):空/缺省表示
|
|
43
|
+
* 上游没给这条模型开关,宿主就不该显示推理档位选择器。
|
|
44
|
+
*/
|
|
45
|
+
thinking?: CodeArtsModelThinking;
|
|
32
46
|
/**
|
|
33
47
|
* 积分消耗倍率(上游 `credit[].ratio_display` 的原值,如 `0.18x`)。
|
|
34
48
|
*
|
|
@@ -148,7 +162,9 @@ interface PreparedChatBody {
|
|
|
148
162
|
* (两者任一超限,上游都只会流内回 InferHub.001001005.400
|
|
149
163
|
* "The request param is invalid, Please check it",不告诉我们是哪一个)
|
|
150
164
|
* 3. 丢弃非白名单字段(stream_options / store / logprobs 等虽实测可容忍,
|
|
151
|
-
* 但按 codearts2api
|
|
165
|
+
* 但按 codearts2api 观察到的真实流量收窄,避免网关后续收紧);
|
|
166
|
+
* 例外是 `reasoning_effort`(DSH 的思考档位):官方内核同样以这个顶层字段
|
|
167
|
+
* 透传 variant,见 kernel 的 `inferhub getArgs`。
|
|
152
168
|
* 4. tools[].function 去掉 strict(OpenAI 专有)
|
|
153
169
|
* 5. 注入 CodeArts 特有字段:chat_id(按对话派生,见 conversationChatId)、
|
|
154
170
|
* prompt_cache_key、tool_stream
|
|
@@ -178,8 +194,11 @@ interface CallerIdentity {
|
|
|
178
194
|
userName: string;
|
|
179
195
|
domainId: string;
|
|
180
196
|
}
|
|
181
|
-
/**
|
|
182
|
-
|
|
197
|
+
/**
|
|
198
|
+
* 模型清单的来源。前三路是目录本身(任一路失败都不阻断,只有三路全失败才算失败),
|
|
199
|
+
* `thinking` 只是目录之上的可选增强:它失败时模型照常可用,只是没有思考档位选择器。
|
|
200
|
+
*/
|
|
201
|
+
type ModelSource = 'builtin' | 'benefit' | 'agent' | 'thinking';
|
|
183
202
|
interface ModelDiscoveryResult {
|
|
184
203
|
/** 合并后的清单(已去重、已排序)。 */
|
|
185
204
|
models: readonly CodeArtsModelInfo[];
|
|
@@ -191,6 +210,13 @@ interface ModelDiscoveryResult {
|
|
|
191
210
|
message: string;
|
|
192
211
|
}[];
|
|
193
212
|
}
|
|
213
|
+
/** 一条请求在上游队列里的位置。 */
|
|
214
|
+
interface CodeArtsQueueStatus {
|
|
215
|
+
status: string;
|
|
216
|
+
queueLength: number;
|
|
217
|
+
queuePosition: number;
|
|
218
|
+
message: string;
|
|
219
|
+
}
|
|
194
220
|
/**
|
|
195
221
|
* 动态发现当前账号可用的模型清单(三路合并)。
|
|
196
222
|
*
|
|
@@ -405,6 +431,77 @@ declare class CodeArtsAccountPool {
|
|
|
405
431
|
hasUsable(): boolean;
|
|
406
432
|
}
|
|
407
433
|
//#endregion
|
|
434
|
+
//#region src/queue.d.ts
|
|
435
|
+
interface CodeArtsQueueEntry {
|
|
436
|
+
/** chat_id,与上游 task_id 的尾段一致。 */
|
|
437
|
+
id: string;
|
|
438
|
+
model: string;
|
|
439
|
+
position: number;
|
|
440
|
+
length: number;
|
|
441
|
+
status: string;
|
|
442
|
+
message?: string;
|
|
443
|
+
updatedAt: number;
|
|
444
|
+
}
|
|
445
|
+
type CodeArtsQueueOutcome =
|
|
446
|
+
/** 轮到了(或模型本来就不排队),可以发请求。 */
|
|
447
|
+
{
|
|
448
|
+
kind: 'working';
|
|
449
|
+
} |
|
|
450
|
+
/** 等了 `maxWaitMs` 还在队里:把位置报给用户,让他稍后再发。 */
|
|
451
|
+
{
|
|
452
|
+
kind: 'queued';
|
|
453
|
+
position: number;
|
|
454
|
+
length: number;
|
|
455
|
+
message: string;
|
|
456
|
+
} |
|
|
457
|
+
/** 队列本身已满,上游让用户换模型/升级套餐。 */
|
|
458
|
+
{
|
|
459
|
+
kind: 'queue_full';
|
|
460
|
+
message: string;
|
|
461
|
+
} |
|
|
462
|
+
/** 排队接口连续失败,放弃排队并报出原因。 */
|
|
463
|
+
{
|
|
464
|
+
kind: 'error';
|
|
465
|
+
message: string;
|
|
466
|
+
} |
|
|
467
|
+
/** 用户取消或插件退出。 */
|
|
468
|
+
{
|
|
469
|
+
kind: 'aborted';
|
|
470
|
+
};
|
|
471
|
+
interface CodeArtsQueueGateOptions {
|
|
472
|
+
/** 两次查询之间的间隔(官方客户端是 5 秒)。 */
|
|
473
|
+
intervalMs?: number;
|
|
474
|
+
/** 最多替用户等多久。超过就回 `queued`,别把请求挂着。 */
|
|
475
|
+
maxWaitMs?: number;
|
|
476
|
+
/** 连续失败几次就放弃。 */
|
|
477
|
+
maxErrors?: number;
|
|
478
|
+
/** 注入查询函数(测试用)。 */
|
|
479
|
+
fetchStatus?: (credential: CodeArtsResolved, chatId: string, model: string, signal: AbortSignal) => Promise<CodeArtsQueueStatus>;
|
|
480
|
+
/** 注入清号函数(测试用)。 */
|
|
481
|
+
clearStatus?: (credential: CodeArtsResolved, chatId: string, model: string) => Promise<void>;
|
|
482
|
+
now?: () => number;
|
|
483
|
+
}
|
|
484
|
+
declare class CodeArtsQueueGate {
|
|
485
|
+
private readonly entries;
|
|
486
|
+
private readonly controllers;
|
|
487
|
+
private readonly intervalMs;
|
|
488
|
+
private readonly maxWaitMs;
|
|
489
|
+
private readonly maxErrors;
|
|
490
|
+
private readonly query;
|
|
491
|
+
private readonly clear;
|
|
492
|
+
private readonly now;
|
|
493
|
+
constructor(options?: CodeArtsQueueGateOptions);
|
|
494
|
+
/**
|
|
495
|
+
* 占号并**最多等 `maxWaitMs`** 轮到。调用方拿到 `working` 后才发 chat 请求;
|
|
496
|
+
* 其余结果(含 `queued`)都不要再发 —— `queued` 是要把位置原样告诉用户。
|
|
497
|
+
*/
|
|
498
|
+
wait(credential: CodeArtsResolved, chatId: string, model: string, signal?: AbortSignal): Promise<CodeArtsQueueOutcome>;
|
|
499
|
+
/** 正在排队的条目(给底部条)。 */
|
|
500
|
+
snapshot(): readonly CodeArtsQueueEntry[];
|
|
501
|
+
dispose(): void;
|
|
502
|
+
private pause;
|
|
503
|
+
}
|
|
504
|
+
//#endregion
|
|
408
505
|
//#region src/usage.d.ts
|
|
409
506
|
/**
|
|
410
507
|
* 用量账本:把聊天流里的 usage 帧累计成「今天用了多少」。
|
|
@@ -520,6 +617,11 @@ interface CodeArtsShimOptions {
|
|
|
520
617
|
pool?: CodeArtsAccountPool;
|
|
521
618
|
/** 用量账本。给了就在转发流的同时把 usage 帧记下来(卡片上的「今日用量」)。 */
|
|
522
619
|
usage?: CodeArtsUsageTracker;
|
|
620
|
+
/**
|
|
621
|
+
* 上游排队闸门。给了就在发请求前占号、轮询到轮到再开流
|
|
622
|
+
* (底部积分条旁边展示「排队第 N 位」)。
|
|
623
|
+
*/
|
|
624
|
+
queue?: CodeArtsQueueGate;
|
|
523
625
|
logger?: ShimLogger;
|
|
524
626
|
}
|
|
525
627
|
declare function createCodeArtsShim(options: CodeArtsShimOptions): CodeArtsShim;
|
package/lib/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as filterEnabledModels, C as rateLimitFrame, D as FALLBACK_CODEARTS_MODELS, E as CodeArtsCatalog, F as codeartsAccountsPath, I as codeartsAuthPath, L as startLogin, M as isBenefitModel, N as CodeArtsAccountStore, O as clearBenefitBlocked, P as CodeArtsCredentialStore, S as prepareChatBody, T as upstreamErrorMessage, _ as isQueueableRate, a as SNAP_HOST, b as maxTokensCeilingFor, c as claimDailyWelfare, d as fetchCallerIdentity, f as fetchModels, g as isParamInvalidFrame, h as isConcurrencyCap, i as refreshCatalog, j as isBenefitBlocked, k as creditRatioLabel, l as clearQueueStatus, m as fetchWelfareCampaigns, n as codeartsUsagePath, o as canonicalModel, p as fetchQueueStatus, r as discoverModels, s as chatStream, t as CodeArtsUsageTracker, u as degradeTokenCeiling, v as isTpmLimit, w as signedGet, x as pickDailyWelfareCampaign, y as isWelfareClaimedStatus } from "./usage-Cie0IlC4.mjs";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
4
4
|
import { createServer } from "node:http";
|
|
@@ -136,6 +136,155 @@ var CodeArtsAccountPool = class {
|
|
|
136
136
|
}
|
|
137
137
|
};
|
|
138
138
|
//#endregion
|
|
139
|
+
//#region src/queue.ts
|
|
140
|
+
/**
|
|
141
|
+
* 上游排队的「闸门」。
|
|
142
|
+
*
|
|
143
|
+
* 26.10.0 起上游有 `GET /api/v1/queue/status`,但它**不是只读查询**:GET 本身会把
|
|
144
|
+
* 这个 task 挂进队列、返回队尾位置,`DELETE` 才把它清掉(2026-10-21 实测)。真正
|
|
145
|
+
* 的 chat 请求并不看这个号 —— 拿着一张第 535 位的票去 POST,942ms 就开流了。
|
|
146
|
+
* 官方客户端的做法是:只对 `enable_queue` 的模型,先轮询排队接口,等状态变成
|
|
147
|
+
* `working` 才发聊天请求(见 vscode-codebot 的 `startQueuePolling`)。
|
|
148
|
+
*
|
|
149
|
+
* 这里**不照搬官方那种「一直等到轮到」**:宿主的流空闲超时只有 90 秒(等待期间
|
|
150
|
+
* 一个字节都发不出去,SSE 注释帧也不算事件,实测每 90 秒就被掐一次重来),而
|
|
151
|
+
* flash 高峰期队尾有 500+ 号、要等二十几分钟 —— 没有人会挂着对话等它。所以
|
|
152
|
+
* `wait()` 只等 `maxWaitMs`(默认 20 秒):
|
|
153
|
+
* - 等到 working → 放行;
|
|
154
|
+
* - 还在队里 → 返回 `queued` 带上位置和队长度,由 shim 直接回一句「排队中,
|
|
155
|
+
* 稍后再试」,让用户自己决定要不要再发一次;
|
|
156
|
+
* - 队列已满 → `queue_full`;连续失败 → `error`。
|
|
157
|
+
*
|
|
158
|
+
* 三种收尾都会 DELETE 清号,别把幽灵排队留在上游把队列越撑越长。
|
|
159
|
+
*
|
|
160
|
+
* @module dsh-codearts/queue
|
|
161
|
+
*/
|
|
162
|
+
const CLEAR_TIMEOUT_MS = 5e3;
|
|
163
|
+
var CodeArtsQueueGate = class {
|
|
164
|
+
entries = /* @__PURE__ */ new Map();
|
|
165
|
+
controllers = /* @__PURE__ */ new Set();
|
|
166
|
+
intervalMs;
|
|
167
|
+
maxWaitMs;
|
|
168
|
+
maxErrors;
|
|
169
|
+
query;
|
|
170
|
+
clear;
|
|
171
|
+
now;
|
|
172
|
+
constructor(options = {}) {
|
|
173
|
+
this.intervalMs = options.intervalMs ?? 5e3;
|
|
174
|
+
this.maxWaitMs = options.maxWaitMs ?? 2e4;
|
|
175
|
+
this.maxErrors = options.maxErrors ?? 3;
|
|
176
|
+
this.query = options.fetchStatus ?? fetchQueueStatus;
|
|
177
|
+
this.clear = options.clearStatus ?? ((credential, chatId, model) => clearQueueStatus(credential, chatId, model, AbortSignal.timeout(CLEAR_TIMEOUT_MS)));
|
|
178
|
+
this.now = options.now ?? Date.now;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 占号并**最多等 `maxWaitMs`** 轮到。调用方拿到 `working` 后才发 chat 请求;
|
|
182
|
+
* 其余结果(含 `queued`)都不要再发 —— `queued` 是要把位置原样告诉用户。
|
|
183
|
+
*/
|
|
184
|
+
async wait(credential, chatId, model, signal) {
|
|
185
|
+
if (chatId === "") return { kind: "working" };
|
|
186
|
+
if (signal?.aborted === true) return { kind: "aborted" };
|
|
187
|
+
const controller = new AbortController();
|
|
188
|
+
const onAbort = () => controller.abort();
|
|
189
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
190
|
+
this.controllers.add(controller);
|
|
191
|
+
const entry = {
|
|
192
|
+
id: chatId,
|
|
193
|
+
model,
|
|
194
|
+
position: 0,
|
|
195
|
+
length: 0,
|
|
196
|
+
status: "",
|
|
197
|
+
updatedAt: this.now()
|
|
198
|
+
};
|
|
199
|
+
this.entries.set(chatId, entry);
|
|
200
|
+
let errors = 0;
|
|
201
|
+
const deadlineAt = this.now() + this.maxWaitMs;
|
|
202
|
+
try {
|
|
203
|
+
for (;;) {
|
|
204
|
+
if (controller.signal.aborted) return { kind: "aborted" };
|
|
205
|
+
let status;
|
|
206
|
+
try {
|
|
207
|
+
status = await this.query(credential, chatId, model, controller.signal);
|
|
208
|
+
errors = 0;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (controller.signal.aborted) return { kind: "aborted" };
|
|
211
|
+
errors += 1;
|
|
212
|
+
if (errors >= this.maxErrors) return {
|
|
213
|
+
kind: "error",
|
|
214
|
+
message: `查询上游排队状态连续失败 ${errors} 次:${errorText(error)}`
|
|
215
|
+
};
|
|
216
|
+
await this.pause(controller.signal);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
entry.status = status.status;
|
|
220
|
+
entry.position = status.queuePosition;
|
|
221
|
+
entry.length = status.queueLength;
|
|
222
|
+
entry.message = status.message;
|
|
223
|
+
entry.updatedAt = this.now();
|
|
224
|
+
const verdict = verdictOf(status);
|
|
225
|
+
if (verdict !== void 0) return verdict;
|
|
226
|
+
if (this.now() >= deadlineAt) return {
|
|
227
|
+
kind: "queued",
|
|
228
|
+
position: status.queuePosition,
|
|
229
|
+
length: status.queueLength,
|
|
230
|
+
message: status.message
|
|
231
|
+
};
|
|
232
|
+
await this.pause(controller.signal, Math.min(this.intervalMs, deadlineAt - this.now()));
|
|
233
|
+
}
|
|
234
|
+
} finally {
|
|
235
|
+
this.controllers.delete(controller);
|
|
236
|
+
signal?.removeEventListener("abort", onAbort);
|
|
237
|
+
if (this.entries.get(chatId) === entry) this.entries.delete(chatId);
|
|
238
|
+
await this.clear(credential, chatId, model).catch(() => {});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/** 正在排队的条目(给底部条)。 */
|
|
242
|
+
snapshot() {
|
|
243
|
+
return [...this.entries.values()].filter((entry) => entry.position > 0 || entry.status !== "" && entry.status !== "working").map((entry) => ({
|
|
244
|
+
id: entry.id,
|
|
245
|
+
model: entry.model,
|
|
246
|
+
position: entry.position,
|
|
247
|
+
length: entry.length,
|
|
248
|
+
status: entry.status,
|
|
249
|
+
...entry.message === "" ? {} : { message: entry.message },
|
|
250
|
+
updatedAt: entry.updatedAt
|
|
251
|
+
}));
|
|
252
|
+
}
|
|
253
|
+
dispose() {
|
|
254
|
+
for (const controller of [...this.controllers]) controller.abort();
|
|
255
|
+
}
|
|
256
|
+
pause(signal, delay = this.intervalMs) {
|
|
257
|
+
return new Promise((resolve) => {
|
|
258
|
+
const timer = setTimeout(finish, Math.max(0, delay));
|
|
259
|
+
timer.unref?.();
|
|
260
|
+
function finish() {
|
|
261
|
+
clearTimeout(timer);
|
|
262
|
+
signal.removeEventListener("abort", finish);
|
|
263
|
+
resolve();
|
|
264
|
+
}
|
|
265
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* 这个状态还要不要继续等。
|
|
271
|
+
*
|
|
272
|
+
* `working` = 轮到;`queue_full` = 队列已满,上游建议换模型;其余(waiting 等)继续。
|
|
273
|
+
* 空状态且位置为 0 说明这个模型根本没排队(如 GLM),直接放行。
|
|
274
|
+
*/
|
|
275
|
+
function verdictOf(status) {
|
|
276
|
+
const text = status.status.trim().toLowerCase();
|
|
277
|
+
if (text === "working") return { kind: "working" };
|
|
278
|
+
if (text === "queue_full") return {
|
|
279
|
+
kind: "queue_full",
|
|
280
|
+
message: status.message
|
|
281
|
+
};
|
|
282
|
+
if (text === "" && status.queuePosition === 0 && status.queueLength === 0) return { kind: "working" };
|
|
283
|
+
}
|
|
284
|
+
function errorText(error) {
|
|
285
|
+
return error instanceof Error ? error.message : String(error);
|
|
286
|
+
}
|
|
287
|
+
//#endregion
|
|
139
288
|
//#region src/credits.ts
|
|
140
289
|
/**
|
|
141
290
|
* CodeArts 积分与额度。
|
|
@@ -392,7 +541,8 @@ var CodeArtsCreditCache = class {
|
|
|
392
541
|
* MVP simplifications vs dsh-codebuddy-cli:
|
|
393
542
|
* - only the default-slot credit multiplier goes into the model name (`(x0.18)`);
|
|
394
543
|
* per-request cost is not modelled (subscription quota, cost reported as zero)
|
|
395
|
-
* -
|
|
544
|
+
* - reasoning levels are surfaced only where upstream's thinking-variants names
|
|
545
|
+
* them (`GLM-5.2` → high/max); everything else stays a plain chat model
|
|
396
546
|
*
|
|
397
547
|
* @module dsh-codearts/adapter
|
|
398
548
|
*/
|
|
@@ -438,8 +588,38 @@ const INERT_AUTH = {
|
|
|
438
588
|
}
|
|
439
589
|
}
|
|
440
590
|
};
|
|
591
|
+
/** pi-ai's selectable levels, in escalation order. */
|
|
592
|
+
const REASONING_LEVELS = [
|
|
593
|
+
"off",
|
|
594
|
+
"minimal",
|
|
595
|
+
"low",
|
|
596
|
+
"medium",
|
|
597
|
+
"high",
|
|
598
|
+
"xhigh",
|
|
599
|
+
"max"
|
|
600
|
+
];
|
|
601
|
+
/**
|
|
602
|
+
* Turn upstream's thinking strengths into pi-ai reasoning metadata.
|
|
603
|
+
*
|
|
604
|
+
* Every level upstream does not declare is pinned to `null`: pi-ai treats a
|
|
605
|
+
* missing entry as supported, so leaving them out would offer Off/Minimal/Low/
|
|
606
|
+
* Medium for a model that only knows high/max. `off` is pinned as well — the
|
|
607
|
+
* gateway's default is already high, and claiming Off would send no effort
|
|
608
|
+
* field and still think.
|
|
609
|
+
*/
|
|
610
|
+
function thinkingFields(strengths) {
|
|
611
|
+
const offered = new Set(strengths ?? []);
|
|
612
|
+
if (!REASONING_LEVELS.some((level) => level !== "off" && offered.has(level))) return { reasoning: false };
|
|
613
|
+
const map = {};
|
|
614
|
+
for (const level of REASONING_LEVELS) map[level] = offered.has(level) ? level : null;
|
|
615
|
+
return {
|
|
616
|
+
reasoning: true,
|
|
617
|
+
thinkingLevelMap: map
|
|
618
|
+
};
|
|
619
|
+
}
|
|
441
620
|
/** Build one pi-ai model descriptor pointing at the loopback shim. */
|
|
442
621
|
function toPiModel(info, baseUrl) {
|
|
622
|
+
const thinking = thinkingFields(info.thinking?.strengths);
|
|
443
623
|
return {
|
|
444
624
|
id: info.id,
|
|
445
625
|
name: info.name,
|
|
@@ -447,7 +627,8 @@ function toPiModel(info, baseUrl) {
|
|
|
447
627
|
provider: CODEARTS_PROVIDER,
|
|
448
628
|
baseUrl,
|
|
449
629
|
input: info.supportsImages === true ? ["text", "image"] : ["text"],
|
|
450
|
-
reasoning:
|
|
630
|
+
reasoning: thinking.reasoning,
|
|
631
|
+
...thinking.thinkingLevelMap === void 0 ? {} : { thinkingLevelMap: thinking.thinkingLevelMap },
|
|
451
632
|
cost: NO_COST,
|
|
452
633
|
contextWindow: info.contextWindow,
|
|
453
634
|
maxTokens: info.maxTokens
|
|
@@ -627,6 +808,14 @@ function writeOpenAIError(res, status, kind, message) {
|
|
|
627
808
|
code: kind
|
|
628
809
|
} });
|
|
629
810
|
}
|
|
811
|
+
/**
|
|
812
|
+
* 排队没等到时回给用户的那句:优先用上游自己的文案(它就是写给人看的,带位置和
|
|
813
|
+
* 「建议切换模型/升级套餐」),上游没给才自己拼。
|
|
814
|
+
*/
|
|
815
|
+
function queuedMessage(model, gate) {
|
|
816
|
+
const upstream = gate.message.trim();
|
|
817
|
+
return `${upstream === "" ? `上游排队中:${model} 第 ${gate.position} 位(共 ${gate.length} 条)` : upstream} 插件没有一直等下去,请稍后重试。`;
|
|
818
|
+
}
|
|
630
819
|
function readBody$1(req) {
|
|
631
820
|
return new Promise((resolve, reject) => {
|
|
632
821
|
const chunks = [];
|
|
@@ -670,6 +859,7 @@ function createCodeArtsShim(options) {
|
|
|
670
859
|
const { store, catalog } = options;
|
|
671
860
|
const logger = options.logger;
|
|
672
861
|
const usage = options.usage;
|
|
862
|
+
const queue = options.queue;
|
|
673
863
|
const pool = options.pool ?? new CodeArtsAccountPool([]);
|
|
674
864
|
const SHARED_SECRET = randomBytes(32).toString("base64url");
|
|
675
865
|
function bearerOk(req) {
|
|
@@ -898,6 +1088,18 @@ function createCodeArtsShim(options) {
|
|
|
898
1088
|
pool.disable(id, "no credential");
|
|
899
1089
|
continue;
|
|
900
1090
|
}
|
|
1091
|
+
if (queue !== void 0) {
|
|
1092
|
+
const gate = await queue.wait(credential, checked.chatId, checked.model, controller.signal);
|
|
1093
|
+
if (controller.signal.aborted) return;
|
|
1094
|
+
if (gate.kind === "queued") {
|
|
1095
|
+
writeOpenAIError(res, 400, "queued", queuedMessage(checked.model, gate));
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
if (gate.kind === "queue_full" || gate.kind === "error") {
|
|
1099
|
+
writeOpenAIError(res, gate.kind === "queue_full" ? 429 : 502, gate.kind, gate.message);
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
901
1103
|
const opened = await openWithCeilingHeal(credential, raw, contextWindow, controller.signal);
|
|
902
1104
|
const { prepared, result } = opened;
|
|
903
1105
|
if (controller.signal.aborted) {
|
|
@@ -1068,6 +1270,8 @@ const CODEARTS_ACCOUNTS_PATH = "/api/codearts/accounts";
|
|
|
1068
1270
|
const CODEARTS_CLAIM_PATH = "/api/codearts/claim";
|
|
1069
1271
|
/** POST /api/codearts/refresh-models — 立刻重拉一次模型目录,并清掉「福利今日不可用」标记。 */
|
|
1070
1272
|
const CODEARTS_REFRESH_MODELS_PATH = "/api/codearts/refresh-models";
|
|
1273
|
+
/** GET /api/codearts/queue — 上游排队位置(只在等开流期间有值,底部积分条旁边展示)。 */
|
|
1274
|
+
const CODEARTS_QUEUE_PATH = "/api/codearts/queue";
|
|
1071
1275
|
/**
|
|
1072
1276
|
* 由完整目录 + 已存 enabled 计算 selection。
|
|
1073
1277
|
* - enabled 为空/未存 => 全部勾选(restricted=false)
|
|
@@ -1507,6 +1711,20 @@ function refreshModelsHandler(deps) {
|
|
|
1507
1711
|
}
|
|
1508
1712
|
};
|
|
1509
1713
|
}
|
|
1714
|
+
/** GET /api/codearts/queue — 排队位置快照(无跟踪器/无排队时给空数组)。 */
|
|
1715
|
+
function queueHandler(deps) {
|
|
1716
|
+
return async (req, res) => {
|
|
1717
|
+
if (req.method !== "GET") {
|
|
1718
|
+
json(res, 405, { error: "method not allowed" });
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
if (!loopbackRequest(req)) {
|
|
1722
|
+
json(res, 403, { error: "request-not-trusted" });
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
json(res, 200, { queue: deps.queue?.() ?? [] });
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1510
1728
|
/** Mount the status / models / token routes. */
|
|
1511
1729
|
function registerCodeArtsStatusRoute(ctx, deps) {
|
|
1512
1730
|
ctx.effect(() => {
|
|
@@ -1558,6 +1776,11 @@ function registerCodeArtsStatusRoute(ctx, deps) {
|
|
|
1558
1776
|
path: CODEARTS_REFRESH_MODELS_PATH,
|
|
1559
1777
|
handler: refreshModelsHandler(deps)
|
|
1560
1778
|
});
|
|
1779
|
+
const disposeQueue = ctx.webServer.register({
|
|
1780
|
+
kind: "exact",
|
|
1781
|
+
path: CODEARTS_QUEUE_PATH,
|
|
1782
|
+
handler: queueHandler(deps)
|
|
1783
|
+
});
|
|
1561
1784
|
return () => {
|
|
1562
1785
|
disposeStatus();
|
|
1563
1786
|
disposeModels();
|
|
@@ -1568,6 +1791,7 @@ function registerCodeArtsStatusRoute(ctx, deps) {
|
|
|
1568
1791
|
disposeAccounts();
|
|
1569
1792
|
disposeClaim();
|
|
1570
1793
|
disposeRefresh();
|
|
1794
|
+
disposeQueue();
|
|
1571
1795
|
for (const s of activeLogins.values()) s.close();
|
|
1572
1796
|
activeLogins.clear();
|
|
1573
1797
|
};
|
|
@@ -1740,6 +1964,7 @@ function apply(ctx, rawConfig) {
|
|
|
1740
1964
|
const pool = new CodeArtsAccountPool([]);
|
|
1741
1965
|
const catalog = new CodeArtsCatalog();
|
|
1742
1966
|
const usage = new CodeArtsUsageTracker({ path: codeartsUsagePath() });
|
|
1967
|
+
const queue = new CodeArtsQueueGate();
|
|
1743
1968
|
const credits = new CodeArtsCreditCache();
|
|
1744
1969
|
/** /status 是卡片与对话底部都会轮询的接口,积分慢请求不能把它拖成转圈。 */
|
|
1745
1970
|
const CREDIT_TIMEOUT_MS = 8e3;
|
|
@@ -1809,6 +2034,7 @@ function apply(ctx, rawConfig) {
|
|
|
1809
2034
|
setEnabledModels,
|
|
1810
2035
|
pool: () => pool.list(),
|
|
1811
2036
|
usage: () => usage.snapshot(),
|
|
2037
|
+
queue: () => queue.snapshot(),
|
|
1812
2038
|
credits: (signal) => loadCredits(signal),
|
|
1813
2039
|
claim: async (accountId) => {
|
|
1814
2040
|
const ids = accountId === void 0 ? await store.ids() : [accountId];
|
|
@@ -1886,6 +2112,7 @@ function apply(ctx, rawConfig) {
|
|
|
1886
2112
|
let stopped = false;
|
|
1887
2113
|
ctx.effect(() => () => {
|
|
1888
2114
|
stopped = true;
|
|
2115
|
+
queue.dispose();
|
|
1889
2116
|
usage.flush().catch(() => {});
|
|
1890
2117
|
shim.close().catch(() => {});
|
|
1891
2118
|
});
|
|
@@ -1917,6 +2144,7 @@ function apply(ctx, rawConfig) {
|
|
|
1917
2144
|
catalog,
|
|
1918
2145
|
pool,
|
|
1919
2146
|
usage,
|
|
2147
|
+
queue,
|
|
1920
2148
|
logger: ctx.logger
|
|
1921
2149
|
});
|
|
1922
2150
|
usage.load().catch(() => {});
|
|
@@ -1079,6 +1079,7 @@ function richerModel(current, next) {
|
|
|
1079
1079
|
const keep = richerThan(next, current) ? next : current;
|
|
1080
1080
|
const drop = keep === next ? current : next;
|
|
1081
1081
|
const ratio = keep.ratio ?? drop.ratio;
|
|
1082
|
+
const thinking = keep.thinking ?? drop.thinking;
|
|
1082
1083
|
return {
|
|
1083
1084
|
id: keep.id,
|
|
1084
1085
|
name: keep.name || drop.name || keep.id,
|
|
@@ -1086,6 +1087,7 @@ function richerModel(current, next) {
|
|
|
1086
1087
|
maxTokens: keep.maxTokens > 0 ? keep.maxTokens : drop.maxTokens,
|
|
1087
1088
|
supportsImages: keep.supportsImages || drop.supportsImages,
|
|
1088
1089
|
...ratio === void 0 ? {} : { ratio },
|
|
1090
|
+
...thinking === void 0 ? {} : { thinking },
|
|
1089
1091
|
...benefit ? { benefit: true } : {}
|
|
1090
1092
|
};
|
|
1091
1093
|
}
|
|
@@ -1116,6 +1118,7 @@ function backfillParams(discovered, params = FALLBACK_CODEARTS_MODELS) {
|
|
|
1116
1118
|
maxTokens: current.maxTokens > 0 ? current.maxTokens : fallback.maxTokens,
|
|
1117
1119
|
supportsImages: current.supportsImages || fallback.supportsImages,
|
|
1118
1120
|
...current.ratio === void 0 ? {} : { ratio: current.ratio },
|
|
1121
|
+
...current.thinking === void 0 ? {} : { thinking: current.thinking },
|
|
1119
1122
|
...current.benefit === true ? { benefit: true } : {}
|
|
1120
1123
|
});
|
|
1121
1124
|
}
|
|
@@ -1232,10 +1235,12 @@ var upstream_exports = /* @__PURE__ */ __exportAll({
|
|
|
1232
1235
|
chatStream: () => chatStream,
|
|
1233
1236
|
claimBenefit: () => claimBenefit,
|
|
1234
1237
|
claimDailyWelfare: () => claimDailyWelfare,
|
|
1238
|
+
clearQueueStatus: () => clearQueueStatus,
|
|
1235
1239
|
degradeTokenCeiling: () => degradeTokenCeiling,
|
|
1236
1240
|
estimateTokens: () => estimateTokens,
|
|
1237
1241
|
fetchCallerIdentity: () => fetchCallerIdentity,
|
|
1238
1242
|
fetchModels: () => fetchModels,
|
|
1243
|
+
fetchQueueStatus: () => fetchQueueStatus,
|
|
1239
1244
|
fetchWelfareCampaigns: () => fetchWelfareCampaigns,
|
|
1240
1245
|
isBenefitMissing: () => isBenefitMissing,
|
|
1241
1246
|
isConcurrencyCap: () => isConcurrencyCap,
|
|
@@ -1259,6 +1264,13 @@ const EP_BENEFIT_CONFIG = "/api/v1/gateway/config";
|
|
|
1259
1264
|
const EP_AGENT_LIST = "/v1/agent-center/agents/useragents";
|
|
1260
1265
|
const EP_AGENT_DETAIL = "/v1/agent-center/agents/detail";
|
|
1261
1266
|
/**
|
|
1267
|
+
* 模型的思考强度清单(26.10.0 新增)。**必须带 `Agent-Type: PromptCenter`**:
|
|
1268
|
+
* 不带头时上游回 `TM.00001001 请求头Agent-Type为空`(实测)。
|
|
1269
|
+
*/
|
|
1270
|
+
const EP_THINKING_VARIANTS = "/v1/model/thinking-variants";
|
|
1271
|
+
/** 排队位置(26.10.0 新增):按 chat_id 查这条请求在上游队里的排位。 */
|
|
1272
|
+
const EP_QUEUE_STATUS = "/api/v1/queue/status";
|
|
1273
|
+
/**
|
|
1262
1274
|
* 当前登录身份。**必须带 `/snap-manager` 前缀**:2026-09-30 实测
|
|
1263
1275
|
* `/v1/current/user` 是 APIG.0101(未发布),`/snap-manager/v1/current/user`
|
|
1264
1276
|
* 才返回 `{user_id, user_name, domain_id}`。codearts2api 的 const.go 里少了这段前缀
|
|
@@ -1425,7 +1437,9 @@ function sessionIdFor(chatId) {
|
|
|
1425
1437
|
* (两者任一超限,上游都只会流内回 InferHub.001001005.400
|
|
1426
1438
|
* "The request param is invalid, Please check it",不告诉我们是哪一个)
|
|
1427
1439
|
* 3. 丢弃非白名单字段(stream_options / store / logprobs 等虽实测可容忍,
|
|
1428
|
-
* 但按 codearts2api
|
|
1440
|
+
* 但按 codearts2api 观察到的真实流量收窄,避免网关后续收紧);
|
|
1441
|
+
* 例外是 `reasoning_effort`(DSH 的思考档位):官方内核同样以这个顶层字段
|
|
1442
|
+
* 透传 variant,见 kernel 的 `inferhub getArgs`。
|
|
1429
1443
|
* 4. tools[].function 去掉 strict(OpenAI 专有)
|
|
1430
1444
|
* 5. 注入 CodeArts 特有字段:chat_id(按对话派生,见 conversationChatId)、
|
|
1431
1445
|
* prompt_cache_key、tool_stream
|
|
@@ -1459,7 +1473,8 @@ function prepareChatBody(rawJson, contextWindow = 0) {
|
|
|
1459
1473
|
"tools",
|
|
1460
1474
|
"tool_choice",
|
|
1461
1475
|
"user",
|
|
1462
|
-
"stream_options"
|
|
1476
|
+
"stream_options",
|
|
1477
|
+
"reasoning_effort"
|
|
1463
1478
|
]) if (parsed[key] !== void 0) body[key] = parsed[key];
|
|
1464
1479
|
if (body.stream_options === void 0 || typeof body.stream_options !== "object") body.stream_options = { include_usage: true };
|
|
1465
1480
|
else body.stream_options.include_usage = true;
|
|
@@ -1994,6 +2009,128 @@ async function fetchAgentModels(credential, signal) {
|
|
|
1994
2009
|
return out;
|
|
1995
2010
|
}
|
|
1996
2011
|
/**
|
|
2012
|
+
* 思考强度清单:`POST /v1/model/thinking-variants`,body `{ model_names }`。
|
|
2013
|
+
*
|
|
2014
|
+
* 实测响应 `{ variants: { "GLM-5.2": { strengths: ["high","max"],
|
|
2015
|
+
* default_strength: "high" }, ... } }`,键是请求里的名字(大小写原样),
|
|
2016
|
+
* 不支持的模型给空 `strengths`。官方客户端限 50 个名字,这里照搬。
|
|
2017
|
+
*
|
|
2018
|
+
* 这是**可选增强**:上游没给(旧账号/接口漂移)时模型照常可用,只是宿主不显示
|
|
2019
|
+
* 推理档位选择器。失败由调用方记进 errors,不抛断发现。
|
|
2020
|
+
*/
|
|
2021
|
+
async function fetchThinkingVariants(credential, modelIds, signal) {
|
|
2022
|
+
const out = /* @__PURE__ */ new Map();
|
|
2023
|
+
const modelNames = modelIds.slice(0, 50);
|
|
2024
|
+
if (modelNames.length === 0) return out;
|
|
2025
|
+
const bodyBuf = Buffer.from(JSON.stringify({ model_names: modelNames }));
|
|
2026
|
+
const req = new Request(`${SNAP_HOST}${EP_THINKING_VARIANTS}`, {
|
|
2027
|
+
method: "POST",
|
|
2028
|
+
headers: {
|
|
2029
|
+
"Content-Type": "application/json",
|
|
2030
|
+
"x-auth-token": credential.token,
|
|
2031
|
+
"X-Security-Token": credential.securityToken,
|
|
2032
|
+
"x-snap-traceid": randomTraceId(),
|
|
2033
|
+
"X-Language": "zh-cn",
|
|
2034
|
+
"Agent-Type": "PromptCenter"
|
|
2035
|
+
},
|
|
2036
|
+
body: bodyBuf
|
|
2037
|
+
});
|
|
2038
|
+
if (credential.accessKeyId && credential.secretAccessKey) signRequest(req, bodyBuf, {
|
|
2039
|
+
accessKeyId: credential.accessKeyId,
|
|
2040
|
+
secretAccessKey: credential.secretAccessKey,
|
|
2041
|
+
securityToken: credential.securityToken
|
|
2042
|
+
});
|
|
2043
|
+
const resp = await fetch(req, { signal });
|
|
2044
|
+
if (!resp.ok) {
|
|
2045
|
+
const text = await resp.text().catch(() => "");
|
|
2046
|
+
throw new Error(`thinking-variants HTTP ${resp.status} ${text.slice(0, 200)}`);
|
|
2047
|
+
}
|
|
2048
|
+
const body = asRecord(parseJson(await resp.text()));
|
|
2049
|
+
for (const [name, raw] of Object.entries(asRecord(body.variants))) {
|
|
2050
|
+
const variant = asRecord(raw);
|
|
2051
|
+
const strengths = asArray(variant.strengths).map((value) => firstString(value)).filter((value) => value !== "");
|
|
2052
|
+
if (strengths.length === 0) continue;
|
|
2053
|
+
const fallback = firstString(variant.default_strength);
|
|
2054
|
+
out.set(name.trim().toLowerCase(), {
|
|
2055
|
+
strengths,
|
|
2056
|
+
...fallback === "" ? {} : { default: fallback }
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
return out;
|
|
2060
|
+
}
|
|
2061
|
+
/**
|
|
2062
|
+
* 查一条请求的排队位置:`GET /api/v1/queue/status?task_id=<chat_id>&model=<model>`。
|
|
2063
|
+
*
|
|
2064
|
+
* ⚠ 这个 GET 是**占号**,不是只读查询:每次调用都会把 task 挂进队列、返回队尾位置,
|
|
2065
|
+
* 用 `clearQueueStatus` 才清得掉(2026-10-21 实测)。队列空闲的模型(如 GLM)直接
|
|
2066
|
+
* 回 `status:"working"`、位置 0;拥堵的模型(如 deepseek-v4.1-flash)对新号一律
|
|
2067
|
+
* 回 `waiting` + 当前队尾。真正的 chat 请求不看这张票,所以要不要等由调用方决定
|
|
2068
|
+
* (见 `src/queue.ts`)。
|
|
2069
|
+
*/
|
|
2070
|
+
async function fetchQueueStatus(credential, chatId, model, signal) {
|
|
2071
|
+
const url = new URL(`${SNAP_HOST}${EP_QUEUE_STATUS}`);
|
|
2072
|
+
url.searchParams.set("task_id", chatId);
|
|
2073
|
+
url.searchParams.set("model", model);
|
|
2074
|
+
const req = new Request(url, {
|
|
2075
|
+
method: "GET",
|
|
2076
|
+
headers: {
|
|
2077
|
+
"Content-Type": "application/json",
|
|
2078
|
+
"x-auth-token": credential.token,
|
|
2079
|
+
"X-Security-Token": credential.securityToken,
|
|
2080
|
+
"x-snap-traceid": randomTraceId(),
|
|
2081
|
+
"X-Language": "zh-cn"
|
|
2082
|
+
}
|
|
2083
|
+
});
|
|
2084
|
+
if (credential.accessKeyId && credential.secretAccessKey) signRequest(req, Buffer.alloc(0), {
|
|
2085
|
+
accessKeyId: credential.accessKeyId,
|
|
2086
|
+
secretAccessKey: credential.secretAccessKey,
|
|
2087
|
+
securityToken: credential.securityToken
|
|
2088
|
+
});
|
|
2089
|
+
const resp = await fetch(req, { signal });
|
|
2090
|
+
if (!resp.ok) {
|
|
2091
|
+
const text = await resp.text().catch(() => "");
|
|
2092
|
+
throw new Error(`queue status HTTP ${resp.status} ${text.slice(0, 200)}`);
|
|
2093
|
+
}
|
|
2094
|
+
const body = asRecord(parseJson(await resp.text()));
|
|
2095
|
+
return {
|
|
2096
|
+
status: firstString(body.status),
|
|
2097
|
+
queueLength: positive(body.queue_length),
|
|
2098
|
+
queuePosition: positive(body.queue_position),
|
|
2099
|
+
message: firstString(body.message)
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
/**
|
|
2103
|
+
* 清掉 `fetchQueueStatus` 挂上去的占号:`DELETE /api/v1/queue/status?task_id=&model=`。
|
|
2104
|
+
*
|
|
2105
|
+
* 官方客户端在回答结束、用户取消、排队出错时都会调;不清的话每次查询都会在上游
|
|
2106
|
+
* 队列里留一个幽灵任务,队列只会越来越长。
|
|
2107
|
+
*/
|
|
2108
|
+
async function clearQueueStatus(credential, chatId, model, signal) {
|
|
2109
|
+
const url = new URL(`${SNAP_HOST}${EP_QUEUE_STATUS}`);
|
|
2110
|
+
url.searchParams.set("task_id", chatId);
|
|
2111
|
+
url.searchParams.set("model", model);
|
|
2112
|
+
const req = new Request(url, {
|
|
2113
|
+
method: "DELETE",
|
|
2114
|
+
headers: {
|
|
2115
|
+
"Content-Type": "application/json",
|
|
2116
|
+
"x-auth-token": credential.token,
|
|
2117
|
+
"X-Security-Token": credential.securityToken,
|
|
2118
|
+
"x-snap-traceid": randomTraceId(),
|
|
2119
|
+
"X-Language": "zh-cn"
|
|
2120
|
+
}
|
|
2121
|
+
});
|
|
2122
|
+
if (credential.accessKeyId && credential.secretAccessKey) signRequest(req, Buffer.alloc(0), {
|
|
2123
|
+
accessKeyId: credential.accessKeyId,
|
|
2124
|
+
secretAccessKey: credential.secretAccessKey,
|
|
2125
|
+
securityToken: credential.securityToken
|
|
2126
|
+
});
|
|
2127
|
+
const resp = await fetch(req, { signal });
|
|
2128
|
+
if (!resp.ok) {
|
|
2129
|
+
const text = await resp.text().catch(() => "");
|
|
2130
|
+
throw new Error(`queue clear HTTP ${resp.status} ${text.slice(0, 200)}`);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
/**
|
|
1997
2134
|
* 动态发现当前账号可用的模型清单(三路合并)。
|
|
1998
2135
|
*
|
|
1999
2136
|
* 与 codearts2api 的 `Client.FetchModels` 等价:内置、限时福利、agent-center 三路
|
|
@@ -2046,12 +2183,25 @@ async function fetchModels(credential, signal) {
|
|
|
2046
2183
|
if (sources.length === 0) throw new Error(`model discovery failed on every source: ${errors.map((e) => `${e.source}: ${e.message}`).join("; ")}`);
|
|
2047
2184
|
const merged = backfillParams(mergeModelLists(discovered));
|
|
2048
2185
|
if (merged.length === 0) throw new Error("model discovery returned an empty catalog");
|
|
2186
|
+
let variants = /* @__PURE__ */ new Map();
|
|
2187
|
+
try {
|
|
2188
|
+
variants = await fetchThinkingVariants(credential, merged.map((model) => model.id), signal);
|
|
2189
|
+
} catch (error) {
|
|
2190
|
+
errors.push({
|
|
2191
|
+
source: "thinking",
|
|
2192
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2049
2195
|
return {
|
|
2050
|
-
models: merged.map((model) =>
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2196
|
+
models: merged.map((model) => {
|
|
2197
|
+
const thinking = variants.get(model.id.toLowerCase());
|
|
2198
|
+
return {
|
|
2199
|
+
...model,
|
|
2200
|
+
...thinking === void 0 ? {} : { thinking },
|
|
2201
|
+
contextWindow: model.contextWindow > 0 ? model.contextWindow : UNKNOWN_CONTEXT_WINDOW,
|
|
2202
|
+
maxTokens: Math.min(model.maxTokens > 0 ? model.maxTokens : UNKNOWN_MAX_TOKENS, maxTokensCeilingFor(model.id))
|
|
2203
|
+
};
|
|
2204
|
+
}),
|
|
2055
2205
|
sources,
|
|
2056
2206
|
errors
|
|
2057
2207
|
};
|
|
@@ -2586,4 +2736,4 @@ function sumOf(store) {
|
|
|
2586
2736
|
return out;
|
|
2587
2737
|
}
|
|
2588
2738
|
//#endregion
|
|
2589
|
-
export {
|
|
2739
|
+
export { filterEnabledModels as A, rateLimitFrame as C, FALLBACK_CODEARTS_MODELS as D, CodeArtsCatalog as E, codeartsAccountsPath as F, codeartsAuthPath as I, startLogin as L, isBenefitModel as M, CodeArtsAccountStore as N, clearBenefitBlocked as O, CodeArtsCredentialStore as P, prepareChatBody as S, upstreamErrorMessage as T, isQueueableRate as _, SNAP_HOST as a, maxTokensCeilingFor as b, claimDailyWelfare as c, fetchCallerIdentity as d, fetchModels as f, isParamInvalidFrame as g, isConcurrencyCap as h, refreshCatalog as i, isBenefitBlocked as j, creditRatioLabel as k, clearQueueStatus as l, fetchWelfareCampaigns as m, codeartsUsagePath as n, canonicalModel as o, fetchQueueStatus as p, discoverModels as r, chatStream as s, CodeArtsUsageTracker as t, degradeTokenCeiling as u, isTpmLimit as v, signedGet as w, pickDailyWelfareCampaign as x, isWelfareClaimedStatus as y };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-codearts",
|
|
3
3
|
"displayName": "DSH CodeArts Connect",
|
|
4
4
|
"description": "把华为云 CodeArts 接入 DeepSeek Harness —— 内置 OAuth2 登录(PKCE + DPoP),登录后即可在 DSH 对话窗口使用 CodeArts 的模型。",
|
|
5
|
-
"version": "1.0
|
|
5
|
+
"version": "1.1.0",
|
|
6
6
|
"packageManager": "pnpm@11.24.0",
|
|
7
7
|
"author": "elegant01",
|
|
8
8
|
"keywords": [
|