dsh-layered-memory 0.7.1 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +165 -75
- package/README.md +174 -104
- package/assets/img/EmbeddingSource.png +0 -0
- package/assets/img/MemoryTools.png +0 -0
- package/assets/img/ToolTrajectory.png +0 -0
- package/assets/readme/bench-dialog.svg +87 -0
- package/assets/readme/bench-workflow.svg +79 -0
- package/assets/readme/flow.svg +74 -74
- package/assets/readme/storage.svg +56 -56
- package/dist/client.js +380 -2
- package/dist/config.d.ts +41 -1
- package/dist/config.js +10 -3
- package/dist/hooks/recall.d.ts +11 -8
- package/dist/hooks/recall.js +70 -51
- package/dist/index.d.ts +20 -0
- package/dist/index.js +15 -4
- package/dist/llm.d.ts +28 -0
- package/dist/llm.js +33 -0
- package/dist/pipeline/l1.js +5 -4
- package/dist/pipeline/l2.js +7 -2
- package/dist/pipeline/l3.js +7 -2
- package/dist/pipeline/rebuild.js +2 -2
- package/dist/pipeline/runner.d.ts +45 -8
- package/dist/pipeline/runner.js +185 -39
- package/dist/pipeline/trigger.d.ts +38 -0
- package/dist/pipeline/trigger.js +64 -0
- package/dist/settings.d.ts +14 -0
- package/dist/settings.js +41 -2
- package/dist/stats.js +113 -1
- package/dist/store/bm25.js +2 -1
- package/dist/store/download-queue.d.ts +35 -2
- package/dist/store/download-queue.js +102 -5
- package/dist/store/embedding.d.ts +12 -6
- package/dist/store/embedding.js +10 -7
- package/dist/store/l0.d.ts +2 -0
- package/dist/store/l0.js +4 -0
- package/dist/store/l1.d.ts +2 -0
- package/dist/store/l1.js +2 -2
- package/dist/store/model-catalog.js +1 -1
- package/dist/store/pending.d.ts +27 -6
- package/dist/store/pending.js +49 -9
- package/dist/store/search-utils.js +3 -2
- package/dist/store/session-modes.d.ts +4 -0
- package/dist/store/session-modes.js +16 -0
- package/dist/store/sqlite.d.ts +12 -0
- package/dist/store/sqlite.js +103 -5
- package/dist/util/recall-budget.d.ts +32 -0
- package/dist/util/recall-budget.js +85 -0
- package/dist/util/text.d.ts +8 -3
- package/dist/util/text.js +41 -15
- package/dist/util/tokenizer.d.ts +14 -0
- package/dist/util/tokenizer.js +53 -0
- package/package.json +6 -3
package/dist/client.js
CHANGED
|
@@ -1463,6 +1463,363 @@ window.__ModuleLoader__.load({
|
|
|
1463
1463
|
);
|
|
1464
1464
|
}
|
|
1465
1465
|
|
|
1466
|
+
// ── 蒸馏模型选择器(供应商/模型两级下拉)──
|
|
1467
|
+
// 数据源:dsh-memory/llm-providers(供应商目录 + 当前覆盖 + 实际生效路由)与
|
|
1468
|
+
// dsh-memory/llm-models(所选供应商的模型列表)。写入走 settings-set 的
|
|
1469
|
+
// distillProvider/distillModel 两键(成对提交)。部署静态 pin(pinned)时禁用。
|
|
1470
|
+
function LlmModelRow(props) {
|
|
1471
|
+
var rpc = props.rpc;
|
|
1472
|
+
var disabled = !!props.disabled;
|
|
1473
|
+
|
|
1474
|
+
// 注意:全部 hook 必须在任何提前返回之前声明(Rules of Hooks)——
|
|
1475
|
+
// info 未加载的首渲染会 return null,晚于它的 useState 会让 hook 数随渲染漂移而崩溃
|
|
1476
|
+
var infoState = react.useState(null);
|
|
1477
|
+
var setInfo = infoState[1];
|
|
1478
|
+
var modelsState = react.useState(null);
|
|
1479
|
+
var setModels = modelsState[1];
|
|
1480
|
+
var draftState = react.useState("");
|
|
1481
|
+
var setDraft = draftState[1];
|
|
1482
|
+
var manualState = react.useState("");
|
|
1483
|
+
var manualModel = manualState[0];
|
|
1484
|
+
var setManualModel = manualState[1];
|
|
1485
|
+
|
|
1486
|
+
react.useEffect(function () {
|
|
1487
|
+
var load = function () {
|
|
1488
|
+
rpc("dsh-memory/llm-providers", {})
|
|
1489
|
+
.then(function (r) { if (r && r.ok) setInfo(r.value); })
|
|
1490
|
+
.catch(function () {});
|
|
1491
|
+
};
|
|
1492
|
+
load();
|
|
1493
|
+
var timer = setInterval(load, 5000);
|
|
1494
|
+
return function () { clearInterval(timer); };
|
|
1495
|
+
}, [rpc]);
|
|
1496
|
+
|
|
1497
|
+
var info = infoState[0];
|
|
1498
|
+
// 草稿供应商(已选待挑模型)优先于已存选择
|
|
1499
|
+
var draft = draftState[0];
|
|
1500
|
+
var curProvider = draft || (info && info.current ? info.current.provider : "");
|
|
1501
|
+
var curModel = info && info.current ? info.current.model : "";
|
|
1502
|
+
|
|
1503
|
+
react.useEffect(function () {
|
|
1504
|
+
if (!curProvider) { setModels(null); return undefined; }
|
|
1505
|
+
var alive = true;
|
|
1506
|
+
setModels(null);
|
|
1507
|
+
rpc("dsh-memory/llm-models", { provider: curProvider })
|
|
1508
|
+
.then(function (r) { if (alive && r && r.ok) setModels(r.value.models || []); })
|
|
1509
|
+
.catch(function () { if (alive) setModels([]); });
|
|
1510
|
+
return function () { alive = false; };
|
|
1511
|
+
}, [rpc, curProvider]);
|
|
1512
|
+
|
|
1513
|
+
var writeLlm = function (patch) {
|
|
1514
|
+
if (!info) return;
|
|
1515
|
+
// 乐观更新:立即写视图字段,失败回滚
|
|
1516
|
+
var prev = info;
|
|
1517
|
+
setInfo(Object.assign({}, prev, {
|
|
1518
|
+
current: Object.assign({}, prev.current, patch),
|
|
1519
|
+
}));
|
|
1520
|
+
setDraft("");
|
|
1521
|
+
rpc("dsh-memory/settings-set", patch)
|
|
1522
|
+
.then(function (r) {
|
|
1523
|
+
if (!r || !r.ok) {
|
|
1524
|
+
setInfo(prev);
|
|
1525
|
+
setDraft(draft);
|
|
1526
|
+
}
|
|
1527
|
+
})
|
|
1528
|
+
.catch(function () {
|
|
1529
|
+
setInfo(prev);
|
|
1530
|
+
setDraft(draft);
|
|
1531
|
+
});
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1534
|
+
if (!info) return null;
|
|
1535
|
+
if (info.pinned) {
|
|
1536
|
+
// 部署静态 pin:显示固定路由,选择器不出场
|
|
1537
|
+
return react.createElement(
|
|
1538
|
+
"div", { style: S.switchRow },
|
|
1539
|
+
react.createElement("div", null,
|
|
1540
|
+
react.createElement("div", { style: S.switchLabel }, "蒸馏模型"),
|
|
1541
|
+
react.createElement(
|
|
1542
|
+
"div", { style: S.switchDesc },
|
|
1543
|
+
"已由部署配置固定:" + info.effective.provider + " / " + info.effective.model,
|
|
1544
|
+
)),
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
var providers = info.providers || [];
|
|
1549
|
+
var models = modelsState[0];
|
|
1550
|
+
var providerKnown = curProvider === "" || providers.some(function (p) { return p.id === curProvider; });
|
|
1551
|
+
var modelKnown = models && models.some(function (m) { return m.id === curModel; });
|
|
1552
|
+
// 供应商列表为空数组 = 适配器不提供模型目录:降级为手动输入模型 id
|
|
1553
|
+
var manualInput = models !== null && models.length === 0;
|
|
1554
|
+
|
|
1555
|
+
var defaultLabel = info.default
|
|
1556
|
+
? "跟随默认(" + info.default.provider + " / " + info.default.model + ")"
|
|
1557
|
+
: "跟随默认";
|
|
1558
|
+
|
|
1559
|
+
var providerOptions = [{ id: "", label: defaultLabel }].concat(
|
|
1560
|
+
providers.map(function (p) { return { id: p.id, label: p.name + "(" + p.id + ")" }; }),
|
|
1561
|
+
);
|
|
1562
|
+
if (curProvider && !providerKnown) {
|
|
1563
|
+
providerOptions.push({ id: curProvider, label: curProvider + "(已不在列表)" });
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
var modelOptions = [{ id: "", label: "跟随默认" }].concat(
|
|
1567
|
+
(models || []).map(function (m) { return { id: m.id, label: m.name !== m.id ? m.name + "(" + m.id + ")" : m.id }; }),
|
|
1568
|
+
);
|
|
1569
|
+
if (curModel && !modelKnown) {
|
|
1570
|
+
modelOptions.push({ id: curModel, label: curModel + "(已不在列表)" });
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
return react.createElement(
|
|
1574
|
+
"div", { style: Object.assign({}, S.switchRow, { flexWrap: "wrap" }) },
|
|
1575
|
+
react.createElement(
|
|
1576
|
+
"select",
|
|
1577
|
+
{
|
|
1578
|
+
className: "dsh-mem-select",
|
|
1579
|
+
style: { flexShrink: 0, maxWidth: 220 },
|
|
1580
|
+
value: curProvider,
|
|
1581
|
+
disabled: disabled,
|
|
1582
|
+
onChange: function (e) {
|
|
1583
|
+
var v = e.target.value;
|
|
1584
|
+
if (v === "") {
|
|
1585
|
+
writeLlm({ distillProvider: "", distillModel: "" });
|
|
1586
|
+
} else {
|
|
1587
|
+
setDraft(v);
|
|
1588
|
+
}
|
|
1589
|
+
},
|
|
1590
|
+
},
|
|
1591
|
+
providerOptions.map(function (o) {
|
|
1592
|
+
return react.createElement("option", { key: o.id, value: o.id }, o.label);
|
|
1593
|
+
}),
|
|
1594
|
+
),
|
|
1595
|
+
!curProvider
|
|
1596
|
+
? null
|
|
1597
|
+
: manualInput
|
|
1598
|
+
? react.createElement(NInput, {
|
|
1599
|
+
style: { flex: 1, minWidth: 160 },
|
|
1600
|
+
placeholder: "模型 id(该供应商未提供列表,手动输入后回车)…",
|
|
1601
|
+
value: manualModel,
|
|
1602
|
+
onChange: function (e) { setManualModel(e.target.value); },
|
|
1603
|
+
onKeyDown: function (e) {
|
|
1604
|
+
if (e.key === "Enter" && manualModel.trim()) {
|
|
1605
|
+
writeLlm({ distillProvider: curProvider, distillModel: manualModel.trim() });
|
|
1606
|
+
setManualModel("");
|
|
1607
|
+
}
|
|
1608
|
+
},
|
|
1609
|
+
})
|
|
1610
|
+
: react.createElement(
|
|
1611
|
+
"select",
|
|
1612
|
+
{
|
|
1613
|
+
className: "dsh-mem-select",
|
|
1614
|
+
style: { flex: 1, minWidth: 160 },
|
|
1615
|
+
value: curModel,
|
|
1616
|
+
disabled: disabled || models === null,
|
|
1617
|
+
onChange: function (e) {
|
|
1618
|
+
writeLlm({ distillProvider: curProvider, distillModel: e.target.value });
|
|
1619
|
+
},
|
|
1620
|
+
},
|
|
1621
|
+
modelOptions.map(function (o) {
|
|
1622
|
+
return react.createElement("option", { key: o.id, value: o.id }, o.label);
|
|
1623
|
+
}),
|
|
1624
|
+
),
|
|
1625
|
+
react.createElement("div", null,
|
|
1626
|
+
react.createElement("div", { style: S.switchLabel }, "蒸馏模型"),
|
|
1627
|
+
react.createElement(
|
|
1628
|
+
"div", { style: S.switchDesc },
|
|
1629
|
+
info.effective
|
|
1630
|
+
? "当前生效 " + info.effective.provider + " / " + info.effective.model +
|
|
1631
|
+
(curProvider ? "" : "(跟随默认选择)")
|
|
1632
|
+
: "暂无可用路由(未选择且无默认)",
|
|
1633
|
+
),
|
|
1634
|
+
!providerKnown || (curModel && models !== null && !modelKnown)
|
|
1635
|
+
? react.createElement(
|
|
1636
|
+
"div", { style: { fontSize: 12, color: "var(--dsh-mem-danger)" } },
|
|
1637
|
+
"所选供应商或模型已不在已配置列表中,蒸馏调用可能失败,建议重新选择。",
|
|
1638
|
+
)
|
|
1639
|
+
: null),
|
|
1640
|
+
);
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
// ── 蒸馏预算(分层输出 token 上限 + 输入字符上限)──
|
|
1644
|
+
// 数据源:settings-get 的 budgets(current 运行时覆盖 0=跟随默认 / defaults
|
|
1645
|
+
// 内置默认 / effective 实际生效)与 inputBudget(current 0=跟随配置 /
|
|
1646
|
+
// fallback 静态配置值 / effective)。输出四键经 settings-set 的
|
|
1647
|
+
// distillBudgets 成对提交;输入走 distillMaxInputChars 单键提交。
|
|
1648
|
+
// 思考档 high/max 的 ×4 放大只作用于输出预算(提示文案注明)。
|
|
1649
|
+
function BudgetInputs(props) {
|
|
1650
|
+
var rpc = props.rpc;
|
|
1651
|
+
var disabled = !!props.disabled;
|
|
1652
|
+
var data = props.data;
|
|
1653
|
+
var setData = props.setData;
|
|
1654
|
+
var onError = props.onError;
|
|
1655
|
+
var layers = [
|
|
1656
|
+
["extract", "抽取"],
|
|
1657
|
+
["dedup", "去重"],
|
|
1658
|
+
["l2", "L2 场景"],
|
|
1659
|
+
["l3", "L3 画像"],
|
|
1660
|
+
];
|
|
1661
|
+
// 输入草稿(string|null):击键只改本地,blur/回车才提交(input 键 = 输入预算)
|
|
1662
|
+
var draftState = react.useState(null);
|
|
1663
|
+
var draft = draftState[0];
|
|
1664
|
+
var setDraft = draftState[1];
|
|
1665
|
+
|
|
1666
|
+
if (!data || !data.budgets) return null;
|
|
1667
|
+
var cur = data.budgets.current || {};
|
|
1668
|
+
var def = data.budgets.defaults || {};
|
|
1669
|
+
var eff = data.budgets.effective || {};
|
|
1670
|
+
var ib = data.inputBudget || {};
|
|
1671
|
+
var curIn = ib.current || 0;
|
|
1672
|
+
var effIn = ib.effective || ib.fallback || 0;
|
|
1673
|
+
|
|
1674
|
+
var shown = function (key) {
|
|
1675
|
+
if (draft && draft[key] !== undefined) return draft[key];
|
|
1676
|
+
var c = key === "input" ? curIn : cur[key];
|
|
1677
|
+
return c > 0 ? String(c) : "";
|
|
1678
|
+
};
|
|
1679
|
+
|
|
1680
|
+
/** 通用提交:patchKey 为 settings-set 载荷键名;pick 从草稿取值并校验。 */
|
|
1681
|
+
var commitPart = function (keys, buildPayload, applyView) {
|
|
1682
|
+
if (!draft) return;
|
|
1683
|
+
var values = {};
|
|
1684
|
+
for (var i = 0; i < keys.length; i++) {
|
|
1685
|
+
var key = keys[i];
|
|
1686
|
+
var raw = (draft[key] !== undefined ? draft[key] : shown(key)).trim();
|
|
1687
|
+
var n = raw === "" ? 0 : Number(raw);
|
|
1688
|
+
var max = key === "input" ? 1000000 : 1000000;
|
|
1689
|
+
var min = key === "input" ? (raw === "" ? 0 : 1000) : 0;
|
|
1690
|
+
if (!Number.isInteger(n) || n < min || n > max) {
|
|
1691
|
+
onError(key === "input"
|
|
1692
|
+
? "输入预算须为 0 或 1000~1000000 的整数(留空或 0 = 跟随配置)"
|
|
1693
|
+
: "输出预算须为 0~1000000 的整数(留空或 0 = 跟随默认)");
|
|
1694
|
+
setDraft(null);
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
values[key] = n;
|
|
1698
|
+
}
|
|
1699
|
+
setDraft(null);
|
|
1700
|
+
var prev = data;
|
|
1701
|
+
setData(applyView(prev, values));
|
|
1702
|
+
rpc("dsh-memory/settings-set", buildPayload(values))
|
|
1703
|
+
.then(function (r) {
|
|
1704
|
+
if (!r || !r.ok) {
|
|
1705
|
+
setData(prev);
|
|
1706
|
+
onError(r && r.error ? "预算写入失败:" + r.error.message : "预算写入失败");
|
|
1707
|
+
} else {
|
|
1708
|
+
onError(null);
|
|
1709
|
+
}
|
|
1710
|
+
})
|
|
1711
|
+
.catch(function (e) {
|
|
1712
|
+
setData(prev);
|
|
1713
|
+
onError("预算写入失败:" + String((e && e.message) || e));
|
|
1714
|
+
});
|
|
1715
|
+
};
|
|
1716
|
+
|
|
1717
|
+
/** 输出四键成对提交(distillBudgets)。 */
|
|
1718
|
+
var commitOutputs = function () {
|
|
1719
|
+
commitPart(
|
|
1720
|
+
["extract", "dedup", "l2", "l3"],
|
|
1721
|
+
function (v) { return { distillBudgets: v }; },
|
|
1722
|
+
function (prev, v) {
|
|
1723
|
+
var effNext = {};
|
|
1724
|
+
for (var j = 0; j < layers.length; j++) {
|
|
1725
|
+
var k = layers[j][0];
|
|
1726
|
+
effNext[k] = v[k] > 0 ? v[k] : (def[k] || 0);
|
|
1727
|
+
}
|
|
1728
|
+
return Object.assign({}, prev, {
|
|
1729
|
+
settings: Object.assign({}, prev.settings, { distillBudgets: v }),
|
|
1730
|
+
budgets: Object.assign({}, prev.budgets, { current: v, effective: effNext }),
|
|
1731
|
+
});
|
|
1732
|
+
},
|
|
1733
|
+
);
|
|
1734
|
+
};
|
|
1735
|
+
|
|
1736
|
+
/** 输入预算单键提交(distillMaxInputChars)。 */
|
|
1737
|
+
var commitInput = function () {
|
|
1738
|
+
commitPart(
|
|
1739
|
+
["input"],
|
|
1740
|
+
function (v) { return { distillMaxInputChars: v.input }; },
|
|
1741
|
+
function (prev, v) {
|
|
1742
|
+
return Object.assign({}, prev, {
|
|
1743
|
+
settings: Object.assign({}, prev.settings, { distillMaxInputChars: v.input }),
|
|
1744
|
+
inputBudget: Object.assign({}, prev.inputBudget || {}, {
|
|
1745
|
+
current: v.input,
|
|
1746
|
+
effective: v.input > 0 ? v.input : (ib.fallback || 0),
|
|
1747
|
+
}),
|
|
1748
|
+
});
|
|
1749
|
+
},
|
|
1750
|
+
);
|
|
1751
|
+
};
|
|
1752
|
+
|
|
1753
|
+
/** 某组键是否有未提交改动(触发 blur/Enter 提交)。 */
|
|
1754
|
+
var dirty = function (keys) {
|
|
1755
|
+
if (!draft) return false;
|
|
1756
|
+
for (var i = 0; i < keys.length; i++) {
|
|
1757
|
+
var key = keys[i];
|
|
1758
|
+
var want = (draft[key] !== undefined ? draft[key] : "").trim();
|
|
1759
|
+
var was = key === "input"
|
|
1760
|
+
? (curIn > 0 ? String(curIn) : "")
|
|
1761
|
+
: (cur[key] > 0 ? String(cur[key]) : "");
|
|
1762
|
+
if (want !== was) return true;
|
|
1763
|
+
}
|
|
1764
|
+
return false;
|
|
1765
|
+
};
|
|
1766
|
+
var outKeys = ["extract", "dedup", "l2", "l3"];
|
|
1767
|
+
|
|
1768
|
+
var inputBox = function (key, label, title, width, placeholder, onCommit) {
|
|
1769
|
+
return react.createElement(NInput, {
|
|
1770
|
+
key: key,
|
|
1771
|
+
type: "number",
|
|
1772
|
+
min: 0,
|
|
1773
|
+
max: 1000000,
|
|
1774
|
+
style: { width: width },
|
|
1775
|
+
title: title,
|
|
1776
|
+
placeholder: String(placeholder || ""),
|
|
1777
|
+
value: shown(key),
|
|
1778
|
+
disabled: disabled,
|
|
1779
|
+
onChange: function (e) {
|
|
1780
|
+
var v = e.target.value;
|
|
1781
|
+
var d = Object.assign({}, draft || {});
|
|
1782
|
+
d[key] = v;
|
|
1783
|
+
setDraft(d);
|
|
1784
|
+
},
|
|
1785
|
+
onBlur: function () { if (dirty([key])) onCommit(); },
|
|
1786
|
+
onKeyDown: function (e) { if (e.key === "Enter" && dirty([key])) onCommit(); },
|
|
1787
|
+
});
|
|
1788
|
+
};
|
|
1789
|
+
|
|
1790
|
+
return react.createElement(
|
|
1791
|
+
"div", null,
|
|
1792
|
+
react.createElement(
|
|
1793
|
+
"div", { style: S.switchRow },
|
|
1794
|
+
react.createElement(
|
|
1795
|
+
"div", { style: Object.assign({}, S.flexRow, { gap: 6 }) },
|
|
1796
|
+
layers.map(function (l) {
|
|
1797
|
+
return inputBox(l[0], l[1], l[1] + " 输出预算(token,留空 = 默认 " + (def[l[0]] || "?") + ")", 92, def[l[0]], commitOutputs);
|
|
1798
|
+
}),
|
|
1799
|
+
),
|
|
1800
|
+
react.createElement("div", null,
|
|
1801
|
+
react.createElement("div", { style: S.switchLabel }, "输出预算"),
|
|
1802
|
+
react.createElement(
|
|
1803
|
+
"div", { style: S.switchDesc },
|
|
1804
|
+
"各蒸馏层单次输出的 token 上限(抽取/去重/L2/L3);留空或 0 = 跟随默认(当前生效 " +
|
|
1805
|
+
layers.map(function (l) { return eff[l[0]] || "?"; }).join(" / ") +
|
|
1806
|
+
");思考档 high/max 时实际限额自动 ×4",
|
|
1807
|
+
)),
|
|
1808
|
+
),
|
|
1809
|
+
react.createElement(
|
|
1810
|
+
"div", { style: S.switchRow },
|
|
1811
|
+
inputBox("input", "输入", "单次蒸馏输入字符上限(≈token,中文 1 字≈1 token;留空 = 跟随配置 " + (ib.fallback || "?") + ")", 120, ib.fallback, commitInput),
|
|
1812
|
+
react.createElement("div", null,
|
|
1813
|
+
react.createElement("div", { style: S.switchLabel }, "输入预算"),
|
|
1814
|
+
react.createElement(
|
|
1815
|
+
"div", { style: S.switchDesc },
|
|
1816
|
+
"单次蒸馏调用的输入字符上限(≈token):L1 抽取按此分块、L2/L3 超限截断;" +
|
|
1817
|
+
"留空或 0 = 跟随配置(当前生效 " + (effIn || "?") + ",来自 llm.maxInputChars)",
|
|
1818
|
+
)),
|
|
1819
|
+
),
|
|
1820
|
+
);
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1466
1823
|
// ── Tab:概览(开关面板 + 计数) ──
|
|
1467
1824
|
function OverviewTab(props) {
|
|
1468
1825
|
var rpc = props.rpc;
|
|
@@ -1500,11 +1857,20 @@ window.__ModuleLoader__.load({
|
|
|
1500
1857
|
if (!settingsData) return;
|
|
1501
1858
|
// 乐观更新:立即翻 UI,失败回滚
|
|
1502
1859
|
var prev = settingsData;
|
|
1860
|
+
var patch = (function () { var o = {}; o[key] = value; return o; })();
|
|
1503
1861
|
var next = Object.assign({}, prev, {
|
|
1504
|
-
settings: Object.assign({}, prev.settings,
|
|
1862
|
+
settings: Object.assign({}, prev.settings, patch),
|
|
1505
1863
|
});
|
|
1864
|
+
// 蒸馏思考选择器读 effort.current(非 settings 键):乐观更新须同步该视图字段,
|
|
1865
|
+
// 否则点击后选择器在下一个 5s 轮询前"弹回"旧值,看起来没生效
|
|
1866
|
+
if (key === "reasoningEffort" && next.effort) {
|
|
1867
|
+
next.effort = Object.assign({}, prev.effort, {
|
|
1868
|
+
current: value,
|
|
1869
|
+
effective: value || prev.effort.fallback,
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1506
1872
|
setSettingsData(next);
|
|
1507
|
-
rpc("dsh-memory/settings-set",
|
|
1873
|
+
rpc("dsh-memory/settings-set", patch)
|
|
1508
1874
|
.then(function (r) {
|
|
1509
1875
|
if (!r || !r.ok) {
|
|
1510
1876
|
setSettingsData(prev);
|
|
@@ -1558,6 +1924,8 @@ window.__ModuleLoader__.load({
|
|
|
1558
1924
|
: settingsData
|
|
1559
1925
|
? react.createElement(
|
|
1560
1926
|
"div", { style: S.switchPanel },
|
|
1927
|
+
// 分组:记忆模式(总闸与三个分项开关)
|
|
1928
|
+
react.createElement("div", { style: S.panelLabel }, "记忆模式"),
|
|
1561
1929
|
react.createElement(SwitchRow, {
|
|
1562
1930
|
label: "记忆模式",
|
|
1563
1931
|
desc: master ? "已开启:捕获对话并蒸馏记忆" : "已关闭:不捕获、不蒸馏、不注入(数据保留)",
|
|
@@ -1585,6 +1953,8 @@ window.__ModuleLoader__.load({
|
|
|
1585
1953
|
disabled: !master,
|
|
1586
1954
|
onChange: function (v) { toggle("recall", v); },
|
|
1587
1955
|
}),
|
|
1956
|
+
// 分组:蒸馏参数(模型路由 / 思考档位 / 输出预算)
|
|
1957
|
+
react.createElement("div", { style: S.panelLabel }, "蒸馏参数"),
|
|
1588
1958
|
settingsData.effort
|
|
1589
1959
|
? react.createElement(
|
|
1590
1960
|
"div",
|
|
@@ -1610,6 +1980,14 @@ window.__ModuleLoader__.load({
|
|
|
1610
1980
|
)),
|
|
1611
1981
|
)
|
|
1612
1982
|
: null,
|
|
1983
|
+
react.createElement(LlmModelRow, { rpc: rpc, disabled: !master }),
|
|
1984
|
+
react.createElement(BudgetInputs, {
|
|
1985
|
+
rpc: rpc,
|
|
1986
|
+
disabled: !master,
|
|
1987
|
+
data: settingsData,
|
|
1988
|
+
setData: setSettingsData,
|
|
1989
|
+
onError: setError,
|
|
1990
|
+
}),
|
|
1613
1991
|
ceilingNote
|
|
1614
1992
|
? react.createElement("p", { style: S.hint }, ceilingNote)
|
|
1615
1993
|
: null,
|
package/dist/config.d.ts
CHANGED
|
@@ -19,8 +19,11 @@ export interface MemoryConfig {
|
|
|
19
19
|
};
|
|
20
20
|
extract: {
|
|
21
21
|
enabled: boolean;
|
|
22
|
-
/**
|
|
22
|
+
/** 稳态触发阈值:单会话攒够多少条新消息才跑一次 L1 抽取(省 token)。
|
|
23
|
+
* 起步阶段生效阈值从 1 翻倍爬坡到此值(渐进阈值,ADR-0003)。 */
|
|
23
24
|
minMessages: number;
|
|
25
|
+
/** 闲置兜底:会话静默多少秒后把未蒸馏切片落袋(接住"没攒够阈值用户就离开");0 = 关闭。 */
|
|
26
|
+
idleSeconds: number;
|
|
24
27
|
/** L1 抽取时的背景消息条数(供上下文推断,不参与提取)。 */
|
|
25
28
|
backgroundMessages: number;
|
|
26
29
|
/** 去重候选池大小(每条新记忆的相似候选数)。 */
|
|
@@ -44,6 +47,12 @@ export interface MemoryConfig {
|
|
|
44
47
|
enabled: boolean;
|
|
45
48
|
/** 每步自动召回注入的 L1 条数。 */
|
|
46
49
|
maxResults: number;
|
|
50
|
+
/** 单条注入记忆的字符上限(超限截断并提示用工具查全文);0 = 不限。 */
|
|
51
|
+
maxCharsPerMemory: number;
|
|
52
|
+
/** 整轮注入总字符上限(超限按相关性丢尾部);0 = 不限。 */
|
|
53
|
+
maxTotalRecallChars: number;
|
|
54
|
+
/** 召回总预算(ms):超时跳过本轮注入、不阻塞对话;0 = 不限时。 */
|
|
55
|
+
timeoutMs: number;
|
|
47
56
|
includePersona: boolean;
|
|
48
57
|
includeSceneNav: boolean;
|
|
49
58
|
/** 检索策略:keyword(FTS5 BM25)| embedding(向量)| hybrid(双路 + RRF 融合)。 */
|
|
@@ -68,6 +77,9 @@ export interface MemoryConfig {
|
|
|
68
77
|
allowLocalModels: boolean;
|
|
69
78
|
/** 模型下载镜像根地址(默认国内可达的 hf-mirror.com,可改回官方)。 */
|
|
70
79
|
mirror: string;
|
|
80
|
+
/** 模型下载代理三态:''(默认)= 探测代理环境变量(HTTPS_PROXY/ALL_PROXY 等);
|
|
81
|
+
* 'none' = 禁用强制直连;其他值 = 代理 URL(如 http://127.0.0.1:7890)。 */
|
|
82
|
+
proxy: string;
|
|
71
83
|
};
|
|
72
84
|
llm: {
|
|
73
85
|
/** 蒸馏用的 provider 路由;留空用当前默认选择。 */
|
|
@@ -83,6 +95,14 @@ export interface MemoryConfig {
|
|
|
83
95
|
maxInputChars: number;
|
|
84
96
|
/** 单次蒸馏调用超时(ms)。 */
|
|
85
97
|
timeoutMs: number;
|
|
98
|
+
/** 分层输出预算运行时覆盖(设置页 distillBudgets 经 effectiveCfg 注入;
|
|
99
|
+
* 0/缺省 = 用内置默认。不属于静态 schema——预算无部署上限语义,只有运行时偏好)。 */
|
|
100
|
+
budgets?: Partial<{
|
|
101
|
+
extract: number;
|
|
102
|
+
dedup: number;
|
|
103
|
+
l2: number;
|
|
104
|
+
l3: number;
|
|
105
|
+
}>;
|
|
86
106
|
};
|
|
87
107
|
/** 是否注册模型可调用的记忆工具。 */
|
|
88
108
|
tools: boolean;
|
|
@@ -102,11 +122,13 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
102
122
|
extract: Schema<Schemastery.ObjectS<{
|
|
103
123
|
enabled: Schema<boolean, boolean>;
|
|
104
124
|
minMessages: Schema<number, number>;
|
|
125
|
+
idleSeconds: Schema<number, number>;
|
|
105
126
|
backgroundMessages: Schema<number, number>;
|
|
106
127
|
candidatePool: Schema<number, number>;
|
|
107
128
|
}>, Schemastery.ObjectT<{
|
|
108
129
|
enabled: Schema<boolean, boolean>;
|
|
109
130
|
minMessages: Schema<number, number>;
|
|
131
|
+
idleSeconds: Schema<number, number>;
|
|
110
132
|
backgroundMessages: Schema<number, number>;
|
|
111
133
|
candidatePool: Schema<number, number>;
|
|
112
134
|
}>>;
|
|
@@ -131,6 +153,9 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
131
153
|
recall: Schema<Schemastery.ObjectS<{
|
|
132
154
|
enabled: Schema<boolean, boolean>;
|
|
133
155
|
maxResults: Schema<number, number>;
|
|
156
|
+
maxCharsPerMemory: Schema<number, number>;
|
|
157
|
+
maxTotalRecallChars: Schema<number, number>;
|
|
158
|
+
timeoutMs: Schema<number, number>;
|
|
134
159
|
includePersona: Schema<boolean, boolean>;
|
|
135
160
|
includeSceneNav: Schema<boolean, boolean>;
|
|
136
161
|
strategy: Schema<"keyword" | "embedding" | "hybrid", "keyword" | "embedding" | "hybrid">;
|
|
@@ -138,6 +163,9 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
138
163
|
}>, Schemastery.ObjectT<{
|
|
139
164
|
enabled: Schema<boolean, boolean>;
|
|
140
165
|
maxResults: Schema<number, number>;
|
|
166
|
+
maxCharsPerMemory: Schema<number, number>;
|
|
167
|
+
maxTotalRecallChars: Schema<number, number>;
|
|
168
|
+
timeoutMs: Schema<number, number>;
|
|
141
169
|
includePersona: Schema<boolean, boolean>;
|
|
142
170
|
includeSceneNav: Schema<boolean, boolean>;
|
|
143
171
|
strategy: Schema<"keyword" | "embedding" | "hybrid", "keyword" | "embedding" | "hybrid">;
|
|
@@ -153,6 +181,7 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
153
181
|
timeoutMs: Schema<number, number>;
|
|
154
182
|
allowLocalModels: Schema<boolean, boolean>;
|
|
155
183
|
mirror: Schema<string, string>;
|
|
184
|
+
proxy: Schema<string, string>;
|
|
156
185
|
}>, Schemastery.ObjectT<{
|
|
157
186
|
enabled: Schema<boolean, boolean>;
|
|
158
187
|
baseUrl: Schema<string, string>;
|
|
@@ -163,6 +192,7 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
163
192
|
timeoutMs: Schema<number, number>;
|
|
164
193
|
allowLocalModels: Schema<boolean, boolean>;
|
|
165
194
|
mirror: Schema<string, string>;
|
|
195
|
+
proxy: Schema<string, string>;
|
|
166
196
|
}>>;
|
|
167
197
|
llm: Schema<Schemastery.ObjectS<{
|
|
168
198
|
provider: Schema<string, string>;
|
|
@@ -197,11 +227,13 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
197
227
|
extract: Schema<Schemastery.ObjectS<{
|
|
198
228
|
enabled: Schema<boolean, boolean>;
|
|
199
229
|
minMessages: Schema<number, number>;
|
|
230
|
+
idleSeconds: Schema<number, number>;
|
|
200
231
|
backgroundMessages: Schema<number, number>;
|
|
201
232
|
candidatePool: Schema<number, number>;
|
|
202
233
|
}>, Schemastery.ObjectT<{
|
|
203
234
|
enabled: Schema<boolean, boolean>;
|
|
204
235
|
minMessages: Schema<number, number>;
|
|
236
|
+
idleSeconds: Schema<number, number>;
|
|
205
237
|
backgroundMessages: Schema<number, number>;
|
|
206
238
|
candidatePool: Schema<number, number>;
|
|
207
239
|
}>>;
|
|
@@ -226,6 +258,9 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
226
258
|
recall: Schema<Schemastery.ObjectS<{
|
|
227
259
|
enabled: Schema<boolean, boolean>;
|
|
228
260
|
maxResults: Schema<number, number>;
|
|
261
|
+
maxCharsPerMemory: Schema<number, number>;
|
|
262
|
+
maxTotalRecallChars: Schema<number, number>;
|
|
263
|
+
timeoutMs: Schema<number, number>;
|
|
229
264
|
includePersona: Schema<boolean, boolean>;
|
|
230
265
|
includeSceneNav: Schema<boolean, boolean>;
|
|
231
266
|
strategy: Schema<"keyword" | "embedding" | "hybrid", "keyword" | "embedding" | "hybrid">;
|
|
@@ -233,6 +268,9 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
233
268
|
}>, Schemastery.ObjectT<{
|
|
234
269
|
enabled: Schema<boolean, boolean>;
|
|
235
270
|
maxResults: Schema<number, number>;
|
|
271
|
+
maxCharsPerMemory: Schema<number, number>;
|
|
272
|
+
maxTotalRecallChars: Schema<number, number>;
|
|
273
|
+
timeoutMs: Schema<number, number>;
|
|
236
274
|
includePersona: Schema<boolean, boolean>;
|
|
237
275
|
includeSceneNav: Schema<boolean, boolean>;
|
|
238
276
|
strategy: Schema<"keyword" | "embedding" | "hybrid", "keyword" | "embedding" | "hybrid">;
|
|
@@ -248,6 +286,7 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
248
286
|
timeoutMs: Schema<number, number>;
|
|
249
287
|
allowLocalModels: Schema<boolean, boolean>;
|
|
250
288
|
mirror: Schema<string, string>;
|
|
289
|
+
proxy: Schema<string, string>;
|
|
251
290
|
}>, Schemastery.ObjectT<{
|
|
252
291
|
enabled: Schema<boolean, boolean>;
|
|
253
292
|
baseUrl: Schema<string, string>;
|
|
@@ -258,6 +297,7 @@ export declare const memorySchema: Schema<Schemastery.ObjectS<{
|
|
|
258
297
|
timeoutMs: Schema<number, number>;
|
|
259
298
|
allowLocalModels: Schema<boolean, boolean>;
|
|
260
299
|
mirror: Schema<string, string>;
|
|
300
|
+
proxy: Schema<string, string>;
|
|
261
301
|
}>>;
|
|
262
302
|
llm: Schema<Schemastery.ObjectS<{
|
|
263
303
|
provider: Schema<string, string>;
|
package/dist/config.js
CHANGED
|
@@ -15,7 +15,8 @@ export const memorySchema = Schema.object({
|
|
|
15
15
|
}),
|
|
16
16
|
extract: Schema.object({
|
|
17
17
|
enabled: Schema.boolean().default(true),
|
|
18
|
-
minMessages: Schema.number().min(1).max(100).default(
|
|
18
|
+
minMessages: Schema.number().min(1).max(100).default(6),
|
|
19
|
+
idleSeconds: Schema.number().min(0).max(86_400).default(300),
|
|
19
20
|
backgroundMessages: Schema.number().min(0).max(50).default(10),
|
|
20
21
|
candidatePool: Schema.number().min(1).max(20).default(5),
|
|
21
22
|
}),
|
|
@@ -32,6 +33,10 @@ export const memorySchema = Schema.object({
|
|
|
32
33
|
recall: Schema.object({
|
|
33
34
|
enabled: Schema.boolean().default(true),
|
|
34
35
|
maxResults: Schema.number().min(1).max(20).default(5),
|
|
36
|
+
// 预算与超时(ADR-0001 / 规格 A 节):截断是引流——工具路径返回全文
|
|
37
|
+
maxCharsPerMemory: Schema.number().min(0).max(100_000).default(500),
|
|
38
|
+
maxTotalRecallChars: Schema.number().min(0).max(100_000).default(2000),
|
|
39
|
+
timeoutMs: Schema.number().min(0).max(60_000).default(5000),
|
|
35
40
|
includePersona: Schema.boolean().default(true),
|
|
36
41
|
includeSceneNav: Schema.boolean().default(true),
|
|
37
42
|
strategy: Schema.union(['keyword', 'embedding', 'hybrid']).default('hybrid'),
|
|
@@ -48,12 +53,14 @@ export const memorySchema = Schema.object({
|
|
|
48
53
|
timeoutMs: Schema.number().min(1000).max(300_000).default(10_000),
|
|
49
54
|
allowLocalModels: Schema.boolean().default(true),
|
|
50
55
|
mirror: Schema.string().default('https://hf-mirror.com'),
|
|
56
|
+
proxy: Schema.string().default(''),
|
|
51
57
|
}),
|
|
52
58
|
llm: Schema.object({
|
|
53
59
|
provider: Schema.string().default(''),
|
|
54
60
|
model: Schema.string().default(''),
|
|
55
|
-
// 推理模型(如 v4-flash)的 reasoning 计入输出预算:预算不足会被思考吃光导致正文 0
|
|
56
|
-
|
|
61
|
+
// 推理模型(如 v4-flash)的 reasoning 计入输出预算:预算不足会被思考吃光导致正文 0 字符。
|
|
62
|
+
// 0.8.0 起各蒸馏层显式传分层预算(见 llm.ts LAYER_MAX_TOKENS_*),本值为未分层调用的兜底总闸
|
|
63
|
+
maxTokens: Schema.number().min(1024).max(1_000_000).default(65_536),
|
|
57
64
|
// 蒸馏是结构化抽取任务,默认关思考(off):v4-flash 默认 high 档的思考可把任意 maxTokens
|
|
58
65
|
// 预算全部吃光导致正文 0 字符;非推理模型不认识 effort 时会报 UNSUPPORTED_REASONING_EFFORT,设空串跳过
|
|
59
66
|
reasoningEffort: Schema.union(['', 'off', 'high', 'max']).default('off'),
|
package/dist/hooks/recall.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 召回 Hook
|
|
3
|
-
* 1. agent/pre-step(waterfall
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* 召回 Hook(消息侧注入版,ADR-0001):
|
|
3
|
+
* 1. agent/pre-step(waterfall,prepend 注册):有新的用户来源消息到达的步骤
|
|
4
|
+
* (轮首 claim 或 steering 插话)→ 检索 L1 → 以合成消息形式注入到用户消息之前。
|
|
5
|
+
* 注入消息携带插件来源(form: 'recall'),对用户可见、随会话历史持久累积——
|
|
6
|
+
* 这就是"记忆生效"的显式提示(dsh-time-context 同款官方范式)。
|
|
7
|
+
* 纯工具步透传;召回超时(recall.timeoutMs 总预算)跳过本轮,绝不阻塞对话。
|
|
8
|
+
* 2. agent/created:在 agent 作用域注册动态上下文 provider(L3 画像 + L2 场景导航 +
|
|
9
|
+
* 工具指南——系统提示只保留稳定内容;指南三条件门控:工具开启 && 有内容)。
|
|
6
10
|
*
|
|
7
|
-
* 注入内容使用 <relevant-memories>
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* 分域(子块只出现在外层标签内部,随外层一起被 sanitize 剥离,无泄漏风险)。
|
|
11
|
+
* 注入内容使用 <relevant-memories> 标签包裹(模型侧语义边界 + 助手回显剥离锚点),
|
|
12
|
+
* 召回预算(单条/整轮)超限截断并引导模型用记忆工具查全文。
|
|
13
|
+
* L0 防污染零成本:capture 侧只收 source.kind === 'user' 的消息,注入消息天然被排除。
|
|
11
14
|
*
|
|
12
15
|
* 注意:PromptContext.text 是**同步**函数,画像/场景导航这类异步文件读取必须走内存缓存,
|
|
13
16
|
* 由定时刷新 + 管线更新后的主动失效来更新。
|