dsh-layered-memory 0.8.8 → 0.8.9
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.en.md +21 -1
- package/README.md +25 -1
- package/assets/changelog/0.8.9/01-panel.png +0 -0
- package/assets/changelog/0.8.9/02-halo.png +0 -0
- package/assets/changelog/0.8.9/03-layer-segmented-panel.png +0 -0
- package/assets/changelog/0.8.9/04-layer-l1-panel.png +0 -0
- package/dist/client.js +1275 -749
- package/dist/config.d.ts +237 -1
- package/dist/config.js +19 -0
- package/dist/contract.d.ts +37 -0
- package/dist/hooks/recall.d.ts +11 -0
- package/dist/hooks/recall.js +143 -20
- package/dist/index.d.ts +228 -0
- package/dist/index.js +3 -0
- package/dist/llm.d.ts +34 -8
- package/dist/llm.js +93 -10
- package/dist/pipeline/runner.js +15 -1
- package/dist/settings.d.ts +9 -2
- package/dist/settings.js +51 -25
- package/dist/stats.d.ts +7 -0
- package/dist/stats.js +71 -3
- package/dist/store/occupancy.d.ts +30 -0
- package/dist/store/occupancy.js +134 -0
- package/dist/util/context-occupancy.d.ts +68 -0
- package/dist/util/context-occupancy.js +92 -0
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -31,7 +31,7 @@ var __defProp = Object.defineProperty;
|
|
|
31
31
|
module.exports = __toCommonJS(entry_exports);
|
|
32
32
|
|
|
33
33
|
// client/src/panel.tsx
|
|
34
|
-
var
|
|
34
|
+
var import_react14 = require("react");
|
|
35
35
|
|
|
36
36
|
// client/src/sidebar-icon.ts
|
|
37
37
|
var BOOK_ICON_SVG = '<svg data-mem-icon="1" viewBox="0 0 16 16" width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" style="flex-shrink:0"><path d="M8 3.4C6.6 2.5 4.6 2.4 2.9 3.1v9.3c1.7-.7 3.7-.6 5.1.3 1.4-.9 3.4-1 5.1-.3V3.1C11.4 2.4 9.4 2.5 8 3.4Z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/><path d="M8 3.4v9.3" stroke="currentColor" stroke-width="1.2"/></svg>';
|
|
@@ -524,7 +524,7 @@ var __defProp = Object.defineProperty;
|
|
|
524
524
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
525
525
|
"span",
|
|
526
526
|
{
|
|
527
|
-
title: opt.disabledTitle || "",
|
|
527
|
+
title: opt.disabledTitle || opt.title || "",
|
|
528
528
|
style: {
|
|
529
529
|
...S.segBtn,
|
|
530
530
|
...on ? S.segBtnOn : null,
|
|
@@ -918,7 +918,7 @@ var __defProp = Object.defineProperty;
|
|
|
918
918
|
}
|
|
919
919
|
|
|
920
920
|
// client/src/tabs/OverviewTab.tsx
|
|
921
|
-
var
|
|
921
|
+
var import_react10 = require("react");
|
|
922
922
|
|
|
923
923
|
// client/src/format.ts
|
|
924
924
|
var TYPE_LABELS = {
|
|
@@ -998,6 +998,9 @@ var __defProp = Object.defineProperty;
|
|
|
998
998
|
return 3;
|
|
999
999
|
}
|
|
1000
1000
|
|
|
1001
|
+
// client/src/tabs/DistillSettings.tsx
|
|
1002
|
+
var import_react7 = require("react");
|
|
1003
|
+
|
|
1001
1004
|
// client/src/tabs/BudgetInputs.tsx
|
|
1002
1005
|
var import_react4 = require("react");
|
|
1003
1006
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
@@ -1007,13 +1010,19 @@ var __defProp = Object.defineProperty;
|
|
|
1007
1010
|
["l2", "L2 场景"],
|
|
1008
1011
|
["l3", "L3 画像"]
|
|
1009
1012
|
];
|
|
1013
|
+
var SCOPE_KEYS = {
|
|
1014
|
+
l1: ["extract", "dedup"],
|
|
1015
|
+
l2: ["l2"],
|
|
1016
|
+
l3: ["l3"]
|
|
1017
|
+
};
|
|
1010
1018
|
function BudgetInputs(props) {
|
|
1011
1019
|
const rpc = props.rpc;
|
|
1012
1020
|
const disabled = !!props.disabled;
|
|
1013
1021
|
const data = props.data;
|
|
1014
1022
|
const setData = props.setData;
|
|
1015
1023
|
const onError = props.onError;
|
|
1016
|
-
const
|
|
1024
|
+
const scope = props.scope ?? "all";
|
|
1025
|
+
const layers = scope === "all" || scope === "input" ? LAYERS : LAYERS.filter((l) => SCOPE_KEYS[scope].includes(l[0]));
|
|
1017
1026
|
const [draft, setDraft] = (0, import_react4.useState)(null);
|
|
1018
1027
|
if (!data || !data.budgets) return null;
|
|
1019
1028
|
const cur = data.budgets.current || {};
|
|
@@ -1137,518 +1146,174 @@ var __defProp = Object.defineProperty;
|
|
|
1137
1146
|
key
|
|
1138
1147
|
);
|
|
1139
1148
|
};
|
|
1149
|
+
const showOutputs = scope !== "input";
|
|
1150
|
+
const showInput = scope === "all" || scope === "input";
|
|
1151
|
+
const effNote = layers.map((l) => eff[l[0]] || "?").join(" / ");
|
|
1152
|
+
const rowLabel = (key) => key === "extract" ? "抽取输出" : key === "dedup" ? "去重输出" : key === "l2" ? "L2 输出" : "L3 输出";
|
|
1153
|
+
const rowStyle = { display: "flex", alignItems: "center", gap: 8 };
|
|
1140
1154
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
1141
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style:
|
|
1142
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1143
|
-
|
|
1155
|
+
showOutputs ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginTop: 12 }, children: [
|
|
1156
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1157
|
+
"div",
|
|
1158
|
+
{
|
|
1159
|
+
style: S.switchLabel,
|
|
1160
|
+
title: (scope === "l1" ? "L1 的抽取与去重是两次独立调用,输出限额各自设置(路由共用同一条链)。" : "") + "留空或 0 = 跟随默认(当前生效 " + effNote + ");思考档 high/xhigh/max 时实际限额自动 ×4",
|
|
1161
|
+
children: "输出预算"
|
|
1162
|
+
}
|
|
1163
|
+
),
|
|
1164
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: layers.map((l) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: rowStyle, children: [
|
|
1165
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { width: 68, flexShrink: 0, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: rowLabel(l[0]) }),
|
|
1166
|
+
inputBox(
|
|
1144
1167
|
l[0],
|
|
1145
1168
|
l[1],
|
|
1146
1169
|
l[1] + " 输出预算(token,留空 = 默认 " + (def[l[0]] || "?") + ")",
|
|
1147
|
-
|
|
1170
|
+
110,
|
|
1148
1171
|
def[l[0]],
|
|
1149
1172
|
commitOutputs
|
|
1150
|
-
)
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
120,
|
|
1163
|
-
ib.fallback,
|
|
1164
|
-
commitInput
|
|
1173
|
+
),
|
|
1174
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: "token" })
|
|
1175
|
+
] }, l[0])) })
|
|
1176
|
+
] }) : null,
|
|
1177
|
+
showInput ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginTop: 12 }, children: [
|
|
1178
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1179
|
+
"div",
|
|
1180
|
+
{
|
|
1181
|
+
style: S.switchLabel,
|
|
1182
|
+
title: "单次蒸馏调用的输入字符上限(≈token):L1 抽取按此分块、L2/L3 超限截断;留空或 0 = 跟随配置(当前生效 " + (effIn || "?") + ",来自 llm.maxInputChars)",
|
|
1183
|
+
children: "输入预算"
|
|
1184
|
+
}
|
|
1165
1185
|
),
|
|
1166
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
1167
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1186
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: rowStyle, children: [
|
|
1187
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { width: 68, flexShrink: 0, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: "单次输入" }),
|
|
1188
|
+
inputBox(
|
|
1189
|
+
"input",
|
|
1190
|
+
"输入",
|
|
1191
|
+
"单次蒸馏输入字符上限(≈token,中文 1 字≈1 token;留空 = 跟随配置 " + (ib.fallback || "?") + ")",
|
|
1192
|
+
110,
|
|
1193
|
+
ib.fallback,
|
|
1194
|
+
commitInput
|
|
1195
|
+
),
|
|
1196
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: "字符" })
|
|
1197
|
+
] }) })
|
|
1198
|
+
] }) : null
|
|
1171
1199
|
] });
|
|
1172
1200
|
}
|
|
1173
1201
|
|
|
1174
|
-
// client/src/tabs/
|
|
1175
|
-
var
|
|
1176
|
-
|
|
1177
|
-
// client/src/rpc.ts
|
|
1178
|
-
function makeRpc(ctx) {
|
|
1179
|
-
return (endpoint, payload) => {
|
|
1180
|
-
if (!ctx.connection || !ctx.connection.rpc) return Promise.reject(new Error("connection 服务不可用"));
|
|
1181
|
-
return ctx.connection.rpc.call("/rpc", endpoint, payload ?? {});
|
|
1182
|
-
};
|
|
1183
|
-
}
|
|
1184
|
-
function asLoose(rpc) {
|
|
1185
|
-
return rpc;
|
|
1186
|
-
}
|
|
1202
|
+
// client/src/tabs/RouteChainEditor.tsx
|
|
1203
|
+
var import_react6 = require("react");
|
|
1187
1204
|
|
|
1188
|
-
// client/src/
|
|
1205
|
+
// client/src/ui/NSel.tsx
|
|
1206
|
+
var import_react5 = require("react");
|
|
1189
1207
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1190
|
-
function
|
|
1191
|
-
const
|
|
1192
|
-
const
|
|
1193
|
-
const
|
|
1194
|
-
const [
|
|
1195
|
-
const
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
}
|
|
1203
|
-
setSt(v);
|
|
1204
|
-
setErr(null);
|
|
1205
|
-
} else if (r && r.ok && r.value && r.value.supported === false) {
|
|
1206
|
-
setSt(null);
|
|
1207
|
-
setErr("__unsupported__");
|
|
1208
|
-
} else {
|
|
1209
|
-
setErr(r && !r.ok ? r.error.message : "RPC error");
|
|
1210
|
-
}
|
|
1211
|
-
}).catch((e) => {
|
|
1212
|
-
setErr(String(e && e.message || e));
|
|
1213
|
-
});
|
|
1214
|
-
}, [rpc]);
|
|
1215
|
-
(0, import_react5.useEffect)(() => {
|
|
1216
|
-
load();
|
|
1217
|
-
}, [load]);
|
|
1208
|
+
function NSel(props) {
|
|
1209
|
+
const options = props.options || [];
|
|
1210
|
+
const value = props.value || "";
|
|
1211
|
+
const disabled = !!props.disabled;
|
|
1212
|
+
const [open, setOpen] = (0, import_react5.useState)(false);
|
|
1213
|
+
const [idx, setIdx] = (0, import_react5.useState)(-1);
|
|
1214
|
+
const wrapRef = (0, import_react5.useRef)(null);
|
|
1215
|
+
const listRef = (0, import_react5.useRef)(null);
|
|
1216
|
+
let selectedLabel = "";
|
|
1217
|
+
for (let si = 0; si < options.length; si++) {
|
|
1218
|
+
if (options[si].id === value) selectedLabel = options[si].label;
|
|
1219
|
+
}
|
|
1218
1220
|
(0, import_react5.useEffect)(() => {
|
|
1219
|
-
|
|
1220
|
-
const
|
|
1221
|
-
|
|
1222
|
-
const tick = () => {
|
|
1223
|
-
if (stopped) return;
|
|
1224
|
-
load();
|
|
1225
|
-
timer = setTimeout(tick, busyFlag.v ? 1200 : 5e3);
|
|
1221
|
+
if (!open) return void 0;
|
|
1222
|
+
const onDown = (e) => {
|
|
1223
|
+
if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
|
|
1226
1224
|
};
|
|
1227
|
-
|
|
1225
|
+
document.addEventListener("mousedown", onDown);
|
|
1228
1226
|
return () => {
|
|
1229
|
-
|
|
1230
|
-
clearTimeout(timer);
|
|
1231
|
-
busyPollRef.current = null;
|
|
1227
|
+
document.removeEventListener("mousedown", onDown);
|
|
1232
1228
|
};
|
|
1233
|
-
}, [
|
|
1234
|
-
|
|
1235
|
-
if (
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
}).catch((e) => {
|
|
1243
|
-
setErr(String(e && e.message || e));
|
|
1244
|
-
});
|
|
1229
|
+
}, [open]);
|
|
1230
|
+
(0, import_react5.useEffect)(() => {
|
|
1231
|
+
if (!open || !listRef.current) return;
|
|
1232
|
+
const el = listRef.current.querySelector('[data-active="1"]');
|
|
1233
|
+
if (el && el.scrollIntoView) el.scrollIntoView({ block: "nearest" });
|
|
1234
|
+
}, [open, idx]);
|
|
1235
|
+
const indexOfValue = () => {
|
|
1236
|
+
for (let i = 0; i < options.length; i++) if (options[i].id === value) return i;
|
|
1237
|
+
return -1;
|
|
1245
1238
|
};
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
}
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
if (
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1239
|
+
const closeMenu = (refocus) => {
|
|
1240
|
+
setOpen(false);
|
|
1241
|
+
setIdx(-1);
|
|
1242
|
+
if (refocus && wrapRef.current) {
|
|
1243
|
+
const btn = wrapRef.current.querySelector("button");
|
|
1244
|
+
if (btn && btn.focus) btn.focus();
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1247
|
+
const pick = (id) => {
|
|
1248
|
+
closeMenu(true);
|
|
1249
|
+
if (id !== value && props.onChange) props.onChange(id);
|
|
1250
|
+
};
|
|
1251
|
+
const moveActive = (delta) => {
|
|
1252
|
+
const n = options.length;
|
|
1253
|
+
if (n === 0) return;
|
|
1254
|
+
let cur = idx >= 0 ? idx : indexOfValue();
|
|
1255
|
+
if (cur < 0) cur = delta > 0 ? -1 : 0;
|
|
1256
|
+
setIdx(delta > 0 ? ((cur + 1) % n + n) % n : ((cur - 1) % n + n) % n);
|
|
1257
|
+
};
|
|
1258
|
+
const onKey = (e) => {
|
|
1259
|
+
if (disabled) return;
|
|
1260
|
+
if (e.key === "Escape") {
|
|
1261
|
+
if (open) {
|
|
1262
|
+
e.preventDefault();
|
|
1263
|
+
closeMenu(true);
|
|
1266
1264
|
}
|
|
1267
|
-
const current = st.activeModel && ready.indexOf(st.activeModel) >= 0 ? st.activeModel : ready[0];
|
|
1268
|
-
call("dsh-memory/embedding-source-set", { source: "local", activeModel: current }, switchConfirm);
|
|
1269
1265
|
return;
|
|
1270
1266
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
let runtimeRow = null;
|
|
1279
|
-
if (rt.phase === "installing") {
|
|
1280
|
-
runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { marginTop: 8, fontSize: 12 }, children: [
|
|
1281
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: S.flexRow, children: [
|
|
1282
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "安装推理运行时中… 已耗时 " + Math.round(rt.elapsedMs / 1e3) + "s(约 100~200MB,视网络)" }),
|
|
1283
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.grow }),
|
|
1284
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-runtime-cancel", {}), children: "取消" })
|
|
1285
|
-
] }),
|
|
1286
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { style: { ...S.pre, maxHeight: 68, marginTop: 6, fontSize: 11, opacity: 0.85 }, children: (rt.lastLines || []).join("\n") || "等待 npm 输出…" })
|
|
1287
|
-
] });
|
|
1288
|
-
} else if (rt.phase === "error") {
|
|
1289
|
-
runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "运行时安装失败:" + (rt.error || "未知") + "(重新切换嵌入源可重试)" });
|
|
1290
|
-
} else if (rt.phase === "ready") {
|
|
1291
|
-
runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "推理运行时就绪(transformers.js v" + rt.installedVersion + ")" });
|
|
1292
|
-
} else if (st.source === "local") {
|
|
1293
|
-
runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "首次启用本地嵌入时会自动安装推理运行时(约 100~200MB)。" });
|
|
1294
|
-
}
|
|
1295
|
-
let applyRow = null;
|
|
1296
|
-
if (ap.phase === "warming") {
|
|
1297
|
-
applyRow = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "加载嵌入模型中…(首次需数秒)" });
|
|
1298
|
-
} else if (ap.phase === "switching") {
|
|
1299
|
-
applyRow = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "切换嵌入源中…" });
|
|
1300
|
-
} else if (ap.phase === "error") {
|
|
1301
|
-
applyRow = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "切换失败:" + ap.message + "(已保存的嵌入源不变,重启后仍按原源运行)" });
|
|
1302
|
-
} else if (st.reindex && st.reindex.running) {
|
|
1303
|
-
const rj = st.reindex;
|
|
1304
|
-
const rDone = rj.l1Done + rj.l0Done;
|
|
1305
|
-
const rTotal = rj.l1Total + rj.l0Total;
|
|
1306
|
-
const rPct = rTotal > 0 ? Math.round(rDone / rTotal * 100) : 0;
|
|
1307
|
-
applyRow = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { marginTop: 8 }, children: [
|
|
1308
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: S.flexRow, children: [
|
|
1309
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-mem-rb-muted", children: "重嵌入中 L1 " + rj.l1Done + "/" + rj.l1Total + " · L0 " + rj.l0Done + "/" + rj.l0Total + "(" + rPct + "%)" }),
|
|
1310
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.grow }),
|
|
1311
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-reindex-cancel", {}), children: "取消" })
|
|
1312
|
-
] }),
|
|
1313
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { ...S.flexRow, marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: rPct + "%" } }) }) })
|
|
1314
|
-
] });
|
|
1315
|
-
}
|
|
1316
|
-
const modelCards = st.models.map((m) => {
|
|
1317
|
-
const isActive = st.source === "local" && st.activeModel === m.id;
|
|
1318
|
-
const mDl = dlActive && dl.modelId === m.id;
|
|
1319
|
-
const pct = mDl && dl.overallTotal > 0 ? Math.round(dl.overallReceived / dl.overallTotal * 100) : 0;
|
|
1320
|
-
let action = null;
|
|
1321
|
-
if (mDl) {
|
|
1322
|
-
action = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { flex: 1, minWidth: 200 }, children: [
|
|
1323
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: S.flexRow, children: [
|
|
1324
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" }, children: (dl.phase === "verifying" ? "校验中 " : "") + fmtMB(dl.overallReceived) + " / " + fmtMB(dl.overallTotal) + "(文件 " + dl.fileIndex + "/" + dl.fileCount + "," + pct + "%" + (dl.speedBps > 0 && dl.phase === "downloading" ? "," + fmtMB(dl.speedBps) + "/s" : "") + ")" }),
|
|
1325
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.grow }),
|
|
1326
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-download-cancel", {}), children: "取消" })
|
|
1327
|
-
] }),
|
|
1328
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { ...S.flexRow, marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: pct + "%" } }) }) })
|
|
1329
|
-
] });
|
|
1330
|
-
} else if (isActive) {
|
|
1331
|
-
action = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: S.flexRow, children: [
|
|
1332
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-work-task", children: "使用中" }),
|
|
1333
|
-
localInfo && localInfo.state === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-mem-rb-muted", children: "模型加载中…" }) : null,
|
|
1334
|
-
localInfo && localInfo.state === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "加载失败:" + (localInfo.error || "") }) : null,
|
|
1335
|
-
localInfo && localInfo.state === "ready" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-mem-rb-muted", children: "已就绪" }) : null
|
|
1336
|
-
] });
|
|
1337
|
-
} else if (m.state === "downloaded") {
|
|
1338
|
-
action = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: S.flexRow, children: [
|
|
1339
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1340
|
-
NButton,
|
|
1341
|
-
{
|
|
1342
|
-
disabled: ap.busy,
|
|
1343
|
-
onClick: () => call("dsh-memory/embedding-source-set", { source: "local", activeModel: m.id }, switchConfirm),
|
|
1344
|
-
children: "启用"
|
|
1345
|
-
}
|
|
1346
|
-
),
|
|
1347
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1348
|
-
NButton,
|
|
1349
|
-
{
|
|
1350
|
-
disabled: dlActive,
|
|
1351
|
-
onClick: () => call("dsh-memory/embedding-model-delete", { modelId: m.id }, "删除已下载的 " + m.name + "(" + fmtMB(m.totalBytes) + ")?"),
|
|
1352
|
-
children: "删除"
|
|
1353
|
-
}
|
|
1354
|
-
)
|
|
1355
|
-
] });
|
|
1356
|
-
} else {
|
|
1357
|
-
action = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1358
|
-
NButton,
|
|
1359
|
-
{
|
|
1360
|
-
disabled: dlActive || !st.ceilings.local,
|
|
1361
|
-
title: !st.ceilings.local ? "部署已禁用本地嵌入模型" : "",
|
|
1362
|
-
onClick: () => call("dsh-memory/embedding-download-start", { modelId: m.id }),
|
|
1363
|
-
children: (m.state === "partial" ? "继续下载 " : "下载 ") + fmtMB(m.totalBytes)
|
|
1364
|
-
}
|
|
1365
|
-
);
|
|
1267
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
1268
|
+
e.preventDefault();
|
|
1269
|
+
if (!open) {
|
|
1270
|
+
setOpen(true);
|
|
1271
|
+
setIdx(indexOfValue());
|
|
1272
|
+
} else moveActive(e.key === "ArrowDown" ? 1 : -1);
|
|
1273
|
+
return;
|
|
1366
1274
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1275
|
+
if (!open && (e.key === "Enter" || e.key === " ")) {
|
|
1276
|
+
e.preventDefault();
|
|
1277
|
+
setOpen(true);
|
|
1278
|
+
setIdx(indexOfValue());
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
if (open && e.key === "Enter") {
|
|
1282
|
+
e.preventDefault();
|
|
1283
|
+
let t = idx >= 0 ? idx : indexOfValue();
|
|
1284
|
+
if (t < 0) t = 0;
|
|
1285
|
+
if (options[t]) pick(options[t].id);
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
const onBlur = (e) => {
|
|
1289
|
+
if (!open) return;
|
|
1290
|
+
const to = e.relatedTarget;
|
|
1291
|
+
if (!to || wrapRef.current && !wrapRef.current.contains(to)) setOpen(false);
|
|
1292
|
+
};
|
|
1293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-mem-sel", style: props.style, ref: wrapRef, onKeyDown: onKey, onBlur, children: [
|
|
1294
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1295
|
+
"button",
|
|
1369
1296
|
{
|
|
1370
|
-
|
|
1297
|
+
type: "button",
|
|
1298
|
+
className: "dsh-mem-select" + (open ? " dsh-mem-select-open" : ""),
|
|
1299
|
+
disabled,
|
|
1300
|
+
"aria-haspopup": "listbox",
|
|
1301
|
+
"aria-expanded": open,
|
|
1302
|
+
onClick: () => {
|
|
1303
|
+
if (open) closeMenu(false);
|
|
1304
|
+
else {
|
|
1305
|
+
setOpen(true);
|
|
1306
|
+
setIdx(-1);
|
|
1307
|
+
}
|
|
1308
|
+
},
|
|
1371
1309
|
children: [
|
|
1372
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.
|
|
1373
|
-
|
|
1374
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-rb-muted", children: m.tags.join(" · ") + " · " + m.dims + " 维 · 上下文 " + m.contextTokens })
|
|
1375
|
-
] }),
|
|
1376
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { flex: 1, minWidth: 180, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: m.description }),
|
|
1377
|
-
action
|
|
1378
|
-
]
|
|
1379
|
-
},
|
|
1380
|
-
m.id
|
|
1381
|
-
);
|
|
1382
|
-
});
|
|
1383
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-mem-rb-card", children: [
|
|
1384
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: S.flexRow, children: [
|
|
1385
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "语义检索(嵌入源)" }),
|
|
1386
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.grow }),
|
|
1387
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1388
|
-
Segmented,
|
|
1389
|
-
{
|
|
1390
|
-
value: st.source,
|
|
1391
|
-
options: [
|
|
1392
|
-
{ key: "off", label: "关闭" },
|
|
1393
|
-
{ key: "local", label: "本地", disabled: !st.ceilings.local, disabledTitle: "部署已禁用本地嵌入模型" },
|
|
1394
|
-
{ key: "remote", label: "远程", disabled: !st.ceilings.remote, disabledTitle: "部署未配置远程嵌入(baseUrl/apiKey/model/dimensions)" }
|
|
1395
|
-
],
|
|
1396
|
-
onChange: onSource
|
|
1397
|
-
}
|
|
1398
|
-
)
|
|
1399
|
-
] }),
|
|
1400
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 4 }, children: st.source === "off" ? "当前:关键词(BM25)检索,不做向量嵌入" : st.source === "remote" ? "当前:远程嵌入(" + (rt ? "" : "") + "模型由部署配置给定)" : "当前:本地嵌入" + (st.activeModel ? "(" + st.activeModel + ")" : "") }),
|
|
1401
|
-
st.activeNote ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { marginTop: 4, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: st.activeNote }) : null,
|
|
1402
|
-
err && err !== "__unsupported__" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: err }) : null,
|
|
1403
|
-
dl && dl.phase === "error" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "下载失败:" + (dl.error || "") }) : null,
|
|
1404
|
-
runtimeRow,
|
|
1405
|
-
applyRow,
|
|
1406
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.panelLabel, children: "本地模型目录(下载后离线可用,不随插件分发)" }),
|
|
1407
|
-
modelCards
|
|
1408
|
-
] });
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
// client/src/tabs/RebuildPanel.tsx
|
|
1412
|
-
var import_react6 = require("react");
|
|
1413
|
-
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1414
|
-
var RB_PHASE_LABEL = {
|
|
1415
|
-
preparing: "准备中(归档旧数据 · 清空检索库)",
|
|
1416
|
-
distilling: "分块蒸馏中",
|
|
1417
|
-
finalizing: "收尾(强制 L2 场景 + L3 画像)"
|
|
1418
|
-
};
|
|
1419
|
-
function RebuildPanel(props) {
|
|
1420
|
-
const rpc = props.rpc;
|
|
1421
|
-
const [rbRaw, setRb] = (0, import_react6.useState)(null);
|
|
1422
|
-
const [confirmOpen, setConfirmOpen] = (0, import_react6.useState)(false);
|
|
1423
|
-
const [busy, setBusy] = (0, import_react6.useState)(false);
|
|
1424
|
-
const [rbError, setRbError] = (0, import_react6.useState)(null);
|
|
1425
|
-
const refresh = (0, import_react6.useCallback)(() => {
|
|
1426
|
-
rpc("dsh-memory/rebuild-status", {}).then((r) => {
|
|
1427
|
-
if (r && r.ok) setRb(r.value);
|
|
1428
|
-
}).catch(() => {
|
|
1429
|
-
});
|
|
1430
|
-
}, [rpc]);
|
|
1431
|
-
(0, import_react6.useEffect)(() => {
|
|
1432
|
-
refresh();
|
|
1433
|
-
}, [refresh]);
|
|
1434
|
-
const running = !!(rbRaw && rbRaw.running);
|
|
1435
|
-
(0, import_react6.useEffect)(() => {
|
|
1436
|
-
if (!running) return;
|
|
1437
|
-
const timer = setInterval(refresh, 1500);
|
|
1438
|
-
return () => {
|
|
1439
|
-
clearInterval(timer);
|
|
1440
|
-
};
|
|
1441
|
-
}, [running, refresh]);
|
|
1442
|
-
if (!rbRaw || rbRaw.supported === false) return null;
|
|
1443
|
-
const rb = rbRaw;
|
|
1444
|
-
ensureThemeStyle();
|
|
1445
|
-
const start = () => {
|
|
1446
|
-
setBusy(true);
|
|
1447
|
-
setRbError(null);
|
|
1448
|
-
rpc("dsh-memory/rebuild-start", {}).then((r) => {
|
|
1449
|
-
setBusy(false);
|
|
1450
|
-
if (r && r.ok) {
|
|
1451
|
-
setConfirmOpen(false);
|
|
1452
|
-
setRb(r.value);
|
|
1453
|
-
} else {
|
|
1454
|
-
setRbError(r && r.error ? r.error.message : "启动失败");
|
|
1455
|
-
}
|
|
1456
|
-
}).catch((e) => {
|
|
1457
|
-
setBusy(false);
|
|
1458
|
-
setRbError(String(e && e.message || e));
|
|
1459
|
-
});
|
|
1460
|
-
};
|
|
1461
|
-
const cancel = () => {
|
|
1462
|
-
setBusy(true);
|
|
1463
|
-
rpc("dsh-memory/rebuild-cancel", {}).then((r) => {
|
|
1464
|
-
setBusy(false);
|
|
1465
|
-
if (r && r.ok) setRb(r.value);
|
|
1466
|
-
}).catch(() => {
|
|
1467
|
-
setBusy(false);
|
|
1468
|
-
});
|
|
1469
|
-
};
|
|
1470
|
-
const empty = !running && (rb.messageCount === 0 || rb.estCalls === 0);
|
|
1471
|
-
const pct = rb.total > 0 ? Math.round(rb.done / rb.total * 100) : 0;
|
|
1472
|
-
let lastNote = null;
|
|
1473
|
-
if (!running && rb.phase === "done") {
|
|
1474
|
-
lastNote = "上次重建:完成(" + rb.done + "/" + rb.total + " 会话,产出 " + rb.recordsBuilt + " 条记录)" + (rb.finishedAt ? " · " + fmtTime(new Date(rb.finishedAt).toISOString()) : "");
|
|
1475
|
-
} else if (!running && rb.phase === "cancelled") {
|
|
1476
|
-
lastNote = "上次重建:已取消(完成 " + rb.done + "/" + rb.total + " 会话,已重建部分保留)";
|
|
1477
|
-
} else if (!running && rb.phase === "failed") {
|
|
1478
|
-
lastNote = "上次重建:失败:" + (rb.error || "未知错误");
|
|
1479
|
-
}
|
|
1480
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-mem-rb-card", children: [
|
|
1481
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
|
|
1482
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "重建记忆" }),
|
|
1483
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-mem-rb-muted", style: { flex: 1, minWidth: 180 }, children: running ? (RB_PHASE_LABEL[rb.phase] || rb.phase) + " · " + rb.done + "/" + rb.total + " 会话(" + pct + "%)" : "从 L0 原始对话重新蒸馏 L1/L2/L3;旧数据先归档(不删除)" }),
|
|
1484
|
-
running ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NButton, { disabled: busy || rb.cancelRequested, onClick: cancel, children: rb.cancelRequested ? "取消中…" : "取消重建" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1485
|
-
NButton,
|
|
1486
|
-
{
|
|
1487
|
-
disabled: busy || empty,
|
|
1488
|
-
title: empty ? "L0 无消息,无可重建内容" : "重新蒸馏全部记忆",
|
|
1489
|
-
style: { color: "var(--dsh-mem-danger)" },
|
|
1490
|
-
onClick: () => {
|
|
1491
|
-
setConfirmOpen(true);
|
|
1492
|
-
},
|
|
1493
|
-
children: busy ? "…" : "开始重建"
|
|
1494
|
-
}
|
|
1495
|
-
)
|
|
1496
|
-
] }),
|
|
1497
|
-
running ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 10 }, children: [
|
|
1498
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: pct + "%" } }) }),
|
|
1499
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" }, children: "产出 " + rb.recordsBuilt + " 条" })
|
|
1500
|
-
] }) : null,
|
|
1501
|
-
lastNote ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: lastNote }) : null,
|
|
1502
|
-
rbError ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: rbError }) : null,
|
|
1503
|
-
confirmOpen ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1504
|
-
NModal,
|
|
1505
|
-
{
|
|
1506
|
-
open: true,
|
|
1507
|
-
onClose: () => {
|
|
1508
|
-
setConfirmOpen(false);
|
|
1509
|
-
},
|
|
1510
|
-
title: "确认重建全部记忆?",
|
|
1511
|
-
footer: [
|
|
1512
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1513
|
-
NButton,
|
|
1514
|
-
{
|
|
1515
|
-
onClick: () => {
|
|
1516
|
-
setConfirmOpen(false);
|
|
1517
|
-
},
|
|
1518
|
-
children: "取消"
|
|
1519
|
-
},
|
|
1520
|
-
"cancel"
|
|
1521
|
-
),
|
|
1522
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NButton, { variant: "primary", disabled: busy, onClick: start, children: busy ? "启动中…" : "开始重建" }, "confirm")
|
|
1523
|
-
],
|
|
1524
|
-
children: [
|
|
1525
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1526
|
-
"将以 L0 原始对话为事实源重新蒸馏:",
|
|
1527
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("b", { children: rb.sessionCount + " 个会话 · " + rb.messageCount + " 条消息" }),
|
|
1528
|
-
",预计 ≥" + rb.estCalls + " 次蒸馏调用。"
|
|
1529
|
-
] }),
|
|
1530
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { marginTop: 8 }, children: "现有 L1 记忆 / L2 场景 / L3 画像会整体归档(*.bak.时间戳,可手工找回),随后清空重建;重建期间可正常对话,新对话的蒸馏优先进行;中途可取消,已重建部分保留。" })
|
|
1531
|
-
]
|
|
1532
|
-
}
|
|
1533
|
-
) : null
|
|
1534
|
-
] });
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
|
-
// client/src/tabs/RouteChainEditor.tsx
|
|
1538
|
-
var import_react8 = require("react");
|
|
1539
|
-
|
|
1540
|
-
// client/src/ui/NSel.tsx
|
|
1541
|
-
var import_react7 = require("react");
|
|
1542
|
-
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1543
|
-
function NSel(props) {
|
|
1544
|
-
const options = props.options || [];
|
|
1545
|
-
const value = props.value || "";
|
|
1546
|
-
const disabled = !!props.disabled;
|
|
1547
|
-
const [open, setOpen] = (0, import_react7.useState)(false);
|
|
1548
|
-
const [idx, setIdx] = (0, import_react7.useState)(-1);
|
|
1549
|
-
const wrapRef = (0, import_react7.useRef)(null);
|
|
1550
|
-
const listRef = (0, import_react7.useRef)(null);
|
|
1551
|
-
let selectedLabel = "";
|
|
1552
|
-
for (let si = 0; si < options.length; si++) {
|
|
1553
|
-
if (options[si].id === value) selectedLabel = options[si].label;
|
|
1554
|
-
}
|
|
1555
|
-
(0, import_react7.useEffect)(() => {
|
|
1556
|
-
if (!open) return void 0;
|
|
1557
|
-
const onDown = (e) => {
|
|
1558
|
-
if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
|
|
1559
|
-
};
|
|
1560
|
-
document.addEventListener("mousedown", onDown);
|
|
1561
|
-
return () => {
|
|
1562
|
-
document.removeEventListener("mousedown", onDown);
|
|
1563
|
-
};
|
|
1564
|
-
}, [open]);
|
|
1565
|
-
(0, import_react7.useEffect)(() => {
|
|
1566
|
-
if (!open || !listRef.current) return;
|
|
1567
|
-
const el = listRef.current.querySelector('[data-active="1"]');
|
|
1568
|
-
if (el && el.scrollIntoView) el.scrollIntoView({ block: "nearest" });
|
|
1569
|
-
}, [open, idx]);
|
|
1570
|
-
const indexOfValue = () => {
|
|
1571
|
-
for (let i = 0; i < options.length; i++) if (options[i].id === value) return i;
|
|
1572
|
-
return -1;
|
|
1573
|
-
};
|
|
1574
|
-
const closeMenu = (refocus) => {
|
|
1575
|
-
setOpen(false);
|
|
1576
|
-
setIdx(-1);
|
|
1577
|
-
if (refocus && wrapRef.current) {
|
|
1578
|
-
const btn = wrapRef.current.querySelector("button");
|
|
1579
|
-
if (btn && btn.focus) btn.focus();
|
|
1580
|
-
}
|
|
1581
|
-
};
|
|
1582
|
-
const pick = (id) => {
|
|
1583
|
-
closeMenu(true);
|
|
1584
|
-
if (id !== value && props.onChange) props.onChange(id);
|
|
1585
|
-
};
|
|
1586
|
-
const moveActive = (delta) => {
|
|
1587
|
-
const n = options.length;
|
|
1588
|
-
if (n === 0) return;
|
|
1589
|
-
let cur = idx >= 0 ? idx : indexOfValue();
|
|
1590
|
-
if (cur < 0) cur = delta > 0 ? -1 : 0;
|
|
1591
|
-
setIdx(delta > 0 ? ((cur + 1) % n + n) % n : ((cur - 1) % n + n) % n);
|
|
1592
|
-
};
|
|
1593
|
-
const onKey = (e) => {
|
|
1594
|
-
if (disabled) return;
|
|
1595
|
-
if (e.key === "Escape") {
|
|
1596
|
-
if (open) {
|
|
1597
|
-
e.preventDefault();
|
|
1598
|
-
closeMenu(true);
|
|
1599
|
-
}
|
|
1600
|
-
return;
|
|
1601
|
-
}
|
|
1602
|
-
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
1603
|
-
e.preventDefault();
|
|
1604
|
-
if (!open) {
|
|
1605
|
-
setOpen(true);
|
|
1606
|
-
setIdx(indexOfValue());
|
|
1607
|
-
} else moveActive(e.key === "ArrowDown" ? 1 : -1);
|
|
1608
|
-
return;
|
|
1609
|
-
}
|
|
1610
|
-
if (!open && (e.key === "Enter" || e.key === " ")) {
|
|
1611
|
-
e.preventDefault();
|
|
1612
|
-
setOpen(true);
|
|
1613
|
-
setIdx(indexOfValue());
|
|
1614
|
-
return;
|
|
1615
|
-
}
|
|
1616
|
-
if (open && e.key === "Enter") {
|
|
1617
|
-
e.preventDefault();
|
|
1618
|
-
let t = idx >= 0 ? idx : indexOfValue();
|
|
1619
|
-
if (t < 0) t = 0;
|
|
1620
|
-
if (options[t]) pick(options[t].id);
|
|
1621
|
-
}
|
|
1622
|
-
};
|
|
1623
|
-
const onBlur = (e) => {
|
|
1624
|
-
if (!open) return;
|
|
1625
|
-
const to = e.relatedTarget;
|
|
1626
|
-
if (!to || wrapRef.current && !wrapRef.current.contains(to)) setOpen(false);
|
|
1627
|
-
};
|
|
1628
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-mem-sel", style: props.style, ref: wrapRef, onKeyDown: onKey, onBlur, children: [
|
|
1629
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1630
|
-
"button",
|
|
1631
|
-
{
|
|
1632
|
-
type: "button",
|
|
1633
|
-
className: "dsh-mem-select" + (open ? " dsh-mem-select-open" : ""),
|
|
1634
|
-
disabled,
|
|
1635
|
-
"aria-haspopup": "listbox",
|
|
1636
|
-
"aria-expanded": open,
|
|
1637
|
-
onClick: () => {
|
|
1638
|
-
if (open) closeMenu(false);
|
|
1639
|
-
else {
|
|
1640
|
-
setOpen(true);
|
|
1641
|
-
setIdx(-1);
|
|
1642
|
-
}
|
|
1643
|
-
},
|
|
1644
|
-
children: [
|
|
1645
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-mem-select-label", children: selectedLabel || props.placeholder || "(请选择)" }),
|
|
1646
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-mem-sel-chev" + (open ? " dsh-mem-sel-chev-open" : ""), "aria-hidden": true })
|
|
1310
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-mem-select-label", children: selectedLabel || props.placeholder || "(请选择)" }),
|
|
1311
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-mem-sel-chev" + (open ? " dsh-mem-sel-chev-open" : ""), "aria-hidden": true })
|
|
1647
1312
|
]
|
|
1648
1313
|
}
|
|
1649
1314
|
),
|
|
1650
|
-
open && !disabled ? /* @__PURE__ */ (0,
|
|
1651
|
-
return /* @__PURE__ */ (0,
|
|
1315
|
+
open && !disabled ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-pop", ref: listRef, role: "listbox", children: options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-mem-pop-empty", children: "无选项" }) : options.map((o, i) => {
|
|
1316
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1652
1317
|
"button",
|
|
1653
1318
|
{
|
|
1654
1319
|
type: "button",
|
|
@@ -1666,8 +1331,8 @@ var __defProp = Object.defineProperty;
|
|
|
1666
1331
|
if (idx !== i) setIdx(i);
|
|
1667
1332
|
},
|
|
1668
1333
|
children: [
|
|
1669
|
-
/* @__PURE__ */ (0,
|
|
1670
|
-
o.id === value ? /* @__PURE__ */ (0,
|
|
1334
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-mem-pop-opt-label", children: o.label }),
|
|
1335
|
+
o.id === value ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-mem-pop-check", children: "✓" }) : null
|
|
1671
1336
|
]
|
|
1672
1337
|
},
|
|
1673
1338
|
o.id
|
|
@@ -1677,7 +1342,7 @@ var __defProp = Object.defineProperty;
|
|
|
1677
1342
|
}
|
|
1678
1343
|
|
|
1679
1344
|
// client/src/tabs/RouteChainEditor.tsx
|
|
1680
|
-
var
|
|
1345
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1681
1346
|
var modelsCache = {};
|
|
1682
1347
|
var EFFORT_VOCAB = ["", "off", "none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
1683
1348
|
var STY = {
|
|
@@ -1704,26 +1369,28 @@ var __defProp = Object.defineProperty;
|
|
|
1704
1369
|
function RouteChainEditor(props) {
|
|
1705
1370
|
const rpc = props.rpc;
|
|
1706
1371
|
const disabled = !!props.disabled;
|
|
1707
|
-
const
|
|
1708
|
-
const
|
|
1709
|
-
const [
|
|
1710
|
-
const [
|
|
1711
|
-
const [
|
|
1712
|
-
const
|
|
1372
|
+
const scope = props.scope ?? "global";
|
|
1373
|
+
const isLayer = scope !== "global";
|
|
1374
|
+
const [info, setInfo] = (0, import_react6.useState)(null);
|
|
1375
|
+
const [rows, setRows] = (0, import_react6.useState)(null);
|
|
1376
|
+
const [rowErrs, setRowErrs] = (0, import_react6.useState)({});
|
|
1377
|
+
const [err, setErr] = (0, import_react6.useState)(null);
|
|
1378
|
+
const [manual, setManual] = (0, import_react6.useState)({ idx: -1, text: "" });
|
|
1379
|
+
const pendingWrites = (0, import_react6.useRef)(0);
|
|
1713
1380
|
function refreshInfo() {
|
|
1714
1381
|
rpc("dsh-memory/llm-providers", {}).then((r) => {
|
|
1715
1382
|
if (r && r.ok && pendingWrites.current === 0) setInfo(r.value);
|
|
1716
1383
|
}).catch(() => {
|
|
1717
1384
|
});
|
|
1718
1385
|
}
|
|
1719
|
-
(0,
|
|
1386
|
+
(0, import_react6.useEffect)(() => {
|
|
1720
1387
|
refreshInfo();
|
|
1721
1388
|
const timer = setInterval(refreshInfo, 5e3);
|
|
1722
1389
|
return () => {
|
|
1723
1390
|
clearInterval(timer);
|
|
1724
1391
|
};
|
|
1725
1392
|
}, [rpc]);
|
|
1726
|
-
(0,
|
|
1393
|
+
(0, import_react6.useEffect)(() => {
|
|
1727
1394
|
if (!info || !info.providers) return;
|
|
1728
1395
|
info.providers.forEach((p) => {
|
|
1729
1396
|
if (!p.id || modelsCache[p.id]) return;
|
|
@@ -1733,11 +1400,13 @@ var __defProp = Object.defineProperty;
|
|
|
1733
1400
|
});
|
|
1734
1401
|
});
|
|
1735
1402
|
}, [rpc, info]);
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1403
|
+
const layerView = isLayer && info && info.layerChains ? info.layerChains[scope] : null;
|
|
1404
|
+
const savedRows = isLayer ? layerView?.runtime ?? [] : info && info.chain ? info.chain.current : [];
|
|
1405
|
+
(0, import_react6.useEffect)(() => {
|
|
1406
|
+
if (rows === null && info && savedRows.length) {
|
|
1407
|
+
setRows(savedRows.map(copyRow));
|
|
1739
1408
|
}
|
|
1740
|
-
}, [info, rows]);
|
|
1409
|
+
}, [info, rows, savedRows]);
|
|
1741
1410
|
function updateRow(i, patch) {
|
|
1742
1411
|
setRows(rows.map((r, j) => j === i ? { ...r, ...patch } : r));
|
|
1743
1412
|
setRowErrs({});
|
|
@@ -1764,8 +1433,12 @@ var __defProp = Object.defineProperty;
|
|
|
1764
1433
|
setRowErrs({});
|
|
1765
1434
|
}
|
|
1766
1435
|
function removeRow(i) {
|
|
1767
|
-
if (i === 0)
|
|
1768
|
-
|
|
1436
|
+
if (i === 0) {
|
|
1437
|
+
if (isLayer) setRows(rows.length > 1 ? rows.slice(1) : rows);
|
|
1438
|
+
else setRows([EMPTY_ROW].concat(rows.slice(1)));
|
|
1439
|
+
} else {
|
|
1440
|
+
setRows(rows.slice(0, i).concat(rows.slice(i + 1)));
|
|
1441
|
+
}
|
|
1769
1442
|
setRowErrs({});
|
|
1770
1443
|
}
|
|
1771
1444
|
function addRow() {
|
|
@@ -1775,6 +1448,16 @@ var __defProp = Object.defineProperty;
|
|
|
1775
1448
|
setRowErrs({});
|
|
1776
1449
|
}
|
|
1777
1450
|
function forkStatic() {
|
|
1451
|
+
if (isLayer) {
|
|
1452
|
+
const st2 = layerView && layerView.static || [];
|
|
1453
|
+
if (st2.length) setRows(st2.slice(0, 8).map(copyRow));
|
|
1454
|
+
else {
|
|
1455
|
+
const first = info.providers && info.providers[0] || { id: "" };
|
|
1456
|
+
setRows([{ provider: first.id, model: "", reasoningEffort: "" }]);
|
|
1457
|
+
}
|
|
1458
|
+
setRowErrs({});
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1778
1461
|
const st = info.chain && info.chain.static || [];
|
|
1779
1462
|
setRows([EMPTY_ROW].concat(st.slice(0, 7).map(copyRow)));
|
|
1780
1463
|
setRowErrs({});
|
|
@@ -1782,7 +1465,9 @@ var __defProp = Object.defineProperty;
|
|
|
1782
1465
|
function save() {
|
|
1783
1466
|
const errs = {};
|
|
1784
1467
|
const seen = {};
|
|
1785
|
-
if (
|
|
1468
|
+
if (isLayer && (!rows[0].provider || !rows[0].model)) {
|
|
1469
|
+
errs[0] = "主路由行必须显式选择供应商与模型(层链不支持跟随默认模型)";
|
|
1470
|
+
} else if (rows[0].provider && !rows[0].model || !rows[0].provider && rows[0].model) {
|
|
1786
1471
|
errs[0] = "主路由行供应商与模型须成对(双空 = 跟随默认模型)";
|
|
1787
1472
|
}
|
|
1788
1473
|
if (rows[0].provider && rows[0].model) seen[rows[0].provider + "::" + rows[0].model] = 0;
|
|
@@ -1805,11 +1490,22 @@ var __defProp = Object.defineProperty;
|
|
|
1805
1490
|
}
|
|
1806
1491
|
if (Object.keys(errs).length) return;
|
|
1807
1492
|
pendingWrites.current += 1;
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1493
|
+
if (isLayer && info && info.layerChains) {
|
|
1494
|
+
setInfo({
|
|
1495
|
+
...info,
|
|
1496
|
+
layerChains: {
|
|
1497
|
+
...info.layerChains,
|
|
1498
|
+
[scope]: { ...info.layerChains[scope], runtime: rows.map(copyRow), source: "runtime" }
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
} else {
|
|
1502
|
+
setInfo({
|
|
1503
|
+
...info,
|
|
1504
|
+
chain: { ...info.chain, current: rows.map(copyRow), source: "runtime" }
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
const payload = isLayer ? { distillLayerChains: { [scope]: rows } } : { distillChain: rows };
|
|
1508
|
+
rpc("dsh-memory/settings-set", payload).then((r) => {
|
|
1813
1509
|
pendingWrites.current -= 1;
|
|
1814
1510
|
setErr(!r || r.ok ? null : "路由链保存失败:" + (r && r.error && r.error.message || "未知错误"));
|
|
1815
1511
|
refreshInfo();
|
|
@@ -1821,6 +1517,20 @@ var __defProp = Object.defineProperty;
|
|
|
1821
1517
|
}
|
|
1822
1518
|
function clearToFollow() {
|
|
1823
1519
|
pendingWrites.current += 1;
|
|
1520
|
+
if (isLayer) {
|
|
1521
|
+
rpc("dsh-memory/settings-set", { distillLayerChains: { [scope]: [] } }).then((r) => {
|
|
1522
|
+
pendingWrites.current -= 1;
|
|
1523
|
+
setRows(null);
|
|
1524
|
+
setRowErrs({});
|
|
1525
|
+
setErr(!r || r.ok ? null : "清空失败,请重试");
|
|
1526
|
+
refreshInfo();
|
|
1527
|
+
}).catch((e) => {
|
|
1528
|
+
pendingWrites.current -= 1;
|
|
1529
|
+
setErr("清空失败:" + String(e && e.message || e));
|
|
1530
|
+
refreshInfo();
|
|
1531
|
+
});
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1824
1534
|
rpc("dsh-memory/settings-set", { distillChain: [], distillProvider: "", distillModel: "", reasoningEffort: "" }).then((r) => {
|
|
1825
1535
|
pendingWrites.current -= 1;
|
|
1826
1536
|
setRows(null);
|
|
@@ -1840,38 +1550,52 @@ var __defProp = Object.defineProperty;
|
|
|
1840
1550
|
providersById[p.id] = p;
|
|
1841
1551
|
});
|
|
1842
1552
|
function roRow(e, i) {
|
|
1843
|
-
return /* @__PURE__ */ (0,
|
|
1844
|
-
/* @__PURE__ */ (0,
|
|
1845
|
-
/* @__PURE__ */ (0,
|
|
1846
|
-
/* @__PURE__ */ (0,
|
|
1553
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.roRow, children: [
|
|
1554
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.badge, children: i === 0 ? "主" : String(i + 1) }),
|
|
1555
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { ...STY.mono, color: "var(--dsh-mem-text-2)" }, children: e.provider + " / " + e.model }),
|
|
1556
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { marginLeft: "auto", flexShrink: 0, fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: e.effort ? "档位 " + e.effort : "跟随部署配置" })
|
|
1847
1557
|
] }, "ro" + i);
|
|
1848
1558
|
}
|
|
1849
1559
|
if (info.pinned) {
|
|
1850
|
-
const effPin = info.chain && info.chain.effectiveChain || [];
|
|
1851
|
-
return /* @__PURE__ */ (0,
|
|
1560
|
+
const effPin = isLayer ? layerView?.effectiveChain ?? [] : info.chain && info.chain.effectiveChain || [];
|
|
1561
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.wrap, children: [
|
|
1852
1562
|
effPin.map(roRow),
|
|
1853
|
-
/* @__PURE__ */ (0,
|
|
1563
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: STY.note, children: "部署已锁定路由(pin),调整请修改 cordis.patch.yml 中 llm 的配置。" })
|
|
1854
1564
|
] });
|
|
1855
1565
|
}
|
|
1856
1566
|
if (rows === null) {
|
|
1567
|
+
if (isLayer) {
|
|
1568
|
+
const lv = layerView;
|
|
1569
|
+
const src = lv?.source ?? "global";
|
|
1570
|
+
if (src === "static" && lv) {
|
|
1571
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.wrap, children: [
|
|
1572
|
+
lv.static.map((e, i) => roRow({ provider: e.provider, model: e.model, effort: e.reasoningEffort || "" }, i)),
|
|
1573
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NButton, { onClick: forkStatic, disabled, children: "自定义本层链" }) })
|
|
1574
|
+
] });
|
|
1575
|
+
}
|
|
1576
|
+
const previewRows = lv?.effectiveChain ?? [];
|
|
1577
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.wrap, children: [
|
|
1578
|
+
previewRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: S.switchDesc, children: "本层跟随全局链,暂无可用路由。" }) : previewRows.map(roRow),
|
|
1579
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NButton, { onClick: forkStatic, disabled, children: "自定义本层链" }) })
|
|
1580
|
+
] });
|
|
1581
|
+
}
|
|
1857
1582
|
const effFollow = info.chain && info.chain.effectiveChain || [];
|
|
1858
|
-
return /* @__PURE__ */ (0,
|
|
1859
|
-
effFollow.length === 0 ? /* @__PURE__ */ (0,
|
|
1860
|
-
/* @__PURE__ */ (0,
|
|
1861
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: STY.note, children: "未配置运行时链时跟随部署配置;「编辑为运行时链」会拷贝部署回退链为草稿,保存起运行时链接管。" })
|
|
1583
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.wrap, children: [
|
|
1584
|
+
effFollow.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: S.switchDesc, children: "蒸馏跟随默认模型,未配置回退链。" }) : effFollow.map(roRow),
|
|
1585
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NButton, { onClick: forkStatic, disabled, children: "编辑为运行时链" }) })
|
|
1862
1586
|
] });
|
|
1863
1587
|
}
|
|
1864
1588
|
const capped = rows.length >= 8;
|
|
1865
|
-
const dirty = JSON.stringify(rows) !== JSON.stringify(
|
|
1866
|
-
const rowEls = rows.map((
|
|
1589
|
+
const dirty = JSON.stringify(rows) !== JSON.stringify(savedRows);
|
|
1590
|
+
const rowEls = rows.map((row2, i) => {
|
|
1867
1591
|
const isPrimary = i === 0;
|
|
1868
|
-
const known = !
|
|
1869
|
-
const modelsLoaded =
|
|
1870
|
-
const modelList = modelsLoaded ? modelsCache[
|
|
1592
|
+
const known = !row2.provider || !!providersById[row2.provider];
|
|
1593
|
+
const modelsLoaded = row2.provider ? Object.prototype.hasOwnProperty.call(modelsCache, row2.provider) : false;
|
|
1594
|
+
const modelList = modelsLoaded ? modelsCache[row2.provider] : [];
|
|
1871
1595
|
const manualInput = modelsLoaded && modelList.length === 0;
|
|
1872
1596
|
let curEfforts = [];
|
|
1873
1597
|
for (let mi = 0; mi < modelList.length; mi++) {
|
|
1874
|
-
if (modelList[mi].id ===
|
|
1598
|
+
if (modelList[mi].id === row2.model) {
|
|
1875
1599
|
curEfforts = modelList[mi].efforts || [];
|
|
1876
1600
|
break;
|
|
1877
1601
|
}
|
|
@@ -1879,7 +1603,7 @@ var __defProp = Object.defineProperty;
|
|
|
1879
1603
|
let providerOptions = providers.map((p) => {
|
|
1880
1604
|
return { id: p.id, label: p.name !== p.id ? p.name + "(" + p.id + ")" : p.id };
|
|
1881
1605
|
});
|
|
1882
|
-
if (isPrimary) {
|
|
1606
|
+
if (isPrimary && !isLayer) {
|
|
1883
1607
|
providerOptions = [
|
|
1884
1608
|
{
|
|
1885
1609
|
id: "",
|
|
@@ -1887,27 +1611,27 @@ var __defProp = Object.defineProperty;
|
|
|
1887
1611
|
}
|
|
1888
1612
|
].concat(providerOptions);
|
|
1889
1613
|
}
|
|
1890
|
-
if (
|
|
1891
|
-
providerOptions.push({ id:
|
|
1614
|
+
if (row2.provider && !providersById[row2.provider]) {
|
|
1615
|
+
providerOptions.push({ id: row2.provider, label: row2.provider + "(已不在列表)" });
|
|
1892
1616
|
}
|
|
1893
1617
|
const modelOptions = modelList.map((m) => {
|
|
1894
1618
|
return { id: m.id, label: m.name !== m.id ? m.name + "(" + m.id + ")" : m.id };
|
|
1895
1619
|
});
|
|
1896
|
-
if (
|
|
1897
|
-
modelOptions.push({ id:
|
|
1620
|
+
if (row2.model && !modelList.some((m) => m.id === row2.model)) {
|
|
1621
|
+
modelOptions.push({ id: row2.model, label: row2.model + "(已不在列表)" });
|
|
1898
1622
|
}
|
|
1899
1623
|
const effortOptions = [{ id: "", label: "跟随部署配置" }].concat(
|
|
1900
1624
|
curEfforts.filter((k) => EFFORT_VOCAB.indexOf(k) >= 0).map((k) => ({ id: k, label: k }))
|
|
1901
1625
|
);
|
|
1902
|
-
return /* @__PURE__ */ (0,
|
|
1903
|
-
/* @__PURE__ */ (0,
|
|
1904
|
-
/* @__PURE__ */ (0,
|
|
1905
|
-
/* @__PURE__ */ (0,
|
|
1626
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { ...STY.row, ...rowErrs[i] ? STY.rowErr : null }, children: [
|
|
1627
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.line, children: [
|
|
1628
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.badge, children: isPrimary ? "主" : String(i + 1) }),
|
|
1629
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1906
1630
|
NSel,
|
|
1907
1631
|
{
|
|
1908
1632
|
style: { flex: 1, minWidth: 150 },
|
|
1909
1633
|
options: providerOptions,
|
|
1910
|
-
value:
|
|
1634
|
+
value: row2.provider,
|
|
1911
1635
|
disabled,
|
|
1912
1636
|
placeholder: isPrimary ? "跟随默认模型" : "供应商",
|
|
1913
1637
|
onChange: (v) => {
|
|
@@ -1915,12 +1639,12 @@ var __defProp = Object.defineProperty;
|
|
|
1915
1639
|
}
|
|
1916
1640
|
}
|
|
1917
1641
|
),
|
|
1918
|
-
!
|
|
1642
|
+
!row2.provider ? null : manualInput ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1919
1643
|
NInput,
|
|
1920
1644
|
{
|
|
1921
1645
|
style: { flex: 1, minWidth: 150 },
|
|
1922
1646
|
placeholder: "模型 id(该供应商未提供列表,输入后回车)…",
|
|
1923
|
-
value: manual.idx === i ? manual.text :
|
|
1647
|
+
value: manual.idx === i ? manual.text : row2.model,
|
|
1924
1648
|
onChange: (e) => {
|
|
1925
1649
|
setManual({ idx: i, text: e.target.value });
|
|
1926
1650
|
},
|
|
@@ -1934,12 +1658,12 @@ var __defProp = Object.defineProperty;
|
|
|
1934
1658
|
}
|
|
1935
1659
|
}
|
|
1936
1660
|
}
|
|
1937
|
-
) : /* @__PURE__ */ (0,
|
|
1661
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1938
1662
|
NSel,
|
|
1939
1663
|
{
|
|
1940
1664
|
style: { flex: 1, minWidth: 150 },
|
|
1941
1665
|
options: modelOptions,
|
|
1942
|
-
value:
|
|
1666
|
+
value: row2.model,
|
|
1943
1667
|
disabled: disabled || !modelsLoaded,
|
|
1944
1668
|
placeholder: modelsLoaded ? isPrimary ? "(选择模型,可留空跟随默认)" : "(选择模型)" : "加载模型列表…",
|
|
1945
1669
|
onChange: (v) => {
|
|
@@ -1947,13 +1671,13 @@ var __defProp = Object.defineProperty;
|
|
|
1947
1671
|
}
|
|
1948
1672
|
}
|
|
1949
1673
|
),
|
|
1950
|
-
/* @__PURE__ */ (0,
|
|
1674
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1951
1675
|
NSel,
|
|
1952
1676
|
{
|
|
1953
1677
|
style: { flexShrink: 0, width: 118 },
|
|
1954
1678
|
options: effortOptions,
|
|
1955
|
-
value:
|
|
1956
|
-
disabled: disabled || !(
|
|
1679
|
+
value: row2.reasoningEffort,
|
|
1680
|
+
disabled: disabled || !(row2.provider && row2.model),
|
|
1957
1681
|
placeholder: "跟随部署配置",
|
|
1958
1682
|
onChange: (v) => {
|
|
1959
1683
|
updateRow(i, { reasoningEffort: v });
|
|
@@ -1961,8 +1685,8 @@ var __defProp = Object.defineProperty;
|
|
|
1961
1685
|
}
|
|
1962
1686
|
)
|
|
1963
1687
|
] }),
|
|
1964
|
-
/* @__PURE__ */ (0,
|
|
1965
|
-
/* @__PURE__ */ (0,
|
|
1688
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.actions, children: [
|
|
1689
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1966
1690
|
NButton,
|
|
1967
1691
|
{
|
|
1968
1692
|
style: STY.ico,
|
|
@@ -1974,11 +1698,11 @@ var __defProp = Object.defineProperty;
|
|
|
1974
1698
|
children: "↑"
|
|
1975
1699
|
}
|
|
1976
1700
|
),
|
|
1977
|
-
/* @__PURE__ */ (0,
|
|
1701
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1978
1702
|
NButton,
|
|
1979
1703
|
{
|
|
1980
1704
|
style: STY.ico,
|
|
1981
|
-
disabled: disabled || i === rows.length - 1 || i === 0 && !
|
|
1705
|
+
disabled: disabled || i === rows.length - 1 || i === 0 && !row2.provider,
|
|
1982
1706
|
title: "下移",
|
|
1983
1707
|
onClick: () => {
|
|
1984
1708
|
moveRow(i, 1);
|
|
@@ -1986,7 +1710,7 @@ var __defProp = Object.defineProperty;
|
|
|
1986
1710
|
children: "↓"
|
|
1987
1711
|
}
|
|
1988
1712
|
),
|
|
1989
|
-
/* @__PURE__ */ (0,
|
|
1713
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1990
1714
|
NButton,
|
|
1991
1715
|
{
|
|
1992
1716
|
style: STY.ico,
|
|
@@ -1999,43 +1723,525 @@ var __defProp = Object.defineProperty;
|
|
|
1999
1723
|
}
|
|
2000
1724
|
)
|
|
2001
1725
|
] }),
|
|
2002
|
-
isPrimary && !
|
|
2003
|
-
!known ? /* @__PURE__ */ (0,
|
|
2004
|
-
rowErrs[i] ? /* @__PURE__ */ (0,
|
|
1726
|
+
isPrimary && !row2.provider ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: STY.note, children: "跟随默认模型" + (info.default ? ":" + info.default.provider + " / " + info.default.model : "") + "(档位跟随部署配置,选定模型后可单独设置)" }) : null,
|
|
1727
|
+
!known ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: STY.warn, children: "⚠ 供应商 " + row2.provider + " 已不在已注册路由中:该路由调用会失败并被链跳过(不阻止保存)。" }) : null,
|
|
1728
|
+
rowErrs[i] ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: STY.warn, children: "✕ " + rowErrs[i] }) : null
|
|
2005
1729
|
] }, "row" + i);
|
|
2006
1730
|
});
|
|
2007
|
-
const effChain = info.chain && info.chain.effectiveChain || [];
|
|
2008
|
-
return /* @__PURE__ */ (0,
|
|
2009
|
-
/* @__PURE__ */ (0,
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
1731
|
+
const effChain = isLayer ? layerView?.effectiveChain ?? [] : info.chain && info.chain.effectiveChain || [];
|
|
1732
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.wrap, children: [
|
|
1733
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1734
|
+
"div",
|
|
1735
|
+
{
|
|
1736
|
+
style: { ...S.switchDesc, marginBottom: 8 },
|
|
1737
|
+
title: isLayer ? "本层链失败只在层内降级,绝不落到全局链;每行档位独立" : "第 1 行主路由;失败(报错/掐断/网络异常/空输出)按序降级;每行档位独立",
|
|
1738
|
+
children: isLayer ? "主路由失败,只降级到本层回退" : "主路由失败,按序降级"
|
|
1739
|
+
}
|
|
1740
|
+
),
|
|
1741
|
+
rowEls,
|
|
1742
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NButton, { style: STY.add, disabled: disabled || capped, onClick: addRow, children: capped ? "已达上限(8 条)" : "+ 添加回退路由" }),
|
|
1743
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 10 }, children: [
|
|
1744
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1745
|
+
NButton,
|
|
1746
|
+
{
|
|
1747
|
+
style: isLayer ? { ...STY.ghost, color: "var(--dsh-mem-danger)", border: "1px solid var(--dsh-mem-danger)" } : STY.ghost,
|
|
1748
|
+
disabled,
|
|
1749
|
+
onClick: clearToFollow,
|
|
1750
|
+
children: isLayer ? "清除自定义 · 跟随全局" : "清空并跟随部署配置"
|
|
1751
|
+
}
|
|
1752
|
+
),
|
|
1753
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: S.grow }),
|
|
1754
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NButton, { variant: "primary", disabled, onClick: save, children: "保存" })
|
|
1755
|
+
] }),
|
|
1756
|
+
err ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { ...STY.warn, marginTop: 8 }, children: "✕ " + err }) : null,
|
|
1757
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--dsh-mem-border)" }, children: [
|
|
1758
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)", marginBottom: 4 }, children: "实际链" + (dirty ? "(保存后更新;当前显示已保存值)" : "") }),
|
|
1759
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1760
|
+
"div",
|
|
1761
|
+
{
|
|
1762
|
+
style: { fontSize: 12, color: "var(--dsh-mem-text-2)", wordBreak: "break-all", fontFamily: "ui-monospace, SFMono-Regular, Consolas, monospace" },
|
|
1763
|
+
children: effChain.map((e) => e.provider + "/" + e.model + (e.effort ? "(" + e.effort + ")" : "")).join(" → ") || "(暂无可用路由)"
|
|
1764
|
+
}
|
|
1765
|
+
)
|
|
1766
|
+
] })
|
|
1767
|
+
] });
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
// client/src/tabs/DistillSettings.tsx
|
|
1771
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1772
|
+
var STY2 = {
|
|
1773
|
+
hint: { fontSize: 11, color: "var(--dsh-mem-text-3)", margin: "0 0 8px" },
|
|
1774
|
+
panelHead: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginBottom: 8 },
|
|
1775
|
+
panelTitle: { fontSize: 13.5, fontWeight: 650, color: "var(--dsh-mem-text-1)" },
|
|
1776
|
+
chip: {
|
|
1777
|
+
display: "inline-flex",
|
|
1778
|
+
alignItems: "center",
|
|
1779
|
+
borderRadius: 999,
|
|
1780
|
+
padding: "1px 8px",
|
|
1781
|
+
fontSize: 11,
|
|
1782
|
+
fontWeight: 600,
|
|
1783
|
+
lineHeight: "18px",
|
|
1784
|
+
whiteSpace: "nowrap"
|
|
1785
|
+
},
|
|
1786
|
+
chipAccent: { background: "var(--dsh-mem-accent-weak)", color: "var(--dsh-mem-accent-text)" },
|
|
1787
|
+
chipMuted: { background: "var(--dsh-mem-bg-inset)", color: "var(--dsh-mem-text-2)" },
|
|
1788
|
+
inUse: { fontSize: 11, color: "var(--dsh-mem-text-3)", margin: "6px 0 0" }
|
|
1789
|
+
};
|
|
1790
|
+
function Dot(props) {
|
|
1791
|
+
const base = {
|
|
1792
|
+
display: "inline-block",
|
|
1793
|
+
width: 7,
|
|
1794
|
+
height: 7,
|
|
1795
|
+
borderRadius: "50%",
|
|
1796
|
+
marginRight: 4,
|
|
1797
|
+
verticalAlign: "middle",
|
|
1798
|
+
flexShrink: 0
|
|
1799
|
+
};
|
|
1800
|
+
if (props.kind === "runtime") return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...base, background: "var(--dsh-mem-accent)" } });
|
|
1801
|
+
if (props.kind === "static") return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...base, background: "transparent", border: "1.5px solid var(--dsh-mem-text-3)" } });
|
|
1802
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...base, background: "var(--dsh-mem-track)" } });
|
|
1803
|
+
}
|
|
1804
|
+
var LAYER_META = {
|
|
1805
|
+
l1: { seg: "L1", title: "L1 · 抽取 / 去重" },
|
|
1806
|
+
l2: { seg: "L2", title: "L2 · 场景摘要" },
|
|
1807
|
+
l3: { seg: "L3", title: "L3 · 画像蒸馏" }
|
|
1808
|
+
};
|
|
1809
|
+
function DistillSettings(props) {
|
|
1810
|
+
const rpc = props.rpc;
|
|
1811
|
+
const disabled = !!props.disabled;
|
|
1812
|
+
const [info, setInfo] = (0, import_react7.useState)(null);
|
|
1813
|
+
const [tab, setTab] = (0, import_react7.useState)("g");
|
|
1814
|
+
(0, import_react7.useEffect)(() => {
|
|
1815
|
+
let alive = true;
|
|
1816
|
+
const refresh = () => {
|
|
1817
|
+
rpc("dsh-memory/llm-providers", {}).then((r) => {
|
|
1818
|
+
if (alive && r && r.ok) setInfo(r.value);
|
|
1819
|
+
}).catch(() => {
|
|
1820
|
+
});
|
|
1821
|
+
};
|
|
1822
|
+
refresh();
|
|
1823
|
+
const timer = setInterval(refresh, 5e3);
|
|
1824
|
+
return () => {
|
|
1825
|
+
alive = false;
|
|
1826
|
+
clearInterval(timer);
|
|
1827
|
+
};
|
|
1828
|
+
}, [rpc]);
|
|
1829
|
+
const layers = info?.layerChains;
|
|
1830
|
+
const dotOf = (k) => layers?.[k]?.source ?? "global";
|
|
1831
|
+
const users = ["l1", "l2", "l3"].filter((k) => dotOf(k) === "global");
|
|
1832
|
+
const segOptions = [
|
|
1833
|
+
{ key: "g", label: "全局默认", title: "未单独配置的层走这条链(当前在用:" + (users.length ? users.map((k) => LAYER_META[k].seg).join("、") : "无") + ")" },
|
|
1834
|
+
...["l1", "l2", "l3"].map((k) => ({
|
|
1835
|
+
key: k,
|
|
1836
|
+
title: LAYER_META[k].title + " · " + (dotOf(k) === "runtime" ? "运行时自定义" : dotOf(k) === "static" ? "部署 YAML 层链(只读)" : "跟随全局"),
|
|
1837
|
+
label: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
|
|
1838
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Dot, { kind: dotOf(k) }),
|
|
1839
|
+
LAYER_META[k].seg
|
|
1840
|
+
] })
|
|
1841
|
+
}))
|
|
1842
|
+
];
|
|
1843
|
+
const chipTitle = (k) => dotOf(k) === "runtime" ? "本层走设置页自定义链" : dotOf(k) === "static" ? "本层走部署 YAML 层链(UI 只读,自定义可覆盖)" : "本层未单独配置,走全局默认链";
|
|
1844
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
1845
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Segmented, { value: tab, options: segOptions, onChange: (k) => setTab(k) }),
|
|
1846
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: STY2.hint, children: [
|
|
1847
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Dot, { kind: "runtime" }),
|
|
1848
|
+
" 自定义 · ",
|
|
1849
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Dot, { kind: "static" }),
|
|
1850
|
+
" 部署 YAML · ",
|
|
1851
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Dot, { kind: "global" }),
|
|
1852
|
+
" 跟随全局",
|
|
1853
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { title: "每层实际链:运行时自定义 → 部署 YAML 层链 → 全局默认链,逐级兜底;部署 pin 时运行时编辑只读", children: "(层链优先于全局)" })
|
|
1854
|
+
] }),
|
|
1855
|
+
tab === "g" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
1856
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: STY2.panelHead, children: [
|
|
1857
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: STY2.panelTitle, children: "全局默认链" }),
|
|
1858
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...STY2.chip, ...STY2.chipAccent }, children: "运行时 · 可编辑" }),
|
|
1859
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...STY2.inUse, marginLeft: "auto" }, children: users.length ? "在用:" + users.map((k) => LAYER_META[k].seg).join("、") : "当前无层使用" })
|
|
1860
|
+
] }),
|
|
1861
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RouteChainEditor, { rpc, disabled }, "g"),
|
|
1862
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BudgetInputs, { rpc, disabled, data: props.data, setData: props.setData, onError: props.onError, scope: "input" }, "g-budget")
|
|
1863
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
1864
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: STY2.panelHead, children: [
|
|
1865
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: STY2.panelTitle, children: LAYER_META[tab].title }),
|
|
1866
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...STY2.chip, ...dotOf(tab) === "runtime" ? STY2.chipAccent : STY2.chipMuted }, title: chipTitle(tab), children: dotOf(tab) === "runtime" ? "运行时自定义" : dotOf(tab) === "static" ? "静态 · YAML" : "跟随全局" })
|
|
1867
|
+
] }),
|
|
1868
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RouteChainEditor, { rpc, disabled, scope: tab }, tab),
|
|
1869
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BudgetInputs, { rpc, disabled, data: props.data, setData: props.setData, onError: props.onError, scope: tab }, tab + "-budget")
|
|
1870
|
+
] })
|
|
1871
|
+
] });
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
// client/src/tabs/EmbeddingSection.tsx
|
|
1875
|
+
var import_react8 = require("react");
|
|
1876
|
+
|
|
1877
|
+
// client/src/rpc.ts
|
|
1878
|
+
function makeRpc(ctx) {
|
|
1879
|
+
return (endpoint, payload) => {
|
|
1880
|
+
if (!ctx.connection || !ctx.connection.rpc) return Promise.reject(new Error("connection 服务不可用"));
|
|
1881
|
+
return ctx.connection.rpc.call("/rpc", endpoint, payload ?? {});
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
function asLoose(rpc) {
|
|
1885
|
+
return rpc;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
// client/src/tabs/EmbeddingSection.tsx
|
|
1889
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1890
|
+
function EmbeddingSection(props) {
|
|
1891
|
+
const rpc = props.rpc;
|
|
1892
|
+
const loose = asLoose(rpc);
|
|
1893
|
+
const [st, setSt] = (0, import_react8.useState)(null);
|
|
1894
|
+
const [err, setErr] = (0, import_react8.useState)(null);
|
|
1895
|
+
const busyPollRef = (0, import_react8.useRef)(null);
|
|
1896
|
+
const load = (0, import_react8.useCallback)(() => {
|
|
1897
|
+
rpc("dsh-memory/embedding-state-get", {}).then((r) => {
|
|
1898
|
+
if (r && r.ok && r.value && r.value.supported !== false) {
|
|
1899
|
+
const v = r.value;
|
|
1900
|
+
if (busyPollRef.current) {
|
|
1901
|
+
busyPollRef.current.v = !!(v.download && (v.download.phase === "downloading" || v.download.phase === "verifying") || v.apply.busy || v.reindex && v.reindex.running || v.runtime && v.runtime.phase === "installing");
|
|
1902
|
+
}
|
|
1903
|
+
setSt(v);
|
|
1904
|
+
setErr(null);
|
|
1905
|
+
} else if (r && r.ok && r.value && r.value.supported === false) {
|
|
1906
|
+
setSt(null);
|
|
1907
|
+
setErr("__unsupported__");
|
|
1908
|
+
} else {
|
|
1909
|
+
setErr(r && !r.ok ? r.error.message : "RPC error");
|
|
1910
|
+
}
|
|
1911
|
+
}).catch((e) => {
|
|
1912
|
+
setErr(String(e && e.message || e));
|
|
1913
|
+
});
|
|
1914
|
+
}, [rpc]);
|
|
1915
|
+
(0, import_react8.useEffect)(() => {
|
|
1916
|
+
load();
|
|
1917
|
+
}, [load]);
|
|
1918
|
+
(0, import_react8.useEffect)(() => {
|
|
1919
|
+
let stopped = false;
|
|
1920
|
+
const busyFlag = { v: false };
|
|
1921
|
+
busyPollRef.current = busyFlag;
|
|
1922
|
+
const tick = () => {
|
|
1923
|
+
if (stopped) return;
|
|
1924
|
+
load();
|
|
1925
|
+
timer = setTimeout(tick, busyFlag.v ? 1200 : 5e3);
|
|
1926
|
+
};
|
|
1927
|
+
let timer = setTimeout(tick, 1200);
|
|
1928
|
+
return () => {
|
|
1929
|
+
stopped = true;
|
|
1930
|
+
clearTimeout(timer);
|
|
1931
|
+
busyPollRef.current = null;
|
|
1932
|
+
};
|
|
1933
|
+
}, [load]);
|
|
1934
|
+
const call = (endpoint, payload, confirmText) => {
|
|
1935
|
+
if (confirmText && !window.confirm(confirmText)) return;
|
|
1936
|
+
loose(endpoint, payload).then((r) => {
|
|
1937
|
+
if (!r || !r.ok) setErr(r && r.error ? r.error.message : "操作失败");
|
|
1938
|
+
else {
|
|
1939
|
+
setErr(null);
|
|
1940
|
+
load();
|
|
1941
|
+
}
|
|
1942
|
+
}).catch((e) => {
|
|
1943
|
+
setErr(String(e && e.message || e));
|
|
1944
|
+
});
|
|
1945
|
+
};
|
|
1946
|
+
if (err === "__unsupported__") {
|
|
1947
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-mem-rb-card", children: [
|
|
1948
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "语义检索(嵌入)" }),
|
|
1949
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-muted", children: "存储处于降级状态,嵌入管理不可用。" })
|
|
1950
|
+
] });
|
|
1951
|
+
}
|
|
1952
|
+
if (!st) {
|
|
1953
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-mem-rb-card", children: [
|
|
1954
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-muted", children: err ? "嵌入状态读取失败:" + err : "嵌入状态读取中…" }),
|
|
1955
|
+
err ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { style: { marginTop: 8 }, onClick: load, children: "重试" }) : null
|
|
1956
|
+
] });
|
|
1957
|
+
}
|
|
1958
|
+
const switchConfirm = "切换嵌入源后将按新模型重建向量索引(期间语义检索暂退化为关键词匹配,不影响对话)。确定切换?";
|
|
1959
|
+
const onSource = (key) => {
|
|
1960
|
+
if (key === "local") {
|
|
1961
|
+
const ready = [];
|
|
1962
|
+
for (let i = 0; i < st.models.length; i++) if (st.models[i].state === "downloaded") ready.push(st.models[i].id);
|
|
1963
|
+
if (ready.length === 0) {
|
|
1964
|
+
setErr("请先在下方下载一个本地嵌入模型,再切换到本地档。");
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
const current = st.activeModel && ready.indexOf(st.activeModel) >= 0 ? st.activeModel : ready[0];
|
|
1968
|
+
call("dsh-memory/embedding-source-set", { source: "local", activeModel: current }, switchConfirm);
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
call("dsh-memory/embedding-source-set", { source: key }, key === "remote" ? switchConfirm : null);
|
|
1972
|
+
};
|
|
1973
|
+
const dl = st.download;
|
|
1974
|
+
const dlActive = dl && (dl.phase === "downloading" || dl.phase === "verifying");
|
|
1975
|
+
const ap = st.apply;
|
|
1976
|
+
const localInfo = st.local;
|
|
1977
|
+
const rt = st.runtime;
|
|
1978
|
+
let runtimeRow = null;
|
|
1979
|
+
if (rt.phase === "installing") {
|
|
1980
|
+
runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 8, fontSize: 12 }, children: [
|
|
1981
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: S.flexRow, children: [
|
|
1982
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "安装推理运行时中… 已耗时 " + Math.round(rt.elapsedMs / 1e3) + "s(约 100~200MB,视网络)" }),
|
|
1983
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.grow }),
|
|
1984
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-runtime-cancel", {}), children: "取消" })
|
|
1985
|
+
] }),
|
|
1986
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("pre", { style: { ...S.pre, maxHeight: 68, marginTop: 6, fontSize: 11, opacity: 0.85 }, children: (rt.lastLines || []).join("\n") || "等待 npm 输出…" })
|
|
1987
|
+
] });
|
|
1988
|
+
} else if (rt.phase === "error") {
|
|
1989
|
+
runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "运行时安装失败:" + (rt.error || "未知") + "(重新切换嵌入源可重试)" });
|
|
1990
|
+
} else if (rt.phase === "ready") {
|
|
1991
|
+
runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "推理运行时就绪(transformers.js v" + rt.installedVersion + ")" });
|
|
1992
|
+
} else if (st.source === "local") {
|
|
1993
|
+
runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "首次启用本地嵌入时会自动安装推理运行时(约 100~200MB)。" });
|
|
1994
|
+
}
|
|
1995
|
+
let applyRow = null;
|
|
1996
|
+
if (ap.phase === "warming") {
|
|
1997
|
+
applyRow = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "加载嵌入模型中…(首次需数秒)" });
|
|
1998
|
+
} else if (ap.phase === "switching") {
|
|
1999
|
+
applyRow = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "切换嵌入源中…" });
|
|
2000
|
+
} else if (ap.phase === "error") {
|
|
2001
|
+
applyRow = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "切换失败:" + ap.message + "(已保存的嵌入源不变,重启后仍按原源运行)" });
|
|
2002
|
+
} else if (st.reindex && st.reindex.running) {
|
|
2003
|
+
const rj = st.reindex;
|
|
2004
|
+
const rDone = rj.l1Done + rj.l0Done;
|
|
2005
|
+
const rTotal = rj.l1Total + rj.l0Total;
|
|
2006
|
+
const rPct = rTotal > 0 ? Math.round(rDone / rTotal * 100) : 0;
|
|
2007
|
+
applyRow = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 8 }, children: [
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: S.flexRow, children: [
|
|
2009
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-mem-rb-muted", children: "重嵌入中 L1 " + rj.l1Done + "/" + rj.l1Total + " · L0 " + rj.l0Done + "/" + rj.l0Total + "(" + rPct + "%)" }),
|
|
2010
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.grow }),
|
|
2011
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-reindex-cancel", {}), children: "取消" })
|
|
2012
|
+
] }),
|
|
2013
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { ...S.flexRow, marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: rPct + "%" } }) }) })
|
|
2014
|
+
] });
|
|
2015
|
+
}
|
|
2016
|
+
const modelCards = st.models.map((m) => {
|
|
2017
|
+
const isActive = st.source === "local" && st.activeModel === m.id;
|
|
2018
|
+
const mDl = dlActive && dl.modelId === m.id;
|
|
2019
|
+
const pct = mDl && dl.overallTotal > 0 ? Math.round(dl.overallReceived / dl.overallTotal * 100) : 0;
|
|
2020
|
+
let action = null;
|
|
2021
|
+
if (mDl) {
|
|
2022
|
+
action = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { flex: 1, minWidth: 200 }, children: [
|
|
2023
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: S.flexRow, children: [
|
|
2024
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" }, children: (dl.phase === "verifying" ? "校验中 " : "") + fmtMB(dl.overallReceived) + " / " + fmtMB(dl.overallTotal) + "(文件 " + dl.fileIndex + "/" + dl.fileCount + "," + pct + "%" + (dl.speedBps > 0 && dl.phase === "downloading" ? "," + fmtMB(dl.speedBps) + "/s" : "") + ")" }),
|
|
2025
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.grow }),
|
|
2026
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-download-cancel", {}), children: "取消" })
|
|
2027
|
+
] }),
|
|
2028
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { ...S.flexRow, marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: pct + "%" } }) }) })
|
|
2029
|
+
] });
|
|
2030
|
+
} else if (isActive) {
|
|
2031
|
+
action = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: S.flexRow, children: [
|
|
2032
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-work-task", children: "使用中" }),
|
|
2033
|
+
localInfo && localInfo.state === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-mem-rb-muted", children: "模型加载中…" }) : null,
|
|
2034
|
+
localInfo && localInfo.state === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "加载失败:" + (localInfo.error || "") }) : null,
|
|
2035
|
+
localInfo && localInfo.state === "ready" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-mem-rb-muted", children: "已就绪" }) : null
|
|
2036
|
+
] });
|
|
2037
|
+
} else if (m.state === "downloaded") {
|
|
2038
|
+
action = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: S.flexRow, children: [
|
|
2039
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2040
|
+
NButton,
|
|
2041
|
+
{
|
|
2042
|
+
disabled: ap.busy,
|
|
2043
|
+
onClick: () => call("dsh-memory/embedding-source-set", { source: "local", activeModel: m.id }, switchConfirm),
|
|
2044
|
+
children: "启用"
|
|
2045
|
+
}
|
|
2046
|
+
),
|
|
2047
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2048
|
+
NButton,
|
|
2049
|
+
{
|
|
2050
|
+
disabled: dlActive,
|
|
2051
|
+
onClick: () => call("dsh-memory/embedding-model-delete", { modelId: m.id }, "删除已下载的 " + m.name + "(" + fmtMB(m.totalBytes) + ")?"),
|
|
2052
|
+
children: "删除"
|
|
2053
|
+
}
|
|
2054
|
+
)
|
|
2055
|
+
] });
|
|
2056
|
+
} else {
|
|
2057
|
+
action = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2058
|
+
NButton,
|
|
2059
|
+
{
|
|
2060
|
+
disabled: dlActive || !st.ceilings.local,
|
|
2061
|
+
title: !st.ceilings.local ? "部署已禁用本地嵌入模型" : "",
|
|
2062
|
+
onClick: () => call("dsh-memory/embedding-download-start", { modelId: m.id }),
|
|
2063
|
+
children: (m.state === "partial" ? "继续下载 " : "下载 ") + fmtMB(m.totalBytes)
|
|
2064
|
+
}
|
|
2065
|
+
);
|
|
2066
|
+
}
|
|
2067
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2068
|
+
"div",
|
|
2069
|
+
{
|
|
2070
|
+
style: { ...S.flexRow, padding: "8px 0", borderBottom: "1px solid var(--dsh-mem-border)", flexWrap: "wrap" },
|
|
2071
|
+
children: [
|
|
2072
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { minWidth: 150 }, children: [
|
|
2073
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontWeight: 600 }, children: m.name }),
|
|
2074
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-muted", children: m.tags.join(" · ") + " · " + m.dims + " 维 · 上下文 " + m.contextTokens })
|
|
2075
|
+
] }),
|
|
2076
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { flex: 1, minWidth: 180, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: m.description }),
|
|
2077
|
+
action
|
|
2078
|
+
]
|
|
2079
|
+
},
|
|
2080
|
+
m.id
|
|
2081
|
+
);
|
|
2082
|
+
});
|
|
2083
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-mem-rb-card", children: [
|
|
2084
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: S.flexRow, children: [
|
|
2085
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "语义检索(嵌入源)" }),
|
|
2014
2086
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.grow }),
|
|
2015
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { variant: "primary", disabled, onClick: save, children: "保存" })
|
|
2016
|
-
] }),
|
|
2017
|
-
err ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { ...STY.warn, marginTop: 8 }, children: "✕ " + err }) : null,
|
|
2018
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--dsh-mem-border)" }, children: [
|
|
2019
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)", marginBottom: 4 }, children: "实际链" + (dirty ? "(保存后更新;当前显示已保存值)" : "") }),
|
|
2020
2087
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2021
|
-
|
|
2088
|
+
Segmented,
|
|
2022
2089
|
{
|
|
2023
|
-
|
|
2024
|
-
|
|
2090
|
+
value: st.source,
|
|
2091
|
+
options: [
|
|
2092
|
+
{ key: "off", label: "关闭" },
|
|
2093
|
+
{ key: "local", label: "本地", disabled: !st.ceilings.local, disabledTitle: "部署已禁用本地嵌入模型" },
|
|
2094
|
+
{ key: "remote", label: "远程", disabled: !st.ceilings.remote, disabledTitle: "部署未配置远程嵌入(baseUrl/apiKey/model/dimensions)" }
|
|
2095
|
+
],
|
|
2096
|
+
onChange: onSource
|
|
2025
2097
|
}
|
|
2026
2098
|
)
|
|
2027
|
-
] })
|
|
2099
|
+
] }),
|
|
2100
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 4 }, children: st.source === "off" ? "当前:关键词(BM25)检索,不做向量嵌入" : st.source === "remote" ? "当前:远程嵌入(" + (rt ? "" : "") + "模型由部署配置给定)" : "当前:本地嵌入" + (st.activeModel ? "(" + st.activeModel + ")" : "") }),
|
|
2101
|
+
st.activeNote ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { marginTop: 4, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: st.activeNote }) : null,
|
|
2102
|
+
err && err !== "__unsupported__" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: err }) : null,
|
|
2103
|
+
dl && dl.phase === "error" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "下载失败:" + (dl.error || "") }) : null,
|
|
2104
|
+
runtimeRow,
|
|
2105
|
+
applyRow,
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.panelLabel, children: "本地模型目录(下载后离线可用,不随插件分发)" }),
|
|
2107
|
+
modelCards
|
|
2028
2108
|
] });
|
|
2029
2109
|
}
|
|
2030
2110
|
|
|
2031
|
-
// client/src/tabs/
|
|
2111
|
+
// client/src/tabs/RebuildPanel.tsx
|
|
2112
|
+
var import_react9 = require("react");
|
|
2032
2113
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2114
|
+
var RB_PHASE_LABEL = {
|
|
2115
|
+
preparing: "准备中(归档旧数据 · 清空检索库)",
|
|
2116
|
+
distilling: "分块蒸馏中",
|
|
2117
|
+
finalizing: "收尾(强制 L2 场景 + L3 画像)"
|
|
2118
|
+
};
|
|
2119
|
+
function RebuildPanel(props) {
|
|
2120
|
+
const rpc = props.rpc;
|
|
2121
|
+
const [rbRaw, setRb] = (0, import_react9.useState)(null);
|
|
2122
|
+
const [confirmOpen, setConfirmOpen] = (0, import_react9.useState)(false);
|
|
2123
|
+
const [busy, setBusy] = (0, import_react9.useState)(false);
|
|
2124
|
+
const [rbError, setRbError] = (0, import_react9.useState)(null);
|
|
2125
|
+
const refresh = (0, import_react9.useCallback)(() => {
|
|
2126
|
+
rpc("dsh-memory/rebuild-status", {}).then((r) => {
|
|
2127
|
+
if (r && r.ok) setRb(r.value);
|
|
2128
|
+
}).catch(() => {
|
|
2129
|
+
});
|
|
2130
|
+
}, [rpc]);
|
|
2131
|
+
(0, import_react9.useEffect)(() => {
|
|
2132
|
+
refresh();
|
|
2133
|
+
}, [refresh]);
|
|
2134
|
+
const running = !!(rbRaw && rbRaw.running);
|
|
2135
|
+
(0, import_react9.useEffect)(() => {
|
|
2136
|
+
if (!running) return;
|
|
2137
|
+
const timer = setInterval(refresh, 1500);
|
|
2138
|
+
return () => {
|
|
2139
|
+
clearInterval(timer);
|
|
2140
|
+
};
|
|
2141
|
+
}, [running, refresh]);
|
|
2142
|
+
if (!rbRaw || rbRaw.supported === false) return null;
|
|
2143
|
+
const rb = rbRaw;
|
|
2144
|
+
ensureThemeStyle();
|
|
2145
|
+
const start = () => {
|
|
2146
|
+
setBusy(true);
|
|
2147
|
+
setRbError(null);
|
|
2148
|
+
rpc("dsh-memory/rebuild-start", {}).then((r) => {
|
|
2149
|
+
setBusy(false);
|
|
2150
|
+
if (r && r.ok) {
|
|
2151
|
+
setConfirmOpen(false);
|
|
2152
|
+
setRb(r.value);
|
|
2153
|
+
} else {
|
|
2154
|
+
setRbError(r && r.error ? r.error.message : "启动失败");
|
|
2155
|
+
}
|
|
2156
|
+
}).catch((e) => {
|
|
2157
|
+
setBusy(false);
|
|
2158
|
+
setRbError(String(e && e.message || e));
|
|
2159
|
+
});
|
|
2160
|
+
};
|
|
2161
|
+
const cancel = () => {
|
|
2162
|
+
setBusy(true);
|
|
2163
|
+
rpc("dsh-memory/rebuild-cancel", {}).then((r) => {
|
|
2164
|
+
setBusy(false);
|
|
2165
|
+
if (r && r.ok) setRb(r.value);
|
|
2166
|
+
}).catch(() => {
|
|
2167
|
+
setBusy(false);
|
|
2168
|
+
});
|
|
2169
|
+
};
|
|
2170
|
+
const empty = !running && (rb.messageCount === 0 || rb.estCalls === 0);
|
|
2171
|
+
const pct = rb.total > 0 ? Math.round(rb.done / rb.total * 100) : 0;
|
|
2172
|
+
let lastNote = null;
|
|
2173
|
+
if (!running && rb.phase === "done") {
|
|
2174
|
+
lastNote = "上次重建:完成(" + rb.done + "/" + rb.total + " 会话,产出 " + rb.recordsBuilt + " 条记录)" + (rb.finishedAt ? " · " + fmtTime(new Date(rb.finishedAt).toISOString()) : "");
|
|
2175
|
+
} else if (!running && rb.phase === "cancelled") {
|
|
2176
|
+
lastNote = "上次重建:已取消(完成 " + rb.done + "/" + rb.total + " 会话,已重建部分保留)";
|
|
2177
|
+
} else if (!running && rb.phase === "failed") {
|
|
2178
|
+
lastNote = "上次重建:失败:" + (rb.error || "未知错误");
|
|
2179
|
+
}
|
|
2180
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dsh-mem-rb-card", children: [
|
|
2181
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
|
|
2182
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "重建记忆" }),
|
|
2183
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "dsh-mem-rb-muted", style: { flex: 1, minWidth: 180 }, children: running ? (RB_PHASE_LABEL[rb.phase] || rb.phase) + " · " + rb.done + "/" + rb.total + " 会话(" + pct + "%)" : "从 L0 原始对话重新蒸馏 L1/L2/L3;旧数据先归档(不删除)" }),
|
|
2184
|
+
running ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(NButton, { disabled: busy || rb.cancelRequested, onClick: cancel, children: rb.cancelRequested ? "取消中…" : "取消重建" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2185
|
+
NButton,
|
|
2186
|
+
{
|
|
2187
|
+
disabled: busy || empty,
|
|
2188
|
+
title: empty ? "L0 无消息,无可重建内容" : "重新蒸馏全部记忆",
|
|
2189
|
+
style: { color: "var(--dsh-mem-danger)" },
|
|
2190
|
+
onClick: () => {
|
|
2191
|
+
setConfirmOpen(true);
|
|
2192
|
+
},
|
|
2193
|
+
children: busy ? "…" : "开始重建"
|
|
2194
|
+
}
|
|
2195
|
+
)
|
|
2196
|
+
] }),
|
|
2197
|
+
running ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 10 }, children: [
|
|
2198
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: pct + "%" } }) }),
|
|
2199
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" }, children: "产出 " + rb.recordsBuilt + " 条" })
|
|
2200
|
+
] }) : null,
|
|
2201
|
+
lastNote ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: lastNote }) : null,
|
|
2202
|
+
rbError ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: rbError }) : null,
|
|
2203
|
+
confirmOpen ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2204
|
+
NModal,
|
|
2205
|
+
{
|
|
2206
|
+
open: true,
|
|
2207
|
+
onClose: () => {
|
|
2208
|
+
setConfirmOpen(false);
|
|
2209
|
+
},
|
|
2210
|
+
title: "确认重建全部记忆?",
|
|
2211
|
+
footer: [
|
|
2212
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2213
|
+
NButton,
|
|
2214
|
+
{
|
|
2215
|
+
onClick: () => {
|
|
2216
|
+
setConfirmOpen(false);
|
|
2217
|
+
},
|
|
2218
|
+
children: "取消"
|
|
2219
|
+
},
|
|
2220
|
+
"cancel"
|
|
2221
|
+
),
|
|
2222
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(NButton, { variant: "primary", disabled: busy, onClick: start, children: busy ? "启动中…" : "开始重建" }, "confirm")
|
|
2223
|
+
],
|
|
2224
|
+
children: [
|
|
2225
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2226
|
+
"将以 L0 原始对话为事实源重新蒸馏:",
|
|
2227
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("b", { children: rb.sessionCount + " 个会话 · " + rb.messageCount + " 条消息" }),
|
|
2228
|
+
",预计 ≥" + rb.estCalls + " 次蒸馏调用。"
|
|
2229
|
+
] }),
|
|
2230
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { marginTop: 8 }, children: "现有 L1 记忆 / L2 场景 / L3 画像会整体归档(*.bak.时间戳,可手工找回),随后清空重建;重建期间可正常对话,新对话的蒸馏优先进行;中途可取消,已重建部分保留。" })
|
|
2231
|
+
]
|
|
2232
|
+
}
|
|
2233
|
+
) : null
|
|
2234
|
+
] });
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
// client/src/tabs/OverviewTab.tsx
|
|
2238
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2033
2239
|
function OverviewTab(props) {
|
|
2034
2240
|
const rpc = props.rpc;
|
|
2035
|
-
const [stats, setStats] = (0,
|
|
2036
|
-
const [settingsData, setSettingsData] = (0,
|
|
2037
|
-
const [error, setError] = (0,
|
|
2038
|
-
const load = (0,
|
|
2241
|
+
const [stats, setStats] = (0, import_react10.useState)(null);
|
|
2242
|
+
const [settingsData, setSettingsData] = (0, import_react10.useState)(null);
|
|
2243
|
+
const [error, setError] = (0, import_react10.useState)(null);
|
|
2244
|
+
const load = (0, import_react10.useCallback)(() => {
|
|
2039
2245
|
rpc("dsh-memory/stats", {}).then((r) => {
|
|
2040
2246
|
if (r && r.ok) setStats(r.value);
|
|
2041
2247
|
else setError(r && r.error ? r.error.message : "RPC error");
|
|
@@ -2047,7 +2253,7 @@ var __defProp = Object.defineProperty;
|
|
|
2047
2253
|
}).catch(() => {
|
|
2048
2254
|
});
|
|
2049
2255
|
}, [rpc]);
|
|
2050
|
-
(0,
|
|
2256
|
+
(0, import_react10.useEffect)(() => {
|
|
2051
2257
|
load();
|
|
2052
2258
|
const timer = setInterval(load, 5e3);
|
|
2053
2259
|
return () => {
|
|
@@ -2058,7 +2264,7 @@ var __defProp = Object.defineProperty;
|
|
|
2058
2264
|
if (!settingsData) return;
|
|
2059
2265
|
const prev = settingsData;
|
|
2060
2266
|
const patch = { [key]: value };
|
|
2061
|
-
const next = { ...prev, settings: { ...prev.settings,
|
|
2267
|
+
const next = { ...prev, settings: { ...prev.settings, [key]: value } };
|
|
2062
2268
|
setSettingsData(next);
|
|
2063
2269
|
rpc("dsh-memory/settings-set", patch).then((r) => {
|
|
2064
2270
|
if (!r || !r.ok) {
|
|
@@ -2101,10 +2307,10 @@ var __defProp = Object.defineProperty;
|
|
|
2101
2307
|
if (!settingsData.ceilings.recall) off.push("召回");
|
|
2102
2308
|
if (off.length > 0) ceilingNote = "注意:部署配置已停用 " + off.join("、") + "(运行时开关无法开启)";
|
|
2103
2309
|
}
|
|
2104
|
-
return /* @__PURE__ */ (0,
|
|
2105
|
-
settingsData && settingsData.supported === false ? /* @__PURE__ */ (0,
|
|
2106
|
-
/* @__PURE__ */ (0,
|
|
2107
|
-
/* @__PURE__ */ (0,
|
|
2310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
2311
|
+
settingsData && settingsData.supported === false ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: S.hint, children: "settings 服务不可用,记忆模式开关未启用(记忆保持全开)。" }) : settingsData ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.switchPanel, children: [
|
|
2312
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.panelLabel, children: "记忆模式" }),
|
|
2313
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2108
2314
|
SwitchRow,
|
|
2109
2315
|
{
|
|
2110
2316
|
label: "记忆模式",
|
|
@@ -2115,7 +2321,7 @@ var __defProp = Object.defineProperty;
|
|
|
2115
2321
|
}
|
|
2116
2322
|
}
|
|
2117
2323
|
),
|
|
2118
|
-
/* @__PURE__ */ (0,
|
|
2324
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2119
2325
|
SwitchRow,
|
|
2120
2326
|
{
|
|
2121
2327
|
label: "捕获",
|
|
@@ -2127,7 +2333,7 @@ var __defProp = Object.defineProperty;
|
|
|
2127
2333
|
}
|
|
2128
2334
|
}
|
|
2129
2335
|
),
|
|
2130
|
-
/* @__PURE__ */ (0,
|
|
2336
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2131
2337
|
SwitchRow,
|
|
2132
2338
|
{
|
|
2133
2339
|
label: "蒸馏",
|
|
@@ -2139,7 +2345,7 @@ var __defProp = Object.defineProperty;
|
|
|
2139
2345
|
}
|
|
2140
2346
|
}
|
|
2141
2347
|
),
|
|
2142
|
-
/* @__PURE__ */ (0,
|
|
2348
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2143
2349
|
SwitchRow,
|
|
2144
2350
|
{
|
|
2145
2351
|
label: "召回",
|
|
@@ -2151,42 +2357,41 @@ var __defProp = Object.defineProperty;
|
|
|
2151
2357
|
}
|
|
2152
2358
|
}
|
|
2153
2359
|
),
|
|
2154
|
-
/* @__PURE__ */ (0,
|
|
2155
|
-
/* @__PURE__ */ (0,
|
|
2156
|
-
/* @__PURE__ */ (0,
|
|
2157
|
-
ceilingNote ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: S.hint, children: ceilingNote }) : null
|
|
2360
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.panelLabel, children: "蒸馏参数" }),
|
|
2361
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DistillSettings, { rpc, disabled: !master, data: settingsData, setData: setSettingsData, onError: setError }),
|
|
2362
|
+
ceilingNote ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: S.hint, children: ceilingNote }) : null
|
|
2158
2363
|
] }) : null,
|
|
2159
|
-
/* @__PURE__ */ (0,
|
|
2160
|
-
/* @__PURE__ */ (0,
|
|
2161
|
-
degraded ? /* @__PURE__ */ (0,
|
|
2162
|
-
error ? /* @__PURE__ */ (0,
|
|
2163
|
-
/* @__PURE__ */ (0,
|
|
2164
|
-
/* @__PURE__ */ (0,
|
|
2165
|
-
return /* @__PURE__ */ (0,
|
|
2166
|
-
/* @__PURE__ */ (0,
|
|
2167
|
-
/* @__PURE__ */ (0,
|
|
2364
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(EmbeddingSection, { rpc }),
|
|
2365
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RebuildPanel, { rpc }),
|
|
2366
|
+
degraded ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { ...S.error, marginBottom: 10 }, children: "⚠ " + stats.message + "。上方数据为最后一次成功读取的值,记忆功能当前未工作。" }) : null,
|
|
2367
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.error, children: "获取状态失败:" + error }) : !stats ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: S.intro, children: "正在读取记忆状态…" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
2368
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.panelLabel, children: "记忆概况" }),
|
|
2369
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.statGrid, children: tiles.map((t) => {
|
|
2370
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dsh-mem-card", style: S.statTile, children: [
|
|
2371
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.statNum, children: t.num }),
|
|
2372
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.statLabel, children: t.label })
|
|
2168
2373
|
] }, t.label);
|
|
2169
2374
|
}) }),
|
|
2170
|
-
/* @__PURE__ */ (0,
|
|
2171
|
-
infos.map((
|
|
2172
|
-
return /* @__PURE__ */ (0,
|
|
2173
|
-
/* @__PURE__ */ (0,
|
|
2174
|
-
/* @__PURE__ */ (0,
|
|
2175
|
-
] },
|
|
2375
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.panelLabel, children: "运行状态" }),
|
|
2376
|
+
infos.map((row2) => {
|
|
2377
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.infoRow, children: [
|
|
2378
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: S.infoKey, children: row2[0] }),
|
|
2379
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: S.infoVal, children: row2[1] })
|
|
2380
|
+
] }, row2[0]);
|
|
2176
2381
|
})
|
|
2177
2382
|
] }),
|
|
2178
|
-
/* @__PURE__ */ (0,
|
|
2383
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: S.hint, children: "浏览各层记忆内容请切换上方 Tab;原始对话(L0)不入浏览器,可由模型侧 conversation_search 工具查询。" })
|
|
2179
2384
|
] });
|
|
2180
2385
|
}
|
|
2181
2386
|
|
|
2182
2387
|
// client/src/tabs/PersonaTab.tsx
|
|
2183
|
-
var
|
|
2184
|
-
var
|
|
2388
|
+
var import_react11 = require("react");
|
|
2389
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2185
2390
|
function PersonaTab(props) {
|
|
2186
2391
|
const rpc = props.rpc;
|
|
2187
|
-
const [content, setContent] = (0,
|
|
2188
|
-
const [error, setError] = (0,
|
|
2189
|
-
const load = (0,
|
|
2392
|
+
const [content, setContent] = (0, import_react11.useState)(null);
|
|
2393
|
+
const [error, setError] = (0, import_react11.useState)(null);
|
|
2394
|
+
const load = (0, import_react11.useCallback)(() => {
|
|
2190
2395
|
setError(null);
|
|
2191
2396
|
rpc("dsh-memory/persona", {}).then((r) => {
|
|
2192
2397
|
if (r && r.ok) setContent(r.value.content);
|
|
@@ -2195,23 +2400,23 @@ var __defProp = Object.defineProperty;
|
|
|
2195
2400
|
setError(String(e && e.message || e));
|
|
2196
2401
|
});
|
|
2197
2402
|
}, [rpc]);
|
|
2198
|
-
(0,
|
|
2403
|
+
(0, import_react11.useEffect)(() => {
|
|
2199
2404
|
load();
|
|
2200
2405
|
}, [load]);
|
|
2201
|
-
return /* @__PURE__ */ (0,
|
|
2202
|
-
/* @__PURE__ */ (0,
|
|
2203
|
-
/* @__PURE__ */ (0,
|
|
2204
|
-
/* @__PURE__ */ (0,
|
|
2205
|
-
/* @__PURE__ */ (0,
|
|
2406
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
2407
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
|
|
2408
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: S.muted, children: error ? "加载失败" : content === null ? "加载中…" : content ? content.length + " 字符" : "未生成画像" }),
|
|
2409
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: S.grow }),
|
|
2410
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(NButton, { onClick: load, children: "刷新" })
|
|
2206
2411
|
] }),
|
|
2207
|
-
error ? /* @__PURE__ */ (0,
|
|
2208
|
-
content ? /* @__PURE__ */ (0,
|
|
2412
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { ...S.error, marginBottom: 10 }, children: "画像读取失败:" + error + "(点右上“刷新”重试)" }) : null,
|
|
2413
|
+
content ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("pre", { style: S.pre, children: content }) : content === null ? null : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: S.intro, children: "画像尚未生成;蒸馏若干记忆后 L3 会自动产出。" })
|
|
2209
2414
|
] });
|
|
2210
2415
|
}
|
|
2211
2416
|
|
|
2212
2417
|
// client/src/tabs/RecordsTab.tsx
|
|
2213
|
-
var
|
|
2214
|
-
var
|
|
2418
|
+
var import_react12 = require("react");
|
|
2419
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2215
2420
|
var TYPE_CHOICES = [
|
|
2216
2421
|
"persona",
|
|
2217
2422
|
"episodic",
|
|
@@ -2224,20 +2429,20 @@ var __defProp = Object.defineProperty;
|
|
|
2224
2429
|
function RecordsTab(props) {
|
|
2225
2430
|
const rpc = props.rpc;
|
|
2226
2431
|
const limit = 50;
|
|
2227
|
-
const [items, setItems] = (0,
|
|
2228
|
-
const [hasMore, setHasMore] = (0,
|
|
2229
|
-
const [total, setTotal] = (0,
|
|
2230
|
-
const [sceneOptions, setSceneOptions] = (0,
|
|
2231
|
-
const [loading, setLoading] = (0,
|
|
2232
|
-
const [truncated, setTruncated] = (0,
|
|
2233
|
-
const [error, setError] = (0,
|
|
2234
|
-
const [expandedId, setExpandedId] = (0,
|
|
2235
|
-
const [query, setQuery] = (0,
|
|
2236
|
-
const [typeFilter, setTypeFilter] = (0,
|
|
2237
|
-
const [sceneFilter, setSceneFilter] = (0,
|
|
2238
|
-
const [last, setLast] = (0,
|
|
2239
|
-
const seqRef = (0,
|
|
2240
|
-
const fetchPage = (0,
|
|
2432
|
+
const [items, setItems] = (0, import_react12.useState)([]);
|
|
2433
|
+
const [hasMore, setHasMore] = (0, import_react12.useState)(false);
|
|
2434
|
+
const [total, setTotal] = (0, import_react12.useState)(null);
|
|
2435
|
+
const [sceneOptions, setSceneOptions] = (0, import_react12.useState)([]);
|
|
2436
|
+
const [loading, setLoading] = (0, import_react12.useState)(false);
|
|
2437
|
+
const [truncated, setTruncated] = (0, import_react12.useState)(false);
|
|
2438
|
+
const [error, setError] = (0, import_react12.useState)(null);
|
|
2439
|
+
const [expandedId, setExpandedId] = (0, import_react12.useState)(null);
|
|
2440
|
+
const [query, setQuery] = (0, import_react12.useState)("");
|
|
2441
|
+
const [typeFilter, setTypeFilter] = (0, import_react12.useState)("");
|
|
2442
|
+
const [sceneFilter, setSceneFilter] = (0, import_react12.useState)("");
|
|
2443
|
+
const [last, setLast] = (0, import_react12.useState)({ query: "", type: "", scene: "" });
|
|
2444
|
+
const seqRef = (0, import_react12.useRef)(0);
|
|
2445
|
+
const fetchPage = (0, import_react12.useCallback)(
|
|
2241
2446
|
(conds, offset, append) => {
|
|
2242
2447
|
setLoading(true);
|
|
2243
2448
|
setError(null);
|
|
@@ -2272,13 +2477,13 @@ var __defProp = Object.defineProperty;
|
|
|
2272
2477
|
setLast(conds);
|
|
2273
2478
|
fetchPage(conds, 0, false);
|
|
2274
2479
|
};
|
|
2275
|
-
(0,
|
|
2480
|
+
(0, import_react12.useEffect)(() => {
|
|
2276
2481
|
fetchPage({ query: "", type: "", scene: "" }, 0, false);
|
|
2277
2482
|
}, [fetchPage]);
|
|
2278
2483
|
const countText = total !== null ? "共 " + total + " 条" : items.length + " 条" + (hasMore ? "+" : "");
|
|
2279
|
-
return /* @__PURE__ */ (0,
|
|
2280
|
-
/* @__PURE__ */ (0,
|
|
2281
|
-
/* @__PURE__ */ (0,
|
|
2484
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
2485
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: S.toolbar, children: [
|
|
2486
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2282
2487
|
NInput,
|
|
2283
2488
|
{
|
|
2284
2489
|
style: { flex: 1, minWidth: 160 },
|
|
@@ -2292,7 +2497,7 @@ var __defProp = Object.defineProperty;
|
|
|
2292
2497
|
}
|
|
2293
2498
|
}
|
|
2294
2499
|
),
|
|
2295
|
-
/* @__PURE__ */ (0,
|
|
2500
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2296
2501
|
NSel,
|
|
2297
2502
|
{
|
|
2298
2503
|
style: { maxWidth: 200 },
|
|
@@ -2305,7 +2510,7 @@ var __defProp = Object.defineProperty;
|
|
|
2305
2510
|
onChange: setTypeFilter
|
|
2306
2511
|
}
|
|
2307
2512
|
),
|
|
2308
|
-
/* @__PURE__ */ (0,
|
|
2513
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2309
2514
|
NSel,
|
|
2310
2515
|
{
|
|
2311
2516
|
style: { maxWidth: 220 },
|
|
@@ -2318,12 +2523,12 @@ var __defProp = Object.defineProperty;
|
|
|
2318
2523
|
onChange: setSceneFilter
|
|
2319
2524
|
}
|
|
2320
2525
|
),
|
|
2321
|
-
/* @__PURE__ */ (0,
|
|
2526
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NButton, { onClick: search, children: "搜索" })
|
|
2322
2527
|
] }),
|
|
2323
|
-
/* @__PURE__ */ (0,
|
|
2324
|
-
/* @__PURE__ */ (0,
|
|
2325
|
-
/* @__PURE__ */ (0,
|
|
2326
|
-
/* @__PURE__ */ (0,
|
|
2528
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
|
|
2529
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: S.muted, children: loading ? "加载中…" : countText }),
|
|
2530
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.grow }),
|
|
2531
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2327
2532
|
NButton,
|
|
2328
2533
|
{
|
|
2329
2534
|
onClick: () => {
|
|
@@ -2333,11 +2538,11 @@ var __defProp = Object.defineProperty;
|
|
|
2333
2538
|
}
|
|
2334
2539
|
)
|
|
2335
2540
|
] }),
|
|
2336
|
-
error ? /* @__PURE__ */ (0,
|
|
2337
|
-
truncated ? /* @__PURE__ */ (0,
|
|
2338
|
-
items.length === 0 && !loading && !error ? /* @__PURE__ */ (0,
|
|
2541
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.error, children: error }) : null,
|
|
2542
|
+
truncated ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.hint, children: "搜索分页已达检索上限(200 条),更早的结果未显示。请用更精确的关键词或类型/情境过滤。" }) : null,
|
|
2543
|
+
items.length === 0 && !loading && !error ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: S.intro, children: "暂无记忆。对话几轮后,蒸馏管线会自动抽取记忆。" }) : items.map((m) => {
|
|
2339
2544
|
const open = expandedId === m.id;
|
|
2340
|
-
return /* @__PURE__ */ (0,
|
|
2545
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2341
2546
|
"div",
|
|
2342
2547
|
{
|
|
2343
2548
|
className: "dsh-mem-card dsh-mem-card-hover",
|
|
@@ -2346,23 +2551,23 @@ var __defProp = Object.defineProperty;
|
|
|
2346
2551
|
setExpandedId(open ? null : m.id);
|
|
2347
2552
|
},
|
|
2348
2553
|
children: [
|
|
2349
|
-
/* @__PURE__ */ (0,
|
|
2350
|
-
/* @__PURE__ */ (0,
|
|
2351
|
-
/* @__PURE__ */ (0,
|
|
2352
|
-
m.score !== null && m.score !== void 0 ? /* @__PURE__ */ (0,
|
|
2353
|
-
/* @__PURE__ */ (0,
|
|
2354
|
-
/* @__PURE__ */ (0,
|
|
2554
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: S.cardHead, children: [
|
|
2555
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + m.type, children: TYPE_LABELS[m.type] || m.type }),
|
|
2556
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: S.muted, children: "优先级 " + m.priority }),
|
|
2557
|
+
m.score !== null && m.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: S.muted, children: "相关度 " + Number(m.score).toFixed(2) }) : null,
|
|
2558
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.grow }),
|
|
2559
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: S.muted, children: fmtTime(m.updatedAt) })
|
|
2355
2560
|
] }),
|
|
2356
|
-
/* @__PURE__ */ (0,
|
|
2357
|
-
open ? /* @__PURE__ */ (0,
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.content, children: m.content }),
|
|
2562
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.detail, children: "id: " + m.id + "\n情境: " + (m.scene || "-") + "\n版本: v" + m.version + "(去重合并次数 " + m.version + ")\n创建: " + fmtTime(m.createdAt) + "\n活跃时间: " + (m.timestamps && m.timestamps.length > 0 ? m.timestamps.map(fmtTime).join(" → ") : "-") + "\n" + (m.sourceMessageIds && m.sourceMessageIds.length > 0 ? "来源消息: " + m.sourceMessageIds.join(", ") : "来源消息: -") }) : null
|
|
2358
2563
|
]
|
|
2359
2564
|
},
|
|
2360
2565
|
m.id
|
|
2361
2566
|
);
|
|
2362
2567
|
}),
|
|
2363
|
-
hasMore ? /* @__PURE__ */ (0,
|
|
2364
|
-
/* @__PURE__ */ (0,
|
|
2365
|
-
/* @__PURE__ */ (0,
|
|
2568
|
+
hasMore ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: S.flexRow, children: [
|
|
2569
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.grow }),
|
|
2570
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2366
2571
|
NButton,
|
|
2367
2572
|
{
|
|
2368
2573
|
disabled: loading,
|
|
@@ -2377,13 +2582,13 @@ var __defProp = Object.defineProperty;
|
|
|
2377
2582
|
}
|
|
2378
2583
|
|
|
2379
2584
|
// client/src/tabs/ScenesTab.tsx
|
|
2380
|
-
var
|
|
2381
|
-
var
|
|
2585
|
+
var import_react13 = require("react");
|
|
2586
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2382
2587
|
function SceneCard(props) {
|
|
2383
2588
|
const s = props.s;
|
|
2384
|
-
const [open, setOpen] = (0,
|
|
2385
|
-
return /* @__PURE__ */ (0,
|
|
2386
|
-
/* @__PURE__ */ (0,
|
|
2589
|
+
const [open, setOpen] = (0, import_react13.useState)(false);
|
|
2590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "dsh-mem-card dsh-mem-card-hover", style: S.card, children: [
|
|
2591
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2387
2592
|
"div",
|
|
2388
2593
|
{
|
|
2389
2594
|
style: { ...S.sceneHead, cursor: "pointer", userSelect: "none" },
|
|
@@ -2391,23 +2596,23 @@ var __defProp = Object.defineProperty;
|
|
|
2391
2596
|
setOpen(!open);
|
|
2392
2597
|
},
|
|
2393
2598
|
children: [
|
|
2394
|
-
/* @__PURE__ */ (0,
|
|
2395
|
-
/* @__PURE__ */ (0,
|
|
2396
|
-
s.heat ? /* @__PURE__ */ (0,
|
|
2397
|
-
/* @__PURE__ */ (0,
|
|
2398
|
-
/* @__PURE__ */ (0,
|
|
2599
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "dsh-mem-scene-chev", style: { transform: open ? "rotate(90deg)" : "none" }, children: "▸" }),
|
|
2600
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: S.sceneTitle, children: s.path }),
|
|
2601
|
+
s.heat ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: S.muted, children: "热度 " + s.heat }) : null,
|
|
2602
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.grow }),
|
|
2603
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: S.muted, children: "更新 " + fmtTime(s.updated) })
|
|
2399
2604
|
]
|
|
2400
2605
|
}
|
|
2401
2606
|
),
|
|
2402
|
-
s.summary ? /* @__PURE__ */ (0,
|
|
2403
|
-
open ? /* @__PURE__ */ (0,
|
|
2607
|
+
s.summary ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { ...S.muted, marginBottom: 6 }, children: s.summary }) : null,
|
|
2608
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("pre", { style: S.pre, children: s.content || "(空)" }) : null
|
|
2404
2609
|
] });
|
|
2405
2610
|
}
|
|
2406
2611
|
function ScenesTab(props) {
|
|
2407
2612
|
const rpc = props.rpc;
|
|
2408
|
-
const [items, setItems] = (0,
|
|
2409
|
-
const [error, setError] = (0,
|
|
2410
|
-
const load = (0,
|
|
2613
|
+
const [items, setItems] = (0, import_react13.useState)(null);
|
|
2614
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
2615
|
+
const load = (0, import_react13.useCallback)(() => {
|
|
2411
2616
|
rpc("dsh-memory/scenes", {}).then((r) => {
|
|
2412
2617
|
if (r && r.ok) {
|
|
2413
2618
|
setItems(r.value.items);
|
|
@@ -2417,25 +2622,25 @@ var __defProp = Object.defineProperty;
|
|
|
2417
2622
|
setError(String(e && e.message || e));
|
|
2418
2623
|
});
|
|
2419
2624
|
}, [rpc]);
|
|
2420
|
-
(0,
|
|
2625
|
+
(0, import_react13.useEffect)(() => {
|
|
2421
2626
|
load();
|
|
2422
2627
|
}, [load]);
|
|
2423
|
-
return /* @__PURE__ */ (0,
|
|
2424
|
-
/* @__PURE__ */ (0,
|
|
2425
|
-
/* @__PURE__ */ (0,
|
|
2426
|
-
/* @__PURE__ */ (0,
|
|
2427
|
-
/* @__PURE__ */ (0,
|
|
2628
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
2629
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
|
|
2630
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: S.muted, children: items ? items.length + " 个场景块" : "加载中…" }),
|
|
2631
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.grow }),
|
|
2632
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(NButton, { onClick: load, children: "刷新" })
|
|
2428
2633
|
] }),
|
|
2429
|
-
error ? /* @__PURE__ */ (0,
|
|
2430
|
-
items && items.length === 0 ? /* @__PURE__ */ (0,
|
|
2634
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.error, children: error }) : null,
|
|
2635
|
+
items && items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: S.intro, children: "暂无场景块。累计 5 条新记忆后 L2 会自动整合出第一个场景。" }) : null,
|
|
2431
2636
|
(items || []).map((s) => {
|
|
2432
|
-
return /* @__PURE__ */ (0,
|
|
2637
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SceneCard, { s }, s.path);
|
|
2433
2638
|
})
|
|
2434
2639
|
] });
|
|
2435
2640
|
}
|
|
2436
2641
|
|
|
2437
2642
|
// client/src/panel.tsx
|
|
2438
|
-
var
|
|
2643
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2439
2644
|
var TABS = [
|
|
2440
2645
|
["overview", "概览"],
|
|
2441
2646
|
["records", "记忆"],
|
|
@@ -2446,23 +2651,23 @@ var __defProp = Object.defineProperty;
|
|
|
2446
2651
|
];
|
|
2447
2652
|
function MemoryPanel(props) {
|
|
2448
2653
|
const rpc = props.rpc;
|
|
2449
|
-
const [tab, setTab] = (0,
|
|
2654
|
+
const [tab, setTab] = (0, import_react14.useState)("overview");
|
|
2450
2655
|
ensureThemeStyle();
|
|
2451
|
-
(0,
|
|
2656
|
+
(0, import_react14.useEffect)(() => {
|
|
2452
2657
|
watchSidebarIcon();
|
|
2453
2658
|
}, []);
|
|
2454
2659
|
let body;
|
|
2455
|
-
if (tab === "overview") body = /* @__PURE__ */ (0,
|
|
2456
|
-
else if (tab === "records") body = /* @__PURE__ */ (0,
|
|
2457
|
-
else if (tab === "scenes") body = /* @__PURE__ */ (0,
|
|
2458
|
-
else if (tab === "persona") body = /* @__PURE__ */ (0,
|
|
2459
|
-
else if (tab === "cost") body = /* @__PURE__ */ (0,
|
|
2460
|
-
else body = /* @__PURE__ */ (0,
|
|
2461
|
-
return /* @__PURE__ */ (0,
|
|
2462
|
-
/* @__PURE__ */ (0,
|
|
2463
|
-
/* @__PURE__ */ (0,
|
|
2464
|
-
/* @__PURE__ */ (0,
|
|
2465
|
-
return /* @__PURE__ */ (0,
|
|
2660
|
+
if (tab === "overview") body = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(OverviewTab, { rpc });
|
|
2661
|
+
else if (tab === "records") body = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(RecordsTab, { rpc });
|
|
2662
|
+
else if (tab === "scenes") body = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ScenesTab, { rpc });
|
|
2663
|
+
else if (tab === "persona") body = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PersonaTab, { rpc });
|
|
2664
|
+
else if (tab === "cost") body = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CostTab, { rpc });
|
|
2665
|
+
else body = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LogTab, { rpc });
|
|
2666
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "dsh-mem-root", style: S.section, children: [
|
|
2667
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { style: S.heading, children: "记忆 (Memory)" }),
|
|
2668
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { style: S.intro, children: "L0~L3 分层蒸馏记忆:浏览被记住的内容,控制记忆模式开关。" }),
|
|
2669
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: S.tabbar, children: TABS.map((t) => {
|
|
2670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2466
2671
|
"button",
|
|
2467
2672
|
{
|
|
2468
2673
|
className: tab === t[0] ? "dsh-mem-tab dsh-mem-tab-on" : "dsh-mem-tab",
|
|
@@ -2479,26 +2684,339 @@ var __defProp = Object.defineProperty;
|
|
|
2479
2684
|
}
|
|
2480
2685
|
|
|
2481
2686
|
// client/src/pill/MemoryModePill.tsx
|
|
2482
|
-
var
|
|
2687
|
+
var import_react17 = require("react");
|
|
2688
|
+
|
|
2689
|
+
// src/util/context-occupancy.ts
|
|
2690
|
+
var CONTEXT_METER_CIRCUMFERENCE = 34.55751918948772;
|
|
2691
|
+
function haloDashArray(occupancyRatio, circumference = CONTEXT_METER_CIRCUMFERENCE, minLen = 0) {
|
|
2692
|
+
const clamped = Number.isFinite(occupancyRatio) ? Math.min(1, Math.max(0, occupancyRatio)) : 0;
|
|
2693
|
+
const len = Math.max(clamped * circumference, clamped > 0 ? minLen : 0);
|
|
2694
|
+
return `${len} ${circumference}`;
|
|
2695
|
+
}
|
|
2696
|
+
var RADIUS_EPSILON = 1e-6;
|
|
2697
|
+
function isContextMeterAnchor(sig) {
|
|
2698
|
+
if (sig.ariaHasPopup !== "dialog") return false;
|
|
2699
|
+
if (sig.viewBox !== "0 0 14 14") return false;
|
|
2700
|
+
const radii = sig.circleRadii;
|
|
2701
|
+
if (!Array.isArray(radii) || radii.length !== 2) return false;
|
|
2702
|
+
return radii.every((r) => Math.abs(r - 5.5) < RADIUS_EPSILON);
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
// client/src/meter/occupancy-indicator.ts
|
|
2706
|
+
var statsCall = null;
|
|
2707
|
+
var snapshotBySession = /* @__PURE__ */ new Map();
|
|
2708
|
+
var activeSessionId = null;
|
|
2709
|
+
var panelListeners = /* @__PURE__ */ new Set();
|
|
2710
|
+
var panelWasOpen = false;
|
|
2711
|
+
var snapshotListeners = /* @__PURE__ */ new Set();
|
|
2712
|
+
function initOccupancyIndicator(call) {
|
|
2713
|
+
statsCall = call;
|
|
2714
|
+
}
|
|
2715
|
+
function noteOccupancySession(sessionId) {
|
|
2716
|
+
if (sessionId === activeSessionId) return;
|
|
2717
|
+
activeSessionId = sessionId;
|
|
2718
|
+
panelWasOpen = false;
|
|
2719
|
+
if (sessionId !== null && !snapshotBySession.has(sessionId)) void fetchSnapshot(true);
|
|
2720
|
+
}
|
|
2721
|
+
function onMeterPanelOpen(listener) {
|
|
2722
|
+
panelListeners.add(listener);
|
|
2723
|
+
return () => panelListeners.delete(listener);
|
|
2724
|
+
}
|
|
2725
|
+
function onMeterSnapshotUpdate(listener) {
|
|
2726
|
+
snapshotListeners.add(listener);
|
|
2727
|
+
return () => snapshotListeners.delete(listener);
|
|
2728
|
+
}
|
|
2729
|
+
function effectiveView(snap) {
|
|
2730
|
+
const recall = Math.max(snap?.backfillRecallTokens ?? 0, snap?.recallTokens ?? 0);
|
|
2731
|
+
const ledgerProfile = snap?.profileTokens ?? 0;
|
|
2732
|
+
const profile = ledgerProfile > 0 ? ledgerProfile : snap?.backfillProfileTokens ?? 0;
|
|
2733
|
+
return { stock: recall + profile, recall, profile, window: snap?.contextWindowTokens ?? null };
|
|
2734
|
+
}
|
|
2735
|
+
function currentMeterSnapshot() {
|
|
2736
|
+
const sid = activeSessionId;
|
|
2737
|
+
if (sid === null) return null;
|
|
2738
|
+
const snap = snapshotBySession.get(sid);
|
|
2739
|
+
if (!snap) return null;
|
|
2740
|
+
const v = effectiveView(snap);
|
|
2741
|
+
return {
|
|
2742
|
+
stockTokens: v.stock,
|
|
2743
|
+
recallTokens: v.recall,
|
|
2744
|
+
profileTokens: v.profile,
|
|
2745
|
+
contextWindowTokens: v.window,
|
|
2746
|
+
mode: snap.mode
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
function currentAnchor() {
|
|
2750
|
+
return anchorCache?.button ?? null;
|
|
2751
|
+
}
|
|
2752
|
+
var FETCH_MIN_INTERVAL_MS = 2e3;
|
|
2753
|
+
var FETCH_FAILURE_REMOVE_AFTER = 3;
|
|
2754
|
+
var lastFetchStartedAt = 0;
|
|
2755
|
+
var fetchInFlight = false;
|
|
2756
|
+
var fetchFailureStreak = 0;
|
|
2757
|
+
async function fetchSnapshot(force = false) {
|
|
2758
|
+
const sid = activeSessionId;
|
|
2759
|
+
if (!statsCall || !sid || fetchInFlight) return;
|
|
2760
|
+
if (!force && Date.now() - lastFetchStartedAt < FETCH_MIN_INTERVAL_MS) return;
|
|
2761
|
+
fetchInFlight = true;
|
|
2762
|
+
lastFetchStartedAt = Date.now();
|
|
2763
|
+
try {
|
|
2764
|
+
const res = await statsCall("dsh-memory/session-stats", { sessionId: sid });
|
|
2765
|
+
const v = res && res.ok ? res.value : void 0;
|
|
2766
|
+
if (!res || !res.ok) {
|
|
2767
|
+
fetchFailureStreak++;
|
|
2768
|
+
scheduleReconcile();
|
|
2769
|
+
return;
|
|
2770
|
+
}
|
|
2771
|
+
if (sid !== activeSessionId) return;
|
|
2772
|
+
snapshotBySession.set(sid, {
|
|
2773
|
+
stockTokens: v?.supported && v.memoryOccupancy ? v.memoryOccupancy.stockTokens : null,
|
|
2774
|
+
recallTokens: v?.supported && v.memoryOccupancy ? v.memoryOccupancy.recallTokens : null,
|
|
2775
|
+
profileTokens: v?.supported && v.memoryOccupancy ? v.memoryOccupancy.profileTokens : null,
|
|
2776
|
+
backfillRecallTokens: v?.supported ? v.occupancyBackfill?.recallTokens ?? null : null,
|
|
2777
|
+
backfillProfileTokens: v?.supported ? v.occupancyBackfill?.profileTokens ?? null : null,
|
|
2778
|
+
contextWindowTokens: v?.supported ? v.contextWindowTokens ?? null : null,
|
|
2779
|
+
mode: v?.supported ? v.mode ?? null : null,
|
|
2780
|
+
updatedAt: Date.now()
|
|
2781
|
+
});
|
|
2782
|
+
fetchFailureStreak = 0;
|
|
2783
|
+
for (const l of snapshotListeners) l();
|
|
2784
|
+
scheduleReconcile();
|
|
2785
|
+
} catch {
|
|
2786
|
+
fetchFailureStreak++;
|
|
2787
|
+
scheduleReconcile();
|
|
2788
|
+
} finally {
|
|
2789
|
+
fetchInFlight = false;
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
var PARASITE_CLASS = "dsh-mem-parasite";
|
|
2793
|
+
var MIN_VISIBLE_ARC = 2;
|
|
2794
|
+
var observer = null;
|
|
2795
|
+
var reconcileScheduled = false;
|
|
2796
|
+
var anchorCache = null;
|
|
2797
|
+
function watchContextMeter() {
|
|
2798
|
+
if (observer !== null || typeof document === "undefined" || !document.body) return;
|
|
2799
|
+
observer = new MutationObserver(onMutations);
|
|
2800
|
+
observer.observe(document.body, {
|
|
2801
|
+
childList: true,
|
|
2802
|
+
subtree: true,
|
|
2803
|
+
attributes: true,
|
|
2804
|
+
attributeFilter: ["stroke-dasharray", "aria-expanded", "aria-label"]
|
|
2805
|
+
});
|
|
2806
|
+
scheduleReconcile();
|
|
2807
|
+
}
|
|
2808
|
+
function onMutations(mutations) {
|
|
2809
|
+
for (const m of mutations) {
|
|
2810
|
+
const target = m.target;
|
|
2811
|
+
if (target && typeof target.classList?.contains === "function" && target.classList.contains(PARASITE_CLASS)) {
|
|
2812
|
+
continue;
|
|
2813
|
+
}
|
|
2814
|
+
scheduleReconcile();
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
function scheduleReconcile() {
|
|
2819
|
+
if (reconcileScheduled) return;
|
|
2820
|
+
reconcileScheduled = true;
|
|
2821
|
+
queueMicrotask(() => {
|
|
2822
|
+
reconcileScheduled = false;
|
|
2823
|
+
reconcile();
|
|
2824
|
+
});
|
|
2825
|
+
}
|
|
2826
|
+
function findAnchor() {
|
|
2827
|
+
const candidates = document.querySelectorAll('button[aria-haspopup="dialog"]');
|
|
2828
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
2829
|
+
const button = candidates[i];
|
|
2830
|
+
if (!button) continue;
|
|
2831
|
+
const svg = button.querySelector("svg");
|
|
2832
|
+
if (!svg) continue;
|
|
2833
|
+
const circles = svg.querySelectorAll("circle");
|
|
2834
|
+
const radii = [];
|
|
2835
|
+
for (let c = 0; c < circles.length; c++) {
|
|
2836
|
+
const r = parseFloat(circles[c]?.getAttribute("r") ?? "");
|
|
2837
|
+
if (Number.isFinite(r)) radii.push(r);
|
|
2838
|
+
}
|
|
2839
|
+
if (isContextMeterAnchor({
|
|
2840
|
+
ariaHasPopup: button.getAttribute("aria-haspopup"),
|
|
2841
|
+
viewBox: svg.getAttribute("viewBox"),
|
|
2842
|
+
circleRadii: radii
|
|
2843
|
+
})) {
|
|
2844
|
+
return { button, svg };
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
return null;
|
|
2848
|
+
}
|
|
2849
|
+
function reconcile() {
|
|
2850
|
+
if (!anchorCache || !anchorCache.button.isConnected || !anchorCache.svg.isConnected) {
|
|
2851
|
+
const found = document.body ? findAnchor() : null;
|
|
2852
|
+
anchorCache = found ? found : null;
|
|
2853
|
+
}
|
|
2854
|
+
if (!anchorCache) return;
|
|
2855
|
+
const open = anchorCache.button.getAttribute("aria-expanded") === "true";
|
|
2856
|
+
if (open !== panelWasOpen) {
|
|
2857
|
+
panelWasOpen = open;
|
|
2858
|
+
for (const l of panelListeners) l(open);
|
|
2859
|
+
if (open) void fetchSnapshot(true);
|
|
2860
|
+
}
|
|
2861
|
+
applyHalo();
|
|
2862
|
+
}
|
|
2863
|
+
function applyHalo() {
|
|
2864
|
+
const svg = anchorCache?.svg;
|
|
2865
|
+
if (!svg) return;
|
|
2866
|
+
const snap = activeSessionId !== null ? snapshotBySession.get(activeSessionId) : void 0;
|
|
2867
|
+
const failing = fetchFailureStreak >= FETCH_FAILURE_REMOVE_AFTER;
|
|
2868
|
+
const view = failing ? { stock: 0, recall: 0, profile: 0, window: null } : effectiveView(snap);
|
|
2869
|
+
const win = view.window;
|
|
2870
|
+
const ratio = win !== null && win > 0 ? Math.min(1, Math.max(0, view.stock / win)) : null;
|
|
2871
|
+
const existing = svg.querySelector(`circle.${PARASITE_CLASS}`);
|
|
2872
|
+
if (ratio === null || ratio <= 0) {
|
|
2873
|
+
existing?.remove();
|
|
2874
|
+
return;
|
|
2875
|
+
}
|
|
2876
|
+
let halo = existing;
|
|
2877
|
+
if (!halo) {
|
|
2878
|
+
halo = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
2879
|
+
halo.setAttribute("class", PARASITE_CLASS);
|
|
2880
|
+
halo.setAttribute("cx", "7");
|
|
2881
|
+
halo.setAttribute("cy", "7");
|
|
2882
|
+
halo.setAttribute("r", "6.4");
|
|
2883
|
+
halo.setAttribute("fill", "none");
|
|
2884
|
+
halo.setAttribute("stroke", "var(--dsh-mem-accent)");
|
|
2885
|
+
halo.setAttribute("stroke-width", "0.85");
|
|
2886
|
+
halo.setAttribute("transform", "rotate(-90 7 7)");
|
|
2887
|
+
halo.style.filter = "drop-shadow(0 0 3px var(--dsh-mem-accent))";
|
|
2888
|
+
halo.setAttribute("aria-hidden", "true");
|
|
2889
|
+
halo.style.pointerEvents = "none";
|
|
2890
|
+
svg.appendChild(halo);
|
|
2891
|
+
}
|
|
2892
|
+
halo.setAttribute(
|
|
2893
|
+
"stroke-dasharray",
|
|
2894
|
+
haloDashArray(ratio, CONTEXT_METER_CIRCUMFERENCE, MIN_VISIBLE_ARC)
|
|
2895
|
+
);
|
|
2896
|
+
const fill = svg.querySelector("circle:nth-of-type(2)");
|
|
2897
|
+
const offKey = fill?.getAttribute("stroke-dasharray") ?? "";
|
|
2898
|
+
if (svg.dataset.offKey !== void 0 && svg.dataset.offKey !== offKey) {
|
|
2899
|
+
void fetchSnapshot();
|
|
2900
|
+
}
|
|
2901
|
+
svg.dataset.offKey = offKey;
|
|
2902
|
+
}
|
|
2903
|
+
|
|
2904
|
+
// client/src/meter/panel-section.ts
|
|
2905
|
+
var SECTION_TAG = "dsh-mem-panel";
|
|
2906
|
+
var inited = false;
|
|
2907
|
+
var mounted = null;
|
|
2908
|
+
var panelOpen = false;
|
|
2909
|
+
function fmtTokens(n) {
|
|
2910
|
+
if (n >= 1e6) {
|
|
2911
|
+
const v = n / 1e6;
|
|
2912
|
+
return `${v < 100 ? v.toFixed(1) : Math.round(v)}M`;
|
|
2913
|
+
}
|
|
2914
|
+
if (n >= 1e3) {
|
|
2915
|
+
const v = n / 1e3;
|
|
2916
|
+
return `${v < 100 ? v.toFixed(1) : Math.round(v)}K`;
|
|
2917
|
+
}
|
|
2918
|
+
return String(Math.round(n));
|
|
2919
|
+
}
|
|
2920
|
+
function initPanelSection(read) {
|
|
2921
|
+
if (inited) return;
|
|
2922
|
+
inited = true;
|
|
2923
|
+
onMeterPanelOpen((open) => {
|
|
2924
|
+
panelOpen = open;
|
|
2925
|
+
if (!open) {
|
|
2926
|
+
mounted?.remove();
|
|
2927
|
+
mounted = null;
|
|
2928
|
+
return;
|
|
2929
|
+
}
|
|
2930
|
+
tryMount(read);
|
|
2931
|
+
});
|
|
2932
|
+
onMeterSnapshotUpdate(() => {
|
|
2933
|
+
if (panelOpen && !mounted) tryMount(read);
|
|
2934
|
+
});
|
|
2935
|
+
}
|
|
2936
|
+
function tryMount(read) {
|
|
2937
|
+
const view = read();
|
|
2938
|
+
if (!view || view.stockTokens === null || view.stockTokens <= 0) return;
|
|
2939
|
+
const anchor = currentAnchor();
|
|
2940
|
+
if (!anchor || document.activeElement !== anchor) return;
|
|
2941
|
+
const target = findDialogRoot();
|
|
2942
|
+
if (!target) return;
|
|
2943
|
+
mounted?.remove();
|
|
2944
|
+
mounted = renderSection(view);
|
|
2945
|
+
target.appendChild(mounted);
|
|
2946
|
+
}
|
|
2947
|
+
function findDialogRoot() {
|
|
2948
|
+
const dialogs = document.querySelectorAll('[role="dialog"]');
|
|
2949
|
+
for (let i = dialogs.length - 1; i >= 0; i--) {
|
|
2950
|
+
const el = dialogs[i];
|
|
2951
|
+
if (!el || !el.isConnected) continue;
|
|
2952
|
+
if (el.querySelector(`[data-${SECTION_TAG}]`) || el.hasAttribute(`data-${SECTION_TAG}`)) continue;
|
|
2953
|
+
const style = window.getComputedStyle(el);
|
|
2954
|
+
if (style.display === "none" || style.visibility === "hidden") continue;
|
|
2955
|
+
return el;
|
|
2956
|
+
}
|
|
2957
|
+
return null;
|
|
2958
|
+
}
|
|
2959
|
+
function row(dotColor, label, tokens) {
|
|
2960
|
+
const div = document.createElement("div");
|
|
2961
|
+
div.style.cssText = "display:flex;justify-content:space-between;align-items:center;padding:3px 0;font-size:12px;";
|
|
2962
|
+
const left = document.createElement("span");
|
|
2963
|
+
left.style.cssText = `display:inline-flex;align-items:center;gap:6px;color:var(--dsh-mem-text-2);`;
|
|
2964
|
+
const dot = document.createElement("i");
|
|
2965
|
+
dot.style.cssText = `width:7px;height:7px;border-radius:50%;background:${dotColor};display:inline-block;`;
|
|
2966
|
+
left.append(dot, document.createTextNode(label));
|
|
2967
|
+
const right = document.createElement("span");
|
|
2968
|
+
right.textContent = `~${fmtTokens(tokens)}`;
|
|
2969
|
+
right.style.cssText = "font-variant-numeric:tabular-nums;color:var(--dsh-mem-text-1);";
|
|
2970
|
+
div.append(left, right);
|
|
2971
|
+
return div;
|
|
2972
|
+
}
|
|
2973
|
+
function renderSection(view) {
|
|
2974
|
+
const section = document.createElement("section");
|
|
2975
|
+
section.setAttribute(`data-${SECTION_TAG}`, "");
|
|
2976
|
+
section.style.cssText = [
|
|
2977
|
+
"border-top:1px solid var(--dsh-mem-border)",
|
|
2978
|
+
"margin-top:6px",
|
|
2979
|
+
"padding-top:8px",
|
|
2980
|
+
"font-size:12px",
|
|
2981
|
+
"line-height:1.5"
|
|
2982
|
+
].join(";");
|
|
2983
|
+
const title = document.createElement("div");
|
|
2984
|
+
title.textContent = "记忆占用";
|
|
2985
|
+
title.style.cssText = "color:var(--dsh-mem-text-2);font-weight:600;margin-bottom:4px;";
|
|
2986
|
+
section.append(title);
|
|
2987
|
+
if (view.recallTokens !== null && view.recallTokens > 0) {
|
|
2988
|
+
section.append(row("var(--dsh-mem-accent)", "召回片段", view.recallTokens));
|
|
2989
|
+
}
|
|
2990
|
+
if (view.profileTokens !== null && view.profileTokens > 0) {
|
|
2991
|
+
section.append(row("var(--dsh-mem-accent)", "记忆稳定区", view.profileTokens));
|
|
2992
|
+
}
|
|
2993
|
+
if (view.mode === "off") {
|
|
2994
|
+
const offNote = document.createElement("div");
|
|
2995
|
+
offNote.textContent = "已停用 · 显示现存残留";
|
|
2996
|
+
offNote.style.cssText = "color:var(--dsh-mem-text-3);padding-top:2px;";
|
|
2997
|
+
section.append(offNote);
|
|
2998
|
+
}
|
|
2999
|
+
return section;
|
|
3000
|
+
}
|
|
2483
3001
|
|
|
2484
3002
|
// client/src/pill/ModeSlider.tsx
|
|
2485
|
-
var
|
|
3003
|
+
var import_react16 = require("react");
|
|
2486
3004
|
|
|
2487
3005
|
// client/src/pill/SessionInfoArea.tsx
|
|
2488
|
-
var
|
|
2489
|
-
var
|
|
3006
|
+
var import_react15 = require("react");
|
|
3007
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2490
3008
|
function sinfoCell(val, label, title) {
|
|
2491
|
-
return /* @__PURE__ */ (0,
|
|
2492
|
-
/* @__PURE__ */ (0,
|
|
2493
|
-
/* @__PURE__ */ (0,
|
|
3009
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { title: title || void 0, children: [
|
|
3010
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "dsh-mem-sinfo-val", children: val }),
|
|
3011
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "dsh-mem-sinfo-label", children: label })
|
|
2494
3012
|
] });
|
|
2495
3013
|
}
|
|
2496
3014
|
function SessionInfoArea(props) {
|
|
2497
3015
|
const rpc = props.rpc;
|
|
2498
3016
|
const sessionId = props.sessionId;
|
|
2499
|
-
const [stats, setStats] = (0,
|
|
2500
|
-
const busyRef = (0,
|
|
2501
|
-
(0,
|
|
3017
|
+
const [stats, setStats] = (0, import_react15.useState)(void 0);
|
|
3018
|
+
const busyRef = (0, import_react15.useRef)(false);
|
|
3019
|
+
(0, import_react15.useEffect)(() => {
|
|
2502
3020
|
if (!rpc || !sessionId) return void 0;
|
|
2503
3021
|
let alive = true;
|
|
2504
3022
|
let timer = null;
|
|
@@ -2531,7 +3049,7 @@ var __defProp = Object.defineProperty;
|
|
|
2531
3049
|
}, [rpc, sessionId]);
|
|
2532
3050
|
if (stats === null) return null;
|
|
2533
3051
|
if (stats === void 0) {
|
|
2534
|
-
return /* @__PURE__ */ (0,
|
|
3052
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "dsh-mem-sinfo", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "dsh-mem-sinfo-grid", children: [
|
|
2535
3053
|
sinfoCell("…", "召回命中"),
|
|
2536
3054
|
sinfoCell("…", "攒批进度"),
|
|
2537
3055
|
sinfoCell("…", "本会话记忆"),
|
|
@@ -2574,27 +3092,27 @@ var __defProp = Object.defineProperty;
|
|
|
2574
3092
|
else if (stats.retrieval === "none") note = "检索不可用(FTS 与向量均失效)";
|
|
2575
3093
|
}
|
|
2576
3094
|
const ago = fmtAgo(gl.lastExtractAt);
|
|
2577
|
-
return /* @__PURE__ */ (0,
|
|
2578
|
-
warn ? /* @__PURE__ */ (0,
|
|
2579
|
-
/* @__PURE__ */ (0,
|
|
3095
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "dsh-mem-sinfo", children: [
|
|
3096
|
+
warn ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "dsh-mem-sinfo-warn", children: warn }) : null,
|
|
3097
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "dsh-mem-sinfo-grid", children: [
|
|
2580
3098
|
sinfoCell(rcVal, rcLabel, rcTitle),
|
|
2581
3099
|
sinfoCell(dVal, dLabel, dTitle),
|
|
2582
3100
|
sinfoCell(String(di.producedRecords || 0), "本会话记忆", pTitle),
|
|
2583
3101
|
sinfoCell(stats.l0Count != null ? String(stats.l0Count) : "…", "会话消息")
|
|
2584
3102
|
] }),
|
|
2585
|
-
note ? /* @__PURE__ */ (0,
|
|
2586
|
-
/* @__PURE__ */ (0,
|
|
3103
|
+
note ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "dsh-mem-sinfo-note", children: note }) : null,
|
|
3104
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "dsh-mem-sinfo-sum", children: "待蒸馏 " + (gl.pendingTotal || 0) + " · 上次蒸馏 " + (ago || "尚未蒸馏") })
|
|
2587
3105
|
] });
|
|
2588
3106
|
}
|
|
2589
3107
|
|
|
2590
3108
|
// client/src/pill/ModeSlider.tsx
|
|
2591
|
-
var
|
|
3109
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
2592
3110
|
function ModeSlider(props) {
|
|
2593
3111
|
ensureThemeStyle();
|
|
2594
|
-
const trackRef = (0,
|
|
2595
|
-
const [drag, setDrag] = (0,
|
|
2596
|
-
const canvasRef = (0,
|
|
2597
|
-
const geoRef = (0,
|
|
3112
|
+
const trackRef = (0, import_react16.useRef)(null);
|
|
3113
|
+
const [drag, setDrag] = (0, import_react16.useState)(null);
|
|
3114
|
+
const canvasRef = (0, import_react16.useRef)(null);
|
|
3115
|
+
const geoRef = (0, import_react16.useRef)(null);
|
|
2598
3116
|
const clampX = (x) => {
|
|
2599
3117
|
if (x < 0) return 0;
|
|
2600
3118
|
if (x > INNER_W) return INNER_W;
|
|
@@ -2642,7 +3160,7 @@ var __defProp = Object.defineProperty;
|
|
|
2642
3160
|
// 与填充显隐同源
|
|
2643
3161
|
dragging: drag !== null
|
|
2644
3162
|
};
|
|
2645
|
-
(0,
|
|
3163
|
+
(0, import_react16.useEffect)(() => {
|
|
2646
3164
|
const canvas = canvasRef.current;
|
|
2647
3165
|
if (!canvas) return void 0;
|
|
2648
3166
|
const ctx = canvas.getContext && canvas.getContext("2d");
|
|
@@ -2667,14 +3185,14 @@ var __defProp = Object.defineProperty;
|
|
|
2667
3185
|
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
|
|
2668
3186
|
cell = width < 280 ? 5 : 6;
|
|
2669
3187
|
grid = [];
|
|
2670
|
-
for (let
|
|
3188
|
+
for (let row2 = 0; row2 * cell < height; row2++) {
|
|
2671
3189
|
for (let column = 0; column * cell < width; column++) {
|
|
2672
3190
|
grid.push({
|
|
2673
3191
|
x: column * cell,
|
|
2674
|
-
y:
|
|
2675
|
-
base: Math.abs(Math.sin(column * 12.9898 +
|
|
2676
|
-
tempo: Math.abs(Math.sin(column * 7.13 +
|
|
2677
|
-
phase: Math.abs(Math.sin(column * 31.17 +
|
|
3192
|
+
y: row2 * cell,
|
|
3193
|
+
base: Math.abs(Math.sin(column * 12.9898 + row2 * 78.233) * 43758.5453) % 1,
|
|
3194
|
+
tempo: Math.abs(Math.sin(column * 7.13 + row2 * 19.41) * 19341.731) % 1,
|
|
3195
|
+
phase: Math.abs(Math.sin(column * 31.17 + row2 * 11.93) * 28437.123) % 1
|
|
2678
3196
|
});
|
|
2679
3197
|
}
|
|
2680
3198
|
}
|
|
@@ -2752,7 +3270,7 @@ var __defProp = Object.defineProperty;
|
|
|
2752
3270
|
for (let i = 0; i < MODES.length; i++) {
|
|
2753
3271
|
const stopLeft = i / (MODES.length - 1) * INNER_W + THUMB / 2;
|
|
2754
3272
|
stops.push(
|
|
2755
|
-
/* @__PURE__ */ (0,
|
|
3273
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2756
3274
|
"div",
|
|
2757
3275
|
{
|
|
2758
3276
|
style: {
|
|
@@ -2771,7 +3289,7 @@ var __defProp = Object.defineProperty;
|
|
|
2771
3289
|
)
|
|
2772
3290
|
);
|
|
2773
3291
|
}
|
|
2774
|
-
return /* @__PURE__ */ (0,
|
|
3292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2775
3293
|
"div",
|
|
2776
3294
|
{
|
|
2777
3295
|
style: {
|
|
@@ -2781,13 +3299,13 @@ var __defProp = Object.defineProperty;
|
|
|
2781
3299
|
transform: "translateX(-50%)",
|
|
2782
3300
|
zIndex: 1e3
|
|
2783
3301
|
},
|
|
2784
|
-
children: /* @__PURE__ */ (0,
|
|
3302
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
2785
3303
|
"div",
|
|
2786
3304
|
{
|
|
2787
3305
|
className: "dsh-mem-popover",
|
|
2788
3306
|
style: { position: "relative", padding: "14px 16px" },
|
|
2789
3307
|
children: [
|
|
2790
|
-
/* @__PURE__ */ (0,
|
|
3308
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
2791
3309
|
"div",
|
|
2792
3310
|
{
|
|
2793
3311
|
ref: trackRef,
|
|
@@ -2806,7 +3324,7 @@ var __defProp = Object.defineProperty;
|
|
|
2806
3324
|
onPointerUp,
|
|
2807
3325
|
onPointerCancel: onPointerUp,
|
|
2808
3326
|
children: [
|
|
2809
|
-
activeIdx > 0 || drag !== null ? /* @__PURE__ */ (0,
|
|
3327
|
+
activeIdx > 0 || drag !== null ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2810
3328
|
"div",
|
|
2811
3329
|
{
|
|
2812
3330
|
style: {
|
|
@@ -2824,7 +3342,7 @@ var __defProp = Object.defineProperty;
|
|
|
2824
3342
|
}
|
|
2825
3343
|
) : null,
|
|
2826
3344
|
stops,
|
|
2827
|
-
/* @__PURE__ */ (0,
|
|
3345
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2828
3346
|
"canvas",
|
|
2829
3347
|
{
|
|
2830
3348
|
ref: canvasRef,
|
|
@@ -2841,7 +3359,7 @@ var __defProp = Object.defineProperty;
|
|
|
2841
3359
|
}
|
|
2842
3360
|
}
|
|
2843
3361
|
),
|
|
2844
|
-
/* @__PURE__ */ (0,
|
|
3362
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2845
3363
|
"div",
|
|
2846
3364
|
{
|
|
2847
3365
|
style: {
|
|
@@ -2860,12 +3378,12 @@ var __defProp = Object.defineProperty;
|
|
|
2860
3378
|
}
|
|
2861
3379
|
}
|
|
2862
3380
|
),
|
|
2863
|
-
drag !== null ? /* @__PURE__ */ (0,
|
|
3381
|
+
drag !== null ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "dsh-mem-bubble", style: { left: thumbLeft + THUMB / 2, zIndex: 4 }, children: info.label }) : null
|
|
2864
3382
|
]
|
|
2865
3383
|
}
|
|
2866
3384
|
),
|
|
2867
|
-
props.error ? /* @__PURE__ */ (0,
|
|
2868
|
-
props.rpc && props.sessionId ? /* @__PURE__ */ (0,
|
|
3385
|
+
props.error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-danger)", marginTop: 10, whiteSpace: "nowrap" }, children: props.error }) : null,
|
|
3386
|
+
props.rpc && props.sessionId ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SessionInfoArea, { rpc: props.rpc, sessionId: props.sessionId }) : null
|
|
2869
3387
|
]
|
|
2870
3388
|
}
|
|
2871
3389
|
)
|
|
@@ -2874,16 +3392,16 @@ var __defProp = Object.defineProperty;
|
|
|
2874
3392
|
}
|
|
2875
3393
|
|
|
2876
3394
|
// client/src/pill/MemoryModePill.tsx
|
|
2877
|
-
var
|
|
3395
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
2878
3396
|
function MemoryModePill(props) {
|
|
2879
3397
|
const rpc = props.rpc;
|
|
2880
3398
|
const sessionId = props.sessionId || props.session && props.session.sessionId;
|
|
2881
|
-
const [mode, setMode] = (0,
|
|
2882
|
-
const [error, setError] = (0,
|
|
2883
|
-
const [open, setOpen] = (0,
|
|
2884
|
-
const wrapRef = (0,
|
|
2885
|
-
const seqRef = (0,
|
|
2886
|
-
const load = (0,
|
|
3399
|
+
const [mode, setMode] = (0, import_react17.useState)(null);
|
|
3400
|
+
const [error, setError] = (0, import_react17.useState)(null);
|
|
3401
|
+
const [open, setOpen] = (0, import_react17.useState)(false);
|
|
3402
|
+
const wrapRef = (0, import_react17.useRef)(null);
|
|
3403
|
+
const seqRef = (0, import_react17.useRef)(0);
|
|
3404
|
+
const load = (0, import_react17.useCallback)(() => {
|
|
2887
3405
|
if (!sessionId || !rpc) return;
|
|
2888
3406
|
const token = ++seqRef.current;
|
|
2889
3407
|
setError(null);
|
|
@@ -2896,13 +3414,21 @@ var __defProp = Object.defineProperty;
|
|
|
2896
3414
|
setError(String(e && e.message || e));
|
|
2897
3415
|
});
|
|
2898
3416
|
}, [sessionId, rpc]);
|
|
2899
|
-
(0,
|
|
3417
|
+
(0, import_react17.useEffect)(() => {
|
|
2900
3418
|
load();
|
|
2901
3419
|
}, [load]);
|
|
2902
|
-
(0,
|
|
3420
|
+
(0, import_react17.useEffect)(() => {
|
|
2903
3421
|
watchSidebarIcon();
|
|
2904
3422
|
}, []);
|
|
2905
|
-
(0,
|
|
3423
|
+
(0, import_react17.useEffect)(() => {
|
|
3424
|
+
initOccupancyIndicator(
|
|
3425
|
+
(endpoint, payload) => rpc(endpoint, payload)
|
|
3426
|
+
);
|
|
3427
|
+
initPanelSection(currentMeterSnapshot);
|
|
3428
|
+
watchContextMeter();
|
|
3429
|
+
noteOccupancySession(sessionId ?? null);
|
|
3430
|
+
}, [sessionId, rpc]);
|
|
3431
|
+
(0, import_react17.useEffect)(() => {
|
|
2906
3432
|
if (!open) return;
|
|
2907
3433
|
const onDown = (e) => {
|
|
2908
3434
|
if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
|
|
@@ -2957,8 +3483,8 @@ var __defProp = Object.defineProperty;
|
|
|
2957
3483
|
pillStyle.boxShadow = "0 0 12px color-mix(in srgb, " + info.color + " 30%, transparent)";
|
|
2958
3484
|
pillStyle["--dsh-mem-pill-tint"] = info.color;
|
|
2959
3485
|
}
|
|
2960
|
-
return /* @__PURE__ */ (0,
|
|
2961
|
-
/* @__PURE__ */ (0,
|
|
3486
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: wrapRef, style: { position: "relative", display: "inline-flex" }, children: [
|
|
3487
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
2962
3488
|
"button",
|
|
2963
3489
|
{
|
|
2964
3490
|
type: "button",
|
|
@@ -2971,11 +3497,11 @@ var __defProp = Object.defineProperty;
|
|
|
2971
3497
|
style: pillStyle,
|
|
2972
3498
|
children: [
|
|
2973
3499
|
"记忆 · ",
|
|
2974
|
-
/* @__PURE__ */ (0,
|
|
3500
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: loaded ? info.label : error ? "⚠" : "…" })
|
|
2975
3501
|
]
|
|
2976
3502
|
}
|
|
2977
3503
|
),
|
|
2978
|
-
open ? /* @__PURE__ */ (0,
|
|
3504
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModeSlider, { mode: mode || "auto", onCommit: commit, error, rpc, sessionId }) : null
|
|
2979
3505
|
] });
|
|
2980
3506
|
}
|
|
2981
3507
|
|