dsh-dock 0.10.2 → 0.11.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/README.md +4 -5
- package/client.js +383 -8
- package/features/tokenlog/host.js +290 -98
- package/features/tokenlog/view.jsx +362 -4
- package/package.json +3 -3
- package/src/client.jsx +5 -4
- package/src/host-core.js +38 -0
- package/screenshots/dock-chips.png +0 -0
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
| 功能 | 说明 |
|
|
18
18
|
|---|---|
|
|
19
|
-
| **用量记录** | 记录全部 LLM API 调用:秒级时间筛选、Token
|
|
19
|
+
| **用量记录** | 记录全部 LLM API 调用:秒级时间筛选、Token/费用统计(**可配置各模型单价、持久保存并按自填单价计费**,支持多段分时价,内置峰谷计价 + 官网价目自动同步作兜底)、分组汇总、明细检索与 CSV 导出 |
|
|
20
20
|
| **模型设置** | 编辑各 Provider 模型目录:输入类型(文本/图片 + 标注)与思考强度档位;图片能力可由你手动声明,新增 Provider 自动刷新加载,写回官方配置热生效 |
|
|
21
21
|
| **模型余额** | 展示所有模型 Provider 账户余额(5 分钟自动刷新) |
|
|
22
22
|
| **任务动画** | 19 种任务运行动画(含星际远征、星云潮汐、曲速航道、量子雷达、星座网络、深海脉动、神经电路等),速度随任务活动联动,配置全部持久化 |
|
|
@@ -33,8 +33,6 @@
|
|
|
33
33
|
- **模型余额**:当前选中 Provider 的账户余额(跟随模型切换实时更新),点击打开功能坞并定位到该 Provider 的余额详情;
|
|
34
34
|
- **用量记录**:当前会话的总 Token 与估算花费(10 秒静默刷新),点击打开功能坞并跳转到该会话的用量记录。
|
|
35
35
|
|
|
36
|
-

|
|
37
|
-
|
|
38
36
|
勾选「启用」即显示,点芯片跳转;每个功能可在面板页脚或设置页单独设置「会话页显示/隐藏」。
|
|
39
37
|
|
|
40
38
|
## 界面
|
|
@@ -111,6 +109,7 @@ dsh plugin --profile web remove dsh-dock # 卸载
|
|
|
111
109
|
4. 「分组汇总」按 无 / 提供商 / 模型 / 状态 / 强度 维度切换
|
|
112
110
|
5. 明细表 15 列可排序,100 行/页,点击「会话 ID」即筛选该会话;行右侧「详情」看单次调用信息
|
|
113
111
|
6. 「CSV 导出」下载当前筛选结果
|
|
112
|
+
7. **费用不准?点「单价设置」按模型填真实单价**:官网刊例价是抓来的,与实际计费(第三方中转、折扣、自建端点)常有出入。点开独立的「单价设置」子弹窗(可最大化、拖动、缩放),可按模型逐条配置单价(人民币元 / 百万 tokens,匹配模型名子串即可),并设置兜底单价、USD→CNY 汇率、是否继续自动同步官网价。每个模型还可配置**多段分时价**(如 DeepSeek 的高峰/优惠时段),命中哪段用哪段、时段外回落基准价,支持跨零点(如 `23~7`)与半点边界(如 `8.5` = 08:30)。保存后写入宿主配置**持久保存**,费用立即按自填单价重算(无需重扫历史);计价优先级:**自定义单价 > 官网同步价 > 内置价 > 兜底价**。明细表里走兜底价的金额会标「兜底」,方便你补配。
|
|
114
113
|
|
|
115
114
|
**模型余额**
|
|
116
115
|
|
|
@@ -191,8 +190,6 @@ dsh plugin --profile web remove dsh-dock # 卸载
|
|
|
191
190
|
- **模型余额**:当前选中 Provider 的账户余额(跟随模型切换实时更新),点击打开功能坞并定位到该 Provider 的余额详情;
|
|
192
191
|
- **用量记录**:当前会话的总 Token 与估算花费(10 秒静默刷新),点击打开功能坞并跳转到该会话的用量记录。
|
|
193
192
|
|
|
194
|
-

