dsh-key-usage 0.1.1 → 0.1.3
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 +34 -12
- package/client/client.js +905 -260
- package/client/client.js.map +1 -1
- package/lib/index.js +4 -1
- package/lib/live-quota.js +96 -30
- package/lib/quota-snapshots.js +68 -0
- package/lib/routes.js +70 -11
- package/lib/session-meta.js +101 -0
- package/lib/session-usage.js +33 -1
- package/lib/sessions.js +58 -4
- package/package.json +9 -3
package/client/client.js
CHANGED
|
@@ -46,12 +46,25 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
46
46
|
}
|
|
47
47
|
//#endregion
|
|
48
48
|
//#region src/client/api.ts
|
|
49
|
+
/**
|
|
50
|
+
* 最近一次请求失败的原因,按路径记住。
|
|
51
|
+
* 空态文案需要能指向「怎么做」——最常见的失败是新端点 404(服务端代码要重启宿主才生效)。
|
|
52
|
+
*/
|
|
53
|
+
const failureReasons = /* @__PURE__ */ new Map();
|
|
54
|
+
function failureReasonOf(path) {
|
|
55
|
+
return failureReasons.get(path) ?? null;
|
|
56
|
+
}
|
|
49
57
|
async function fetchJson(path) {
|
|
50
58
|
try {
|
|
51
59
|
const response = await fetch(path, { headers: { Accept: "application/json" } });
|
|
52
|
-
if (!response.ok)
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
failureReasons.set(path, response.status === 404 ? "HTTP 404:宿主没有这个端点 —— 服务端改动需要退出重开 DSH(刷新页面不够)" : "HTTP " + String(response.status));
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
failureReasons.delete(path);
|
|
53
65
|
return await response.json();
|
|
54
|
-
} catch {
|
|
66
|
+
} catch (error) {
|
|
67
|
+
failureReasons.set(path, "请求失败:" + (error instanceof Error ? error.message : String(error)));
|
|
55
68
|
return null;
|
|
56
69
|
}
|
|
57
70
|
}
|
|
@@ -88,6 +101,9 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
88
101
|
function loadProjects(limit = 200) {
|
|
89
102
|
return fetchJson("/dsh-key-usage/projects?limit=" + String(limit));
|
|
90
103
|
}
|
|
104
|
+
function loadModels(limit = 200) {
|
|
105
|
+
return fetchJson("/dsh-key-usage/models?limit=" + String(limit));
|
|
106
|
+
}
|
|
91
107
|
//#endregion
|
|
92
108
|
//#region src/client/format.ts
|
|
93
109
|
/** 展示层格式化:全部与统计口径无关,只负责把数字变成界面文本。 */
|
|
@@ -148,63 +164,63 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
148
164
|
}
|
|
149
165
|
//#endregion
|
|
150
166
|
//#region \0dsh-css:D:\EasyJob\.plugin\dsh-key-usage\src\client\styles\shared.module.css.mjs
|
|
151
|
-
const css$
|
|
152
|
-
const tagId$
|
|
153
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
167
|
+
const css$6 = ".LhdpGW_section{color:var(--dsw-alias-label-primary,#e8e8ec);font-size:13.5px;line-height:1.55}.LhdpGW_title{justify-content:space-between;align-items:center;gap:10px;margin-bottom:12px;font-size:18px;font-weight:650;display:flex}.LhdpGW_titleActions{align-items:center;gap:8px;display:inline-flex}.LhdpGW_refreshBtn{border:1px solid var(--dsw-alias-border-l2,#2c2c34);color:var(--dsw-alias-label-secondary,#9a9aa6);cursor:pointer;background:0 0;border-radius:999px;padding:3px 12px;font-family:inherit;font-size:11.5px}.LhdpGW_refreshBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#80808c1a);color:var(--dsw-alias-label-primary,#e8e8ec)}.LhdpGW_refreshBtn:disabled{opacity:.6;cursor:default}.LhdpGW_tabBar{border-bottom:1px solid var(--dsw-alias-border-l1,#2c2c34);flex-wrap:wrap;gap:2px;margin-bottom:18px;display:flex}.LhdpGW_tabBar button{color:var(--dsw-alias-label-secondary,#9a9aa6);font:inherit;cursor:pointer;background:0 0;border:0;border-bottom:2px solid #0000;margin-bottom:-1px;padding:8px 14px;font-size:13px}.LhdpGW_tabBar button:hover{color:var(--dsw-alias-label-primary,#e8e8ec)}.LhdpGW_tabBar button.LhdpGW_active{color:var(--dsw-alias-state-accent-primary,#5b8cff);border-bottom-color:var(--dsw-alias-state-accent-primary,#5b8cff);font-weight:600}.LhdpGW_card{background:var(--dsw-alias-bg-layer-2,#1d1d22);border:1px solid var(--dsw-alias-border-l1,#2c2c34);border-radius:12px;padding:14px 16px}.LhdpGW_cardGrid3{grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-bottom:12px;display:grid}.LhdpGW_cardGrid4{grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-bottom:18px;display:grid}.LhdpGW_cardGridAuto{grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px;margin-bottom:18px;display:grid}@media (max-width:720px){.LhdpGW_cardGrid4{grid-template-columns:repeat(2,minmax(0,1fr))}}.LhdpGW_statLabel{color:var(--dsw-alias-label-tertiary,#67676f);margin-bottom:6px;font-size:11.5px}.LhdpGW_statValue{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary,#e8e8ec);font-size:20px;font-weight:650}.LhdpGW_statExtra{color:var(--dsw-alias-label-tertiary,#67676f);margin-top:4px;font-size:11.5px}.LhdpGW_groupLabel{color:var(--dsw-alias-label-secondary,#9a9aa6);margin:4px 0 10px;font-size:12.5px;font-weight:600}.LhdpGW_blockLabel{color:var(--dsw-alias-label-secondary,#9a9aa6);margin:18px 0 4px;font-size:11.5px}.LhdpGW_table{border-collapse:collapse;width:100%;margin-top:10px}.LhdpGW_table th{text-align:right;color:var(--dsw-alias-label-tertiary,#67676f);border-bottom:1px solid var(--dsw-alias-border-l1,#2c2c34);white-space:nowrap;padding:5px 8px;font-size:10.5px;font-weight:500}.LhdpGW_table td{text-align:right;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#9a9aa6);vertical-align:middle;border-bottom:1px solid var(--dsw-alias-border-l1,#2c2c34);white-space:normal;overflow-wrap:anywhere;padding:7px 8px;font-size:12px}.LhdpGW_table th:first-child,.LhdpGW_table td:first-child{text-align:left;padding-left:0}.LhdpGW_table td:first-child{color:var(--dsw-alias-label-primary,#e8e8ec)}.LhdpGW_table th.LhdpGW_sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.LhdpGW_table th.LhdpGW_sortable:hover{color:var(--dsw-alias-label-secondary,#9a9aa6)}.LhdpGW_table th.LhdpGW_sortActive{color:var(--dsw-alias-state-accent-primary,#5b8cff)}.LhdpGW_sortArrow{opacity:.8;margin-left:3px;font-size:8px}.LhdpGW_table tr.LhdpGW_clickable{cursor:pointer}.LhdpGW_table tr.LhdpGW_clickable:hover{background:var(--dsw-alias-interactive-bg-hover,#80808c12)}.LhdpGW_table tr.LhdpGW_total td{color:var(--dsw-alias-label-primary,#e8e8ec);border-bottom:0;border-top:1px solid var(--dsw-alias-border-l2,#2c2c34);font-weight:600}.LhdpGW_table tr.LhdpGW_disabled{opacity:.55}.LhdpGW_chipRow{flex-wrap:wrap;gap:8px;margin-bottom:14px;display:flex}.LhdpGW_chip{border:1px solid var(--dsw-alias-border-l2,#2c2c34);color:var(--dsw-alias-label-secondary,#9a9aa6);font:inherit;cursor:pointer;background:0 0;border-radius:999px;align-items:center;gap:6px;padding:5px 13px;font-size:12px;display:inline-flex}.LhdpGW_chip:hover{color:var(--dsw-alias-label-primary,#e8e8ec);border-color:var(--dsw-alias-label-tertiary,#67676f)}.LhdpGW_chip.LhdpGW_active{background:var(--dsw-alias-interactive-bg-active,#5b8cff24);color:var(--dsw-alias-state-accent-primary,#5b8cff);border-color:#5b8cff66;font-weight:600}.LhdpGW_statusDot{border-radius:50%;flex-shrink:0;width:8px;height:8px;display:inline-block}.LhdpGW_dim{color:var(--dsw-alias-label-tertiary,#67676f)}.LhdpGW_mono{color:var(--dsw-alias-label-secondary,#9a9aa6);font-family:ui-monospace,Consolas,monospace;font-size:11px}.LhdpGW_empty{color:var(--dsw-alias-label-tertiary,#67676f);padding:18px 0}.LhdpGW_warning{color:var(--dsw-alias-state-warning-primary,#e0a13c);margin-bottom:10px;font-size:11.5px}.LhdpGW_legend{color:var(--dsw-alias-label-tertiary,#67676f);flex-wrap:wrap;align-items:center;gap:10px;font-size:10.5px;display:inline-flex}.LhdpGW_legend i{border-radius:2px;width:10px;height:10px;margin-right:3px;display:inline-block}.LhdpGW_legend i.LhdpGW_ghost{background:#80808c1a;border:1px solid #80808c47}.LhdpGW_footnote{border-top:1px solid var(--dsw-alias-border-l1,#2c2c34);color:var(--dsw-alias-label-tertiary,#67676f);margin-top:26px;padding-top:14px;font-size:11.5px;line-height:1.8}.LhdpGW_footnote b{color:var(--dsw-alias-label-secondary,#9a9aa6);font-weight:500}.LhdpGW_footnoteList{margin:4px 0 0;padding-left:18px}.LhdpGW_footnoteList li{margin:1px 0}.LhdpGW_tooltip{z-index:9999;pointer-events:none;background:var(--dsw-alias-bg-layer-3,#232329);border:1px solid var(--dsw-alias-border-l2,#2c2c34);box-shadow:var(--dsw-shadow-lv2,0 8px 24px #00000059);min-width:210px;max-width:300px;color:var(--dsw-alias-label-primary,#e8e8ec);border-radius:10px;padding:10px 12px;font-family:inherit;font-size:12px;line-height:1.5;position:fixed}.LhdpGW_tipTitle{margin-bottom:2px;font-size:12px;font-weight:650}.LhdpGW_tipSub{color:var(--dsw-alias-label-secondary,#9a9aa6);margin-bottom:8px;font-size:11.5px}.LhdpGW_tipRow{color:var(--dsw-alias-label-secondary,#9a9aa6);align-items:center;gap:6px;padding:2px 0;font-size:11px;display:flex}.LhdpGW_tipRow i{border-radius:2px;flex-shrink:0;width:7px;height:7px}.LhdpGW_tipName{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.LhdpGW_tipAmount{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-tertiary,#67676f);text-align:right;width:58px}.LhdpGW_tipRate{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary,#e8e8ec);font-weight:600}.LhdpGW_tipBar{background:var(--dsw-alias-fill-l1,#80808c2e);border-radius:2px;height:3px;margin:1px 0 5px;overflow:hidden}.LhdpGW_tipBar>i{border-radius:2px;height:100%;display:block}.LhdpGW_tipDivider{border-top:1px solid var(--dsw-alias-border-l1,#2c2c34);color:var(--dsw-alias-label-secondary,#9a9aa6);justify-content:space-between;gap:12px;margin-top:6px;padding-top:6px;font-size:11px;display:flex}";
|
|
168
|
+
const tagId$6 = "dsh-key-usage/shared.module.css";
|
|
169
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$6) + "]") === null) {
|
|
154
170
|
const tag = document.createElement("style");
|
|
155
171
|
tag.dataset.plugin = "dsh-key-usage";
|
|
156
|
-
tag.dataset.pluginCss = tagId$
|
|
157
|
-
tag.textContent = css$
|
|
172
|
+
tag.dataset.pluginCss = tagId$6;
|
|
173
|
+
tag.textContent = css$6;
|
|
158
174
|
document.head.appendChild(tag);
|
|
159
175
|
}
|
|
160
|
-
const cssRegistry$
|
|
161
|
-
if (!cssRegistry$
|
|
162
|
-
tagId: tagId$
|
|
163
|
-
css: css$
|
|
176
|
+
const cssRegistry$6 = globalThis.__DSH_PLUGIN_CSS__ ??= [];
|
|
177
|
+
if (!cssRegistry$6.some((e) => e.tagId === tagId$6)) cssRegistry$6.push({
|
|
178
|
+
tagId: tagId$6,
|
|
179
|
+
css: css$6
|
|
164
180
|
});
|
|
165
181
|
var shared_module_css_default = {
|
|
166
|
-
"
|
|
182
|
+
"statExtra": "LhdpGW_statExtra",
|
|
183
|
+
"groupLabel": "LhdpGW_groupLabel",
|
|
184
|
+
"statusDot": "LhdpGW_statusDot",
|
|
185
|
+
"statValue": "LhdpGW_statValue",
|
|
167
186
|
"sortable": "LhdpGW_sortable",
|
|
168
|
-
"
|
|
169
|
-
"
|
|
170
|
-
"
|
|
171
|
-
"
|
|
172
|
-
"
|
|
173
|
-
"
|
|
187
|
+
"cardGrid4": "LhdpGW_cardGrid4",
|
|
188
|
+
"empty": "LhdpGW_empty",
|
|
189
|
+
"tooltip": "LhdpGW_tooltip",
|
|
190
|
+
"ghost": "LhdpGW_ghost",
|
|
191
|
+
"tipRate": "LhdpGW_tipRate",
|
|
192
|
+
"statLabel": "LhdpGW_statLabel",
|
|
174
193
|
"footnote": "LhdpGW_footnote",
|
|
175
|
-
"
|
|
194
|
+
"disabled": "LhdpGW_disabled",
|
|
195
|
+
"titleActions": "LhdpGW_titleActions",
|
|
196
|
+
"cardGrid3": "LhdpGW_cardGrid3",
|
|
176
197
|
"tipRow": "LhdpGW_tipRow",
|
|
177
|
-
"
|
|
198
|
+
"section": "LhdpGW_section",
|
|
199
|
+
"refreshBtn": "LhdpGW_refreshBtn",
|
|
200
|
+
"active": "LhdpGW_active",
|
|
201
|
+
"card": "LhdpGW_card",
|
|
202
|
+
"title": "LhdpGW_title",
|
|
178
203
|
"tabBar": "LhdpGW_tabBar",
|
|
204
|
+
"cardGridAuto": "LhdpGW_cardGridAuto",
|
|
179
205
|
"blockLabel": "LhdpGW_blockLabel",
|
|
180
|
-
"
|
|
181
|
-
"total": "LhdpGW_total",
|
|
182
|
-
"statLabel": "LhdpGW_statLabel",
|
|
206
|
+
"sortActive": "LhdpGW_sortActive",
|
|
183
207
|
"sortArrow": "LhdpGW_sortArrow",
|
|
184
|
-
"
|
|
208
|
+
"clickable": "LhdpGW_clickable",
|
|
185
209
|
"mono": "LhdpGW_mono",
|
|
186
|
-
"
|
|
187
|
-
"refreshBtn": "LhdpGW_refreshBtn",
|
|
210
|
+
"warning": "LhdpGW_warning",
|
|
188
211
|
"legend": "LhdpGW_legend",
|
|
189
|
-
"
|
|
190
|
-
"
|
|
212
|
+
"tipSub": "LhdpGW_tipSub",
|
|
213
|
+
"tipName": "LhdpGW_tipName",
|
|
191
214
|
"dim": "LhdpGW_dim",
|
|
192
|
-
"section": "LhdpGW_section",
|
|
193
|
-
"cardGrid3": "LhdpGW_cardGrid3",
|
|
194
|
-
"tipTitle": "LhdpGW_tipTitle",
|
|
195
|
-
"statValue": "LhdpGW_statValue",
|
|
196
|
-
"titleActions": "LhdpGW_titleActions",
|
|
197
|
-
"title": "LhdpGW_title",
|
|
198
|
-
"active": "LhdpGW_active",
|
|
199
|
-
"disabled": "LhdpGW_disabled",
|
|
200
|
-
"groupLabel": "LhdpGW_groupLabel",
|
|
201
|
-
"chipRow": "LhdpGW_chipRow",
|
|
202
215
|
"chip": "LhdpGW_chip",
|
|
203
|
-
"
|
|
204
|
-
"
|
|
216
|
+
"tipBar": "LhdpGW_tipBar",
|
|
217
|
+
"chipRow": "LhdpGW_chipRow",
|
|
205
218
|
"table": "LhdpGW_table",
|
|
206
|
-
"
|
|
207
|
-
"
|
|
219
|
+
"footnoteList": "LhdpGW_footnoteList",
|
|
220
|
+
"tipTitle": "LhdpGW_tipTitle",
|
|
221
|
+
"total": "LhdpGW_total",
|
|
222
|
+
"tipAmount": "LhdpGW_tipAmount",
|
|
223
|
+
"tipDivider": "LhdpGW_tipDivider"
|
|
208
224
|
};
|
|
209
225
|
//#endregion
|
|
210
226
|
//#region src/client/tooltip.tsx
|
|
@@ -355,51 +371,51 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
355
371
|
}
|
|
356
372
|
//#endregion
|
|
357
373
|
//#region \0dsh-css:D:\EasyJob\.plugin\dsh-key-usage\src\client\styles\overview.module.css.mjs
|
|
358
|
-
const css$
|
|
359
|
-
const tagId$
|
|
360
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
374
|
+
const css$5 = ".J7Idbq_keyTable{border-collapse:collapse;width:100%}.J7Idbq_keyTable th{text-align:right;color:var(--dsw-alias-label-tertiary,#67676f);border-bottom:1px solid var(--dsw-alias-border-l1,#2c2c34);white-space:nowrap;padding:6px 10px;font-size:10.5px;font-weight:500}.J7Idbq_keyTable th:first-child{text-align:left;padding-left:0}.J7Idbq_keyTable td{text-align:right;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#9a9aa6);border-bottom:1px solid var(--dsw-alias-fill-l1,#80808c1f);white-space:nowrap;padding:10px;font-size:12.5px}.J7Idbq_keyTable td:first-child{text-align:left;padding-left:0}.J7Idbq_keyTable tr:last-child td{border-bottom:0}.J7Idbq_keyTable tbody tr.J7Idbq_clickable{cursor:pointer}.J7Idbq_keyTable tbody tr.J7Idbq_clickable:hover{background:var(--dsw-alias-interactive-bg-hover,#80808c12)}.J7Idbq_keyTable tr.J7Idbq_disabledRow{opacity:.55}.J7Idbq_cellKey{align-items:center;gap:10px;display:flex}.J7Idbq_keyDisplay{color:var(--dsw-alias-label-primary,#e8e8ec);font-size:13px;font-weight:600}.J7Idbq_keyEnv{color:var(--dsw-alias-label-tertiary,#67676f);margin-top:1px;font-family:ui-monospace,Consolas,monospace;font-size:10px}.J7Idbq_tag{border:1px solid var(--dsw-alias-border-l2,#2c2c34);color:var(--dsw-alias-label-secondary,#9a9aa6);background:var(--dsw-alias-bg-layer-2,#1d1d22);border-radius:5px;padding:1px 7px;font-size:10.5px;display:inline-block}.J7Idbq_tag.J7Idbq_free{color:var(--dsw-alias-state-success-primary,#3fb96f);border-color:#3fb96f59}.J7Idbq_tag.J7Idbq_sub{color:var(--dsw-alias-state-accent-primary,#5b8cff);border-color:#5b8cff59}.J7Idbq_tag.J7Idbq_off{color:var(--dsw-alias-label-tertiary,#67676f)}.J7Idbq_rowLink{color:var(--dsw-alias-label-tertiary,#67676f);font-size:11px}.J7Idbq_keyTable tbody tr.J7Idbq_clickable:hover .J7Idbq_rowLink{color:var(--dsw-alias-state-accent-primary,#5b8cff)}.J7Idbq_quotaCell{align-items:center;gap:8px;display:inline-flex}.J7Idbq_miniBar{background:var(--dsw-alias-fill-l2,#80808c2e);border-radius:3px;flex-shrink:0;width:64px;height:5px;overflow:hidden}.J7Idbq_miniBar>i{border-radius:3px;height:100%;display:block}.J7Idbq_quotaText{text-align:left;min-width:74px}.J7Idbq_quotaText.J7Idbq_warn{color:var(--dsw-alias-state-warning-primary,#e0a13c);font-weight:600}.J7Idbq_quotaText.J7Idbq_danger{color:var(--dsw-alias-state-danger-primary,#e05a5a);font-weight:600}.J7Idbq_quotaText.J7Idbq_dim{color:var(--dsw-alias-label-tertiary,#67676f)}.J7Idbq_heatmapCard{background:var(--dsw-alias-bg-layer-2,#1d1d22);border:1px solid var(--dsw-alias-border-l1,#2c2c34);border-radius:12px;margin-top:24px;padding:14px 16px}.J7Idbq_heatmapHead{flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:6px;margin-bottom:10px;display:flex}.J7Idbq_heatmapTitle{color:var(--dsw-alias-label-secondary,#9a9aa6);font-size:11.5px}.J7Idbq_heatmapScroll{padding-bottom:4px;overflow-x:auto}.J7Idbq_heatmapBody{gap:6px;width:max-content;display:flex}.J7Idbq_heatWeekdays{flex-direction:column;flex-shrink:0;gap:3px;display:flex}.J7Idbq_heatWeekdays span{color:var(--dsw-alias-label-tertiary,#67676f);text-align:right;width:13px;height:28px;font-size:9.5px;line-height:28px}.J7Idbq_heatGridCol{flex-direction:column;display:flex}.J7Idbq_heatGrid{gap:3px;display:flex}.J7Idbq_heatWeek{flex-direction:column;gap:3px;display:flex}.J7Idbq_heatCell{box-sizing:border-box;background:var(--dsw-alias-fill-l1,#80808c2e);cursor:default;border-radius:5px;flex-shrink:0;width:28px;height:28px}.J7Idbq_heatCell.J7Idbq_idle{background:#80808c38}.J7Idbq_heatCell.J7Idbq_empty{background:#80808c1a;box-shadow:inset 0 0 0 1px #80808c47}.J7Idbq_heatMonths{gap:3px;margin-top:6px;display:flex}.J7Idbq_heatMonths span{width:28px;color:var(--dsw-alias-label-tertiary,#67676f);text-align:left;white-space:nowrap;flex-shrink:0;font-size:10px}";
|
|
375
|
+
const tagId$5 = "dsh-key-usage/overview.module.css";
|
|
376
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$5) + "]") === null) {
|
|
361
377
|
const tag = document.createElement("style");
|
|
362
378
|
tag.dataset.plugin = "dsh-key-usage";
|
|
363
|
-
tag.dataset.pluginCss = tagId$
|
|
364
|
-
tag.textContent = css$
|
|
379
|
+
tag.dataset.pluginCss = tagId$5;
|
|
380
|
+
tag.textContent = css$5;
|
|
365
381
|
document.head.appendChild(tag);
|
|
366
382
|
}
|
|
367
|
-
const cssRegistry$
|
|
368
|
-
if (!cssRegistry$
|
|
369
|
-
tagId: tagId$
|
|
370
|
-
css: css$
|
|
383
|
+
const cssRegistry$5 = globalThis.__DSH_PLUGIN_CSS__ ??= [];
|
|
384
|
+
if (!cssRegistry$5.some((e) => e.tagId === tagId$5)) cssRegistry$5.push({
|
|
385
|
+
tagId: tagId$5,
|
|
386
|
+
css: css$5
|
|
371
387
|
});
|
|
372
388
|
var overview_module_css_default = {
|
|
373
|
-
"keyEnv": "J7Idbq_keyEnv",
|
|
374
389
|
"quotaText": "J7Idbq_quotaText",
|
|
375
|
-
"quotaCell": "J7Idbq_quotaCell",
|
|
376
|
-
"heatWeekdays": "J7Idbq_heatWeekdays",
|
|
377
|
-
"heatGrid": "J7Idbq_heatGrid",
|
|
378
|
-
"warn": "J7Idbq_warn",
|
|
379
|
-
"heatmapHead": "J7Idbq_heatmapHead",
|
|
380
|
-
"heatmapBody": "J7Idbq_heatmapBody",
|
|
381
|
-
"off": "J7Idbq_off",
|
|
382
|
-
"idle": "J7Idbq_idle",
|
|
383
390
|
"keyDisplay": "J7Idbq_keyDisplay",
|
|
384
|
-
"
|
|
391
|
+
"warn": "J7Idbq_warn",
|
|
392
|
+
"cellKey": "J7Idbq_cellKey",
|
|
393
|
+
"miniBar": "J7Idbq_miniBar",
|
|
394
|
+
"free": "J7Idbq_free",
|
|
385
395
|
"sub": "J7Idbq_sub",
|
|
386
|
-
"heatGridCol": "J7Idbq_heatGridCol",
|
|
387
|
-
"clickable": "J7Idbq_clickable",
|
|
388
|
-
"danger": "J7Idbq_danger",
|
|
389
396
|
"heatmapTitle": "J7Idbq_heatmapTitle",
|
|
390
|
-
"
|
|
391
|
-
"miniBar": "J7Idbq_miniBar",
|
|
392
|
-
"heatmapCard": "J7Idbq_heatmapCard",
|
|
393
|
-
"tag": "J7Idbq_tag",
|
|
397
|
+
"idle": "J7Idbq_idle",
|
|
394
398
|
"rowLink": "J7Idbq_rowLink",
|
|
395
|
-
"
|
|
396
|
-
"
|
|
399
|
+
"heatCell": "J7Idbq_heatCell",
|
|
400
|
+
"keyEnv": "J7Idbq_keyEnv",
|
|
401
|
+
"heatmapHead": "J7Idbq_heatmapHead",
|
|
397
402
|
"heatMonths": "J7Idbq_heatMonths",
|
|
403
|
+
"heatGridCol": "J7Idbq_heatGridCol",
|
|
398
404
|
"keyTable": "J7Idbq_keyTable",
|
|
399
|
-
"
|
|
400
|
-
"
|
|
405
|
+
"heatmapCard": "J7Idbq_heatmapCard",
|
|
406
|
+
"danger": "J7Idbq_danger",
|
|
407
|
+
"disabledRow": "J7Idbq_disabledRow",
|
|
408
|
+
"heatmapBody": "J7Idbq_heatmapBody",
|
|
409
|
+
"heatWeekdays": "J7Idbq_heatWeekdays",
|
|
410
|
+
"tag": "J7Idbq_tag",
|
|
401
411
|
"heatmapScroll": "J7Idbq_heatmapScroll",
|
|
402
|
-
"
|
|
412
|
+
"off": "J7Idbq_off",
|
|
413
|
+
"heatGrid": "J7Idbq_heatGrid",
|
|
414
|
+
"heatWeek": "J7Idbq_heatWeek",
|
|
415
|
+
"dim": "J7Idbq_dim",
|
|
416
|
+
"quotaCell": "J7Idbq_quotaCell",
|
|
417
|
+
"clickable": "J7Idbq_clickable",
|
|
418
|
+
"empty": "J7Idbq_empty"
|
|
403
419
|
};
|
|
404
420
|
//#endregion
|
|
405
421
|
//#region src/client/components/Heatmap.tsx
|
|
@@ -487,6 +503,12 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
487
503
|
monthLabels: labels
|
|
488
504
|
};
|
|
489
505
|
}, [days, heatmapByDate]);
|
|
506
|
+
const scrollRef = (0, react.useRef)(null);
|
|
507
|
+
(0, react.useEffect)(() => {
|
|
508
|
+
const node = scrollRef.current;
|
|
509
|
+
if (node === null) return;
|
|
510
|
+
node.scrollLeft = node.scrollWidth - node.clientWidth;
|
|
511
|
+
}, []);
|
|
490
512
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
491
513
|
className: overview_module_css_default.heatmapCard,
|
|
492
514
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -506,6 +528,7 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
506
528
|
})]
|
|
507
529
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
508
530
|
className: overview_module_css_default.heatmapScroll,
|
|
531
|
+
ref: scrollRef,
|
|
509
532
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
510
533
|
className: overview_module_css_default.heatmapBody,
|
|
511
534
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -703,45 +726,87 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
703
726
|
});
|
|
704
727
|
}
|
|
705
728
|
//#endregion
|
|
729
|
+
//#region src/client/export.ts
|
|
730
|
+
/**
|
|
731
|
+
* 导出工具:纯函数生成 CSV / JSON 文本 + 用 Blob 触发浏览器下载。
|
|
732
|
+
* 零依赖、零服务端往返,导出的永远是**客户端当前筛选后**的数据。
|
|
733
|
+
*
|
|
734
|
+
* 两条口径约束:
|
|
735
|
+
* - CSV 必须带 UTF-8 BOM:Excel 默认按本地代码页解码,没有 BOM 中文会变乱码;
|
|
736
|
+
* - JSON 不能带 BOM:`JSON.parse` 遇到 BOM 会直接抛错。
|
|
737
|
+
*/
|
|
738
|
+
const UTF8_BOM = "";
|
|
739
|
+
const CSV_MIME = "text/csv;charset=utf-8";
|
|
740
|
+
const JSON_MIME = "application/json;charset=utf-8";
|
|
741
|
+
/** 表头 + 数据行 → CSV 文本(转义见 `csvCell`,换行用 RFC 4180 的 CRLF)。 */
|
|
742
|
+
function toCsv(rows, columns) {
|
|
743
|
+
const lines = [columns.map((column) => csvCell(column.label)).join(",")];
|
|
744
|
+
for (const row of rows) lines.push(columns.map((column) => csvCell(column.value(row))).join(","));
|
|
745
|
+
return UTF8_BOM + lines.join("\r\n") + "\r\n";
|
|
746
|
+
}
|
|
747
|
+
function toJson(value) {
|
|
748
|
+
return JSON.stringify(value, null, 2) + "\n";
|
|
749
|
+
}
|
|
750
|
+
/** 下载文件名:`dsh-usage-<主题>-<当天>.<扩展名>`,主题用英文短名避免跨端中文文件名乱码。 */
|
|
751
|
+
function exportFileName(topic, extension) {
|
|
752
|
+
return "dsh-usage-" + topic + "-" + isoDate(/* @__PURE__ */ new Date()) + "." + extension;
|
|
753
|
+
}
|
|
754
|
+
function downloadFile(name, text, mime) {
|
|
755
|
+
const objectUrl = URL.createObjectURL(new Blob([text], { type: mime }));
|
|
756
|
+
const anchor = document.createElement("a");
|
|
757
|
+
anchor.href = objectUrl;
|
|
758
|
+
anchor.download = name;
|
|
759
|
+
document.body.appendChild(anchor);
|
|
760
|
+
anchor.click();
|
|
761
|
+
anchor.remove();
|
|
762
|
+
window.setTimeout(() => URL.revokeObjectURL(objectUrl), 0);
|
|
763
|
+
}
|
|
764
|
+
/** 逗号、引号、换行都必须包引号并把内部引号翻倍,否则标题里的逗号会撑破列。 */
|
|
765
|
+
function csvCell(value) {
|
|
766
|
+
if (value === null) return "";
|
|
767
|
+
const text = String(value);
|
|
768
|
+
return /[",\r\n]/.test(text) ? "\"" + text.replace(/"/g, "\"\"") + "\"" : text;
|
|
769
|
+
}
|
|
770
|
+
//#endregion
|
|
706
771
|
//#region \0dsh-css:D:\EasyJob\.plugin\dsh-key-usage\src\client\styles\detail.module.css.mjs
|
|
707
|
-
const css$
|
|
708
|
-
const tagId$
|
|
709
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
772
|
+
const css$4 = ".V9QVRa_rangeBar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-bottom:12px;display:flex}.V9QVRa_rangeLeft{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.V9QVRa_rangeTabs{border:1px solid var(--dsw-alias-border-l2,#2c2c34);border-radius:9px;gap:2px;padding:2px;display:inline-flex}.V9QVRa_rangeTabs button{color:var(--dsw-alias-label-secondary,#9a9aa6);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:7px;padding:4px 13px;font-size:12px}.V9QVRa_rangeTabs button.V9QVRa_active{background:var(--dsw-alias-interactive-bg-active,#5b8cff24);color:var(--dsw-alias-state-accent-primary,#5b8cff);font-weight:600}.V9QVRa_rangeInput{border:1px solid var(--dsw-alias-border-l2,#2c2c34);color:var(--dsw-alias-label-primary,#e8e8ec);font:inherit;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;background:0 0;border-radius:7px;padding:3px 8px;font-size:12px}@media (prefers-color-scheme:dark){.V9QVRa_rangeInput{--lightningcss-light: ;--lightningcss-dark:initial}}.V9QVRa_rangeCaption{color:var(--dsw-alias-label-tertiary,#67676f);margin-left:auto;font-size:11.5px}.V9QVRa_shareBar{border-radius:6px;gap:2px;height:12px;margin:8px 0 2px;display:flex;overflow:hidden}.V9QVRa_shareBar>i{cursor:default;height:100%;display:block}.V9QVRa_shareBar>i:hover{filter:brightness(1.15)}.V9QVRa_chartTitle{flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:10px;display:flex}.V9QVRa_bars{align-items:flex-end;gap:3px;height:110px;margin-top:8px;display:flex}.V9QVRa_barsDense{gap:2px}.V9QVRa_barColumn{cursor:default;flex-direction:column;flex:1;justify-content:flex-end;gap:1px;height:100%;display:flex}.V9QVRa_barColumn:hover{filter:brightness(1.2)}.V9QVRa_segCache{background:var(--dsw-alias-state-accent-tertiary,#5b8cff47);border-radius:2px 2px 0 0}.V9QVRa_segIn{background:var(--dsw-alias-state-accent-primary,#5b8cff)}.V9QVRa_segOut{background:var(--dsw-alias-state-business-primary,#9a7bff);border-radius:0 0 2px 2px}.V9QVRa_barLabels{margin-top:6px;display:flex}.V9QVRa_barLabels span{text-align:center;color:var(--dsw-alias-label-tertiary,#67676f);white-space:nowrap;flex:1;font-size:9.5px}.V9QVRa_subRowCell{background:var(--dsw-alias-fill-l1,#80808c0d);text-align:left!important;padding:2px 10px 10px 30px!important}.V9QVRa_subHead,.V9QVRa_subRow{grid-template-columns:minmax(150px,1.5fr) minmax(70px,1fr) minmax(70px,1fr) minmax(70px,1fr) minmax(80px,1.1fr) minmax(70px,1fr);align-items:center;gap:8px;display:grid}.V9QVRa_subHead{color:var(--dsw-alias-label-tertiary,#67676f);border-bottom:1px solid var(--dsw-alias-fill-l2,#80808c33);padding:4px 0;font-size:10px}.V9QVRa_subRow{border-bottom:1px dashed var(--dsw-alias-fill-l1,#80808c24);padding:5px 0;font-size:11.5px}.V9QVRa_subRow:last-child{border-bottom:0}.V9QVRa_subName{flex-direction:column;gap:1px;min-width:0;display:flex}.V9QVRa_subModel{color:var(--dsw-alias-label-primary,#e8e8ec);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Consolas,monospace;font-size:11px;overflow:hidden}.V9QVRa_subChannel{color:var(--dsw-alias-label-tertiary,#67676f);text-overflow:ellipsis;white-space:nowrap;font-family:inherit;font-size:10px;overflow:hidden}.V9QVRa_subNumber{text-align:right;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#9a9aa6)}.V9QVRa_toggleArrow{width:13px;color:var(--dsw-alias-label-tertiary,#67676f);font-size:9px;display:inline-block}";
|
|
773
|
+
const tagId$4 = "dsh-key-usage/detail.module.css";
|
|
774
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
|
|
710
775
|
const tag = document.createElement("style");
|
|
711
776
|
tag.dataset.plugin = "dsh-key-usage";
|
|
712
|
-
tag.dataset.pluginCss = tagId$
|
|
713
|
-
tag.textContent = css$
|
|
777
|
+
tag.dataset.pluginCss = tagId$4;
|
|
778
|
+
tag.textContent = css$4;
|
|
714
779
|
document.head.appendChild(tag);
|
|
715
780
|
}
|
|
716
|
-
const cssRegistry$
|
|
717
|
-
if (!cssRegistry$
|
|
718
|
-
tagId: tagId$
|
|
719
|
-
css: css$
|
|
781
|
+
const cssRegistry$4 = globalThis.__DSH_PLUGIN_CSS__ ??= [];
|
|
782
|
+
if (!cssRegistry$4.some((e) => e.tagId === tagId$4)) cssRegistry$4.push({
|
|
783
|
+
tagId: tagId$4,
|
|
784
|
+
css: css$4
|
|
720
785
|
});
|
|
721
786
|
var detail_module_css_default = {
|
|
722
|
-
"
|
|
723
|
-
"shareBar": "V9QVRa_shareBar",
|
|
724
|
-
"chartTitle": "V9QVRa_chartTitle",
|
|
725
|
-
"bars": "V9QVRa_bars",
|
|
726
|
-
"barsDense": "V9QVRa_barsDense",
|
|
727
|
-
"subRowCell": "V9QVRa_subRowCell",
|
|
787
|
+
"toggleArrow": "V9QVRa_toggleArrow",
|
|
728
788
|
"barColumn": "V9QVRa_barColumn",
|
|
729
|
-
"
|
|
730
|
-
"
|
|
731
|
-
"rangeCaption": "V9QVRa_rangeCaption",
|
|
732
|
-
"barLabels": "V9QVRa_barLabels",
|
|
789
|
+
"barsDense": "V9QVRa_barsDense",
|
|
790
|
+
"shareBar": "V9QVRa_shareBar",
|
|
733
791
|
"subModel": "V9QVRa_subModel",
|
|
734
|
-
"
|
|
735
|
-
"
|
|
792
|
+
"rangeTabs": "V9QVRa_rangeTabs",
|
|
793
|
+
"rangeCaption": "V9QVRa_rangeCaption",
|
|
794
|
+
"subHead": "V9QVRa_subHead",
|
|
795
|
+
"subChannel": "V9QVRa_subChannel",
|
|
796
|
+
"subNumber": "V9QVRa_subNumber",
|
|
797
|
+
"rangeInput": "V9QVRa_rangeInput",
|
|
798
|
+
"chartTitle": "V9QVRa_chartTitle",
|
|
736
799
|
"segIn": "V9QVRa_segIn",
|
|
800
|
+
"segOut": "V9QVRa_segOut",
|
|
737
801
|
"subRow": "V9QVRa_subRow",
|
|
738
|
-
"
|
|
802
|
+
"bars": "V9QVRa_bars",
|
|
803
|
+
"segCache": "V9QVRa_segCache",
|
|
804
|
+
"subRowCell": "V9QVRa_subRowCell",
|
|
805
|
+
"rangeLeft": "V9QVRa_rangeLeft",
|
|
739
806
|
"active": "V9QVRa_active",
|
|
740
|
-
"rangeTabs": "V9QVRa_rangeTabs",
|
|
741
807
|
"rangeBar": "V9QVRa_rangeBar",
|
|
742
|
-
"
|
|
743
|
-
"
|
|
744
|
-
"toggleArrow": "V9QVRa_toggleArrow"
|
|
808
|
+
"subName": "V9QVRa_subName",
|
|
809
|
+
"barLabels": "V9QVRa_barLabels"
|
|
745
810
|
};
|
|
746
811
|
//#endregion
|
|
747
812
|
//#region src/client/components/ShareBar.tsx
|
|
@@ -878,11 +943,31 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
878
943
|
})] });
|
|
879
944
|
}
|
|
880
945
|
//#endregion
|
|
946
|
+
//#region src/client/rows.ts
|
|
947
|
+
function dropEmptyRows(rows, tokensOf) {
|
|
948
|
+
const kept = rows.filter((row) => tokensOf(row) > 0);
|
|
949
|
+
return {
|
|
950
|
+
rows: kept,
|
|
951
|
+
hidden: rows.length - kept.length
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
/** 列表底部的一行小字:说明隐藏了什么、为什么。 */
|
|
955
|
+
function hiddenEmptyRowsText(hidden) {
|
|
956
|
+
return "已隐藏 " + String(hidden) + " 行无 Token 记录(调用失败或未产生用量;调用次数仍计入合计)";
|
|
957
|
+
}
|
|
958
|
+
//#endregion
|
|
881
959
|
//#region src/client/components/DailyDetailTable.tsx
|
|
882
960
|
/**
|
|
883
961
|
* 每日细目:以「天」为折叠单位,展开行用原型子表格展示模型维度
|
|
884
962
|
* (模型 + 调用 / 输入 / 输出 / 缓存读 / 命中率,不占主表列)。
|
|
885
963
|
*/
|
|
964
|
+
/** 输入/输出为 0 时不再显示 `0`(全缓存命中或纯输出模型的 0 是噪音),改显示淡化的 `—`。 */
|
|
965
|
+
function tokenCellOf(value) {
|
|
966
|
+
return value === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
967
|
+
className: shared_module_css_default.dim,
|
|
968
|
+
children: "—"
|
|
969
|
+
}) : fmtTokens(value);
|
|
970
|
+
}
|
|
886
971
|
function hitRateOf$1(cacheReadTokens, inputTokens, outputTokens) {
|
|
887
972
|
const total = inputTokens + outputTokens + cacheReadTokens;
|
|
888
973
|
if (total <= 0) return "—";
|
|
@@ -941,7 +1026,9 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
941
1026
|
}
|
|
942
1027
|
}
|
|
943
1028
|
const comparer = COMPARERS[sort.key] ?? COMPARERS.period;
|
|
944
|
-
const
|
|
1029
|
+
const nonEmpty = [...aggregates.values()].filter((entry) => entry.totalTokens > 0);
|
|
1030
|
+
const hiddenEmpty = aggregates.size - nonEmpty.length;
|
|
1031
|
+
const ordered = nonEmpty.sort((first, second) => {
|
|
945
1032
|
const compared = comparer(first, second);
|
|
946
1033
|
return sort.dir === "asc" ? compared : -compared;
|
|
947
1034
|
});
|
|
@@ -971,116 +1058,143 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
971
1058
|
className: shared_module_css_default.empty,
|
|
972
1059
|
children: "所选范围内无用量记录"
|
|
973
1060
|
})] });
|
|
974
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
className: detail_module_css_default.toggleArrow,
|
|
997
|
-
children: isOpen ? "▼" : "▶"
|
|
998
|
-
}), period.period] }),
|
|
999
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(period.totalTokens) }),
|
|
1000
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtCalls(period.calls) }),
|
|
1001
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(period.inputTokens) }),
|
|
1002
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(period.outputTokens) }),
|
|
1003
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(period.cacheReadTokens) }),
|
|
1004
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: hitRateOf$1(period.cacheReadTokens, period.inputTokens, period.outputTokens) })
|
|
1005
|
-
]
|
|
1006
|
-
}), isOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", {
|
|
1007
|
-
className: detail_module_css_default.subRowCell,
|
|
1008
|
-
colSpan: 7,
|
|
1009
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1010
|
-
className: detail_module_css_default.subHead,
|
|
1011
|
-
children: [
|
|
1012
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {}),
|
|
1013
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1014
|
-
className: detail_module_css_default.subNumber,
|
|
1015
|
-
children: "调用"
|
|
1016
|
-
}),
|
|
1017
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1018
|
-
className: detail_module_css_default.subNumber,
|
|
1019
|
-
children: "输入"
|
|
1020
|
-
}),
|
|
1021
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1022
|
-
className: detail_module_css_default.subNumber,
|
|
1023
|
-
children: "输出"
|
|
1024
|
-
}),
|
|
1025
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1026
|
-
className: detail_module_css_default.subNumber,
|
|
1027
|
-
children: "缓存读"
|
|
1028
|
-
}),
|
|
1029
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1030
|
-
className: detail_module_css_default.subNumber,
|
|
1031
|
-
children: "命中率"
|
|
1032
|
-
})
|
|
1033
|
-
]
|
|
1034
|
-
}), modelsInPeriod.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1035
|
-
className: detail_module_css_default.subRow,
|
|
1061
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
1062
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1063
|
+
className: shared_module_css_default.blockLabel,
|
|
1064
|
+
children: "每日细目"
|
|
1065
|
+
}),
|
|
1066
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
1067
|
+
className: shared_module_css_default.table,
|
|
1068
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
1069
|
+
header("period", "日期"),
|
|
1070
|
+
header("total", "总计"),
|
|
1071
|
+
header("calls", "调用"),
|
|
1072
|
+
header("input", "输入"),
|
|
1073
|
+
header("output", "输出"),
|
|
1074
|
+
header("cacheRead", "缓存读"),
|
|
1075
|
+
header("hit", "命中率")
|
|
1076
|
+
] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tbody", { children: [ordered.map((period) => {
|
|
1077
|
+
const modelsInPeriod = modelRows.filter((row) => row.period === period.period).filter((row) => row.totalTokens > 0).sort((first, second) => second.totalTokens - first.totalTokens);
|
|
1078
|
+
const expandable = modelsInPeriod.length > 1;
|
|
1079
|
+
const isOpen = expandable && expandedPeriods[period.period] === true;
|
|
1080
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", {
|
|
1081
|
+
className: expandable ? shared_module_css_default.clickable : "",
|
|
1082
|
+
onClick: expandable ? () => onTogglePeriod(period.period) : void 0,
|
|
1036
1083
|
children: [
|
|
1037
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1038
|
-
className: detail_module_css_default.
|
|
1039
|
-
children:
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
})
|
|
1047
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1048
|
-
className: detail_module_css_default.subNumber,
|
|
1049
|
-
children: fmtCalls(row.calls)
|
|
1050
|
-
}),
|
|
1051
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1052
|
-
className: detail_module_css_default.subNumber,
|
|
1053
|
-
children: fmtTokens(row.inputTokens)
|
|
1054
|
-
}),
|
|
1055
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1056
|
-
className: detail_module_css_default.subNumber,
|
|
1057
|
-
children: fmtTokens(row.outputTokens)
|
|
1058
|
-
}),
|
|
1059
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1060
|
-
className: detail_module_css_default.subNumber,
|
|
1061
|
-
children: fmtTokens(row.cacheReadTokens)
|
|
1062
|
-
}),
|
|
1063
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1064
|
-
className: detail_module_css_default.subNumber,
|
|
1065
|
-
children: hitRateOf$1(row.cacheReadTokens, row.inputTokens, row.outputTokens)
|
|
1066
|
-
})
|
|
1084
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [expandable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1085
|
+
className: detail_module_css_default.toggleArrow,
|
|
1086
|
+
children: isOpen ? "▼" : "▶"
|
|
1087
|
+
}), period.period] }),
|
|
1088
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(period.totalTokens) }),
|
|
1089
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtCalls(period.calls) }),
|
|
1090
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: tokenCellOf(period.inputTokens) }),
|
|
1091
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: tokenCellOf(period.outputTokens) }),
|
|
1092
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(period.cacheReadTokens) }),
|
|
1093
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: hitRateOf$1(period.cacheReadTokens, period.inputTokens, period.outputTokens) })
|
|
1067
1094
|
]
|
|
1068
|
-
},
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1095
|
+
}), isOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", {
|
|
1096
|
+
className: detail_module_css_default.subRowCell,
|
|
1097
|
+
colSpan: 7,
|
|
1098
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1099
|
+
className: detail_module_css_default.subHead,
|
|
1100
|
+
children: [
|
|
1101
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {}),
|
|
1102
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1103
|
+
className: detail_module_css_default.subNumber,
|
|
1104
|
+
children: "调用"
|
|
1105
|
+
}),
|
|
1106
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1107
|
+
className: detail_module_css_default.subNumber,
|
|
1108
|
+
children: "输入"
|
|
1109
|
+
}),
|
|
1110
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1111
|
+
className: detail_module_css_default.subNumber,
|
|
1112
|
+
children: "输出"
|
|
1113
|
+
}),
|
|
1114
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1115
|
+
className: detail_module_css_default.subNumber,
|
|
1116
|
+
children: "缓存读"
|
|
1117
|
+
}),
|
|
1118
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1119
|
+
className: detail_module_css_default.subNumber,
|
|
1120
|
+
children: "命中率"
|
|
1121
|
+
})
|
|
1122
|
+
]
|
|
1123
|
+
}), modelsInPeriod.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1124
|
+
className: detail_module_css_default.subRow,
|
|
1125
|
+
children: [
|
|
1126
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1127
|
+
className: detail_module_css_default.subName,
|
|
1128
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1129
|
+
className: detail_module_css_default.subModel,
|
|
1130
|
+
children: row.model
|
|
1131
|
+
}), channelFilter === "ALL" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1132
|
+
className: detail_module_css_default.subChannel,
|
|
1133
|
+
children: displayNameOf(channels, row.provider)
|
|
1134
|
+
})]
|
|
1135
|
+
}),
|
|
1136
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1137
|
+
className: detail_module_css_default.subNumber,
|
|
1138
|
+
children: fmtCalls(row.calls)
|
|
1139
|
+
}),
|
|
1140
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1141
|
+
className: detail_module_css_default.subNumber,
|
|
1142
|
+
children: tokenCellOf(row.inputTokens)
|
|
1143
|
+
}),
|
|
1144
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1145
|
+
className: detail_module_css_default.subNumber,
|
|
1146
|
+
children: tokenCellOf(row.outputTokens)
|
|
1147
|
+
}),
|
|
1148
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1149
|
+
className: detail_module_css_default.subNumber,
|
|
1150
|
+
children: fmtTokens(row.cacheReadTokens)
|
|
1151
|
+
}),
|
|
1152
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1153
|
+
className: detail_module_css_default.subNumber,
|
|
1154
|
+
children: hitRateOf$1(row.cacheReadTokens, row.inputTokens, row.outputTokens)
|
|
1155
|
+
})
|
|
1156
|
+
]
|
|
1157
|
+
}, row.period + "|" + row.model + "|" + row.provider))]
|
|
1158
|
+
}) })] }, period.period);
|
|
1159
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", {
|
|
1160
|
+
className: shared_module_css_default.total,
|
|
1161
|
+
children: [
|
|
1162
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: "合计" }),
|
|
1163
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(totalTokens) }),
|
|
1164
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtCalls(totals?.calls ?? 0) }),
|
|
1165
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(totals?.inputTokens ?? 0) }),
|
|
1166
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(totals?.outputTokens ?? 0) }),
|
|
1167
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(totals?.cacheReadTokens ?? 0) }),
|
|
1168
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: hitRateOf$1(totals?.cacheReadTokens ?? 0, totals?.inputTokens ?? 0, totals?.outputTokens ?? 0) })
|
|
1169
|
+
]
|
|
1170
|
+
})] })]
|
|
1171
|
+
}),
|
|
1172
|
+
hiddenEmpty > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1173
|
+
className: shared_module_css_default.statExtra,
|
|
1174
|
+
children: hiddenEmptyRowsText(hiddenEmpty)
|
|
1175
|
+
})
|
|
1176
|
+
] });
|
|
1177
|
+
}
|
|
1178
|
+
//#endregion
|
|
1179
|
+
//#region \0dsh-css:D:\EasyJob\.plugin\dsh-key-usage\src\client\styles\export.module.css.mjs
|
|
1180
|
+
const css$3 = ".m5lYYW_exportGroup{align-items:center;gap:6px;margin-left:auto;display:inline-flex}.m5lYYW_exportBtn{border:1px solid var(--dsw-alias-border-l2,#2c2c34);color:var(--dsw-alias-label-secondary,#9a9aa6);font:inherit;cursor:pointer;white-space:nowrap;background:0 0;border-radius:999px;padding:4px 11px;font-size:11.5px}.m5lYYW_exportBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#80808c1a);color:var(--dsw-alias-label-primary,#e8e8ec)}.m5lYYW_exportBtn:disabled{opacity:.5;cursor:default}";
|
|
1181
|
+
const tagId$3 = "dsh-key-usage/export.module.css";
|
|
1182
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
|
|
1183
|
+
const tag = document.createElement("style");
|
|
1184
|
+
tag.dataset.plugin = "dsh-key-usage";
|
|
1185
|
+
tag.dataset.pluginCss = tagId$3;
|
|
1186
|
+
tag.textContent = css$3;
|
|
1187
|
+
document.head.appendChild(tag);
|
|
1083
1188
|
}
|
|
1189
|
+
const cssRegistry$3 = globalThis.__DSH_PLUGIN_CSS__ ??= [];
|
|
1190
|
+
if (!cssRegistry$3.some((e) => e.tagId === tagId$3)) cssRegistry$3.push({
|
|
1191
|
+
tagId: tagId$3,
|
|
1192
|
+
css: css$3
|
|
1193
|
+
});
|
|
1194
|
+
var export_module_css_default = {
|
|
1195
|
+
"exportGroup": "m5lYYW_exportGroup",
|
|
1196
|
+
"exportBtn": "m5lYYW_exportBtn"
|
|
1197
|
+
};
|
|
1084
1198
|
//#endregion
|
|
1085
1199
|
//#region src/client/tabs/DetailTab.tsx
|
|
1086
1200
|
const RANGE_TABS = [
|
|
@@ -1092,6 +1206,49 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1092
1206
|
];
|
|
1093
1207
|
/** 图表最多铺开的列数:超过这个跨度的范围按天全画会挤成一团。 */
|
|
1094
1208
|
const MAX_AXIS_DAYS = 366;
|
|
1209
|
+
/** 导出列:命中率与成本给数值(不带单位),方便在表格里继续算。 */
|
|
1210
|
+
const EXPORT_COLUMNS$3 = [
|
|
1211
|
+
{
|
|
1212
|
+
key: "date",
|
|
1213
|
+
label: "日期",
|
|
1214
|
+
value: (row) => row.date
|
|
1215
|
+
},
|
|
1216
|
+
{
|
|
1217
|
+
key: "calls",
|
|
1218
|
+
label: "调用",
|
|
1219
|
+
value: (row) => row.calls
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
key: "inputTokens",
|
|
1223
|
+
label: "输入",
|
|
1224
|
+
value: (row) => row.inputTokens
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
key: "outputTokens",
|
|
1228
|
+
label: "输出",
|
|
1229
|
+
value: (row) => row.outputTokens
|
|
1230
|
+
},
|
|
1231
|
+
{
|
|
1232
|
+
key: "cacheReadTokens",
|
|
1233
|
+
label: "缓存读",
|
|
1234
|
+
value: (row) => row.cacheReadTokens
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
key: "cacheHitRate",
|
|
1238
|
+
label: "命中率(%)",
|
|
1239
|
+
value: (row) => hitRateValueOf(row)
|
|
1240
|
+
},
|
|
1241
|
+
{
|
|
1242
|
+
key: "cost",
|
|
1243
|
+
label: "成本(元)",
|
|
1244
|
+
value: (row) => row.cost
|
|
1245
|
+
}
|
|
1246
|
+
];
|
|
1247
|
+
/** 命中率数值(导出用;界面上的带 % 文本由 `hitRateOf` 生成)。 */
|
|
1248
|
+
function hitRateValueOf(row) {
|
|
1249
|
+
const total = row.inputTokens + row.outputTokens + row.cacheReadTokens;
|
|
1250
|
+
return total > 0 ? Math.round(row.cacheReadTokens / total * 1e3) / 10 : null;
|
|
1251
|
+
}
|
|
1095
1252
|
function hitRateOf(cacheReadTokens, inputTokens, outputTokens) {
|
|
1096
1253
|
const total = inputTokens + outputTokens + cacheReadTokens;
|
|
1097
1254
|
return total > 0 ? (cacheReadTokens / total * 100).toFixed(1) + "%" : "—";
|
|
@@ -1130,6 +1287,11 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1130
1287
|
const costCell = costCellText(singleChannel, safeTotals.cost);
|
|
1131
1288
|
const singleDay = bounds.from === bounds.to;
|
|
1132
1289
|
const rangeDays = Math.min(daysBetween(bounds.from, bounds.to), MAX_AXIS_DAYS);
|
|
1290
|
+
/** 导出当前范围与通道筛选后的每日明细(按日期升序,不跟随表格排序)。 */
|
|
1291
|
+
const exportDaily = (format) => {
|
|
1292
|
+
if (format === "csv") downloadFile(exportFileName("daily", "csv"), toCsv(usage.days, EXPORT_COLUMNS$3), CSV_MIME);
|
|
1293
|
+
else downloadFile(exportFileName("daily", "json"), toJson(usage.days), JSON_MIME);
|
|
1294
|
+
};
|
|
1133
1295
|
const activeProviders = /* @__PURE__ */ new Set();
|
|
1134
1296
|
for (const day of usage.days) for (const model of day.models) if (model.inputTokens + model.outputTokens + model.cacheReadTokens > 0) activeProviders.add(model.provider);
|
|
1135
1297
|
const hourlyRows = [];
|
|
@@ -1228,9 +1390,9 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1228
1390
|
const chartRows = useHourAxis ? hourlyRows : dailyRows;
|
|
1229
1391
|
const chartTitle = useHourAxis ? "按小时 Tokens(" + bounds.from + ")" : "按天 Tokens(" + rangeDays + " 天)";
|
|
1230
1392
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
1231
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1393
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1232
1394
|
className: detail_module_css_default.rangeBar,
|
|
1233
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1395
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1234
1396
|
className: detail_module_css_default.rangeLeft,
|
|
1235
1397
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1236
1398
|
className: detail_module_css_default.rangeTabs,
|
|
@@ -1257,7 +1419,20 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1257
1419
|
onChange: (event) => props.onCustomTo(event.target.value)
|
|
1258
1420
|
})
|
|
1259
1421
|
] })]
|
|
1260
|
-
})
|
|
1422
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1423
|
+
className: export_module_css_default.exportGroup,
|
|
1424
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1425
|
+
className: export_module_css_default.exportBtn,
|
|
1426
|
+
disabled: usage.days.length === 0,
|
|
1427
|
+
onClick: () => exportDaily("csv"),
|
|
1428
|
+
children: "导出 CSV"
|
|
1429
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1430
|
+
className: export_module_css_default.exportBtn,
|
|
1431
|
+
disabled: usage.days.length === 0,
|
|
1432
|
+
onClick: () => exportDaily("json"),
|
|
1433
|
+
children: "导出 JSON"
|
|
1434
|
+
})]
|
|
1435
|
+
})]
|
|
1261
1436
|
}),
|
|
1262
1437
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1263
1438
|
className: shared_module_css_default.cardGrid3,
|
|
@@ -1374,7 +1549,7 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1374
1549
|
}
|
|
1375
1550
|
//#endregion
|
|
1376
1551
|
//#region \0dsh-css:D:\EasyJob\.plugin\dsh-key-usage\src\client\styles\lists.module.css.mjs
|
|
1377
|
-
const css$2 = ".QpFKma_filterBar{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:14px;display:flex}.QpFKma_searchInput{border:1px solid var(--dsw-alias-border-l2,#2c2c34);min-width:180px;color:var(--dsw-alias-label-primary,#e8e8ec);font:inherit;background:0 0;border-radius:9px;flex:1;padding:6px 12px;font-size:12.5px}.QpFKma_searchInput::placeholder{color:var(--dsw-alias-label-tertiary,#67676f)}.QpFKma_filterSelect{background:var(--dsw-alias-bg-layer-2,#1d1d22);border:1px solid var(--dsw-alias-border-l2,#2c2c34);color:var(--dsw-alias-label-secondary,#9a9aa6);font:inherit;cursor:pointer;border-radius:9px;padding:6px 10px;font-size:12px}.QpFKma_listTitle{align-items:center;gap:10px;min-width:0;display:flex}.QpFKma_listMain{color:var(--dsw-alias-label-primary,#e8e8ec);text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;font-weight:600;overflow:hidden}.QpFKma_listSub{color:var(--dsw-alias-label-tertiary,#67676f);text-overflow:ellipsis;white-space:nowrap;margin-top:1px;font-size:10.5px;overflow:hidden}.QpFKma_listBar{background:var(--dsw-alias-fill-l2,#80808c2e);vertical-align:middle;border-radius:3px;width:70px;height:5px;display:inline-block;overflow:hidden}.QpFKma_listBar>i{border-radius:3px;height:100%;display:block}.QpFKma_sessionDay td{background:var(--dsw-alias-bg-layer-2,#1d1d22);color:var(--dsw-alias-label-primary,#e8e8ec);border-bottom:1px solid var(--dsw-alias-border-l1,#2c2c34);font-size:12px;font-weight:600;text-align:left!important;padding:7px 10px!important}.QpFKma_dayCount{color:var(--dsw-alias-label-tertiary,#67676f);margin-left:8px;font-size:11px;font-weight:500}";
|
|
1552
|
+
const css$2 = ".QpFKma_filterBar{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:14px;display:flex}.QpFKma_searchInput{border:1px solid var(--dsw-alias-border-l2,#2c2c34);min-width:180px;color:var(--dsw-alias-label-primary,#e8e8ec);font:inherit;background:0 0;border-radius:9px;flex:1;padding:6px 12px;font-size:12.5px}.QpFKma_searchInput::placeholder{color:var(--dsw-alias-label-tertiary,#67676f)}.QpFKma_filterSelect{background:var(--dsw-alias-bg-layer-2,#1d1d22);border:1px solid var(--dsw-alias-border-l2,#2c2c34);color:var(--dsw-alias-label-secondary,#9a9aa6);font:inherit;cursor:pointer;border-radius:9px;padding:6px 10px;font-size:12px}.QpFKma_listTitle{align-items:center;gap:10px;min-width:0;display:flex}.QpFKma_listMain{color:var(--dsw-alias-label-primary,#e8e8ec);text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;font-weight:600;overflow:hidden}.QpFKma_listSub{color:var(--dsw-alias-label-tertiary,#67676f);text-overflow:ellipsis;white-space:nowrap;margin-top:1px;font-size:10.5px;overflow:hidden}.QpFKma_listBar{background:var(--dsw-alias-fill-l2,#80808c2e);vertical-align:middle;border-radius:3px;width:70px;height:5px;display:inline-block;overflow:hidden}.QpFKma_listBar>i{border-radius:3px;height:100%;display:block}.QpFKma_sessionDay td{background:var(--dsw-alias-bg-layer-2,#1d1d22);color:var(--dsw-alias-label-primary,#e8e8ec);border-bottom:1px solid var(--dsw-alias-border-l1,#2c2c34);font-size:12px;font-weight:600;text-align:left!important;padding:7px 10px!important}.QpFKma_dayCount{color:var(--dsw-alias-label-tertiary,#67676f);margin-left:8px;font-size:11px;font-weight:500}.QpFKma_rowArchived td{opacity:.5}.QpFKma_archivedTag{color:var(--dsw-alias-label-tertiary,#67676f);border:1px solid var(--dsw-alias-border-l2,#2c2c34);border-radius:999px;margin-left:6px;padding:0 6px;font-size:10px;font-weight:500}";
|
|
1378
1553
|
const tagId$2 = "dsh-key-usage/lists.module.css";
|
|
1379
1554
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
1380
1555
|
const tag = document.createElement("style");
|
|
@@ -1389,19 +1564,329 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1389
1564
|
css: css$2
|
|
1390
1565
|
});
|
|
1391
1566
|
var lists_module_css_default = {
|
|
1392
|
-
"listTitle": "QpFKma_listTitle",
|
|
1393
|
-
"listBar": "QpFKma_listBar",
|
|
1394
1567
|
"filterSelect": "QpFKma_filterSelect",
|
|
1395
1568
|
"dayCount": "QpFKma_dayCount",
|
|
1396
1569
|
"searchInput": "QpFKma_searchInput",
|
|
1397
|
-
"
|
|
1570
|
+
"archivedTag": "QpFKma_archivedTag",
|
|
1398
1571
|
"listMain": "QpFKma_listMain",
|
|
1572
|
+
"listTitle": "QpFKma_listTitle",
|
|
1573
|
+
"listBar": "QpFKma_listBar",
|
|
1399
1574
|
"sessionDay": "QpFKma_sessionDay",
|
|
1400
|
-
"
|
|
1575
|
+
"listSub": "QpFKma_listSub",
|
|
1576
|
+
"filterBar": "QpFKma_filterBar",
|
|
1577
|
+
"rowArchived": "QpFKma_rowArchived"
|
|
1401
1578
|
};
|
|
1402
1579
|
//#endregion
|
|
1580
|
+
//#region src/client/tabs/ModelsTab.tsx
|
|
1581
|
+
/** 按模型:`provider + model` 聚合的用量与估算成本(数据来自宿主会话事件折叠)。 */
|
|
1582
|
+
const MODELS_PATH = "/dsh-key-usage/models";
|
|
1583
|
+
/** 导出列:取原始数值(金额、命中率不带单位),方便直接进表格做计算。 */
|
|
1584
|
+
const EXPORT_COLUMNS$2 = [
|
|
1585
|
+
{
|
|
1586
|
+
key: "provider",
|
|
1587
|
+
label: "通道 id",
|
|
1588
|
+
value: (row) => row.provider
|
|
1589
|
+
},
|
|
1590
|
+
{
|
|
1591
|
+
key: "providerLabel",
|
|
1592
|
+
label: "通道",
|
|
1593
|
+
value: (row) => row.providerLabel
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
key: "model",
|
|
1597
|
+
label: "模型",
|
|
1598
|
+
value: (row) => row.model
|
|
1599
|
+
},
|
|
1600
|
+
{
|
|
1601
|
+
key: "calls",
|
|
1602
|
+
label: "调用",
|
|
1603
|
+
value: (row) => row.calls
|
|
1604
|
+
},
|
|
1605
|
+
{
|
|
1606
|
+
key: "tokens",
|
|
1607
|
+
label: "Tokens",
|
|
1608
|
+
value: (row) => row.tokens
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
key: "inputTokens",
|
|
1612
|
+
label: "输入",
|
|
1613
|
+
value: (row) => row.buckets.inputTokens
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
key: "outputTokens",
|
|
1617
|
+
label: "输出",
|
|
1618
|
+
value: (row) => row.buckets.outputTokens
|
|
1619
|
+
},
|
|
1620
|
+
{
|
|
1621
|
+
key: "cacheReadTokens",
|
|
1622
|
+
label: "缓存读",
|
|
1623
|
+
value: (row) => row.buckets.cacheReadTokens
|
|
1624
|
+
},
|
|
1625
|
+
{
|
|
1626
|
+
key: "cacheHitRate",
|
|
1627
|
+
label: "命中率(%)",
|
|
1628
|
+
value: (row) => row.cacheHitRate
|
|
1629
|
+
},
|
|
1630
|
+
{
|
|
1631
|
+
key: "cost",
|
|
1632
|
+
label: "成本(估算,元)",
|
|
1633
|
+
value: (row) => row.cost
|
|
1634
|
+
},
|
|
1635
|
+
{
|
|
1636
|
+
key: "costMode",
|
|
1637
|
+
label: "计费模式",
|
|
1638
|
+
value: (row) => row.costMode
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
key: "sessionCount",
|
|
1642
|
+
label: "会话数",
|
|
1643
|
+
value: (row) => row.sessionCount
|
|
1644
|
+
},
|
|
1645
|
+
{
|
|
1646
|
+
key: "lastTime",
|
|
1647
|
+
label: "最近活跃",
|
|
1648
|
+
value: (row) => fmtDateTime(row.lastTime)
|
|
1649
|
+
}
|
|
1650
|
+
];
|
|
1651
|
+
function ModelsTab(props) {
|
|
1652
|
+
const [query, setQuery] = (0, react.useState)("");
|
|
1653
|
+
const [channelFilter, setChannelFilter] = (0, react.useState)("ALL");
|
|
1654
|
+
const [sortKey, setSortKey] = (0, react.useState)("tokensDesc");
|
|
1655
|
+
const [nowMs] = (0, react.useState)(() => Date.now());
|
|
1656
|
+
const rows = props.data?.rows ?? [];
|
|
1657
|
+
const visible = (0, react.useMemo)(() => {
|
|
1658
|
+
const keyword = query.trim().toLowerCase();
|
|
1659
|
+
const filtered = dropEmptyRows(rows, (row) => row.tokens).rows.filter((row) => {
|
|
1660
|
+
const haystack = (row.model + " " + row.provider + " " + row.providerLabel).toLowerCase();
|
|
1661
|
+
const matchesQuery = keyword === "" || haystack.includes(keyword);
|
|
1662
|
+
const matchesChannel = channelFilter === "ALL" || row.provider === channelFilter;
|
|
1663
|
+
return matchesQuery && matchesChannel;
|
|
1664
|
+
});
|
|
1665
|
+
const sorters = {
|
|
1666
|
+
tokensDesc: (first, second) => second.tokens - first.tokens,
|
|
1667
|
+
callsDesc: (first, second) => second.calls - first.calls,
|
|
1668
|
+
costDesc: (first, second) => (second.cost ?? 0) - (first.cost ?? 0),
|
|
1669
|
+
recent: (first, second) => (second.lastTime ?? 0) - (first.lastTime ?? 0)
|
|
1670
|
+
};
|
|
1671
|
+
return [...filtered].sort(sorters[sortKey]);
|
|
1672
|
+
}, [
|
|
1673
|
+
rows,
|
|
1674
|
+
query,
|
|
1675
|
+
channelFilter,
|
|
1676
|
+
sortKey
|
|
1677
|
+
]);
|
|
1678
|
+
const totalTokens = rows.reduce((total, row) => total + row.tokens, 0);
|
|
1679
|
+
const hiddenEmpty = dropEmptyRows(rows, (row) => row.tokens).hidden;
|
|
1680
|
+
/** 导出当前筛选与排序后的行(JSON 取整行,CSV 按固定列映射)。 */
|
|
1681
|
+
const exportRows = (format) => {
|
|
1682
|
+
if (format === "csv") downloadFile(exportFileName("models", "csv"), toCsv(visible, EXPORT_COLUMNS$2), CSV_MIME);
|
|
1683
|
+
else downloadFile(exportFileName("models", "json"), toJson(visible), JSON_MIME);
|
|
1684
|
+
};
|
|
1685
|
+
if (props.data === null) {
|
|
1686
|
+
const reason = failureReasonOf(MODELS_PATH);
|
|
1687
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1688
|
+
className: shared_module_css_default.empty,
|
|
1689
|
+
children: reason === null ? "按模型数据读取中…" : "按模型数据不可用:" + reason
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
1693
|
+
props.data.coverage.status !== "complete" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1694
|
+
className: shared_module_css_default.warning,
|
|
1695
|
+
children: coverageText$1(props.data.coverage.status, props.data.coverage.skippedSessions)
|
|
1696
|
+
}),
|
|
1697
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1698
|
+
className: lists_module_css_default.filterBar,
|
|
1699
|
+
children: [
|
|
1700
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1701
|
+
className: lists_module_css_default.searchInput,
|
|
1702
|
+
placeholder: "搜索模型名、通道…",
|
|
1703
|
+
value: query,
|
|
1704
|
+
onChange: (event) => setQuery(event.target.value)
|
|
1705
|
+
}),
|
|
1706
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1707
|
+
className: lists_module_css_default.filterSelect,
|
|
1708
|
+
value: channelFilter,
|
|
1709
|
+
onChange: (event) => setChannelFilter(event.target.value),
|
|
1710
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1711
|
+
value: "ALL",
|
|
1712
|
+
children: "全部通道"
|
|
1713
|
+
}), props.channels.map((channel) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1714
|
+
value: channel.provider,
|
|
1715
|
+
children: channel.displayName
|
|
1716
|
+
}, channel.provider))]
|
|
1717
|
+
}),
|
|
1718
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1719
|
+
className: lists_module_css_default.filterSelect,
|
|
1720
|
+
value: sortKey,
|
|
1721
|
+
onChange: (event) => setSortKey(event.target.value),
|
|
1722
|
+
children: [
|
|
1723
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1724
|
+
value: "tokensDesc",
|
|
1725
|
+
children: "Tokens 最多"
|
|
1726
|
+
}),
|
|
1727
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1728
|
+
value: "callsDesc",
|
|
1729
|
+
children: "调用最多"
|
|
1730
|
+
}),
|
|
1731
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1732
|
+
value: "costDesc",
|
|
1733
|
+
children: "成本最高"
|
|
1734
|
+
}),
|
|
1735
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1736
|
+
value: "recent",
|
|
1737
|
+
children: "最近活跃"
|
|
1738
|
+
})
|
|
1739
|
+
]
|
|
1740
|
+
}),
|
|
1741
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1742
|
+
className: export_module_css_default.exportGroup,
|
|
1743
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1744
|
+
className: export_module_css_default.exportBtn,
|
|
1745
|
+
disabled: visible.length === 0,
|
|
1746
|
+
onClick: () => exportRows("csv"),
|
|
1747
|
+
children: "导出 CSV"
|
|
1748
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1749
|
+
className: export_module_css_default.exportBtn,
|
|
1750
|
+
disabled: visible.length === 0,
|
|
1751
|
+
onClick: () => exportRows("json"),
|
|
1752
|
+
children: "导出 JSON"
|
|
1753
|
+
})]
|
|
1754
|
+
})
|
|
1755
|
+
]
|
|
1756
|
+
}),
|
|
1757
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
1758
|
+
className: shared_module_css_default.table,
|
|
1759
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
1760
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "模型" }),
|
|
1761
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "调用" }),
|
|
1762
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Tokens" }),
|
|
1763
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "占比" }),
|
|
1764
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "命中率" }),
|
|
1765
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "成本" }),
|
|
1766
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "最近活跃" })
|
|
1767
|
+
] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tbody", { children: [visible.map((row) => {
|
|
1768
|
+
const percent = totalTokens > 0 ? Math.round(row.tokens / totalTokens * 100) : 0;
|
|
1769
|
+
const channel = channelOf(props.channels, row.provider);
|
|
1770
|
+
const costCell = costCellText(channel, row.cost ?? 0);
|
|
1771
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
1772
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1773
|
+
className: lists_module_css_default.listTitle,
|
|
1774
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1775
|
+
className: lists_module_css_default.listMain,
|
|
1776
|
+
children: row.model
|
|
1777
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1778
|
+
className: lists_module_css_default.listSub,
|
|
1779
|
+
children: [
|
|
1780
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1781
|
+
className: shared_module_css_default.statusDot,
|
|
1782
|
+
style: {
|
|
1783
|
+
background: channelColorOf(props.channels, row.provider),
|
|
1784
|
+
marginRight: 4
|
|
1785
|
+
}
|
|
1786
|
+
}),
|
|
1787
|
+
row.providerLabel,
|
|
1788
|
+
" · ",
|
|
1789
|
+
row.sessionCount,
|
|
1790
|
+
" 个会话"
|
|
1791
|
+
]
|
|
1792
|
+
})] })
|
|
1793
|
+
}) }),
|
|
1794
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtCalls(row.calls) }),
|
|
1795
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(row.tokens) }),
|
|
1796
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [
|
|
1797
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1798
|
+
className: lists_module_css_default.listBar,
|
|
1799
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", { style: {
|
|
1800
|
+
width: Math.max(2, percent) + "%",
|
|
1801
|
+
background: channelColorOf(props.channels, row.provider)
|
|
1802
|
+
} })
|
|
1803
|
+
}),
|
|
1804
|
+
" ",
|
|
1805
|
+
percent,
|
|
1806
|
+
"%"
|
|
1807
|
+
] }),
|
|
1808
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: row.cacheHitRate === null ? "—" : row.cacheHitRate.toFixed(1) + "%" }),
|
|
1809
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: row.cost === null || channel?.costMode !== "perToken" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1810
|
+
className: shared_module_css_default.dim,
|
|
1811
|
+
children: costCell.text
|
|
1812
|
+
}) : costCell.text }),
|
|
1813
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtRelative(row.lastTime, nowMs) })
|
|
1814
|
+
] }, row.provider + "|" + row.model);
|
|
1815
|
+
}), visible.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1816
|
+
colSpan: 7,
|
|
1817
|
+
className: shared_module_css_default.empty,
|
|
1818
|
+
children: "没有符合条件的模型"
|
|
1819
|
+
}) })] })]
|
|
1820
|
+
}),
|
|
1821
|
+
hiddenEmpty > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1822
|
+
className: shared_module_css_default.statExtra,
|
|
1823
|
+
children: hiddenEmptyRowsText(hiddenEmpty)
|
|
1824
|
+
}),
|
|
1825
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1826
|
+
className: shared_module_css_default.statExtra,
|
|
1827
|
+
children: [
|
|
1828
|
+
"模型用量按宿主会话事件折叠(每个结算样本计一次调用);成本按台账模型均价估算",
|
|
1829
|
+
props.data.priced ? "" : "(台账无成本数据,暂不可估)",
|
|
1830
|
+
";命中率 = 缓存读 ÷(输入 + 输出 + 缓存读)"
|
|
1831
|
+
]
|
|
1832
|
+
})
|
|
1833
|
+
] });
|
|
1834
|
+
}
|
|
1835
|
+
function coverageText$1(status, skipped) {
|
|
1836
|
+
if (status === "unavailable") return "会话数据不可用(宿主未提供会话服务或全部读取失败)";
|
|
1837
|
+
return "会话数据不完整:" + String(skipped) + " 个会话读取失败,未计入统计";
|
|
1838
|
+
}
|
|
1839
|
+
//#endregion
|
|
1403
1840
|
//#region src/client/tabs/ProjectsTab.tsx
|
|
1404
1841
|
/** 项目用量:按会话工作目录聚合(数据来自宿主会话事件折叠)。 */
|
|
1842
|
+
/** 导出列:成本与命中率都给原始数值,方便在表格里继续算。 */
|
|
1843
|
+
const EXPORT_COLUMNS$1 = [
|
|
1844
|
+
{
|
|
1845
|
+
key: "workspace",
|
|
1846
|
+
label: "项目路径",
|
|
1847
|
+
value: (row) => row.workspace
|
|
1848
|
+
},
|
|
1849
|
+
{
|
|
1850
|
+
key: "dominantProvider",
|
|
1851
|
+
label: "主要通道",
|
|
1852
|
+
value: (row) => row.dominantProvider
|
|
1853
|
+
},
|
|
1854
|
+
{
|
|
1855
|
+
key: "calls",
|
|
1856
|
+
label: "调用",
|
|
1857
|
+
value: (row) => row.calls
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
key: "tokens",
|
|
1861
|
+
label: "Tokens",
|
|
1862
|
+
value: (row) => row.tokens
|
|
1863
|
+
},
|
|
1864
|
+
{
|
|
1865
|
+
key: "cacheHitRate",
|
|
1866
|
+
label: "命中率(%)",
|
|
1867
|
+
value: (row) => row.cacheHitRate
|
|
1868
|
+
},
|
|
1869
|
+
{
|
|
1870
|
+
key: "cost",
|
|
1871
|
+
label: "成本(估算,元)",
|
|
1872
|
+
value: (row) => row.cost
|
|
1873
|
+
},
|
|
1874
|
+
{
|
|
1875
|
+
key: "sessionCount",
|
|
1876
|
+
label: "会话数",
|
|
1877
|
+
value: (row) => row.sessionCount
|
|
1878
|
+
},
|
|
1879
|
+
{
|
|
1880
|
+
key: "archivedSessionCount",
|
|
1881
|
+
label: "已归档会话数",
|
|
1882
|
+
value: (row) => row.archivedSessionCount
|
|
1883
|
+
},
|
|
1884
|
+
{
|
|
1885
|
+
key: "lastTime",
|
|
1886
|
+
label: "最近活跃",
|
|
1887
|
+
value: (row) => fmtDateTime(row.lastTime)
|
|
1888
|
+
}
|
|
1889
|
+
];
|
|
1405
1890
|
function ProjectsTab(props) {
|
|
1406
1891
|
const [query, setQuery] = (0, react.useState)("");
|
|
1407
1892
|
const [channelFilter, setChannelFilter] = (0, react.useState)("ALL");
|
|
@@ -1410,7 +1895,7 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1410
1895
|
const rows = props.data?.rows ?? [];
|
|
1411
1896
|
const visible = (0, react.useMemo)(() => {
|
|
1412
1897
|
const keyword = query.trim().toLowerCase();
|
|
1413
|
-
const filtered = rows.filter((row) => {
|
|
1898
|
+
const filtered = dropEmptyRows(rows, (row) => row.tokens).rows.filter((row) => {
|
|
1414
1899
|
const matchesQuery = keyword === "" || row.workspace.toLowerCase().includes(keyword);
|
|
1415
1900
|
const matchesChannel = channelFilter === "ALL" || row.providers[channelFilter] !== void 0;
|
|
1416
1901
|
return matchesQuery && matchesChannel;
|
|
@@ -1429,6 +1914,12 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1429
1914
|
sortKey
|
|
1430
1915
|
]);
|
|
1431
1916
|
const totalTokens = rows.reduce((total, row) => total + row.tokens, 0);
|
|
1917
|
+
const hiddenEmpty = dropEmptyRows(rows, (row) => row.tokens).hidden;
|
|
1918
|
+
/** 导出当前筛选与排序后的项目行(JSON 取整行,CSV 按固定列映射)。 */
|
|
1919
|
+
const exportRows = (format) => {
|
|
1920
|
+
if (format === "csv") downloadFile(exportFileName("projects", "csv"), toCsv(visible, EXPORT_COLUMNS$1), CSV_MIME);
|
|
1921
|
+
else downloadFile(exportFileName("projects", "json"), toJson(visible), JSON_MIME);
|
|
1922
|
+
};
|
|
1432
1923
|
if (props.data === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1433
1924
|
className: shared_module_css_default.empty,
|
|
1434
1925
|
children: "会话数据读取中或不可用…"
|
|
@@ -1481,6 +1972,20 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1481
1972
|
children: "调用最多"
|
|
1482
1973
|
})
|
|
1483
1974
|
]
|
|
1975
|
+
}),
|
|
1976
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1977
|
+
className: export_module_css_default.exportGroup,
|
|
1978
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1979
|
+
className: export_module_css_default.exportBtn,
|
|
1980
|
+
disabled: visible.length === 0,
|
|
1981
|
+
onClick: () => exportRows("csv"),
|
|
1982
|
+
children: "导出 CSV"
|
|
1983
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1984
|
+
className: export_module_css_default.exportBtn,
|
|
1985
|
+
disabled: visible.length === 0,
|
|
1986
|
+
onClick: () => exportRows("json"),
|
|
1987
|
+
children: "导出 JSON"
|
|
1988
|
+
})]
|
|
1484
1989
|
})
|
|
1485
1990
|
]
|
|
1486
1991
|
}),
|
|
@@ -1510,7 +2015,8 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1510
2015
|
row.workspace,
|
|
1511
2016
|
" · ",
|
|
1512
2017
|
row.sessionCount,
|
|
1513
|
-
" 个会话"
|
|
2018
|
+
" 个会话",
|
|
2019
|
+
row.archivedSessionCount > 0 ? "(含 " + row.archivedSessionCount + " 个已归档,仍计入)" : ""
|
|
1514
2020
|
]
|
|
1515
2021
|
})] })
|
|
1516
2022
|
}) }),
|
|
@@ -1540,6 +2046,10 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1540
2046
|
children: "没有符合条件的项目"
|
|
1541
2047
|
}) })] })]
|
|
1542
2048
|
}),
|
|
2049
|
+
hiddenEmpty > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2050
|
+
className: shared_module_css_default.statExtra,
|
|
2051
|
+
children: hiddenEmptyRowsText(hiddenEmpty)
|
|
2052
|
+
}),
|
|
1543
2053
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1544
2054
|
className: shared_module_css_default.statExtra,
|
|
1545
2055
|
children: [
|
|
@@ -1575,26 +2085,26 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1575
2085
|
css: css$1
|
|
1576
2086
|
});
|
|
1577
2087
|
var quota_module_css_default = {
|
|
1578
|
-
"windowItem": "F7iUHa_windowItem",
|
|
1579
2088
|
"windowBar": "F7iUHa_windowBar",
|
|
1580
|
-
"
|
|
2089
|
+
"windowRow": "F7iUHa_windowRow",
|
|
1581
2090
|
"liveChip": "F7iUHa_liveChip",
|
|
1582
|
-
"
|
|
1583
|
-
"
|
|
1584
|
-
"balanceStrip": "F7iUHa_balanceStrip",
|
|
2091
|
+
"bsLabel": "F7iUHa_bsLabel",
|
|
2092
|
+
"quotaHead": "F7iUHa_quotaHead",
|
|
1585
2093
|
"quotaTitle": "F7iUHa_quotaTitle",
|
|
1586
2094
|
"snapshotStale": "F7iUHa_snapshotStale",
|
|
1587
|
-
"
|
|
2095
|
+
"windowItem": "F7iUHa_windowItem",
|
|
1588
2096
|
"windowValue": "F7iUHa_windowValue",
|
|
1589
|
-
"
|
|
1590
|
-
"
|
|
1591
|
-
"quotaCard": "F7iUHa_quotaCard",
|
|
1592
|
-
"quotaHead": "F7iUHa_quotaHead",
|
|
2097
|
+
"windowReset": "F7iUHa_windowReset",
|
|
2098
|
+
"snapshotHint": "F7iUHa_snapshotHint",
|
|
1593
2099
|
"quotaProvider": "F7iUHa_quotaProvider",
|
|
1594
2100
|
"windowHead": "F7iUHa_windowHead",
|
|
1595
|
-
"
|
|
1596
|
-
"
|
|
1597
|
-
"
|
|
2101
|
+
"quotaList": "F7iUHa_quotaList",
|
|
2102
|
+
"balanceItem": "F7iUHa_balanceItem",
|
|
2103
|
+
"balanceStrip": "F7iUHa_balanceStrip",
|
|
2104
|
+
"windowName": "F7iUHa_windowName",
|
|
2105
|
+
"bsValue": "F7iUHa_bsValue",
|
|
2106
|
+
"quotaEnv": "F7iUHa_quotaEnv",
|
|
2107
|
+
"quotaCard": "F7iUHa_quotaCard"
|
|
1598
2108
|
};
|
|
1599
2109
|
//#endregion
|
|
1600
2110
|
//#region src/client/tabs/QuotaTab.tsx
|
|
@@ -1728,13 +2238,13 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1728
2238
|
"(来自官方接口,最多缓存 1 分钟)"
|
|
1729
2239
|
]
|
|
1730
2240
|
}),
|
|
1731
|
-
snapshot !== null && !isLive && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2241
|
+
snapshot !== null && !isLive && snapshot.supported && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1732
2242
|
className: staleDaysOf(snapshot.updatedAt) >= 1 ? quota_module_css_default.snapshotStale : quota_module_css_default.snapshotHint,
|
|
1733
2243
|
children: [
|
|
1734
2244
|
"实时查询未成功(",
|
|
1735
2245
|
errorTextOf(snapshot.error),
|
|
1736
|
-
"
|
|
1737
|
-
snapshot.updatedAt === null ? "" : " · 快照时间 " + fmtDateTime(snapshot.updatedAt) + (staleDaysOf(snapshot.updatedAt) >= 1 ? ",已 " + staleDaysOf(snapshot.updatedAt) + " 天未更新" : "")
|
|
2246
|
+
")",
|
|
2247
|
+
snapshot.updatedAt === null ? ",且暂无可用快照 —— 点右上角「刷新」重试" : ",显示快照 · 快照时间 " + fmtDateTime(snapshot.updatedAt) + (staleDaysOf(snapshot.updatedAt) >= 1 ? ",已 " + staleDaysOf(snapshot.updatedAt) + " 天未更新" : "")
|
|
1738
2248
|
]
|
|
1739
2249
|
})
|
|
1740
2250
|
]
|
|
@@ -1801,22 +2311,84 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1801
2311
|
//#endregion
|
|
1802
2312
|
//#region src/client/tabs/SessionsTab.tsx
|
|
1803
2313
|
/** 会话用量:按会话列出 tokens / 命中率 / 时长,按最近活跃时按日分组。 */
|
|
2314
|
+
/** 导出列:标题可能带逗号或换行,由 toCsv 统一转义。 */
|
|
2315
|
+
const EXPORT_COLUMNS = [
|
|
2316
|
+
{
|
|
2317
|
+
key: "id",
|
|
2318
|
+
label: "会话 id",
|
|
2319
|
+
value: (row) => row.id
|
|
2320
|
+
},
|
|
2321
|
+
{
|
|
2322
|
+
key: "title",
|
|
2323
|
+
label: "标题",
|
|
2324
|
+
value: (row) => row.title
|
|
2325
|
+
},
|
|
2326
|
+
{
|
|
2327
|
+
key: "workspace",
|
|
2328
|
+
label: "工作区",
|
|
2329
|
+
value: (row) => row.workspace
|
|
2330
|
+
},
|
|
2331
|
+
{
|
|
2332
|
+
key: "provider",
|
|
2333
|
+
label: "通道",
|
|
2334
|
+
value: (row) => row.provider
|
|
2335
|
+
},
|
|
2336
|
+
{
|
|
2337
|
+
key: "firstTime",
|
|
2338
|
+
label: "首次时间",
|
|
2339
|
+
value: (row) => fmtDateTime(row.firstTime)
|
|
2340
|
+
},
|
|
2341
|
+
{
|
|
2342
|
+
key: "lastTime",
|
|
2343
|
+
label: "最近时间",
|
|
2344
|
+
value: (row) => fmtDateTime(row.lastTime)
|
|
2345
|
+
},
|
|
2346
|
+
{
|
|
2347
|
+
key: "calls",
|
|
2348
|
+
label: "调用",
|
|
2349
|
+
value: (row) => row.calls
|
|
2350
|
+
},
|
|
2351
|
+
{
|
|
2352
|
+
key: "tokens",
|
|
2353
|
+
label: "Tokens",
|
|
2354
|
+
value: (row) => row.tokens
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
key: "cacheHitRate",
|
|
2358
|
+
label: "命中率(%)",
|
|
2359
|
+
value: (row) => row.cacheHitRate
|
|
2360
|
+
},
|
|
2361
|
+
{
|
|
2362
|
+
key: "cost",
|
|
2363
|
+
label: "成本(估算,元)",
|
|
2364
|
+
value: (row) => row.cost
|
|
2365
|
+
},
|
|
2366
|
+
{
|
|
2367
|
+
key: "archived",
|
|
2368
|
+
label: "已归档",
|
|
2369
|
+
value: (row) => row.archived ? "是" : "否"
|
|
2370
|
+
}
|
|
2371
|
+
];
|
|
1804
2372
|
function SessionsTab(props) {
|
|
1805
2373
|
const [query, setQuery] = (0, react.useState)("");
|
|
1806
2374
|
const [channelFilter, setChannelFilter] = (0, react.useState)("ALL");
|
|
1807
2375
|
const [daysFilter, setDaysFilter] = (0, react.useState)("30");
|
|
1808
2376
|
const [sortKey, setSortKey] = (0, react.useState)("recent");
|
|
2377
|
+
const [archiveFilter, setArchiveFilter] = (0, react.useState)("hide");
|
|
1809
2378
|
const [nowMs] = (0, react.useState)(() => Date.now());
|
|
1810
2379
|
const rows = props.data?.rows ?? [];
|
|
2380
|
+
const archivedCount = rows.filter((row) => row.archived).length;
|
|
2381
|
+
const hiddenEmpty = dropEmptyRows(rows, (row) => row.tokens).hidden;
|
|
1811
2382
|
const visible = (0, react.useMemo)(() => {
|
|
1812
2383
|
const keyword = query.trim().toLowerCase();
|
|
1813
2384
|
const maxAgeDays = Number(daysFilter);
|
|
1814
|
-
const filtered = rows.filter((row) => {
|
|
2385
|
+
const filtered = dropEmptyRows(rows, (row) => row.tokens).rows.filter((row) => {
|
|
1815
2386
|
const title = (row.title ?? "") + " " + row.id + " " + (row.workspace ?? "");
|
|
1816
2387
|
const matchesQuery = keyword === "" || title.toLowerCase().includes(keyword);
|
|
1817
2388
|
const matchesChannel = channelFilter === "ALL" || row.provider === channelFilter;
|
|
1818
2389
|
const matchesAge = maxAgeDays === 0 || row.lastTime === null || nowMs - row.lastTime <= maxAgeDays * 864e5;
|
|
1819
|
-
|
|
2390
|
+
const matchesArchive = archiveFilter === "all" || (archiveFilter === "only" ? row.archived : !row.archived);
|
|
2391
|
+
return matchesQuery && matchesChannel && matchesAge && matchesArchive;
|
|
1820
2392
|
});
|
|
1821
2393
|
const sorters = {
|
|
1822
2394
|
recent: (first, second) => (second.lastTime ?? 0) - (first.lastTime ?? 0),
|
|
@@ -1831,8 +2403,14 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1831
2403
|
channelFilter,
|
|
1832
2404
|
daysFilter,
|
|
1833
2405
|
sortKey,
|
|
1834
|
-
nowMs
|
|
2406
|
+
nowMs,
|
|
2407
|
+
archiveFilter
|
|
1835
2408
|
]);
|
|
2409
|
+
/** 只导当前筛选后的会话行(归档筛选、时间窗口、搜索都算数)。 */
|
|
2410
|
+
const exportRows = (format) => {
|
|
2411
|
+
if (format === "csv") downloadFile(exportFileName("sessions", "csv"), toCsv(visible, EXPORT_COLUMNS), CSV_MIME);
|
|
2412
|
+
else downloadFile(exportFileName("sessions", "json"), toJson(visible), JSON_MIME);
|
|
2413
|
+
};
|
|
1836
2414
|
if (props.data === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1837
2415
|
className: shared_module_css_default.empty,
|
|
1838
2416
|
children: "会话数据读取中或不可用…"
|
|
@@ -1914,9 +2492,51 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1914
2492
|
children: "时长最长"
|
|
1915
2493
|
})
|
|
1916
2494
|
]
|
|
2495
|
+
}),
|
|
2496
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
2497
|
+
className: lists_module_css_default.filterSelect,
|
|
2498
|
+
value: archiveFilter,
|
|
2499
|
+
onChange: (event) => setArchiveFilter(event.target.value),
|
|
2500
|
+
title: "已归档会话只做标记,仍然计入统计",
|
|
2501
|
+
children: [
|
|
2502
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
2503
|
+
value: "hide",
|
|
2504
|
+
children: "隐藏已归档"
|
|
2505
|
+
}),
|
|
2506
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
2507
|
+
value: "all",
|
|
2508
|
+
children: "全部对话(显示已归档)"
|
|
2509
|
+
}),
|
|
2510
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
2511
|
+
value: "only",
|
|
2512
|
+
children: "仅显示已归档"
|
|
2513
|
+
})
|
|
2514
|
+
]
|
|
2515
|
+
}),
|
|
2516
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2517
|
+
className: export_module_css_default.exportGroup,
|
|
2518
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2519
|
+
className: export_module_css_default.exportBtn,
|
|
2520
|
+
disabled: visible.length === 0,
|
|
2521
|
+
onClick: () => exportRows("csv"),
|
|
2522
|
+
children: "导出 CSV"
|
|
2523
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2524
|
+
className: export_module_css_default.exportBtn,
|
|
2525
|
+
disabled: visible.length === 0,
|
|
2526
|
+
onClick: () => exportRows("json"),
|
|
2527
|
+
children: "导出 JSON"
|
|
2528
|
+
})]
|
|
1917
2529
|
})
|
|
1918
2530
|
]
|
|
1919
2531
|
}),
|
|
2532
|
+
archiveFilter === "hide" && archivedCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2533
|
+
className: shared_module_css_default.statExtra,
|
|
2534
|
+
children: [
|
|
2535
|
+
"另有 ",
|
|
2536
|
+
archivedCount,
|
|
2537
|
+
" 个已归档会话未显示(归档只是标记,用量仍计入上方统计)"
|
|
2538
|
+
]
|
|
2539
|
+
}),
|
|
1920
2540
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
1921
2541
|
className: shared_module_css_default.table,
|
|
1922
2542
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
@@ -1952,6 +2572,10 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1952
2572
|
}) })
|
|
1953
2573
|
] })]
|
|
1954
2574
|
}),
|
|
2575
|
+
hiddenEmpty > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2576
|
+
className: shared_module_css_default.statExtra,
|
|
2577
|
+
children: hiddenEmptyRowsText(hiddenEmpty)
|
|
2578
|
+
}),
|
|
1955
2579
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1956
2580
|
className: shared_module_css_default.statExtra,
|
|
1957
2581
|
children: ["会话用量按宿主会话事件折叠(每个结算样本计一次调用);成本按台账模型均价估算", props.data.priced ? "" : "(台账无成本数据,暂不可估)"]
|
|
@@ -1961,26 +2585,32 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
1961
2585
|
function sessionRowOf(row, channels) {
|
|
1962
2586
|
const channel = channelOf(channels, row.provider);
|
|
1963
2587
|
const costCell = costCellText(channel, row.cost ?? 0);
|
|
1964
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", {
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2588
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", {
|
|
2589
|
+
className: row.archived ? lists_module_css_default.rowArchived : void 0,
|
|
2590
|
+
children: [
|
|
2591
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2592
|
+
className: lists_module_css_default.listMain,
|
|
2593
|
+
children: [row.title ?? "会话 " + shortIdOf(row.id), row.archived && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2594
|
+
className: lists_module_css_default.archivedTag,
|
|
2595
|
+
children: "已归档"
|
|
2596
|
+
})]
|
|
2597
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2598
|
+
className: lists_module_css_default.listSub,
|
|
2599
|
+
children: row.workspace ?? "未归属工作区"
|
|
2600
|
+
})] }),
|
|
2601
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: row.providerLabel ?? displayNameOf(channels, row.provider ?? "") }),
|
|
2602
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: row.lastTime === null ? "—" : dayLabelOf(row.lastTime, Date.now()) + " " + fmtTime(row.lastTime) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2603
|
+
className: lists_module_css_default.listSub,
|
|
2604
|
+
children: ["时长 ", fmtDuration(durationOf(row))]
|
|
2605
|
+
})] }),
|
|
2606
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: fmtTokens(row.tokens) }),
|
|
2607
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: row.cacheHitRate === null ? "—" : row.cacheHitRate.toFixed(1) + "%" }),
|
|
2608
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: row.cost === null || channel === null || channel.costMode !== "perToken" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2609
|
+
className: shared_module_css_default.dim,
|
|
2610
|
+
children: costCell.text
|
|
2611
|
+
}) : costCell.text })
|
|
2612
|
+
]
|
|
2613
|
+
}, row.id);
|
|
1984
2614
|
}
|
|
1985
2615
|
function durationOf(row) {
|
|
1986
2616
|
if (row.firstTime === null || row.lastTime === null) return 0;
|
|
@@ -2008,6 +2638,7 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
2008
2638
|
["overview", "总览"],
|
|
2009
2639
|
["detail", "使用明细"],
|
|
2010
2640
|
["projects", "项目用量"],
|
|
2641
|
+
["models", "按模型"],
|
|
2011
2642
|
["sessions", "会话用量"],
|
|
2012
2643
|
["quota", "余额与配额"]
|
|
2013
2644
|
];
|
|
@@ -2029,6 +2660,7 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
2029
2660
|
const [usage, setUsage] = (0, react.useState)(null);
|
|
2030
2661
|
const [sessions, setSessions] = (0, react.useState)(null);
|
|
2031
2662
|
const [projects, setProjects] = (0, react.useState)(null);
|
|
2663
|
+
const [models, setModels] = (0, react.useState)(null);
|
|
2032
2664
|
const [available, setAvailable] = (0, react.useState)(null);
|
|
2033
2665
|
const [loading, setLoading] = (0, react.useState)(false);
|
|
2034
2666
|
const tooltip = useTooltip();
|
|
@@ -2045,9 +2677,14 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
2045
2677
|
setSummary(summaryResult);
|
|
2046
2678
|
setHeatmap(heatmapResult ?? []);
|
|
2047
2679
|
if (includeSessionData) {
|
|
2048
|
-
const [sessionResult, projectResult] = await Promise.all([
|
|
2680
|
+
const [sessionResult, projectResult, modelResult] = await Promise.all([
|
|
2681
|
+
loadSessions(),
|
|
2682
|
+
loadProjects(),
|
|
2683
|
+
loadModels()
|
|
2684
|
+
]);
|
|
2049
2685
|
setSessions(sessionResult);
|
|
2050
2686
|
setProjects(projectResult);
|
|
2687
|
+
setModels(modelResult);
|
|
2051
2688
|
}
|
|
2052
2689
|
setLoading(false);
|
|
2053
2690
|
}, []);
|
|
@@ -2057,10 +2694,12 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
2057
2694
|
(0, react.useEffect)(() => {
|
|
2058
2695
|
if (activeTab === "sessions" && sessions === null) loadSessions().then(setSessions);
|
|
2059
2696
|
if (activeTab === "projects" && projects === null) loadProjects().then(setProjects);
|
|
2697
|
+
if (activeTab === "models" && models === null) loadModels().then(setModels);
|
|
2060
2698
|
}, [
|
|
2061
2699
|
activeTab,
|
|
2062
2700
|
sessions,
|
|
2063
|
-
projects
|
|
2701
|
+
projects,
|
|
2702
|
+
models
|
|
2064
2703
|
]);
|
|
2065
2704
|
const bounds = (0, react.useMemo)(() => {
|
|
2066
2705
|
const today = isoDate(/* @__PURE__ */ new Date());
|
|
@@ -2109,7 +2748,7 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
2109
2748
|
channelFilter
|
|
2110
2749
|
]);
|
|
2111
2750
|
const refresh = (0, react.useCallback)(() => {
|
|
2112
|
-
reload(activeTab === "sessions" || activeTab === "projects" || sessions !== null || projects !== null, true);
|
|
2751
|
+
reload(activeTab === "sessions" || activeTab === "projects" || activeTab === "models" || sessions !== null || projects !== null || models !== null, true);
|
|
2113
2752
|
loadUsage(bounds.from, bounds.to, channelFilter).then((result) => {
|
|
2114
2753
|
if (result !== null) setUsage(result);
|
|
2115
2754
|
});
|
|
@@ -2118,6 +2757,7 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
2118
2757
|
activeTab,
|
|
2119
2758
|
sessions,
|
|
2120
2759
|
projects,
|
|
2760
|
+
models,
|
|
2121
2761
|
bounds.from,
|
|
2122
2762
|
bounds.to,
|
|
2123
2763
|
channelFilter
|
|
@@ -2181,6 +2821,10 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
2181
2821
|
data: projects,
|
|
2182
2822
|
channels
|
|
2183
2823
|
}),
|
|
2824
|
+
activeTab === "models" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelsTab, {
|
|
2825
|
+
data: models,
|
|
2826
|
+
channels
|
|
2827
|
+
}),
|
|
2184
2828
|
activeTab === "sessions" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionsTab, {
|
|
2185
2829
|
data: sessions,
|
|
2186
2830
|
channels
|
|
@@ -2200,7 +2844,8 @@ window.__ModuleLoader__.load({ id: "dsh-key-usage", factory: (require) => {
|
|
|
2200
2844
|
"。两套来源按天取舍、不相加。"
|
|
2201
2845
|
] }),
|
|
2202
2846
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: "热力图按可见区间内活跃日的三分位分档(少 / 中 / 高)。" }),
|
|
2203
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: "
|
|
2847
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: "项目用量、会话用量、按模型与单日「按小时」均来自会话事件,每个结算样本计一次调用(不计成本)。" }),
|
|
2848
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: "已归档会话只是标记:用量照常计入,会话列表默认隐藏、可切换显示(显示时淡化并标注)。" }),
|
|
2204
2849
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: "命中率 = 缓存读 ÷(输入 + 输出 + 缓存读)。" }),
|
|
2205
2850
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: "余额与订阅窗口实时查询官方接口(失败回落本机快照),原样展示,不做换算与建议。" })
|
|
2206
2851
|
]
|