dsh-prime-memory 0.26.5-beta.0 → 0.26.6
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/CHANGELOG.md +39 -0
- package/README.en.md +62 -1
- package/README.md +52 -1
- package/dist/client.js +36785 -134
- package/dist/config.d.ts +9 -0
- package/dist/config.js +8 -0
- package/dist/contract.d.ts +76 -0
- package/dist/embedding-child.cjs +192 -0
- package/dist/embedding-worker.cjs +2 -0
- package/dist/graph/apply.d.ts +9 -0
- package/dist/graph/apply.js +12 -2
- package/dist/graph/constraints.d.ts +3 -1
- package/dist/graph/constraints.js +4 -2
- package/dist/graph/preview.d.ts +92 -0
- package/dist/graph/preview.js +186 -0
- package/dist/graph/search.d.ts +32 -6
- package/dist/graph/search.js +102 -12
- package/dist/graph/types.d.ts +26 -1
- package/dist/graph/types.js +26 -1
- package/dist/hooks/recall.js +36 -2
- package/dist/index.js +23 -3
- package/dist/llm.d.ts +7 -2
- package/dist/llm.js +14 -22
- package/dist/pipeline/graph.d.ts +26 -1
- package/dist/pipeline/graph.js +74 -4
- package/dist/pipeline/l1.js +87 -26
- package/dist/pipeline/md-sync.js +4 -2
- package/dist/pipeline/runner.d.ts +26 -0
- package/dist/pipeline/runner.js +74 -7
- package/dist/pipeline/slot-migrate.d.ts +3 -2
- package/dist/pipeline/slot-migrate.js +3 -2
- package/dist/projection/recall.d.ts +10 -3
- package/dist/projection/recall.js +14 -0
- package/dist/settings.js +3 -0
- package/dist/stats.d.ts +1 -1
- package/dist/stats.js +47 -12
- package/dist/store/embedding-source.d.ts +4 -2
- package/dist/store/embedding-source.js +5 -2
- package/dist/store/graph-store.d.ts +111 -4
- package/dist/store/graph-store.js +324 -15
- package/dist/store/l1.d.ts +17 -9
- package/dist/store/l1.js +55 -18
- package/dist/store/local-embedding.d.ts +9 -5
- package/dist/store/local-embedding.js +137 -17
- package/dist/store/memory-md-disable.d.ts +3 -2
- package/dist/store/memory-md.d.ts +3 -3
- package/dist/store/memory-md.js +6 -4
- package/dist/store/runtime-installer.d.ts +13 -1
- package/dist/store/runtime-installer.js +40 -2
- package/dist/store/session-recalled.d.ts +9 -3
- package/dist/store/session-recalled.js +24 -5
- package/dist/store/sqlite.d.ts +24 -12
- package/dist/store/sqlite.js +97 -31
- package/dist/store/supersede.d.ts +10 -2
- package/dist/store/supersede.js +10 -2
- package/dist/store/validity.d.ts +150 -0
- package/dist/store/validity.js +209 -0
- package/dist/tools/index.d.ts +15 -1
- package/dist/tools/index.js +393 -11
- package/dist/types.d.ts +9 -0
- package/dist/util/json-repair.d.ts +48 -0
- package/dist/util/json-repair.js +285 -0
- package/dist/validity-backfill.d.ts +78 -0
- package/dist/validity-backfill.js +160 -0
- package/dsh.plugin.json +2 -2
- package/package.json +5 -4
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isRetired } from '../store/supersede.js';
|
|
2
|
+
import { stateOfRecord } from '../store/validity.js';
|
|
2
3
|
export const MEMORY_RECALL_KEY = 'memoryRecall';
|
|
3
4
|
/** view 最多透出条数(最近在上;超出部分只计入 count,防 wire 体量失控)。 */
|
|
4
5
|
export const RECALL_VIEW_CAP = 20;
|
|
@@ -43,6 +44,7 @@ export const stateSchema = {
|
|
|
43
44
|
},
|
|
44
45
|
};
|
|
45
46
|
/** view 校验(出网前调用;未知键容忍,同 slots 投影的语义边界)。 */
|
|
47
|
+
const VALIDITY_STATES_VIEW = ['current', 'historical', 'expired', 'expired_suspect', 'superseded', 'retired'];
|
|
46
48
|
export const viewSchema = {
|
|
47
49
|
parse: (value) => {
|
|
48
50
|
const what = 'memoryRecall.view';
|
|
@@ -73,6 +75,12 @@ export const viewSchema = {
|
|
|
73
75
|
invalid(at, 'retired 只能是 true 或缺席');
|
|
74
76
|
if (item.superseded !== undefined && item.superseded !== true)
|
|
75
77
|
invalid(at, 'superseded 只能是 true 或缺席');
|
|
78
|
+
// 时效标注(task_3.5):缺席=旧帧容忍;有则必须六态枚举内,validUntil 必字符串。
|
|
79
|
+
if (item.validity !== undefined && VALIDITY_STATES_VIEW.indexOf(item.validity) < 0) {
|
|
80
|
+
invalid(at, 'validity 必须是六态之一或缺席');
|
|
81
|
+
}
|
|
82
|
+
if (item.validUntil !== undefined)
|
|
83
|
+
requireString(item.validUntil, at, 'validUntil');
|
|
76
84
|
});
|
|
77
85
|
return value;
|
|
78
86
|
},
|
|
@@ -150,6 +158,11 @@ function makeView(state, l1, feedback) {
|
|
|
150
158
|
? r.metadata?.dsh_usage_scenario
|
|
151
159
|
: undefined;
|
|
152
160
|
const rating = feedback?.lastOf(id);
|
|
161
|
+
// 时效标注(task_3.5):只对"过时两档"写键——current/historical 不必让人看见时效噪声;
|
|
162
|
+
// 旧帧无此键,读侧缺席即活跃。判定现算(stateOfRecord),不吃落库快照;
|
|
163
|
+
// 未退场行才会真判过时两档(退场行由 retired 键表达,validity 不再叠加)。
|
|
164
|
+
const vState = isRetired(r) ? undefined : stateOfRecord(r, Date.now());
|
|
165
|
+
const showValidity = vState === 'expired' || vState === 'expired_suspect';
|
|
153
166
|
items.push({
|
|
154
167
|
id: r.id,
|
|
155
168
|
type: r.type,
|
|
@@ -162,6 +175,7 @@ function makeView(state, l1, feedback) {
|
|
|
162
175
|
...(isRetired(r) ? { retired: true } : {}),
|
|
163
176
|
// 已被摘要取代:正文已不在上下文里。与 retired 正交,两者可同时在。
|
|
164
177
|
...(superseded.has(id) ? { superseded: true } : {}),
|
|
178
|
+
...(showValidity ? { validity: vState, validUntil: r.validTo === undefined ? '' : new Date(r.validTo).toISOString() } : {}),
|
|
165
179
|
});
|
|
166
180
|
}
|
|
167
181
|
const next = { rev: state.rev, sessionId: state.sessionId, count: state.ids.length, items };
|
package/dist/settings.js
CHANGED
|
@@ -85,6 +85,8 @@ const ALWAYS_ON = {
|
|
|
85
85
|
redistillEnabled: false,
|
|
86
86
|
mdSyncEnabled: false,
|
|
87
87
|
memportEnabled: false,
|
|
88
|
+
// 时效分级门2 灰度开关:默认关(注入与改前逐字一致)
|
|
89
|
+
validityTiering: false,
|
|
88
90
|
// md 投射护栏运行时覆盖:0 = 跟随部署 cfg.memExport.maxRecords(面板上可改)
|
|
89
91
|
mdExportMaxRecords: 0,
|
|
90
92
|
// 回填并行度运行时覆盖:0 = 跟随部署 cfg.backfill.concurrency(概览页滑块可改)
|
|
@@ -329,6 +331,7 @@ function resolveSettings(value) {
|
|
|
329
331
|
redistillEnabled: v.redistillEnabled === true,
|
|
330
332
|
mdSyncEnabled: v.mdSyncEnabled === true,
|
|
331
333
|
memportEnabled: v.memportEnabled === true,
|
|
334
|
+
validityTiering: v.validityTiering === true,
|
|
332
335
|
// md 投射护栏钳 0~10000(与 live schema 同上限);0 = 跟随部署默认
|
|
333
336
|
mdExportMaxRecords: Math.min(10_000, Math.max(0, num(v.mdExportMaxRecords))),
|
|
334
337
|
// 回填并行度钳 0~8(与 live schema 同上限);0 = 跟随部署 cfg.backfill.concurrency
|
package/dist/stats.d.ts
CHANGED
|
@@ -183,7 +183,7 @@ export declare function hitToUiRecord(r: {
|
|
|
183
183
|
scope?: string;
|
|
184
184
|
/** 归属工作区路径(仅 scope='workspace' 时非空)。 */
|
|
185
185
|
workspaceId?: string;
|
|
186
|
-
/**
|
|
186
|
+
/** 退场判据:与库侧同源(门1 解耦后 marker 单源,`isRetired`)。由 `listL1`/`getByIds` 透传。 */
|
|
187
187
|
validTo?: number;
|
|
188
188
|
}, extra?: {
|
|
189
189
|
agingWeight?: number | null;
|
package/dist/stats.js
CHANGED
|
@@ -26,7 +26,7 @@ import { RECEIPTS_QUERY_LIMIT_MAX, dimensionOf, toReceiptView } from './store/re
|
|
|
26
26
|
import { getTraceStore } from './store/trace.js';
|
|
27
27
|
import { resolveConflictPair, listConflictPairs } from './conflict-service.js';
|
|
28
28
|
import { sourceAnchorLabels } from './pipeline/anchors.js';
|
|
29
|
-
import { readSupersedeMarker } from './store/supersede.js';
|
|
29
|
+
import { readSupersedeMarker, withSupersedeMarker, isRetired } from './store/supersede.js';
|
|
30
30
|
import { isSnapshotName } from './store/l1-snapshot.js';
|
|
31
31
|
import { WING_CATALOG, WING_FALLBACK } from './types.js';
|
|
32
32
|
import { isRoomSlug } from './metadata-validators.js';
|
|
@@ -107,6 +107,8 @@ export const MEMORY_ENDPOINTS = [
|
|
|
107
107
|
'dsh-memory/conflicts-rejected',
|
|
108
108
|
'dsh-memory/graph-search',
|
|
109
109
|
'dsh-memory/graph-node-get',
|
|
110
|
+
'dsh-memory/graph-preview',
|
|
111
|
+
'dsh-memory/graph-cleanup-dead',
|
|
110
112
|
'dsh-memory/scenes',
|
|
111
113
|
'dsh-memory/persona',
|
|
112
114
|
'dsh-memory/log-tail',
|
|
@@ -474,6 +476,8 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
474
476
|
return { sessionId, records: [], typeCatalog: typeCatalogEntries() };
|
|
475
477
|
// 压缩水位之上的那批:正文已被摘要取代。与 retired 正交(真实实现见 session-recalled.ts)。
|
|
476
478
|
const superseded = stores.recalled?.supersededOf(sessionId);
|
|
479
|
+
// 路归因表(record id → 管道串):不在表里 = 无从归因,不挂 lanes 字段。
|
|
480
|
+
const laneTab = stores.recalled?.lanesOf(sessionId);
|
|
477
481
|
const halfLife = effectiveCfg(cfg, live).recall?.decayHalfLifeDays ?? 0;
|
|
478
482
|
const byId = new Map(stores.l1.getByIds(ids).map((r) => [r.id, r]));
|
|
479
483
|
const records = ids
|
|
@@ -486,7 +490,11 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
486
490
|
userRelevance: stores.feedback?.meanOf(r.id) ?? null,
|
|
487
491
|
userMark: stores.feedback?.lastOf(r.id) ?? null,
|
|
488
492
|
});
|
|
489
|
-
|
|
493
|
+
let out = superseded?.has(r.id) === true ? { ...ui, superseded: true } : ui;
|
|
494
|
+
const laneStr = laneTab?.get(r.id);
|
|
495
|
+
if (laneStr !== undefined && laneStr.length > 0)
|
|
496
|
+
out = { ...out, lanes: laneStr.split('|') };
|
|
497
|
+
return out;
|
|
490
498
|
});
|
|
491
499
|
return { sessionId, records, typeCatalog: typeCatalogEntries() };
|
|
492
500
|
}
|
|
@@ -517,7 +525,7 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
517
525
|
let retired = false;
|
|
518
526
|
let announced = false;
|
|
519
527
|
// 退场**前**的记录快照(带 workspaceId/name)——失效联动要靠它算落点,
|
|
520
|
-
// 而 `retire`
|
|
528
|
+
// 而 `retire` 只写 marker,不会动这两列,所以取在退场前后等价,取在前更直白。
|
|
521
529
|
let rec;
|
|
522
530
|
if (p.score === -1) {
|
|
523
531
|
rec = stores.l1.getByIds([id])[0];
|
|
@@ -539,9 +547,11 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
539
547
|
// (面板无消费方),磁盘凭证的对账走 mem-reconcile。
|
|
540
548
|
if (retired && rec !== undefined && cfg.memExport?.enabled === true && stores.mdLedger !== undefined) {
|
|
541
549
|
void disableProjectionFile({
|
|
542
|
-
//
|
|
543
|
-
//
|
|
544
|
-
record: {
|
|
550
|
+
// 传退场**后**的快照:门1 解耦后 md 层按 marker 判退场,这里补上同样形状的
|
|
551
|
+
// 取代标记,渲染出的 frontmatter 才带 `retired: true`——凭证自己就能说清它为什么存在。
|
|
552
|
+
record: withSupersedeMarker(rec.metadata, { at: retiredAt, reason: 'obsolete' }) === rec.metadata
|
|
553
|
+
? rec
|
|
554
|
+
: { ...rec, metadata: withSupersedeMarker(rec.metadata, { at: retiredAt, reason: 'obsolete' }) },
|
|
545
555
|
cfgRoot: cfg.memExport.root,
|
|
546
556
|
dataDir: deps.dataDir,
|
|
547
557
|
ledger: stores.mdLedger,
|
|
@@ -1344,6 +1354,7 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1344
1354
|
redistillEnabled: live?.get()?.redistillEnabled === true,
|
|
1345
1355
|
mdSyncEnabled: live?.get()?.mdSyncEnabled === true,
|
|
1346
1356
|
memportEnabled: live?.get()?.memportEnabled === true,
|
|
1357
|
+
validityTiering: live?.get()?.validityTiering === true,
|
|
1347
1358
|
mdExportMaxRecords: live?.get()?.mdExportMaxRecords ?? 0,
|
|
1348
1359
|
backfillConcurrency: live?.get()?.backfillConcurrency ?? 0,
|
|
1349
1360
|
}),
|
|
@@ -1742,7 +1753,8 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1742
1753
|
...(Array.isArray(r.metadata?.dsh_keywords) ? { keywords: r.metadata.dsh_keywords.filter((k) => typeof k === 'string' && k !== '') } : {}),
|
|
1743
1754
|
...(Array.isArray(r.metadata?.dsh_usage_scenario) ? { usageScenario: r.metadata.dsh_usage_scenario } : {}),
|
|
1744
1755
|
filePath,
|
|
1745
|
-
|
|
1756
|
+
// 退场判据与库侧同源(门1:marker 单源,valid_to 只是现实有效期)。
|
|
1757
|
+
retired: isRetired(r),
|
|
1746
1758
|
};
|
|
1747
1759
|
});
|
|
1748
1760
|
return { items };
|
|
@@ -1820,8 +1832,8 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1820
1832
|
let targets;
|
|
1821
1833
|
if (explicit.length > 0) {
|
|
1822
1834
|
// 显式 id 也要**复核**是否真的处于已退场态:防止调用方用一个 id 列表
|
|
1823
|
-
// 把活动记忆绕过软删直接物理抹掉(那等于给了一条硬删后门)。
|
|
1824
|
-
targets = explicit.filter((id) => stores.l1.getByIds([id]).some((r) => r
|
|
1835
|
+
// 把活动记忆绕过软删直接物理抹掉(那等于给了一条硬删后门)。判据 marker 单源(门1)。
|
|
1836
|
+
targets = explicit.filter((id) => stores.l1.getByIds([id]).some((r) => isRetired(r)));
|
|
1825
1837
|
}
|
|
1826
1838
|
else {
|
|
1827
1839
|
targets = [];
|
|
@@ -1839,7 +1851,8 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1839
1851
|
id: r.id,
|
|
1840
1852
|
name: r.name ?? '',
|
|
1841
1853
|
type: r.type,
|
|
1842
|
-
|
|
1854
|
+
// 退场时刻:marker.at 优先;旧版 retire 写的 valid_to 作回退(存量行兼容)。
|
|
1855
|
+
retiredAt: readSupersedeMarker(r.metadata)?.at ?? (r.validTo === undefined ? '' : new Date(r.validTo).toISOString()),
|
|
1843
1856
|
}));
|
|
1844
1857
|
const resp = {
|
|
1845
1858
|
dryRun: true,
|
|
@@ -1885,7 +1898,7 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1885
1898
|
id: x.id,
|
|
1886
1899
|
name: x.name ?? '',
|
|
1887
1900
|
type: x.type,
|
|
1888
|
-
retiredAt: x.validTo === undefined ? '' : new Date(x.validTo).toISOString(),
|
|
1901
|
+
retiredAt: readSupersedeMarker(x.metadata)?.at ?? (x.validTo === undefined ? '' : new Date(x.validTo).toISOString()),
|
|
1889
1902
|
})),
|
|
1890
1903
|
};
|
|
1891
1904
|
return resp;
|
|
@@ -2019,6 +2032,27 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
2019
2032
|
return { node: null, edges: [] };
|
|
2020
2033
|
return { node, edges: graph.edgesOf(id) };
|
|
2021
2034
|
}
|
|
2035
|
+
case 'dsh-memory/graph-preview': {
|
|
2036
|
+
// 面板图谱预览:缺省种子推荐榜,seedId 给定展开该邻域(悬挂/不可见→空 seeds 不抛)。
|
|
2037
|
+
// mode==='full' 附全图轻量块(大图星图);非 'full' 一律按缺省——无 full 键零漂移。
|
|
2038
|
+
const p = (payload ?? {});
|
|
2039
|
+
const seedId = typeof p.seedId === 'string' && p.seedId.length > 0 ? p.seedId.slice(0, 200) : undefined;
|
|
2040
|
+
const mode = p.mode === 'full' ? 'full' : undefined;
|
|
2041
|
+
const graph = stores.graph;
|
|
2042
|
+
if (!graph) {
|
|
2043
|
+
const zero = { seeds: [], stats: { nodes: 0, edges: 0, causalEdges: 0, pendingJobs: 0, jobsTotal: 0 } };
|
|
2044
|
+
return mode === 'full' ? { ...zero, full: { nodes: [], edges: [], truncated: false } } : zero;
|
|
2045
|
+
}
|
|
2046
|
+
return graph.buildPreview(seedId, mode);
|
|
2047
|
+
}
|
|
2048
|
+
case 'dsh-memory/graph-cleanup-dead': {
|
|
2049
|
+
const graph = stores.graph;
|
|
2050
|
+
if (!graph)
|
|
2051
|
+
return { removed: 0 };
|
|
2052
|
+
const p = (payload ?? {});
|
|
2053
|
+
const ms = typeof p.olderThanMs === 'number' && p.olderThanMs > 0 ? p.olderThanMs : undefined;
|
|
2054
|
+
return { removed: graph.cleanupDead(ms) };
|
|
2055
|
+
}
|
|
2022
2056
|
case 'dsh-memory/scenes': {
|
|
2023
2057
|
// 两族拼接展示(浏览器保持混合视图;路径冲突时后写入的族覆盖显示名,读取仍各自独立)
|
|
2024
2058
|
const items = [];
|
|
@@ -2322,7 +2356,8 @@ export function agingWeightOf(updatedAtMs, halfLifeDays, now = Date.now()) {
|
|
|
2322
2356
|
return Math.max(0.5, Math.pow(0.5, ageDays / halfLifeDays));
|
|
2323
2357
|
}
|
|
2324
2358
|
export function hitToUiRecord(r, extra) {
|
|
2325
|
-
|
|
2359
|
+
// 退场判据 marker 单源(门1 解耦,与 `isRetired`/`retiredL1Sql` 同口径)。
|
|
2360
|
+
const retired = isRetired(r);
|
|
2326
2361
|
const mark = retired ? readSupersedeMarker(r.metadata) : undefined;
|
|
2327
2362
|
return {
|
|
2328
2363
|
id: r.id,
|
|
@@ -63,8 +63,10 @@ export interface InitialEmbedding {
|
|
|
63
63
|
export declare function remoteCeiling(cfg: MemoryConfig): boolean;
|
|
64
64
|
export declare function resolveInitialEmbedding(cfg: MemoryConfig, sourceStore: EmbeddingSourceStore, downloader: ModelDownloadQueue, makeLocal: (modelId: string) => LocalEmbeddingService | null, logger?: MemoryLogger): Promise<InitialEmbedding>;
|
|
65
65
|
/** 本地服务构造工厂(index.ts 的初始解析与 Manager 共用一份实现,防漂移)。
|
|
66
|
-
* 推理在
|
|
67
|
-
export declare function makeLocalServiceFactory(installer: RuntimeInstaller, downloader: ModelDownloadQueue, logger?: MemoryLogger, maxInputChars?: number
|
|
66
|
+
* 推理在 fork 子进程(见 local-embedding.ts,fix #11);此处只传 runtime 目录与模型目录。 */
|
|
67
|
+
export declare function makeLocalServiceFactory(installer: RuntimeInstaller, downloader: ModelDownloadQueue, logger?: MemoryLogger, maxInputChars?: number,
|
|
68
|
+
/** 测试注入缝:子进程资产路径(源码态下 dist 产物不存在,显式指向 resources 源文件)。 */
|
|
69
|
+
childPath?: string): (modelId: string) => LocalEmbeddingService | null;
|
|
68
70
|
export interface EmbeddingManagerDeps {
|
|
69
71
|
dataDir: string;
|
|
70
72
|
cfg: MemoryConfig;
|
|
@@ -153,14 +153,17 @@ export async function resolveInitialEmbedding(cfg, sourceStore, downloader, make
|
|
|
153
153
|
return { svc, dims: cfg.embedding.dimensions, providerInfo: svc.getProviderInfo() };
|
|
154
154
|
}
|
|
155
155
|
/** 本地服务构造工厂(index.ts 的初始解析与 Manager 共用一份实现,防漂移)。
|
|
156
|
-
* 推理在
|
|
157
|
-
export function makeLocalServiceFactory(installer, downloader, logger, maxInputChars
|
|
156
|
+
* 推理在 fork 子进程(见 local-embedding.ts,fix #11);此处只传 runtime 目录与模型目录。 */
|
|
157
|
+
export function makeLocalServiceFactory(installer, downloader, logger, maxInputChars,
|
|
158
|
+
/** 测试注入缝:子进程资产路径(源码态下 dist 产物不存在,显式指向 resources 源文件)。 */
|
|
159
|
+
childPath) {
|
|
158
160
|
return (modelId) => {
|
|
159
161
|
const entry = catalogById(modelId);
|
|
160
162
|
if (!entry)
|
|
161
163
|
return null;
|
|
162
164
|
return new LocalEmbeddingService(entry, downloader.modelsDir(entry.id), {
|
|
163
165
|
runtimeDir: installer.runtimeDir,
|
|
166
|
+
childPath,
|
|
164
167
|
logger,
|
|
165
168
|
maxInputChars,
|
|
166
169
|
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { buildFullView } from '../graph/preview.js';
|
|
3
|
+
import type { GraphPreviewResult } from '../graph/preview.js';
|
|
2
4
|
import type { GraphEdge, GraphNode, GraphNodeSearchResult, GraphProjectionJob, GraphProjectionResult } from '../graph/types.js';
|
|
3
5
|
import type { MemoryFamily, MemoryLogger, MemoryRecord } from '../types.js';
|
|
4
6
|
/**
|
|
@@ -99,6 +101,37 @@ export declare class GraphStore {
|
|
|
99
101
|
* 宁可漏不可串)。降级/异常返回空数组。
|
|
100
102
|
*/
|
|
101
103
|
searchNodes(query: string, limit: number, families?: readonly MemoryFamily[]): GraphNodeSearchResult[];
|
|
104
|
+
/**
|
|
105
|
+
* §D 第 3 路存储缝(回链记录 id 序;graphLane 直接消费):词法命中 ∪ 路内向量种子
|
|
106
|
+
* → 1-hop 边扩展 → 保序去重的 L1 记录 id 序列。
|
|
107
|
+
*
|
|
108
|
+
* - **向量种子是路内并集、不是新路**:embedding 由调用方传入(装配层惰性查
|
|
109
|
+
* l1.embedText;未就绪/失败传 undefined = 纯词法路,与旧 searchNodes+直连回链
|
|
110
|
+
* 逐位一致);并集不占 RRF 路数名额,"未接线 = 恰为 2 路"不变量不动;
|
|
111
|
+
* - **种子序**:词法命中在前、向量种子追加在后(RRF 用 rank,顺序即权重),
|
|
112
|
+
* 同节点去重;向量种子同样过 visible 族集与 active|disputed 状态闸(宁可漏不可串);
|
|
113
|
+
* - **1-hop 邻居不按族预筛**:来源记录的跨族由 graphLane 尾部记录级 family/scope
|
|
114
|
+
* 双闸兜底(与本路既有纪律同源);
|
|
115
|
+
* - 降级(db 未就绪/空图)返回空数组,不抛。
|
|
116
|
+
*
|
|
117
|
+
* 返回记录 id 而非 hits 是刻意的:回链/扩展属于读路径细节,路侧只需排名序列。
|
|
118
|
+
*/
|
|
119
|
+
searchLaneRecords(query: string, embedding: Float32Array | undefined, limit: number, families?: readonly MemoryFamily[]): readonly string[];
|
|
120
|
+
/**
|
|
121
|
+
* 面板图谱预览存储缝(契约 `dsh-memory/graph-preview` 的数据源):
|
|
122
|
+
* loadGraph 一次 + job 计数(pending/running/failed 未定项与全量分母)传入纯函数。
|
|
123
|
+
* 降级(db 未就绪)返**全零结构**不抛——预览是展示面,永不拖垮端点。
|
|
124
|
+
* mode==='full' 时响应附 `full` 轻量块;**缺省时该键缺席**(可选键零漂移纪律,
|
|
125
|
+
* 不是 undefined 占位——消费方 `'full' in res` 与 JSON 形状双判据都干净)。
|
|
126
|
+
*/
|
|
127
|
+
buildPreview(seedId?: string, mode?: 'full'): GraphPreviewResult & {
|
|
128
|
+
full?: ReturnType<typeof buildFullView>;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* 清理历史 dead 作业行(纯审计痕迹删除,不影响已重入队的记录)。
|
|
132
|
+
* olderThanMs: 只删早于 now-olderThanMs 的;缺省删全部 dead。
|
|
133
|
+
*/
|
|
134
|
+
cleanupDead(olderThanMs?: number): number;
|
|
102
135
|
private newJobId;
|
|
103
136
|
/**
|
|
104
137
|
* 投影入队(按 GRAPH_JOB_BATCH 分片成多个 job;去重下推 SQL):
|
|
@@ -106,6 +139,54 @@ export declare class GraphStore {
|
|
|
106
139
|
* 记录跳过。返回实际新建的 job 数。
|
|
107
140
|
*/
|
|
108
141
|
queueGraphProjection(recordIds: readonly string[], priority: number): number;
|
|
142
|
+
/**
|
|
143
|
+
* 入队的详细形态(与 `queueGraphProjection` 同一实现,只是把新建 job id 也交出来):
|
|
144
|
+
* `memory_reproject` 工具面需要 `{queued, jobIds}` 回报,而既有调用方只数 created——
|
|
145
|
+
* 两个返回形状共享一份去重/分片逻辑,不漂移。
|
|
146
|
+
*/
|
|
147
|
+
private queueGraphProjectionDetailed;
|
|
148
|
+
/**
|
|
149
|
+
* 强制重投(计划 expiry-tiering R10 冻结契约 · 先行刀):
|
|
150
|
+
* 抹掉 `graph_projected_records` 里这些记录的"已按当前版本投过"凭证,再按
|
|
151
|
+
* 新蒸馏优先级入队——绕开 `projector_version` 的永久跳过,让"改了 L1 → 图谱跟随"
|
|
152
|
+
* 有一条不依赖重启/周期 sweep 的纠正通道。
|
|
153
|
+
*
|
|
154
|
+
* **不动在途 `graph_job_records` 映射**(与上游 task_7.5 字面的一处刻意偏差,
|
|
155
|
+
* 理由记入写面计划 findings):claim 在认领时才现读 L1 记录,在途 job 天然投的是
|
|
156
|
+
* **改后**内容;若把映射一并删掉,同一次调用稍后再来一次就会绕过 PK 去重,
|
|
157
|
+
* 堆出内容重叠的双 job——保留映射才是"重复调用不堆重 job"的构造性保证。
|
|
158
|
+
*
|
|
159
|
+
* 幂等:在途有 job → blocked 集吸收,`created=0`;job 已完成 → 凭证刚被
|
|
160
|
+
* `complete` 写回,本方法再删再投,是合法的新重投而非堆积。
|
|
161
|
+
*/
|
|
162
|
+
forceReprojectRecord(recordIds: readonly string[]): {
|
|
163
|
+
queued: number;
|
|
164
|
+
jobIds: string[];
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* 确定性边提案落库面(graph-write-plane R4 兜底支路):把"工具传入的显式边提案"
|
|
168
|
+
* 当作一次确定性投影输入,经 `applyGraphProjection` **纯函数**校验后在同一事务内
|
|
169
|
+
* 写回——不是裸 UPDATE graph_edges:validSources 硬校验、同 (from,relation) 换目标
|
|
170
|
+
* 闭旧边的确定性支路、来源并集封顶,全部复用既有守门代码(R5 不变量由它保证)。
|
|
171
|
+
*
|
|
172
|
+
* 提案形态:两端节点按现有 name/type 重提(消歧合并会命中现有节点,不新铸实体),
|
|
173
|
+
* 边 (from → to, relation) 携调用方给的 sourceRecordIds。claim 同款现读 L1:
|
|
174
|
+
* allowedRecordIds = 命中的来源记录集,未命中的 id 会被纯函数整条拒掉(返回
|
|
175
|
+
* ok:false, reason='dropped')——工具面传的"非现存 L1"在这里撞上硬校验,而非静默落库。
|
|
176
|
+
*/
|
|
177
|
+
applyRelationProposal(opts: {
|
|
178
|
+
fromNodeId: string;
|
|
179
|
+
toNodeId: string;
|
|
180
|
+
relation: string;
|
|
181
|
+
sourceRecordIds: readonly string[];
|
|
182
|
+
now?: string;
|
|
183
|
+
}): {
|
|
184
|
+
ok: boolean;
|
|
185
|
+
reason?: string;
|
|
186
|
+
closedEdgeIds: string[];
|
|
187
|
+
edgeIds: string[];
|
|
188
|
+
touchedNodeIds: string[];
|
|
189
|
+
};
|
|
109
190
|
/**
|
|
110
191
|
* 记录被物理删除后,把两张**索引表**里指向它的行一并抹掉。
|
|
111
192
|
*
|
|
@@ -131,13 +212,20 @@ export declare class GraphStore {
|
|
|
131
212
|
* 校验)→ 写回 touched 行 → job 置 completed + 登记已投影记录 + 放掉 mapping。
|
|
132
213
|
* job 非 running 态(已完成/已 dead/已回收)一律幂等 no-op;任何一步抛错整体
|
|
133
214
|
* 回滚,不留半写。
|
|
215
|
+
*
|
|
216
|
+
* 返回 touched 节点中 active/disputed 的那批(写面向量收尾的输入,G-vec 缺陷修):
|
|
217
|
+
* 调用方只关心"哪些节点刚被写活",向量补算失败不回滚投影——返回值不参与控制流。
|
|
134
218
|
*/
|
|
135
|
-
complete(jobId: string, result: GraphProjectionResult, opts?: CompleteOptions):
|
|
219
|
+
complete(jobId: string, result: GraphProjectionResult, opts?: CompleteOptions): GraphNode[];
|
|
136
220
|
/**
|
|
137
|
-
* 投影失败收尾:attempts 已在 claim 时 +1
|
|
138
|
-
*
|
|
221
|
+
* 投影失败收尾:attempts 已在 claim 时 +1——根据错误分类选不同封顶:
|
|
222
|
+
* - 永久错误(校验/解析):attempts >= GRAPH_JOB_MAX_ATTEMPTS(3) → dead
|
|
223
|
+
* - 瞬态错误(aborted/timeout/网络):attempts >= GRAPH_JOB_TRANSIENT_MAX_ATTEMPTS(6) → dead
|
|
224
|
+
* dead 放掉 mapping(允许下次 backfill 重新入队);未封顶转 failed + 指数退避。
|
|
139
225
|
*/
|
|
140
|
-
fail(jobId: string, error: string
|
|
226
|
+
fail(jobId: string, error: string, opts?: {
|
|
227
|
+
transient?: boolean;
|
|
228
|
+
}): void;
|
|
141
229
|
/** 启动回收:上次进程退出时卡在 running 的 job 放回 pending(dispose 缝不永久卡批)。 */
|
|
142
230
|
recoverRunning(): number;
|
|
143
231
|
/** 最近 job 列表(诊断/面板用;未就绪返回空)。 */
|
|
@@ -154,6 +242,25 @@ export declare class GraphStore {
|
|
|
154
242
|
* 之后;按 L1 存活集判定(而非本次删除集合),跨多次删除与历史孤儿一并收敛。
|
|
155
243
|
*/
|
|
156
244
|
markSourcesDeleted(deletedIds: readonly string[]): void;
|
|
245
|
+
/**
|
|
246
|
+
* 退场传播(计划 expiry-tiering R9 冻结契约 · 先行刀):软删/取代后的图谱
|
|
247
|
+
* 来源活性重算清扫。
|
|
248
|
+
*
|
|
249
|
+
* 与 `markSourcesDeleted` 的分野:那边判"来源 row 是否还活着",对软删(主表行
|
|
250
|
+
* 一直在)结构性失效;这边判"来源是否全部处于**退场态**"——即
|
|
251
|
+
* `classifyValidity` ∈ {superseded, retired}。退场判据单源在 validity 层,
|
|
252
|
+
* 本方不再自写一份 valid_to/marker 比较(写面计划约束 §4)。
|
|
253
|
+
*
|
|
254
|
+
* 重算式 sweep(不是逐条事件改写):每次按当前 L1 全量判定候选节点/边,
|
|
255
|
+
* 双向收敛——
|
|
256
|
+
* - active/disputed 且来源全部退场(含 row 消失)→ `archived` 墓碑(保留行,
|
|
257
|
+
* expand 可见,与物理删传播同落库形态);
|
|
258
|
+
* - archived 且来源全部复活(row 存在且非退场态)→ 回 `active`(restore 的回药;
|
|
259
|
+
* 被物理删清空来源的永不满 足此条,不会误复活)。
|
|
260
|
+
*
|
|
261
|
+
* `changedIds` 只作早退与诊断日志的口径,不参与判定(判定面 = 全图来源集)。
|
|
262
|
+
*/
|
|
263
|
+
markSourcesRetired(changedIds: readonly string[]): void;
|
|
157
264
|
/**
|
|
158
265
|
* §C 矛盾冻结:把图谱的 `disputed` 状态**同步**到给定冲突集。
|
|
159
266
|
*
|