|
|
195
|
-
|
|
196
193
|
勾选「启用」即显示,点芯片跳转;每个功能可在面板页脚或设置页单独设置「会话页显示/隐藏」。
|
|
197
194
|
|
|
198
195
|
## 界面
|
|
@@ -269,6 +266,7 @@ dsh plugin --profile web remove dsh-dock # 卸载
|
|
|
269
266
|
4. 「分组汇总」按 无 / 提供商 / 模型 / 状态 / 强度 维度切换
|
|
270
267
|
5. 明细表 15 列可排序,100 行/页,点击「会话 ID」即筛选该会话;行右侧「详情」看单次调用信息
|
|
271
268
|
6. 「CSV 导出」下载当前筛选结果
|
|
269
|
+
7. **费用不准?点「单价设置」按模型填真实单价**:官网刊例价是抓来的,与实际计费(第三方中转、折扣、自建端点)常有出入。点开独立的「单价设置」子弹窗(可最大化、拖动、缩放),可按模型逐条配置单价(人民币元 / 百万 tokens,匹配模型名子串即可),并设置兜底单价、USD→CNY 汇率、是否继续自动同步官网价。每个模型还可配置**多段分时价**(如 DeepSeek 的高峰/优惠时段),命中哪段用哪段、时段外回落基准价,支持跨零点(如 `23~7`)与半点边界(如 `8.5` = 08:30)。保存后写入宿主配置**持久保存**,费用立即按自填单价重算(无需重扫历史);计价优先级:**自定义单价 > 官网同步价 > 内置价 > 兜底价**。明细表里走兜底价的金额会标「兜底」,方便你补配。
|
|
272
270
|
|
|
273
271
|
**模型余额**
|
|
274
272
|
|
|
@@ -350,6 +348,7 @@ dsh plugin --profile web remove dsh-dock # 卸载
|
|
|
350
348
|
- **功能开关刷新后重置**:v0.5.0 起功能开关已持久化(浏览器本地存储),刷新/重启后保持。
|
|
351
349
|
- **任务动画/任务通知/运行状态不生效**:三者的路由都在宿主进程内(动画/通知的配置持久化也在宿主),旧宿主需重启 `dsh web` 加载(面板页会提示「宿主进程是旧版本」)。另注意通知与运行状态已各自独立成菜单项,需在功能坞里分别开启「任务通知」「运行状态」。
|
|
352
350
|
- **用量记录空白**:确认 dsh 是较新版本且插件宿主已加载(查看启动日志含 `[dsh-dock] host half loaded`);旧宿主进程需重启 dsh web 加载新路由。
|
|
351
|
+
- **费用不准 / 与实际账单对不上**:金额是按单价估算的,官网抓取来的刊例价不代表你的实际计费。在用量记录页点「单价设置」(独立子弹窗,可最大化/拖动/缩放),按模型填真实单价并保存(持久化到宿主配置),费用会立即按自填单价重算;每个模型还支持多段分时价(跨零点 / 半点边界)。计价优先级为「自定义单价 > 官网同步价 > 内置价 > 兜底价」,明细里走兜底价的金额会标「兜底」。
|
|
353
352
|
- **余额显示「需登录 / 无密钥」**:对应 Provider 未在 dsh 凭据中配置可查询余额的密钥,属正常状态提示。
|
|
354
353
|
- **切模型 / 打开历史会话卡住,报 `preset "…" failed to mount`**:这是该 **agent 预设**与 dsh 版本
|
|
355
354
|
的 schema 漂移,不是本插件的问题。预设目录在 `~/.dsh/.agent-presets/<id>/`,dsh 升级后预设里的
|
package/client.js
CHANGED
|
@@ -2297,7 +2297,7 @@ function subscribePanel(fn) {
|
|
|
2297
2297
|
}
|
|
2298
2298
|
|
|
2299
2299
|
// features/tokenlog/view.jsx
|
|
2300
|
-
var import_react2 = require("react");
|
|
2300
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
2301
2301
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
2302
2302
|
function rpcCall(method, args) {
|
|
2303
2303
|
return fetch("/dsh-dock/tokenlog/" + method, {
|
|
@@ -2425,6 +2425,38 @@ var css = `
|
|
|
2425
2425
|
.dtok-pager.top{margin:0;}
|
|
2426
2426
|
.dtok-pager.bottom{margin:0;}
|
|
2427
2427
|
.dtok-err{color:var(--dsw-alias-state-error-primary,#ff7a7a);font-size:12px;}
|
|
2428
|
+
/* ---- \u5355\u4EF7\u8BBE\u7F6E\uFF08\u5B50\u5F39\u7A97\uFF09 ---- */
|
|
2429
|
+
.dtok-pm-backdrop{position:fixed;inset:0;z-index:2100;background:rgba(15,17,21,.5);backdrop-filter:blur(3px);display:flex;align-items:center;justify-content:center;}
|
|
2430
|
+
.dtok-pm{box-sizing:border-box;width:min(980px,calc(100vw - 32px));height:min(760px,calc(100vh - 32px));display:flex;flex-direction:column;border-radius:14px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2,#1c212b);color:var(--dsw-alias-label-primary);box-shadow:0 20px 64px rgb(0 0 0 / .4);overflow:hidden;}
|
|
2431
|
+
.dtok-pm.max{border-radius:10px;}
|
|
2432
|
+
.dtok-pm-head{display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid var(--dsw-alias-border-l1);cursor:move;user-select:none;flex:none;background:var(--dsw-alias-bg-layer-1);}
|
|
2433
|
+
.dtok-pm.max .dtok-pm-head{cursor:default;}
|
|
2434
|
+
.dtok-pm-head b{font-size:14px;}
|
|
2435
|
+
.dtok-pm-sub{font-size:11px;color:var(--dsw-alias-label-secondary);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
2436
|
+
.dtok-pm-ctrls{display:flex;gap:6px;flex:none;}
|
|
2437
|
+
.dtok-pm-body{flex:1;min-height:0;overflow-y:auto;padding:12px 14px;}
|
|
2438
|
+
.dtok-pm-resize{position:absolute;right:0;bottom:0;width:16px;height:16px;cursor:nwse-resize;touch-action:none;}
|
|
2439
|
+
.dtok-pm{position:relative;}
|
|
2440
|
+
/* ---- \u5355\u4EF7\u8BBE\u7F6E\uFF08\u8868\u5355\u5185\u5BB9\uFF09 ---- */
|
|
2441
|
+
.dtok-price{display:flex;flex-direction:column;gap:12px;}
|
|
2442
|
+
.dtok-price-grid{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;}
|
|
2443
|
+
.dtok-price-field{display:flex;flex-direction:column;gap:3px;}
|
|
2444
|
+
.dtok-price-field label{font-size:11px;color:var(--dsw-alias-label-secondary);}
|
|
2445
|
+
.dtok-price-num{width:92px;}
|
|
2446
|
+
.dtok-price input[type="text"],.dtok-price-num{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:4px 6px;font-size:12px;font-family:inherit;}
|
|
2447
|
+
.dtok-price .dtok-btn.tiny{padding:2px 8px;font-size:11px;}
|
|
2448
|
+
.dtok-price-msg{font-size:12px;}
|
|
2449
|
+
.dtok-price-msg.ok{color:var(--dsw-alias-state-success-primary,#4ade80);}
|
|
2450
|
+
.dtok-price-msg.err{color:var(--dsw-alias-state-error-primary,#ff7a7a);}
|
|
2451
|
+
.dtok-price-hint{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.6;}
|
|
2452
|
+
.dtok-prow{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:8px 10px;margin-bottom:8px;background:var(--dsw-alias-bg-layer-1);}
|
|
2453
|
+
.dtok-prow-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:6px;}
|
|
2454
|
+
.dtok-pseg{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:4px 0;}
|
|
2455
|
+
.dtok-pseg-name{font-size:11px;color:var(--dsw-alias-label-secondary);min-width:112px;}
|
|
2456
|
+
.dtok-pseg-tilde{font-size:11px;color:var(--dsw-alias-label-secondary);}
|
|
2457
|
+
.dtok-src{font-size:11px;color:var(--dsw-alias-label-tertiary,#94a3b8);}
|
|
2458
|
+
.dtok-src.custom{color:var(--dsw-alias-state-success-primary,#4ade80);}
|
|
2459
|
+
.dtok-src.fallback{color:var(--dk-warn);}
|
|
2428
2460
|
`;
|
|
2429
2461
|
function Detail({ rec, onClose, rate }) {
|
|
2430
2462
|
const st = statusInfo(rec);
|
|
@@ -2447,6 +2479,7 @@ function Detail({ rec, onClose, rate }) {
|
|
|
2447
2479
|
["\u7F13\u5B58\u547D\u4E2D\u7387", rec.cacheHitPercent + "%"],
|
|
2448
2480
|
["\u603B Token", fmtNum(rec.totalTokens)],
|
|
2449
2481
|
["\u6D88\u8017\u91D1\u989D(\u4F30\u7B97)", fmtCostCny(rec.cost, rateCny)],
|
|
2482
|
+
["\u8BA1\u4EF7\u6765\u6E90", rec.pricingSource ? { custom: "\u81EA\u5B9A\u4E49\u5355\u4EF7", official: "\u5B98\u7F51\u540C\u6B65\u4EF7", builtin: "\u5185\u7F6E\u9ED8\u8BA4\u4EF7", fallback: "\u515C\u5E95\u5355\u4EF7" }[rec.pricingSource] || rec.pricingSource : "\u2014"],
|
|
2450
2483
|
["\u63A8\u7406\u5F3A\u5EA6", rec.effort || "\u2014"],
|
|
2451
2484
|
["\u8017\u65F6", fmtDuration(rec.llmMs)],
|
|
2452
2485
|
["Turn / Step", rec.turn + " / " + rec.step]
|
|
@@ -2460,6 +2493,333 @@ function Detail({ rec, onClose, rate }) {
|
|
|
2460
2493
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", style: { marginTop: 12 }, onClick: onClose, children: "\u5173\u95ED" })
|
|
2461
2494
|
] }) });
|
|
2462
2495
|
}
|
|
2496
|
+
function numStr(v) {
|
|
2497
|
+
return v === void 0 || v === null ? "" : String(v);
|
|
2498
|
+
}
|
|
2499
|
+
function segToDraft(s) {
|
|
2500
|
+
return {
|
|
2501
|
+
start: numStr(s && s.start),
|
|
2502
|
+
end: numStr(s && s.end),
|
|
2503
|
+
input: numStr(s && s.input),
|
|
2504
|
+
output: numStr(s && s.output),
|
|
2505
|
+
cacheRead: numStr(s && s.cacheRead),
|
|
2506
|
+
cacheWrite: numStr(s && s.cacheWrite)
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
function rowToDraft(r) {
|
|
2510
|
+
return {
|
|
2511
|
+
match: r.match || "",
|
|
2512
|
+
input: numStr(r.input),
|
|
2513
|
+
output: numStr(r.output),
|
|
2514
|
+
cacheRead: numStr(r.cacheRead),
|
|
2515
|
+
cacheWrite: numStr(r.cacheWrite),
|
|
2516
|
+
// 旧数据可能是单段 peak(宿主已归一为 peaks,这里再兼容一次直连旧宿主的情况)
|
|
2517
|
+
peaks: (Array.isArray(r.peaks) && r.peaks.length ? r.peaks : r.peak && typeof r.peak === "object" ? [r.peak] : []).map(segToDraft)
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
function newSegDraft() {
|
|
2521
|
+
return segToDraft({ start: 9, end: 14, input: "", output: "", cacheRead: "", cacheWrite: "" });
|
|
2522
|
+
}
|
|
2523
|
+
function PriceModal({ onClose, onSaved }) {
|
|
2524
|
+
const [maxed, setMaxed] = (0, import_react2.useState)(false);
|
|
2525
|
+
const [geom, setGeom] = (0, import_react2.useState)(null);
|
|
2526
|
+
const dlgRef = (0, import_react2.useRef)(null);
|
|
2527
|
+
const dragRef = (0, import_react2.useRef)(null);
|
|
2528
|
+
const startDrag = (0, import_react2.useCallback)((e, kind) => {
|
|
2529
|
+
if (e.button !== void 0 && e.button !== 0) return;
|
|
2530
|
+
if (kind === "move" && e.target && e.target.closest && e.target.closest("button,select,input,label,details,summary")) return;
|
|
2531
|
+
const node = dlgRef.current;
|
|
2532
|
+
if (!node) return;
|
|
2533
|
+
const rect2 = node.getBoundingClientRect();
|
|
2534
|
+
dragRef.current = { kind, startX: e.clientX, startY: e.clientY, left: rect2.left, top: rect2.top, w: rect2.width, h: rect2.height };
|
|
2535
|
+
if (e.preventDefault) e.preventDefault();
|
|
2536
|
+
}, []);
|
|
2537
|
+
(0, import_react2.useEffect)(() => {
|
|
2538
|
+
const onMove = (ev) => {
|
|
2539
|
+
const d = dragRef.current;
|
|
2540
|
+
if (!d) return;
|
|
2541
|
+
const dx = ev.clientX - d.startX, dy = ev.clientY - d.startY;
|
|
2542
|
+
const vw = window.innerWidth, vh = window.innerHeight;
|
|
2543
|
+
if (d.kind === "move") {
|
|
2544
|
+
const x = Math.min(Math.max(0, d.left + dx), Math.max(0, vw - 80));
|
|
2545
|
+
const y = Math.min(Math.max(0, d.top + dy), Math.max(0, vh - 40));
|
|
2546
|
+
setGeom({ x, y, w: d.w, h: d.h });
|
|
2547
|
+
} else {
|
|
2548
|
+
const w = Math.max(560, Math.min(d.w + dx, vw - 16));
|
|
2549
|
+
const h = Math.max(360, Math.min(d.h + dy, vh - 16));
|
|
2550
|
+
setGeom({ x: d.left, y: d.top, w, h });
|
|
2551
|
+
}
|
|
2552
|
+
};
|
|
2553
|
+
const onUp = () => {
|
|
2554
|
+
dragRef.current = null;
|
|
2555
|
+
};
|
|
2556
|
+
window.addEventListener("pointermove", onMove);
|
|
2557
|
+
window.addEventListener("pointerup", onUp);
|
|
2558
|
+
return () => {
|
|
2559
|
+
window.removeEventListener("pointermove", onMove);
|
|
2560
|
+
window.removeEventListener("pointerup", onUp);
|
|
2561
|
+
};
|
|
2562
|
+
}, []);
|
|
2563
|
+
(0, import_react2.useEffect)(() => {
|
|
2564
|
+
const onKey = (e) => {
|
|
2565
|
+
if (e.key === "Escape") onClose();
|
|
2566
|
+
};
|
|
2567
|
+
window.addEventListener("keydown", onKey);
|
|
2568
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
2569
|
+
}, [onClose]);
|
|
2570
|
+
const style = maxed ? { position: "fixed", left: 8, top: 8, right: 8, bottom: 8, width: "auto", height: "auto" } : geom ? { position: "fixed", left: geom.x, top: geom.y, width: geom.w, height: geom.h } : null;
|
|
2571
|
+
const portalTarget = typeof document !== "undefined" && document.body ? document.body : null;
|
|
2572
|
+
const overlay = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-pm-backdrop", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: dlgRef, className: "dtok-pm" + (maxed ? " max" : ""), style, onClick: (e) => e.stopPropagation(), children: [
|
|
2573
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-pm-head", onPointerDown: (e) => startDrag(e, "move"), onDoubleClick: () => setMaxed((v) => !v), children: [
|
|
2574
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "\u5355\u4EF7\u8BBE\u7F6E" }),
|
|
2575
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-pm-sub", children: "\u4EBA\u6C11\u5E01\u5143 / \u767E\u4E07 tokens \xB7 \u81EA\u5B9A\u4E49\u5355\u4EF7\u4F18\u5148\u4E8E\u5B98\u7F51\u4EF7\u4E0E\u5185\u7F6E\u4EF7" }),
|
|
2576
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dtok-pm-ctrls", children: [
|
|
2577
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dtok-btn tiny", title: maxed ? "\u8FD8\u539F" : "\u6700\u5927\u5316", onClick: () => setMaxed((v) => !v), children: maxed ? "\u2750" : "\u25A2" }),
|
|
2578
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dtok-btn tiny", title: "\u5173\u95ED", onClick: onClose, children: "\u2715" })
|
|
2579
|
+
] })
|
|
2580
|
+
] }),
|
|
2581
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-pm-body", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PricingEditor, { onClose, onSaved, embedded: true }) }),
|
|
2582
|
+
maxed ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-pm-resize", title: "\u62D6\u52A8\u7F29\u653E", onPointerDown: (e) => startDrag(e, "resize") })
|
|
2583
|
+
] }) });
|
|
2584
|
+
return portalTarget && typeof import_react2.default.createPortal === "function" ? import_react2.default.createPortal(overlay, portalTarget) : overlay;
|
|
2585
|
+
}
|
|
2586
|
+
function PricingEditor({ onClose, onSaved, embedded }) {
|
|
2587
|
+
const [cfg, setCfg] = (0, import_react2.useState)(null);
|
|
2588
|
+
const [rows, setRows] = (0, import_react2.useState)([]);
|
|
2589
|
+
const [rate, setRate] = (0, import_react2.useState)("7.2");
|
|
2590
|
+
const [fetchOn, setFetchOn] = (0, import_react2.useState)(true);
|
|
2591
|
+
const [fb, setFb] = (0, import_react2.useState)({ input: "", output: "", cacheRead: "", cacheWrite: "" });
|
|
2592
|
+
const [newMatch, setNewMatch] = (0, import_react2.useState)("");
|
|
2593
|
+
const [msg, setMsg] = (0, import_react2.useState)(null);
|
|
2594
|
+
const [saving, setSaving] = (0, import_react2.useState)(false);
|
|
2595
|
+
(0, import_react2.useEffect)(() => {
|
|
2596
|
+
let cancel = false;
|
|
2597
|
+
rpcCall("pricing", {}).then((d) => {
|
|
2598
|
+
if (cancel) return;
|
|
2599
|
+
setCfg(d);
|
|
2600
|
+
setRate(numStr(d.usdCnyRate));
|
|
2601
|
+
setFetchOn(!!d.fetchOfficial);
|
|
2602
|
+
setFb({
|
|
2603
|
+
input: numStr(d.fallback && d.fallback.input),
|
|
2604
|
+
output: numStr(d.fallback && d.fallback.output),
|
|
2605
|
+
cacheRead: numStr(d.fallback && d.fallback.cacheRead),
|
|
2606
|
+
cacheWrite: numStr(d.fallback && d.fallback.cacheWrite)
|
|
2607
|
+
});
|
|
2608
|
+
setRows((d.pricing || []).map(rowToDraft));
|
|
2609
|
+
}).catch((e) => {
|
|
2610
|
+
if (!cancel) setMsg({ ok: false, text: String(e && e.message || e) });
|
|
2611
|
+
});
|
|
2612
|
+
return () => {
|
|
2613
|
+
cancel = true;
|
|
2614
|
+
};
|
|
2615
|
+
}, []);
|
|
2616
|
+
const patchRow = (i, patch) => setRows((rs) => rs.map((r, j) => j === i ? Object.assign({}, r, patch) : r));
|
|
2617
|
+
const delRow = (i) => setRows((rs) => rs.filter((_, j) => j !== i));
|
|
2618
|
+
const addSeg = (i) => setRows((rs) => rs.map((r, j) => j === i ? Object.assign({}, r, { peaks: r.peaks.concat([newSegDraft()]) }) : r));
|
|
2619
|
+
const delSeg = (i, k) => setRows((rs) => rs.map((r, j) => j === i ? Object.assign({}, r, { peaks: r.peaks.filter((_, x) => x !== k) }) : r));
|
|
2620
|
+
const patchSeg = (i, k, patch) => setRows((rs) => rs.map((r, j) => j === i ? Object.assign({}, r, { peaks: r.peaks.map((s, x) => x === k ? Object.assign({}, s, patch) : s) }) : r));
|
|
2621
|
+
const addRow = (m) => {
|
|
2622
|
+
const match = String(m === void 0 ? newMatch : m).trim();
|
|
2623
|
+
if (!match) return;
|
|
2624
|
+
if (rows.some((r) => r.match.toLowerCase() === match.toLowerCase())) {
|
|
2625
|
+
setMsg({ ok: false, text: "\u300C" + match + "\u300D\u5DF2\u5B58\u5728" });
|
|
2626
|
+
return;
|
|
2627
|
+
}
|
|
2628
|
+
setRows((rs) => rs.concat([rowToDraft({ match, input: 0, output: 0, cacheRead: 0, cacheWrite: 0 })]));
|
|
2629
|
+
setNewMatch("");
|
|
2630
|
+
setMsg(null);
|
|
2631
|
+
};
|
|
2632
|
+
const save = () => {
|
|
2633
|
+
if (saving) return;
|
|
2634
|
+
const payload = { usdCnyRate: Number(rate), fetchOfficial: fetchOn };
|
|
2635
|
+
payload.pricing = rows.map((r) => {
|
|
2636
|
+
const out = {
|
|
2637
|
+
match: String(r.match || "").trim(),
|
|
2638
|
+
input: Number(r.input || 0),
|
|
2639
|
+
output: Number(r.output || 0),
|
|
2640
|
+
cacheRead: Number(r.cacheRead || 0),
|
|
2641
|
+
cacheWrite: Number(r.cacheWrite || 0)
|
|
2642
|
+
};
|
|
2643
|
+
const peaks = (r.peaks || []).map((s) => ({
|
|
2644
|
+
start: Number(s.start || 0),
|
|
2645
|
+
end: Number(s.end || 0),
|
|
2646
|
+
input: Number(s.input || 0),
|
|
2647
|
+
output: Number(s.output || 0),
|
|
2648
|
+
cacheRead: Number(s.cacheRead || 0),
|
|
2649
|
+
cacheWrite: Number(s.cacheWrite || 0)
|
|
2650
|
+
}));
|
|
2651
|
+
if (peaks.length) out.peaks = peaks;
|
|
2652
|
+
return out;
|
|
2653
|
+
});
|
|
2654
|
+
payload.fallback = {
|
|
2655
|
+
input: Number(fb.input || 0),
|
|
2656
|
+
output: Number(fb.output || 0),
|
|
2657
|
+
cacheRead: Number(fb.cacheRead || 0),
|
|
2658
|
+
cacheWrite: Number(fb.cacheWrite || 0)
|
|
2659
|
+
};
|
|
2660
|
+
setSaving(true);
|
|
2661
|
+
setMsg(null);
|
|
2662
|
+
rpcCall("setpricing", payload).then((d) => {
|
|
2663
|
+
setCfg(d);
|
|
2664
|
+
setRows((d.pricing || []).map(rowToDraft));
|
|
2665
|
+
setMsg({ ok: true, text: "\u5355\u4EF7\u5DF2\u4FDD\u5B58\uFF0C\u8D39\u7528\u5DF2\u6309\u65B0\u4EF7\u5373\u65F6\u91CD\u7B97" });
|
|
2666
|
+
if (onSaved) onSaved();
|
|
2667
|
+
}).catch((e) => setMsg({ ok: false, text: String(e && e.message || e) })).finally(() => setSaving(false));
|
|
2668
|
+
};
|
|
2669
|
+
const configured = rows.map((r) => String(r.match).toLowerCase());
|
|
2670
|
+
const candidates2 = (cfg && cfg.models || []).filter((m) => configured.indexOf(String(m).toLowerCase()) < 0);
|
|
2671
|
+
const srcOf = (m) => cfg && cfg.sourcesByModel && cfg.sourcesByModel[m] || "";
|
|
2672
|
+
const onFallback = (cfg && cfg.models || []).filter((m) => srcOf(m) === "\u515C\u5E95");
|
|
2673
|
+
const num = (value, onChange, extra) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dtok-price-num", type: "number", min: "0", step: "0.01", value, onChange: (e) => onChange(e.target.value), style: extra });
|
|
2674
|
+
const segNum = (value, onChange, placeholder) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2675
|
+
"input",
|
|
2676
|
+
{
|
|
2677
|
+
className: "dtok-price-num",
|
|
2678
|
+
type: "number",
|
|
2679
|
+
min: "0",
|
|
2680
|
+
step: "0.01",
|
|
2681
|
+
value,
|
|
2682
|
+
placeholder,
|
|
2683
|
+
onChange: (e) => onChange(e.target.value),
|
|
2684
|
+
style: { width: 80 }
|
|
2685
|
+
}
|
|
2686
|
+
);
|
|
2687
|
+
const segLabel = (s) => {
|
|
2688
|
+
const a = numStr(s.start), b = numStr(s.end);
|
|
2689
|
+
if (a === "" || b === "") return "\u65F6\u6BB5";
|
|
2690
|
+
return a + "~" + b + (Number(a) === Number(b) ? "\uFF08\u5168\u5929\uFF09" : Number(a) > Number(b) ? "\uFF08\u8DE8\u96F6\u70B9\uFF09" : "");
|
|
2691
|
+
};
|
|
2692
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: embedded ? "dtok-price emb" : "dtok-price", onClick: (e) => e.stopPropagation(), children: [
|
|
2693
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", children: [
|
|
2694
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2695
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "USD\u2192CNY \u6C47\u7387" }),
|
|
2696
|
+
num(rate, setRate)
|
|
2697
|
+
] }),
|
|
2698
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: 12, color: "var(--dsw-alias-label-secondary)" }, children: [
|
|
2699
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: fetchOn, onChange: (e) => setFetchOn(e.target.checked) }),
|
|
2700
|
+
"\u5B98\u7F51\u4EF7\u76EE\u81EA\u52A8\u540C\u6B65\uFF08\u5173\u95ED\u540E\u4EC5\u7528\u81EA\u5B9A\u4E49/\u5185\u7F6E\u4EF7\uFF09"
|
|
2701
|
+
] })
|
|
2702
|
+
] }),
|
|
2703
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
2704
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-section-title", style: { margin: "0 0 4px" }, children: [
|
|
2705
|
+
"\u6309\u6A21\u578B\u914D\u7F6E\u5355\u4EF7\uFF08",
|
|
2706
|
+
rows.length,
|
|
2707
|
+
" \u6761\uFF09"
|
|
2708
|
+
] }),
|
|
2709
|
+
onFallback.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-hint", children: [
|
|
2710
|
+
"\u4EE5\u4E0B\u7528\u8FC7\u7684\u6A21\u578B\u5F53\u524D\u8D70\u300C\u515C\u5E95\u4EF7\u300D\uFF0C\u5EFA\u8BAE\u4F18\u5148\u8865\u5355\u4EF7\uFF1A",
|
|
2711
|
+
onFallback.join("\u3001")
|
|
2712
|
+
] }) : null,
|
|
2713
|
+
rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-price-hint", children: "\u5C1A\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u5355\u4EF7\uFF0C\u5F53\u524D\u4F7F\u7528\u5B98\u7F51\u540C\u6B65\u4EF7 / \u5185\u7F6E\u4EF7 / \u515C\u5E95\u4EF7\u3002" }) : rows.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-prow", children: [
|
|
2714
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-prow-head", children: [
|
|
2715
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dtok-input", style: { maxWidth: 240 }, type: "text", value: r.match, placeholder: "\u6A21\u578B\u5339\u914D\uFF08\u5B50\u4E32\uFF09", onChange: (e) => patchRow(i, { match: e.target.value }) }),
|
|
2716
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dtok-src " + (srcOf(r.match) === "\u81EA\u5B9A\u4E49" ? "custom" : srcOf(r.match) === "\u515C\u5E95" ? "fallback" : ""), children: [
|
|
2717
|
+
"\u5F53\u524D\u6765\u6E90\uFF1A",
|
|
2718
|
+
srcOf(r.match) || "\u2014"
|
|
2719
|
+
] }),
|
|
2720
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn tiny", onClick: () => delRow(i), children: "\u5220\u9664\u6A21\u578B" })
|
|
2721
|
+
] }),
|
|
2722
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", children: [
|
|
2723
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2724
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u57FA\u51C6\u8F93\u5165" }),
|
|
2725
|
+
num(r.input, (v) => patchRow(i, { input: v }))
|
|
2726
|
+
] }),
|
|
2727
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2728
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u57FA\u51C6\u8F93\u51FA" }),
|
|
2729
|
+
num(r.output, (v) => patchRow(i, { output: v }))
|
|
2730
|
+
] }),
|
|
2731
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2732
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u57FA\u51C6\u7F13\u5B58\u547D\u4E2D" }),
|
|
2733
|
+
num(r.cacheRead, (v) => patchRow(i, { cacheRead: v }))
|
|
2734
|
+
] }),
|
|
2735
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2736
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u57FA\u51C6\u7F13\u5B58\u5199\u5165" }),
|
|
2737
|
+
num(r.cacheWrite, (v) => patchRow(i, { cacheWrite: v }))
|
|
2738
|
+
] })
|
|
2739
|
+
] }),
|
|
2740
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-price-hint", style: { marginTop: 2 }, children: "\u5206\u65F6\u6BB5\u4EF7\uFF08\u53EF\u591A\u6BB5\uFF0C\u547D\u4E2D\u54EA\u6BB5\u7528\u54EA\u6BB5\uFF1B\u65F6\u6BB5\u5916\u56DE\u843D\u5230\u57FA\u51C6\u4EF7\u3002start>end \u8868\u793A\u8DE8\u96F6\u70B9\uFF0C\u5982 23~7\uFF1Bstart=end \u8868\u793A\u5168\u5929\uFF09" }),
|
|
2741
|
+
r.peaks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-price-hint", children: "\uFF08\u672A\u8BBE\u7F6E\u5206\u65F6\u6BB5\uFF0C\u6309\u57FA\u51C6\u4EF7\u8BA1\u8D39\uFF09" }) : null,
|
|
2742
|
+
r.peaks.map((s, k) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-pseg", children: [
|
|
2743
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-pseg-name", children: segLabel(s) }),
|
|
2744
|
+
num(s.start, (v) => patchSeg(i, k, { start: v }), { width: 56 }),
|
|
2745
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-pseg-tilde", children: "~" }),
|
|
2746
|
+
num(s.end, (v) => patchSeg(i, k, { end: v }), { width: 56 }),
|
|
2747
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-pseg-tilde", children: "\u65F6" }),
|
|
2748
|
+
segNum(s.input, (v) => patchSeg(i, k, { input: v }), "\u8F93\u5165"),
|
|
2749
|
+
segNum(s.output, (v) => patchSeg(i, k, { output: v }), "\u8F93\u51FA"),
|
|
2750
|
+
segNum(s.cacheRead, (v) => patchSeg(i, k, { cacheRead: v }), "\u7F13\u5B58\u547D\u4E2D"),
|
|
2751
|
+
segNum(s.cacheWrite, (v) => patchSeg(i, k, { cacheWrite: v }), "\u7F13\u5B58\u5199\u5165"),
|
|
2752
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn tiny", onClick: () => delSeg(i, k), children: "\u5220\u9664\u65F6\u6BB5" })
|
|
2753
|
+
] }, k)),
|
|
2754
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-price-hint", style: { opacity: 0.85 }, children: "\u65F6\u6BB5\u4EF7\u4ECE\u5DE6\u5230\u53F3\uFF1A\u8F93\u5165 / \u8F93\u51FA / \u7F13\u5B58\u547D\u4E2D / \u7F13\u5B58\u5199\u5165" }),
|
|
2755
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn tiny", onClick: () => addSeg(i), children: "+ \u6DFB\u52A0\u65F6\u6BB5" })
|
|
2756
|
+
] }, i)),
|
|
2757
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", style: { marginTop: 6 }, children: [
|
|
2758
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", style: { flex: "0 0 220px" }, children: [
|
|
2759
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u65B0\u589E\u6A21\u578B\u5339\u914D" }),
|
|
2760
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dtok-input", style: { maxWidth: 220 }, type: "text", list: "dtok-model-candidates", placeholder: "\u5982 deepseek-v4-flash \u6216\u67D0\u4E2D\u8F6C\u6A21\u578B\u540D", value: newMatch, onChange: (e) => setNewMatch(e.target.value) }),
|
|
2761
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("datalist", { id: "dtok-model-candidates", children: candidates2.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: m }, m)) })
|
|
2762
|
+
] }),
|
|
2763
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", onClick: () => addRow(), children: "+ \u6DFB\u52A0" })
|
|
2764
|
+
] }),
|
|
2765
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-hint", children: [
|
|
2766
|
+
"\u63D0\u793A\uFF1A\u5339\u914D\u662F\u300C\u6A21\u578B\u540D\u5305\u542B\u8BE5\u5B50\u4E32\u300D\uFF0C\u53EF\u53EA\u5199\u5173\u952E\u7247\u6BB5\uFF08\u5982 ",
|
|
2767
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "v4-flash" }),
|
|
2768
|
+
"\uFF09\uFF1B\u8D8A\u5177\u4F53\u7684\u6761\u76EE\u5EFA\u8BAE\u653E\u8D8A\u524D\uFF08\u5F53\u524D\u6309\u5217\u8868\u987A\u5E8F\u547D\u4E2D\uFF09\u3002"
|
|
2769
|
+
] })
|
|
2770
|
+
] }),
|
|
2771
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
2772
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-section-title", style: { margin: "0 0 4px" }, children: "\u515C\u5E95\u5355\u4EF7\uFF08\u672A\u5339\u914D\u4EFB\u4F55\u6761\u76EE\u65F6\u4F7F\u7528\uFF09" }),
|
|
2773
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", children: [
|
|
2774
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2775
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u8F93\u5165" }),
|
|
2776
|
+
num(fb.input, (v) => setFb((s) => Object.assign({}, s, { input: v })))
|
|
2777
|
+
] }),
|
|
2778
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2779
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u8F93\u51FA" }),
|
|
2780
|
+
num(fb.output, (v) => setFb((s) => Object.assign({}, s, { output: v })))
|
|
2781
|
+
] }),
|
|
2782
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2783
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u7F13\u5B58\u547D\u4E2D" }),
|
|
2784
|
+
num(fb.cacheRead, (v) => setFb((s) => Object.assign({}, s, { cacheRead: v })))
|
|
2785
|
+
] }),
|
|
2786
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2787
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u7F13\u5B58\u5199\u5165" }),
|
|
2788
|
+
num(fb.cacheWrite, (v) => setFb((s) => Object.assign({}, s, { cacheWrite: v })))
|
|
2789
|
+
] })
|
|
2790
|
+
] })
|
|
2791
|
+
] }),
|
|
2792
|
+
cfg && cfg.builtin && cfg.builtin.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { children: [
|
|
2793
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("summary", { className: "dtok-price-hint", style: { cursor: "pointer" }, children: [
|
|
2794
|
+
"\u67E5\u770B\u5185\u7F6E\u9ED8\u8BA4\u5355\u4EF7\uFF08",
|
|
2795
|
+
cfg.builtin.length,
|
|
2796
|
+
" \u6761\uFF0C\u81EA\u5B9A\u4E49\u914D\u7F6E\u672A\u547D\u4E2D\u65F6\u6309\u6B64\u515C\u5E95\uFF09"
|
|
2797
|
+
] }),
|
|
2798
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-table-wrap", style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "dtok-table", children: [
|
|
2799
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
2800
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u5339\u914D" }),
|
|
2801
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165" }),
|
|
2802
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u51FA" }),
|
|
2803
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7F13\u5B58\u547D\u4E2D" }),
|
|
2804
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7F13\u5B58\u5199\u5165" }),
|
|
2805
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u5206\u65F6\u6BB5" })
|
|
2806
|
+
] }) }),
|
|
2807
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: cfg.builtin.map((b) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
2808
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.match }),
|
|
2809
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.input }),
|
|
2810
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.output }),
|
|
2811
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.cacheRead }),
|
|
2812
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.cacheWrite }),
|
|
2813
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.peaks && b.peaks.length ? b.peaks.map((s) => s.start + "~" + s.end).join("\u3001") + "\u65F6" : "\u2014" })
|
|
2814
|
+
] }, b.match)) })
|
|
2815
|
+
] }) })
|
|
2816
|
+
] }) : null,
|
|
2817
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", children: [
|
|
2818
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn primary", disabled: saving, onClick: save, children: saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u5355\u4EF7" }),
|
|
2819
|
+
msg ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-price-msg " + (msg.ok ? "ok" : "err"), children: msg.text }) : null
|
|
2820
|
+
] })
|
|
2821
|
+
] });
|
|
2822
|
+
}
|
|
2463
2823
|
function TokenLogView(props) {
|
|
2464
2824
|
const navSession = props && props.params && props.params.sessionId ? props.params.sessionId : null;
|
|
2465
2825
|
const [savedFilters] = (0, import_react2.useState)(loadSavedFilters);
|
|
@@ -2478,6 +2838,7 @@ function TokenLogView(props) {
|
|
|
2478
2838
|
const [err, setErr] = (0, import_react2.useState)("");
|
|
2479
2839
|
const [page, setPage] = (0, import_react2.useState)(0);
|
|
2480
2840
|
const [detailRec, setDetailRec] = (0, import_react2.useState)(null);
|
|
2841
|
+
const [showPricing, setShowPricing] = (0, import_react2.useState)(() => !!(props && props.params && props.params.openPricing));
|
|
2481
2842
|
const pageSize = 100;
|
|
2482
2843
|
(0, import_react2.useEffect)(() => {
|
|
2483
2844
|
saveFilters({ fromStr, toStr, provider, model, status, effort, sessionId, dim });
|
|
@@ -2648,7 +3009,13 @@ function TokenLogView(props) {
|
|
|
2648
3009
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.outputTokens) }),
|
|
2649
3010
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.reasoningTokens) }),
|
|
2650
3011
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.totalTokens) }),
|
|
2651
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
3012
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { title: r.pricingSource ? "\u8BA1\u4EF7\u6765\u6E90\uFF1A" + ({ custom: "\u81EA\u5B9A\u4E49\u5355\u4EF7", official: "\u5B98\u7F51\u540C\u6B65\u4EF7", builtin: "\u5185\u7F6E\u9ED8\u8BA4\u4EF7", fallback: "\u515C\u5E95\u5355\u4EF7" }[r.pricingSource] || r.pricingSource) : "", children: [
|
|
3013
|
+
fmtCostCny(r.cost, rateCny),
|
|
3014
|
+
r.pricingSource === "fallback" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dtok-src fallback", children: [
|
|
3015
|
+
" ",
|
|
3016
|
+
"\u515C\u5E95"
|
|
3017
|
+
] }) : null
|
|
3018
|
+
] }),
|
|
2652
3019
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.effort || "\u2014" }),
|
|
2653
3020
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
|
|
2654
3021
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-code " + st.cls, children: st.label }) }),
|
|
@@ -2779,8 +3146,16 @@ function TokenLogView(props) {
|
|
|
2779
3146
|
] }),
|
|
2780
3147
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn primary", onClick: runQuery, children: "\u67E5\u8BE2" }),
|
|
2781
3148
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", onClick: resetFilters, children: "\u91CD\u7F6E" }),
|
|
2782
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", onClick: exportCsv, children: "\u5BFC\u51FA CSV" })
|
|
3149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", onClick: exportCsv, children: "\u5BFC\u51FA CSV" }),
|
|
3150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn" + (showPricing ? " primary" : ""), onClick: () => setShowPricing(true), title: "\u914D\u7F6E\u5404\u6A21\u578B\u5355\u4EF7\uFF0C\u8D39\u7528\u6309\u81EA\u586B\u5355\u4EF7\u8BA1\u7B97", children: "\u5355\u4EF7\u8BBE\u7F6E" })
|
|
2783
3151
|
] }),
|
|
3152
|
+
showPricing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PriceModal, { onClose: () => setShowPricing(false), onSaved: runQuery }) : null,
|
|
3153
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-status", children: data && data.pricingInfo ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
3154
|
+
"\u8BA1\u4EF7\uFF1A",
|
|
3155
|
+
data.pricingInfo.hasCustom ? "\u81EA\u5B9A\u4E49\u5355\u4EF7 " + data.pricingInfo.customCount + " \u6761" : "\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u5355\u4EF7",
|
|
3156
|
+
" \xB7 \u5B98\u7F51\u81EA\u52A8\u540C\u6B65\uFF1A" + (data.pricingInfo.fetchOfficial ? "\u5F00" : "\u5173"),
|
|
3157
|
+
data.pricingInfo.sources && data.pricingInfo.sources.length ? " \xB7 \u672C\u9875\u6D89\u53CA\u6765\u6E90\uFF1A" + data.pricingInfo.sources.join("/") : ""
|
|
3158
|
+
] }) : null }),
|
|
2784
3159
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-body", children: bodyNodes }),
|
|
2785
3160
|
detailRec ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Detail, { rec: detailRec, onClose: () => setDetailRec(null), rate: rateCny }) : null
|
|
2786
3161
|
] });
|
|
@@ -2859,7 +3234,7 @@ var feature = {
|
|
|
2859
3234
|
name: "\u7528\u91CF\u8BB0\u5F55",
|
|
2860
3235
|
order: 110,
|
|
2861
3236
|
accent: "#fbbf24",
|
|
2862
|
-
description: "\u8BB0\u5F55\u5168\u90E8 LLM API \u8C03\u7528\uFF1A\u79D2\u7EA7\u65F6\u95F4\u7B5B\u9009\u3001Token/\u8D39\u7528\u7EDF\u8BA1\uFF08\u5CF0\u8C37\u8BA1\u4EF7+\u5B98\u7F51\u4EF7\u76EE\u81EA\u52A8\u540C\u6B65\uFF09\u3001\u5206\u7EC4\u6C47\u603B\u3001\u660E\u7EC6\u68C0\u7D22\u4E0E CSV \u5BFC\u51FA",
|
|
3237
|
+
description: "\u8BB0\u5F55\u5168\u90E8 LLM API \u8C03\u7528\uFF1A\u79D2\u7EA7\u65F6\u95F4\u7B5B\u9009\u3001Token/\u8D39\u7528\u7EDF\u8BA1\uFF08\u53EF\u914D\u7F6E\u5404\u6A21\u578B\u5355\u4EF7\uFF0C\u6301\u4E45\u4FDD\u5B58\u5E76\u6309\u81EA\u586B\u5355\u4EF7\u8BA1\u8D39\uFF1B\u5185\u7F6E\u5CF0\u8C37\u8BA1\u4EF7+\u5B98\u7F51\u4EF7\u76EE\u81EA\u52A8\u540C\u6B65\u4F5C\u515C\u5E95\uFF09\u3001\u5206\u7EC4\u6C47\u603B\u3001\u660E\u7EC6\u68C0\u7D22\u4E0E CSV \u5BFC\u51FA",
|
|
2863
3238
|
css,
|
|
2864
3239
|
View: TokenLogView,
|
|
2865
3240
|
HomeStat: TokenLogHomeStat,
|
|
@@ -10503,7 +10878,7 @@ var feature10 = {
|
|
|
10503
10878
|
};
|
|
10504
10879
|
|
|
10505
10880
|
// src/client.jsx
|
|
10506
|
-
var DOCK_VERSION = "0.
|
|
10881
|
+
var DOCK_VERSION = "0.11.0";
|
|
10507
10882
|
var BUILTIN_FEATURES = [feature, feature2, feature3, feature4, feature5, feature6, feature7, feature8, feature9, feature10];
|
|
10508
10883
|
var PLANNED_FEATURES = [];
|
|
10509
10884
|
var PLANNED_NOTES = {};
|
|
@@ -11035,7 +11410,7 @@ function HomeView(props) {
|
|
|
11035
11410
|
})
|
|
11036
11411
|
);
|
|
11037
11412
|
}
|
|
11038
|
-
function DockPanel() {
|
|
11413
|
+
function DockPanel(props) {
|
|
11039
11414
|
const ctx = ctxRef.current;
|
|
11040
11415
|
const [, force] = import_react22.default.useReducer((n) => n + 1, 0);
|
|
11041
11416
|
useExternalVersion();
|
|
@@ -11061,7 +11436,7 @@ function DockPanel() {
|
|
|
11061
11436
|
import_react22.default.createElement(
|
|
11062
11437
|
FeatureBoundary,
|
|
11063
11438
|
{ key: f.id, label: f.name },
|
|
11064
|
-
import_react22.default.createElement(View, { ctx, feature: f })
|
|
11439
|
+
import_react22.default.createElement(View, { ctx, feature: f, params: props && props.params })
|
|
11065
11440
|
)
|
|
11066
11441
|
) : null;
|
|
11067
11442
|
return import_react22.default.createElement(
|
|
@@ -11164,7 +11539,7 @@ function apply(ctx) {
|
|
|
11164
11539
|
));
|
|
11165
11540
|
slots.inject("settings.section", () => slots.register(
|
|
11166
11541
|
{ name: "settings.section", id: "dsh-dock", order: 90, label: "\u529F\u80FD\u575E" },
|
|
11167
|
-
() => import_react22.default.createElement(DockPanel,
|
|
11542
|
+
(props) => import_react22.default.createElement(DockPanel, props)
|
|
11168
11543
|
));
|
|
11169
11544
|
slots.inject("conversation.input.left", () => slots.register(
|
|
11170
11545
|
{ name: "conversation.input.left", id: "dsh-dock-chips", order: 10, label: "\u529F\u80FD\u575E" },
|