dsh-tdai-memory 0.2.13 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +15 -13
- package/README.zh.md +9 -7
- package/THIRD-PARTY-NOTICES.md +20 -20
- package/client.js +21 -4
- package/cordis.patch.yml +8 -8
- package/index.js +36 -14
- package/package.json +77 -78
- package/recall-inject.js +86 -86
- package/vendor/tdai/adapters/index.js +15 -15
- package/vendor/tdai/adapters/openclaw/host-adapter.js +83 -83
- package/vendor/tdai/adapters/openclaw/index.js +5 -5
- package/vendor/tdai/adapters/openclaw/llm-runner.js +69 -69
- package/vendor/tdai/adapters/standalone/host-adapter.js +58 -58
- package/vendor/tdai/adapters/standalone/index.js +5 -5
- package/vendor/tdai/adapters/standalone/llm-runner.js +34 -6
- package/vendor/tdai/config.js +3 -0
- package/vendor/tdai/core/conversation/l0-recorder.js +420 -420
- package/vendor/tdai/core/hooks/auto-capture.js +236 -236
- package/vendor/tdai/core/hooks/auto-recall.js +640 -640
- package/vendor/tdai/core/index.js +8 -8
- package/vendor/tdai/core/persona/persona-generator.js +184 -184
- package/vendor/tdai/core/persona/persona-trigger.js +94 -94
- package/vendor/tdai/core/profile/profile-sync.js +200 -200
- package/vendor/tdai/core/prompts/l1-dedup.js +72 -72
- package/vendor/tdai/core/prompts/l1-extraction.js +34 -34
- package/vendor/tdai/core/prompts/persona-generation.js +35 -35
- package/vendor/tdai/core/prompts/scene-extraction.js +45 -45
- package/vendor/tdai/core/record/l1-dedup.js +318 -318
- package/vendor/tdai/core/record/l1-extractor.js +379 -379
- package/vendor/tdai/core/record/l1-reader.js +178 -178
- package/vendor/tdai/core/record/l1-writer.js +141 -141
- package/vendor/tdai/core/report/reporter.js +86 -86
- package/vendor/tdai/core/scene/filename-normalizer.js +161 -161
- package/vendor/tdai/core/scene/scene-extractor.js +410 -410
- package/vendor/tdai/core/scene/scene-format.js +53 -53
- package/vendor/tdai/core/scene/scene-index.js +82 -82
- package/vendor/tdai/core/scene/scene-navigation.js +57 -57
- package/vendor/tdai/core/seed/input.js +404 -404
- package/vendor/tdai/core/seed/seed-runtime.js +4 -0
- package/vendor/tdai/core/seed/types.js +9 -9
- package/vendor/tdai/core/store/bm25-client.js +117 -117
- package/vendor/tdai/core/store/bm25-local.js +68 -68
- package/vendor/tdai/core/store/embedding.js +632 -632
- package/vendor/tdai/core/store/factory.js +95 -95
- package/vendor/tdai/core/store/search-utils.js +54 -54
- package/vendor/tdai/core/store/sqlite.js +1632 -1632
- package/vendor/tdai/core/store/tcvdb-client.js +221 -221
- package/vendor/tdai/core/store/tcvdb.js +1047 -1047
- package/vendor/tdai/core/store/types.js +17 -17
- package/vendor/tdai/core/tdai-core.js +4 -0
- package/vendor/tdai/core/tools/conversation-search.js +199 -199
- package/vendor/tdai/core/tools/memory-search.js +207 -207
- package/vendor/tdai/core/types.js +12 -12
- package/vendor/tdai/utils/backup.js +189 -189
- package/vendor/tdai/utils/checkpoint.js +360 -360
- package/vendor/tdai/utils/clean-context-runner.js +472 -472
- package/vendor/tdai/utils/ensure-hook-policy.js +213 -213
- package/vendor/tdai/utils/env.js +13 -13
- package/vendor/tdai/utils/managed-timer.js +127 -127
- package/vendor/tdai/utils/manifest.js +91 -91
- package/vendor/tdai/utils/memory-cleaner.js +300 -300
- package/vendor/tdai/utils/no-think-fetch.js +109 -109
- package/vendor/tdai/utils/openclaw-state-dir.js +26 -26
- package/vendor/tdai/utils/pipeline-factory.js +517 -517
- package/vendor/tdai/utils/pipeline-manager.js +892 -892
- package/vendor/tdai/utils/sanitize.js +360 -360
- package/vendor/tdai/utils/serial-queue.js +103 -103
- package/vendor/tdai/utils/session-filter.js +88 -88
- package/vendor/tdai/utils/session-header-fetch.js +88 -0
- package/vendor/tdai/utils/text-utils.js +29 -29
- package/vendor/tdai/utils/time.js +252 -252
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Store Factory — creates the appropriate storage backend and embedding service
|
|
3
|
-
* based on plugin configuration.
|
|
4
|
-
*
|
|
5
|
-
* Supports:
|
|
6
|
-
* - "sqlite" (default): local SQLite + sqlite-vec + FTS5
|
|
7
|
-
* - "tcvdb": Tencent Cloud VectorDB (server-side embedding + hybridSearch)
|
|
8
|
-
*/
|
|
9
|
-
import path from "node:path";
|
|
10
|
-
import { VectorStore } from "./sqlite.js";
|
|
11
|
-
import { TcvdbMemoryStore } from "./tcvdb.js";
|
|
12
|
-
import { createEmbeddingService, NoopEmbeddingService } from "./embedding.js";
|
|
13
|
-
import { createBM25Encoder } from "./bm25-local.js";
|
|
14
|
-
const TAG = "[memory-tdai][factory]";
|
|
15
|
-
/**
|
|
16
|
-
* Create the storage backend, embedding service, and optional BM25 encoder
|
|
17
|
-
* based on plugin configuration.
|
|
18
|
-
*
|
|
19
|
-
* @param config Fully resolved plugin config.
|
|
20
|
-
* @param options.dataDir Plugin data directory.
|
|
21
|
-
* @param options.logger Logger instance.
|
|
22
|
-
*/
|
|
23
|
-
export function createStoreBundle(config, options) {
|
|
24
|
-
const { logger } = options;
|
|
25
|
-
// ── BM25 local encoder ──
|
|
26
|
-
const bm25Encoder = createBM25Encoder(config.bm25, logger);
|
|
27
|
-
switch (config.storeBackend) {
|
|
28
|
-
case "tcvdb": {
|
|
29
|
-
const tcvdbCfg = config.tcvdb;
|
|
30
|
-
if (!tcvdbCfg.url || !tcvdbCfg.apiKey) {
|
|
31
|
-
throw new Error(`${TAG} TCVDB backend requires tcvdb.url and tcvdb.apiKey`);
|
|
32
|
-
}
|
|
33
|
-
if (!tcvdbCfg.database) {
|
|
34
|
-
throw new Error(`${TAG} TCVDB backend requires tcvdb.database — please set a unique database name in your openclaw.json plugin config`);
|
|
35
|
-
}
|
|
36
|
-
const database = tcvdbCfg.database;
|
|
37
|
-
const store = new TcvdbMemoryStore({
|
|
38
|
-
url: tcvdbCfg.url,
|
|
39
|
-
username: tcvdbCfg.username,
|
|
40
|
-
apiKey: tcvdbCfg.apiKey,
|
|
41
|
-
database,
|
|
42
|
-
embeddingModel: tcvdbCfg.embeddingModel,
|
|
43
|
-
timeout: tcvdbCfg.timeout,
|
|
44
|
-
caPemPath: tcvdbCfg.caPemPath,
|
|
45
|
-
logger,
|
|
46
|
-
bm25Encoder: bm25Encoder ?? undefined,
|
|
47
|
-
});
|
|
48
|
-
logger?.debug?.(`${TAG} Store created: backend=tcvdb, database=${database}, model=${tcvdbCfg.embeddingModel}, ` +
|
|
49
|
-
`bm25=${bm25Encoder ? "enabled" : "disabled"}`);
|
|
50
|
-
return {
|
|
51
|
-
store,
|
|
52
|
-
embedding: new NoopEmbeddingService(),
|
|
53
|
-
bm25Encoder,
|
|
54
|
-
storeSnapshot: {
|
|
55
|
-
type: "tcvdb",
|
|
56
|
-
tcvdbUrl: tcvdbCfg.url,
|
|
57
|
-
tcvdbDatabase: database,
|
|
58
|
-
tcvdbAlias: tcvdbCfg.alias || undefined,
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
case "sqlite":
|
|
63
|
-
default: {
|
|
64
|
-
// ── Embedding service (only when enabled) ──
|
|
65
|
-
let embeddingService;
|
|
66
|
-
if (config.embedding.enabled && config.embedding.provider !== "local" && config.embedding.apiKey) {
|
|
67
|
-
embeddingService = createEmbeddingService({
|
|
68
|
-
provider: config.embedding.provider,
|
|
69
|
-
baseUrl: config.embedding.baseUrl,
|
|
70
|
-
apiKey: config.embedding.apiKey,
|
|
71
|
-
model: config.embedding.model,
|
|
72
|
-
dimensions: config.embedding.dimensions,
|
|
73
|
-
sendDimensions: config.embedding.sendDimensions,
|
|
74
|
-
maxInputChars: config.embedding.maxInputChars,
|
|
75
|
-
}, logger);
|
|
76
|
-
}
|
|
77
|
-
// dimensions from config (0 when provider="none" → vec0 deferred)
|
|
78
|
-
const dims = config.embedding.dimensions;
|
|
79
|
-
const dbPath = path.join(options.dataDir, "vectors.db");
|
|
80
|
-
const store = new VectorStore(dbPath, dims, logger);
|
|
81
|
-
logger?.debug?.(`${TAG} Store created: backend=sqlite, dbPath=${dbPath}, dimensions=${dims}, ` +
|
|
82
|
-
`embedding=${embeddingService ? "enabled" : "disabled"}, ` +
|
|
83
|
-
`bm25=${bm25Encoder ? "enabled" : "disabled"}`);
|
|
84
|
-
return {
|
|
85
|
-
store,
|
|
86
|
-
embedding: embeddingService,
|
|
87
|
-
bm25Encoder,
|
|
88
|
-
storeSnapshot: {
|
|
89
|
-
type: "sqlite",
|
|
90
|
-
sqlitePath: path.relative(options.dataDir, dbPath),
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Store Factory — creates the appropriate storage backend and embedding service
|
|
3
|
+
* based on plugin configuration.
|
|
4
|
+
*
|
|
5
|
+
* Supports:
|
|
6
|
+
* - "sqlite" (default): local SQLite + sqlite-vec + FTS5
|
|
7
|
+
* - "tcvdb": Tencent Cloud VectorDB (server-side embedding + hybridSearch)
|
|
8
|
+
*/
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { VectorStore } from "./sqlite.js";
|
|
11
|
+
import { TcvdbMemoryStore } from "./tcvdb.js";
|
|
12
|
+
import { createEmbeddingService, NoopEmbeddingService } from "./embedding.js";
|
|
13
|
+
import { createBM25Encoder } from "./bm25-local.js";
|
|
14
|
+
const TAG = "[memory-tdai][factory]";
|
|
15
|
+
/**
|
|
16
|
+
* Create the storage backend, embedding service, and optional BM25 encoder
|
|
17
|
+
* based on plugin configuration.
|
|
18
|
+
*
|
|
19
|
+
* @param config Fully resolved plugin config.
|
|
20
|
+
* @param options.dataDir Plugin data directory.
|
|
21
|
+
* @param options.logger Logger instance.
|
|
22
|
+
*/
|
|
23
|
+
export function createStoreBundle(config, options) {
|
|
24
|
+
const { logger } = options;
|
|
25
|
+
// ── BM25 local encoder ──
|
|
26
|
+
const bm25Encoder = createBM25Encoder(config.bm25, logger);
|
|
27
|
+
switch (config.storeBackend) {
|
|
28
|
+
case "tcvdb": {
|
|
29
|
+
const tcvdbCfg = config.tcvdb;
|
|
30
|
+
if (!tcvdbCfg.url || !tcvdbCfg.apiKey) {
|
|
31
|
+
throw new Error(`${TAG} TCVDB backend requires tcvdb.url and tcvdb.apiKey`);
|
|
32
|
+
}
|
|
33
|
+
if (!tcvdbCfg.database) {
|
|
34
|
+
throw new Error(`${TAG} TCVDB backend requires tcvdb.database — please set a unique database name in your openclaw.json plugin config`);
|
|
35
|
+
}
|
|
36
|
+
const database = tcvdbCfg.database;
|
|
37
|
+
const store = new TcvdbMemoryStore({
|
|
38
|
+
url: tcvdbCfg.url,
|
|
39
|
+
username: tcvdbCfg.username,
|
|
40
|
+
apiKey: tcvdbCfg.apiKey,
|
|
41
|
+
database,
|
|
42
|
+
embeddingModel: tcvdbCfg.embeddingModel,
|
|
43
|
+
timeout: tcvdbCfg.timeout,
|
|
44
|
+
caPemPath: tcvdbCfg.caPemPath,
|
|
45
|
+
logger,
|
|
46
|
+
bm25Encoder: bm25Encoder ?? undefined,
|
|
47
|
+
});
|
|
48
|
+
logger?.debug?.(`${TAG} Store created: backend=tcvdb, database=${database}, model=${tcvdbCfg.embeddingModel}, ` +
|
|
49
|
+
`bm25=${bm25Encoder ? "enabled" : "disabled"}`);
|
|
50
|
+
return {
|
|
51
|
+
store,
|
|
52
|
+
embedding: new NoopEmbeddingService(),
|
|
53
|
+
bm25Encoder,
|
|
54
|
+
storeSnapshot: {
|
|
55
|
+
type: "tcvdb",
|
|
56
|
+
tcvdbUrl: tcvdbCfg.url,
|
|
57
|
+
tcvdbDatabase: database,
|
|
58
|
+
tcvdbAlias: tcvdbCfg.alias || undefined,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
case "sqlite":
|
|
63
|
+
default: {
|
|
64
|
+
// ── Embedding service (only when enabled) ──
|
|
65
|
+
let embeddingService;
|
|
66
|
+
if (config.embedding.enabled && config.embedding.provider !== "local" && config.embedding.apiKey) {
|
|
67
|
+
embeddingService = createEmbeddingService({
|
|
68
|
+
provider: config.embedding.provider,
|
|
69
|
+
baseUrl: config.embedding.baseUrl,
|
|
70
|
+
apiKey: config.embedding.apiKey,
|
|
71
|
+
model: config.embedding.model,
|
|
72
|
+
dimensions: config.embedding.dimensions,
|
|
73
|
+
sendDimensions: config.embedding.sendDimensions,
|
|
74
|
+
maxInputChars: config.embedding.maxInputChars,
|
|
75
|
+
}, logger);
|
|
76
|
+
}
|
|
77
|
+
// dimensions from config (0 when provider="none" → vec0 deferred)
|
|
78
|
+
const dims = config.embedding.dimensions;
|
|
79
|
+
const dbPath = path.join(options.dataDir, "vectors.db");
|
|
80
|
+
const store = new VectorStore(dbPath, dims, logger);
|
|
81
|
+
logger?.debug?.(`${TAG} Store created: backend=sqlite, dbPath=${dbPath}, dimensions=${dims}, ` +
|
|
82
|
+
`embedding=${embeddingService ? "enabled" : "disabled"}, ` +
|
|
83
|
+
`bm25=${bm25Encoder ? "enabled" : "disabled"}`);
|
|
84
|
+
return {
|
|
85
|
+
store,
|
|
86
|
+
embedding: embeddingService,
|
|
87
|
+
bm25Encoder,
|
|
88
|
+
storeSnapshot: {
|
|
89
|
+
type: "sqlite",
|
|
90
|
+
sqlitePath: path.relative(options.dataDir, dbPath),
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search utilities — shared helpers for memory search across backends.
|
|
3
|
-
*
|
|
4
|
-
* Contains:
|
|
5
|
-
* - RRF (Reciprocal Rank Fusion) merge — used by SQLite hybrid search
|
|
6
|
-
* (eliminates the 3x duplication in auto-recall, memory-search, conversation-search)
|
|
7
|
-
* - FTS query building — re-exported from sqlite for convenience
|
|
8
|
-
*/
|
|
9
|
-
// ============================
|
|
10
|
-
// RRF (Reciprocal Rank Fusion)
|
|
11
|
-
// ============================
|
|
12
|
-
/**
|
|
13
|
-
* Standard RRF constant from the original RRF paper.
|
|
14
|
-
* Higher k → more weight on lower-ranked items (smoother distribution).
|
|
15
|
-
*/
|
|
16
|
-
export const RRF_K = 60;
|
|
17
|
-
/**
|
|
18
|
-
* Merge multiple ranked lists via Reciprocal Rank Fusion.
|
|
19
|
-
*
|
|
20
|
-
* Each item's RRF score = sum over all lists of 1/(k + rank + 1).
|
|
21
|
-
* Items appearing in multiple lists get their scores summed.
|
|
22
|
-
*
|
|
23
|
-
* @param lists Array of ranked lists. Each list must have items with an `id` field.
|
|
24
|
-
* @param k RRF constant (default: 60).
|
|
25
|
-
* @returns Merged list sorted by descending RRF score, with `rrfScore` attached.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* const merged = rrfMerge(
|
|
30
|
-
* [ftsResults, vecResults],
|
|
31
|
-
* (item) => item.record_id,
|
|
32
|
-
* );
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
export function rrfMerge(lists, getId, k = RRF_K) {
|
|
36
|
-
const map = new Map();
|
|
37
|
-
for (const list of lists) {
|
|
38
|
-
for (let rank = 0; rank < list.length; rank++) {
|
|
39
|
-
const item = list[rank];
|
|
40
|
-
const id = getId(item);
|
|
41
|
-
const score = 1 / (k + rank + 1);
|
|
42
|
-
const existing = map.get(id);
|
|
43
|
-
if (existing) {
|
|
44
|
-
existing.rrfScore += score;
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
map.set(id, { item, rrfScore: score });
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return [...map.values()]
|
|
52
|
-
.sort((a, b) => b.rrfScore - a.rrfScore)
|
|
53
|
-
.map(({ item, rrfScore }) => ({ ...item, rrfScore }));
|
|
54
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Search utilities — shared helpers for memory search across backends.
|
|
3
|
+
*
|
|
4
|
+
* Contains:
|
|
5
|
+
* - RRF (Reciprocal Rank Fusion) merge — used by SQLite hybrid search
|
|
6
|
+
* (eliminates the 3x duplication in auto-recall, memory-search, conversation-search)
|
|
7
|
+
* - FTS query building — re-exported from sqlite for convenience
|
|
8
|
+
*/
|
|
9
|
+
// ============================
|
|
10
|
+
// RRF (Reciprocal Rank Fusion)
|
|
11
|
+
// ============================
|
|
12
|
+
/**
|
|
13
|
+
* Standard RRF constant from the original RRF paper.
|
|
14
|
+
* Higher k → more weight on lower-ranked items (smoother distribution).
|
|
15
|
+
*/
|
|
16
|
+
export const RRF_K = 60;
|
|
17
|
+
/**
|
|
18
|
+
* Merge multiple ranked lists via Reciprocal Rank Fusion.
|
|
19
|
+
*
|
|
20
|
+
* Each item's RRF score = sum over all lists of 1/(k + rank + 1).
|
|
21
|
+
* Items appearing in multiple lists get their scores summed.
|
|
22
|
+
*
|
|
23
|
+
* @param lists Array of ranked lists. Each list must have items with an `id` field.
|
|
24
|
+
* @param k RRF constant (default: 60).
|
|
25
|
+
* @returns Merged list sorted by descending RRF score, with `rrfScore` attached.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const merged = rrfMerge(
|
|
30
|
+
* [ftsResults, vecResults],
|
|
31
|
+
* (item) => item.record_id,
|
|
32
|
+
* );
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function rrfMerge(lists, getId, k = RRF_K) {
|
|
36
|
+
const map = new Map();
|
|
37
|
+
for (const list of lists) {
|
|
38
|
+
for (let rank = 0; rank < list.length; rank++) {
|
|
39
|
+
const item = list[rank];
|
|
40
|
+
const id = getId(item);
|
|
41
|
+
const score = 1 / (k + rank + 1);
|
|
42
|
+
const existing = map.get(id);
|
|
43
|
+
if (existing) {
|
|
44
|
+
existing.rrfScore += score;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
map.set(id, { item, rrfScore: score });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return [...map.values()]
|
|
52
|
+
.sort((a, b) => b.rrfScore - a.rrfScore)
|
|
53
|
+
.map(({ item, rrfScore }) => ({ ...item, rrfScore }));
|
|
54
|
+
}
|