dsh-dock 0.10.3 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/client.js +394 -18
- package/features/tokenlog/host.js +292 -99
- package/features/tokenlog/view.jsx +374 -13
- package/package.json +3 -3
- package/src/client.jsx +5 -4
- package/src/host-core.js +38 -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 种任务运行动画(含星际远征、星云潮汐、曲速航道、量子雷达、星座网络、深海脉动、神经电路等),速度随任务活动联动,配置全部持久化 |
|
|
@@ -109,6 +109,7 @@ dsh plugin --profile web remove dsh-dock # 卸载
|
|
|
109
109
|
4. 「分组汇总」按 无 / 提供商 / 模型 / 状态 / 强度 维度切换
|
|
110
110
|
5. 明细表 15 列可排序,100 行/页,点击「会话 ID」即筛选该会话;行右侧「详情」看单次调用信息
|
|
111
111
|
6. 「CSV 导出」下载当前筛选结果
|
|
112
|
+
7. **费用不准?点「单价设置」按模型填真实单价**:官网刊例价是抓来的,与实际计费(第三方中转、折扣、自建端点)常有出入。点开独立的「单价设置」子弹窗(可最大化、拖动、缩放),可按模型逐条配置单价(人民币元 / 百万 tokens,匹配模型名子串即可),并设置兜底单价、USD→CNY 汇率、是否继续自动同步官网价。每个模型还可配置**多段分时价**(如 DeepSeek 的高峰/优惠时段),命中哪段用哪段、时段外回落基准价,支持跨零点(如 `23~7`)与半点边界(如 `8.5` = 08:30)。保存后写入宿主配置**持久保存**,费用立即按自填单价重算(无需重扫历史);计价优先级:**自定义单价 > 官网同步价 > 内置价 > 兜底价**。明细表里走兜底价的金额会标「兜底」,方便你补配。
|
|
112
113
|
|
|
113
114
|
**模型余额**
|
|
114
115
|
|
|
@@ -265,6 +266,7 @@ dsh plugin --profile web remove dsh-dock # 卸载
|
|
|
265
266
|
4. 「分组汇总」按 无 / 提供商 / 模型 / 状态 / 强度 维度切换
|
|
266
267
|
5. 明细表 15 列可排序,100 行/页,点击「会话 ID」即筛选该会话;行右侧「详情」看单次调用信息
|
|
267
268
|
6. 「CSV 导出」下载当前筛选结果
|
|
269
|
+
7. **费用不准?点「单价设置」按模型填真实单价**:官网刊例价是抓来的,与实际计费(第三方中转、折扣、自建端点)常有出入。点开独立的「单价设置」子弹窗(可最大化、拖动、缩放),可按模型逐条配置单价(人民币元 / 百万 tokens,匹配模型名子串即可),并设置兜底单价、USD→CNY 汇率、是否继续自动同步官网价。每个模型还可配置**多段分时价**(如 DeepSeek 的高峰/优惠时段),命中哪段用哪段、时段外回落基准价,支持跨零点(如 `23~7`)与半点边界(如 `8.5` = 08:30)。保存后写入宿主配置**持久保存**,费用立即按自填单价重算(无需重扫历史);计价优先级:**自定义单价 > 官网同步价 > 内置价 > 兜底价**。明细表里走兜底价的金额会标「兜底」,方便你补配。
|
|
268
270
|
|
|
269
271
|
**模型余额**
|
|
270
272
|
|
|
@@ -346,6 +348,7 @@ dsh plugin --profile web remove dsh-dock # 卸载
|
|
|
346
348
|
- **功能开关刷新后重置**:v0.5.0 起功能开关已持久化(浏览器本地存储),刷新/重启后保持。
|
|
347
349
|
- **任务动画/任务通知/运行状态不生效**:三者的路由都在宿主进程内(动画/通知的配置持久化也在宿主),旧宿主需重启 `dsh web` 加载(面板页会提示「宿主进程是旧版本」)。另注意通知与运行状态已各自独立成菜单项,需在功能坞里分别开启「任务通知」「运行状态」。
|
|
348
350
|
- **用量记录空白**:确认 dsh 是较新版本且插件宿主已加载(查看启动日志含 `[dsh-dock] host half loaded`);旧宿主进程需重启 dsh web 加载新路由。
|
|
351
|
+
- **费用不准 / 与实际账单对不上**:金额是按单价估算的,官网抓取来的刊例价不代表你的实际计费。在用量记录页点「单价设置」(独立子弹窗,可最大化/拖动/缩放),按模型填真实单价并保存(持久化到宿主配置),费用会立即按自填单价重算;每个模型还支持多段分时价(跨零点 / 半点边界)。计价优先级为「自定义单价 > 官网同步价 > 内置价 > 兜底价」,明细里走兜底价的金额会标「兜底」。
|
|
349
352
|
- **余额显示「需登录 / 无密钥」**:对应 Provider 未在 dsh 凭据中配置可查询余额的密钥,属正常状态提示。
|
|
350
353
|
- **切模型 / 打开历史会话卡住,报 `preset "…" failed to mount`**:这是该 **agent 预设**与 dsh 版本
|
|
351
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);
|
|
@@ -2438,15 +2470,16 @@ function Detail({ rec, onClose, rate }) {
|
|
|
2438
2470
|
["\u9519\u8BEF\u4FE1\u606F", rec.errorMsg || "\u2014"],
|
|
2439
2471
|
["\u9519\u8BEF\u7801", rec.errorCode || "\u2014"],
|
|
2440
2472
|
["Request ID", rec.requestId || "\u2014"],
|
|
2441
|
-
["\u8F93\u5165 Token(\
|
|
2473
|
+
["\u8F93\u5165 Token(\u7F13\u5B58\u547D\u4E2D)", fmtNum(rec.cacheReadTokens)],
|
|
2474
|
+
["\u8F93\u5165 Token(\u7F13\u5B58\u672A\u547D\u4E2D)", fmtNum(rec.inputTokens)],
|
|
2442
2475
|
["\u8F93\u51FA Token", fmtNum(rec.outputTokens)],
|
|
2443
|
-
["\u7F13\u5B58\u547D\u4E2D Token", fmtNum(rec.cacheReadTokens)],
|
|
2444
2476
|
["\u7F13\u5B58\u5199\u5165 Token", fmtNum(rec.cacheWriteTokens)],
|
|
2445
2477
|
["\u63A8\u7406 Token", fmtNum(rec.reasoningTokens)],
|
|
2446
2478
|
["\u8BA1\u8D39\u8F93\u5165", fmtNum(rec.billedInput)],
|
|
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,334 @@ 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\uFF08\u7F13\u5B58\u547D\u4E2D\uFF09" }),
|
|
2725
|
+
num(r.cacheRead, (v) => patchRow(i, { cacheRead: 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\u5165\uFF08\u7F13\u5B58\u672A\u547D\u4E2D\uFF09" }),
|
|
2729
|
+
num(r.input, (v) => patchRow(i, { input: 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\u8F93\u51FA" }),
|
|
2733
|
+
num(r.output, (v) => patchRow(i, { output: 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: "\u7F13\u5B58\u5199\u5165\uFF1D\u672A\u547D\u4E2D\u7F13\u5B58\u3001\u672C\u6B21\u65B0\u5EFA\u7F13\u5B58\u7684\u90A3\u90E8\u5206\u8F93\u5165\uFF08Claude \u7B49\u6309\u6EA2\u4EF7\u5355\u72EC\u6536\u5EFA\u7F13\u5B58\u8D39\uFF09\uFF1BDeepSeek \u4E0D\u6536\u6B64\u9879\uFF0C\u586B 0 \u5373\u53EF\u3002" }),
|
|
2741
|
+
/* @__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" }),
|
|
2742
|
+
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,
|
|
2743
|
+
r.peaks.map((s, k) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-pseg", children: [
|
|
2744
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-pseg-name", children: segLabel(s) }),
|
|
2745
|
+
num(s.start, (v) => patchSeg(i, k, { start: v }), { width: 56 }),
|
|
2746
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-pseg-tilde", children: "~" }),
|
|
2747
|
+
num(s.end, (v) => patchSeg(i, k, { end: v }), { width: 56 }),
|
|
2748
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-pseg-tilde", children: "\u65F6" }),
|
|
2749
|
+
segNum(s.cacheRead, (v) => patchSeg(i, k, { cacheRead: v }), "\u7F13\u5B58\u547D\u4E2D"),
|
|
2750
|
+
segNum(s.input, (v) => patchSeg(i, k, { input: v }), "\u672A\u547D\u4E2D"),
|
|
2751
|
+
segNum(s.output, (v) => patchSeg(i, k, { output: v }), "\u8F93\u51FA"),
|
|
2752
|
+
segNum(s.cacheWrite, (v) => patchSeg(i, k, { cacheWrite: v }), "\u5199\u5165"),
|
|
2753
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn tiny", onClick: () => delSeg(i, k), children: "\u5220\u9664\u65F6\u6BB5" })
|
|
2754
|
+
] }, k)),
|
|
2755
|
+
/* @__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\uFF08\u547D\u4E2D\uFF09/ \u8F93\u5165\uFF08\u672A\u547D\u4E2D\uFF09/ \u8F93\u51FA / \u7F13\u5B58\u5199\u5165" }),
|
|
2756
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn tiny", onClick: () => addSeg(i), children: "+ \u6DFB\u52A0\u65F6\u6BB5" })
|
|
2757
|
+
] }, i)),
|
|
2758
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", style: { marginTop: 6 }, children: [
|
|
2759
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", style: { flex: "0 0 220px" }, children: [
|
|
2760
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u65B0\u589E\u6A21\u578B\u5339\u914D" }),
|
|
2761
|
+
/* @__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) }),
|
|
2762
|
+
/* @__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)) })
|
|
2763
|
+
] }),
|
|
2764
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", onClick: () => addRow(), children: "+ \u6DFB\u52A0" })
|
|
2765
|
+
] }),
|
|
2766
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-hint", children: [
|
|
2767
|
+
"\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 ",
|
|
2768
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "v4-flash" }),
|
|
2769
|
+
"\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"
|
|
2770
|
+
] })
|
|
2771
|
+
] }),
|
|
2772
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
2773
|
+
/* @__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" }),
|
|
2774
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", children: [
|
|
2775
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2776
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u8F93\u5165\uFF08\u7F13\u5B58\u547D\u4E2D\uFF09" }),
|
|
2777
|
+
num(fb.cacheRead, (v) => setFb((s) => Object.assign({}, s, { cacheRead: v })))
|
|
2778
|
+
] }),
|
|
2779
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2780
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u8F93\u5165\uFF08\u7F13\u5B58\u672A\u547D\u4E2D\uFF09" }),
|
|
2781
|
+
num(fb.input, (v) => setFb((s) => Object.assign({}, s, { input: v })))
|
|
2782
|
+
] }),
|
|
2783
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2784
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u8F93\u51FA" }),
|
|
2785
|
+
num(fb.output, (v) => setFb((s) => Object.assign({}, s, { output: v })))
|
|
2786
|
+
] }),
|
|
2787
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
|
|
2788
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u7F13\u5B58\u5199\u5165" }),
|
|
2789
|
+
num(fb.cacheWrite, (v) => setFb((s) => Object.assign({}, s, { cacheWrite: v })))
|
|
2790
|
+
] })
|
|
2791
|
+
] })
|
|
2792
|
+
] }),
|
|
2793
|
+
cfg && cfg.builtin && cfg.builtin.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { children: [
|
|
2794
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("summary", { className: "dtok-price-hint", style: { cursor: "pointer" }, children: [
|
|
2795
|
+
"\u67E5\u770B\u5185\u7F6E\u9ED8\u8BA4\u5355\u4EF7\uFF08",
|
|
2796
|
+
cfg.builtin.length,
|
|
2797
|
+
" \u6761\uFF0C\u81EA\u5B9A\u4E49\u914D\u7F6E\u672A\u547D\u4E2D\u65F6\u6309\u6B64\u515C\u5E95\uFF09"
|
|
2798
|
+
] }),
|
|
2799
|
+
/* @__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: [
|
|
2800
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
2801
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u5339\u914D" }),
|
|
2802
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u547D\u4E2D)" }),
|
|
2803
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u672A\u547D\u4E2D)" }),
|
|
2804
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u51FA" }),
|
|
2805
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7F13\u5B58\u5199\u5165" }),
|
|
2806
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u5206\u65F6\u6BB5" })
|
|
2807
|
+
] }) }),
|
|
2808
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: cfg.builtin.map((b) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
2809
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.match }),
|
|
2810
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.cacheRead }),
|
|
2811
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.input }),
|
|
2812
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.output }),
|
|
2813
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.cacheWrite }),
|
|
2814
|
+
/* @__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" })
|
|
2815
|
+
] }, b.match)) })
|
|
2816
|
+
] }) })
|
|
2817
|
+
] }) : null,
|
|
2818
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", children: [
|
|
2819
|
+
/* @__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" }),
|
|
2820
|
+
msg ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-price-msg " + (msg.ok ? "ok" : "err"), children: msg.text }) : null
|
|
2821
|
+
] })
|
|
2822
|
+
] });
|
|
2823
|
+
}
|
|
2463
2824
|
function TokenLogView(props) {
|
|
2464
2825
|
const navSession = props && props.params && props.params.sessionId ? props.params.sessionId : null;
|
|
2465
2826
|
const [savedFilters] = (0, import_react2.useState)(loadSavedFilters);
|
|
@@ -2478,6 +2839,7 @@ function TokenLogView(props) {
|
|
|
2478
2839
|
const [err, setErr] = (0, import_react2.useState)("");
|
|
2479
2840
|
const [page, setPage] = (0, import_react2.useState)(0);
|
|
2480
2841
|
const [detailRec, setDetailRec] = (0, import_react2.useState)(null);
|
|
2842
|
+
const [showPricing, setShowPricing] = (0, import_react2.useState)(() => !!(props && props.params && props.params.openPricing));
|
|
2481
2843
|
const pageSize = 100;
|
|
2482
2844
|
(0, import_react2.useEffect)(() => {
|
|
2483
2845
|
saveFilters({ fromStr, toStr, provider, model, status, effort, sessionId, dim });
|
|
@@ -2608,8 +2970,8 @@ function TokenLogView(props) {
|
|
|
2608
2970
|
const cards = totals ? [
|
|
2609
2971
|
{ v: fmtNum(totals.calls), l: "\u8C03\u7528\u6B21\u6570" },
|
|
2610
2972
|
{ v: fmtCompact(totals.totalTokens), l: "\u603B Token" },
|
|
2611
|
-
{ v: fmtCompact(totals.
|
|
2612
|
-
{ v: fmtCompact(totals.
|
|
2973
|
+
{ v: fmtCompact(totals.cacheReadTokens), l: "\u8F93\u5165(\u547D\u4E2D)" },
|
|
2974
|
+
{ v: fmtCompact(totals.inputTokens), l: "\u8F93\u5165(\u672A\u547D\u4E2D)" },
|
|
2613
2975
|
{ v: totals.cacheHitPct + "%", l: "\u7F13\u5B58\u547D\u4E2D\u7387" },
|
|
2614
2976
|
{ v: fmtCompact(totals.outputTokens), l: "\u8F93\u51FA" },
|
|
2615
2977
|
{ v: fmtCostCny(totals.cost, rateCny), l: "\u6D88\u8017\u91D1\u989D(\u4F30\u7B97\xB7\u4EBA\u6C11\u5E01)" },
|
|
@@ -2620,8 +2982,8 @@ function TokenLogView(props) {
|
|
|
2620
2982
|
const summaryRows = (data && data.summary || []).map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
2621
2983
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.key }),
|
|
2622
2984
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.calls) }),
|
|
2623
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtCompact(r.inputTokens) }),
|
|
2624
2985
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtCompact(r.cacheReadTokens) }),
|
|
2986
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtCompact(r.inputTokens) }),
|
|
2625
2987
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.cacheHitPct + "%" }),
|
|
2626
2988
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtCompact(r.outputTokens) }),
|
|
2627
2989
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtCompact(r.totalTokens) }),
|
|
@@ -2642,13 +3004,19 @@ function TokenLogView(props) {
|
|
|
2642
3004
|
}, children: shortId(r.sessionId) }) }),
|
|
2643
3005
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.provider || "\u2014" }),
|
|
2644
3006
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.model || "\u2014" }),
|
|
2645
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.inputTokens) }),
|
|
2646
3007
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.cacheReadTokens) }),
|
|
3008
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.inputTokens) }),
|
|
2647
3009
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.cacheHitPercent + "%" }),
|
|
2648
3010
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.outputTokens) }),
|
|
2649
3011
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.reasoningTokens) }),
|
|
2650
3012
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.totalTokens) }),
|
|
2651
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
3013
|
+
/* @__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: [
|
|
3014
|
+
fmtCostCny(r.cost, rateCny),
|
|
3015
|
+
r.pricingSource === "fallback" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dtok-src fallback", children: [
|
|
3016
|
+
" ",
|
|
3017
|
+
"\u515C\u5E95"
|
|
3018
|
+
] }) : null
|
|
3019
|
+
] }),
|
|
2652
3020
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.effort || "\u2014" }),
|
|
2653
3021
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
|
|
2654
3022
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-code " + st.cls, children: st.label }) }),
|
|
@@ -2680,8 +3048,8 @@ function TokenLogView(props) {
|
|
|
2680
3048
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
2681
3049
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7EF4\u5EA6" }),
|
|
2682
3050
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8C03\u7528" }),
|
|
2683
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165" }),
|
|
2684
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\
|
|
3051
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u547D\u4E2D)" }),
|
|
3052
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u672A\u547D\u4E2D)" }),
|
|
2685
3053
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u547D\u4E2D\u7387" }),
|
|
2686
3054
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u51FA" }),
|
|
2687
3055
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u603BToken" }),
|
|
@@ -2723,8 +3091,8 @@ function TokenLogView(props) {
|
|
|
2723
3091
|
sortTh("\u4F1A\u8BDDID", "sessionId"),
|
|
2724
3092
|
sortTh("\u63D0\u4F9B\u5546", "provider"),
|
|
2725
3093
|
sortTh("\u6A21\u578B", "model"),
|
|
2726
|
-
sortTh("\u8F93\u5165", "
|
|
2727
|
-
sortTh("\
|
|
3094
|
+
sortTh("\u8F93\u5165(\u547D\u4E2D)", "cacheReadTokens"),
|
|
3095
|
+
sortTh("\u8F93\u5165(\u672A\u547D\u4E2D)", "inputTokens"),
|
|
2728
3096
|
sortTh("\u547D\u4E2D%", "cacheHitPercent"),
|
|
2729
3097
|
sortTh("\u8F93\u51FA", "outputTokens"),
|
|
2730
3098
|
sortTh("\u63A8\u7406", "reasoningTokens"),
|
|
@@ -2779,8 +3147,16 @@ function TokenLogView(props) {
|
|
|
2779
3147
|
] }),
|
|
2780
3148
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn primary", onClick: runQuery, children: "\u67E5\u8BE2" }),
|
|
2781
3149
|
/* @__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" })
|
|
3150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", onClick: exportCsv, children: "\u5BFC\u51FA CSV" }),
|
|
3151
|
+
/* @__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
3152
|
] }),
|
|
3153
|
+
showPricing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PriceModal, { onClose: () => setShowPricing(false), onSaved: runQuery }) : null,
|
|
3154
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-status", children: data && data.pricingInfo ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
3155
|
+
"\u8BA1\u4EF7\uFF1A",
|
|
3156
|
+
data.pricingInfo.hasCustom ? "\u81EA\u5B9A\u4E49\u5355\u4EF7 " + data.pricingInfo.customCount + " \u6761" : "\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u5355\u4EF7",
|
|
3157
|
+
" \xB7 \u5B98\u7F51\u81EA\u52A8\u540C\u6B65\uFF1A" + (data.pricingInfo.fetchOfficial ? "\u5F00" : "\u5173"),
|
|
3158
|
+
data.pricingInfo.sources && data.pricingInfo.sources.length ? " \xB7 \u672C\u9875\u6D89\u53CA\u6765\u6E90\uFF1A" + data.pricingInfo.sources.join("/") : ""
|
|
3159
|
+
] }) : null }),
|
|
2784
3160
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-body", children: bodyNodes }),
|
|
2785
3161
|
detailRec ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Detail, { rec: detailRec, onClose: () => setDetailRec(null), rate: rateCny }) : null
|
|
2786
3162
|
] });
|
|
@@ -2859,7 +3235,7 @@ var feature = {
|
|
|
2859
3235
|
name: "\u7528\u91CF\u8BB0\u5F55",
|
|
2860
3236
|
order: 110,
|
|
2861
3237
|
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",
|
|
3238
|
+
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
3239
|
css,
|
|
2864
3240
|
View: TokenLogView,
|
|
2865
3241
|
HomeStat: TokenLogHomeStat,
|
|
@@ -10503,7 +10879,7 @@ var feature10 = {
|
|
|
10503
10879
|
};
|
|
10504
10880
|
|
|
10505
10881
|
// src/client.jsx
|
|
10506
|
-
var DOCK_VERSION = "0.
|
|
10882
|
+
var DOCK_VERSION = "0.11.1";
|
|
10507
10883
|
var BUILTIN_FEATURES = [feature, feature2, feature3, feature4, feature5, feature6, feature7, feature8, feature9, feature10];
|
|
10508
10884
|
var PLANNED_FEATURES = [];
|
|
10509
10885
|
var PLANNED_NOTES = {};
|
|
@@ -11035,7 +11411,7 @@ function HomeView(props) {
|
|
|
11035
11411
|
})
|
|
11036
11412
|
);
|
|
11037
11413
|
}
|
|
11038
|
-
function DockPanel() {
|
|
11414
|
+
function DockPanel(props) {
|
|
11039
11415
|
const ctx = ctxRef.current;
|
|
11040
11416
|
const [, force] = import_react22.default.useReducer((n) => n + 1, 0);
|
|
11041
11417
|
useExternalVersion();
|
|
@@ -11061,7 +11437,7 @@ function DockPanel() {
|
|
|
11061
11437
|
import_react22.default.createElement(
|
|
11062
11438
|
FeatureBoundary,
|
|
11063
11439
|
{ key: f.id, label: f.name },
|
|
11064
|
-
import_react22.default.createElement(View, { ctx, feature: f })
|
|
11440
|
+
import_react22.default.createElement(View, { ctx, feature: f, params: props && props.params })
|
|
11065
11441
|
)
|
|
11066
11442
|
) : null;
|
|
11067
11443
|
return import_react22.default.createElement(
|
|
@@ -11164,7 +11540,7 @@ function apply(ctx) {
|
|
|
11164
11540
|
));
|
|
11165
11541
|
slots.inject("settings.section", () => slots.register(
|
|
11166
11542
|
{ name: "settings.section", id: "dsh-dock", order: 90, label: "\u529F\u80FD\u575E" },
|
|
11167
|
-
() => import_react22.default.createElement(DockPanel,
|
|
11543
|
+
(props) => import_react22.default.createElement(DockPanel, props)
|
|
11168
11544
|
));
|
|
11169
11545
|
slots.inject("conversation.input.left", () => slots.register(
|
|
11170
11546
|
{ name: "conversation.input.left", id: "dsh-dock-chips", order: 10, label: "\u529F\u80FD\u575E" },
|