dsh-tdai-memory 0.3.2 → 0.3.3
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 +9 -1
- package/README.zh.md +8 -1
- package/THIRD-PARTY-NOTICES.md +20 -20
- package/cordis.patch.yml +8 -8
- package/package.json +16 -4
- 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/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/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/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/text-utils.js +29 -29
- package/vendor/tdai/utils/time.js +252 -252
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared type definitions for the `seed` command.
|
|
3
|
-
*
|
|
4
|
-
* Covers:
|
|
5
|
-
* - Raw input shapes (Format A / B / JSONL)
|
|
6
|
-
* - Normalized internal structures
|
|
7
|
-
* - Validation error descriptors
|
|
8
|
-
*/
|
|
9
|
-
export {};
|
|
1
|
+
/**
|
|
2
|
+
* Shared type definitions for the `seed` command.
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* - Raw input shapes (Format A / B / JSONL)
|
|
6
|
+
* - Normalized internal structures
|
|
7
|
+
* - Validation error descriptors
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BM25 Sparse Vector Encoding Client.
|
|
3
|
-
*
|
|
4
|
-
* HTTP client for the BM25 Python sidecar service (bm25_server.py).
|
|
5
|
-
* Used by TCVDB backend to generate sparse vectors for hybridSearch.
|
|
6
|
-
*
|
|
7
|
-
* Two operations:
|
|
8
|
-
* - `encodeTexts(texts)` — encode documents for upsert (TF-based)
|
|
9
|
-
* - `encodeQueries(texts)` — encode queries for search (IDF-based)
|
|
10
|
-
*
|
|
11
|
-
* Graceful degradation: if the sidecar is unreachable, all methods
|
|
12
|
-
* return empty arrays and `isHealthy()` returns false. Callers can
|
|
13
|
-
* check health to dynamically downgrade to pure semantic search.
|
|
14
|
-
*/
|
|
15
|
-
// ============================
|
|
16
|
-
// Implementation
|
|
17
|
-
// ============================
|
|
18
|
-
const TAG = "[memory-tdai][bm25-client]";
|
|
19
|
-
export class BM25Client {
|
|
20
|
-
baseUrl;
|
|
21
|
-
timeout;
|
|
22
|
-
logger;
|
|
23
|
-
/** Cached health status to avoid repeated checks on every call. */
|
|
24
|
-
_healthy;
|
|
25
|
-
_lastHealthCheck = 0;
|
|
26
|
-
static HEALTH_CHECK_INTERVAL_MS = 30_000; // re-check every 30s
|
|
27
|
-
constructor(config, logger) {
|
|
28
|
-
this.baseUrl = config.serviceUrl.replace(/\/+$/, "");
|
|
29
|
-
this.timeout = config.timeout;
|
|
30
|
-
this.logger = logger;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Encode document texts for upsert (TF-based BM25 scoring).
|
|
34
|
-
* Returns one SparseVector per input text.
|
|
35
|
-
* Returns empty array on error (non-throwing).
|
|
36
|
-
*/
|
|
37
|
-
async encodeTexts(texts) {
|
|
38
|
-
if (texts.length === 0)
|
|
39
|
-
return [];
|
|
40
|
-
return this._encode("/encode_texts", texts);
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Encode query texts for search (IDF-based BM25 scoring).
|
|
44
|
-
* Returns one SparseVector per input text.
|
|
45
|
-
* Returns empty array on error (non-throwing).
|
|
46
|
-
*/
|
|
47
|
-
async encodeQueries(texts) {
|
|
48
|
-
if (texts.length === 0)
|
|
49
|
-
return [];
|
|
50
|
-
return this._encode("/encode_queries", texts);
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Check if the BM25 sidecar is reachable.
|
|
54
|
-
* Result is cached for 30 seconds to avoid spamming health checks.
|
|
55
|
-
*/
|
|
56
|
-
async isHealthy() {
|
|
57
|
-
const now = Date.now();
|
|
58
|
-
if (this._healthy !== undefined &&
|
|
59
|
-
now - this._lastHealthCheck < BM25Client.HEALTH_CHECK_INTERVAL_MS) {
|
|
60
|
-
return this._healthy;
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
const resp = await fetch(`${this.baseUrl}/health`, {
|
|
64
|
-
signal: AbortSignal.timeout(3000),
|
|
65
|
-
});
|
|
66
|
-
this._healthy = resp.ok;
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
this._healthy = false;
|
|
70
|
-
}
|
|
71
|
-
this._lastHealthCheck = now;
|
|
72
|
-
if (!this._healthy) {
|
|
73
|
-
this.logger?.warn(`${TAG} BM25 sidecar health check failed (${this.baseUrl})`);
|
|
74
|
-
}
|
|
75
|
-
return this._healthy;
|
|
76
|
-
}
|
|
77
|
-
// ── Internal ──────────────────────────────────────────────────
|
|
78
|
-
async _encode(path, texts) {
|
|
79
|
-
try {
|
|
80
|
-
const resp = await fetch(`${this.baseUrl}${path}`, {
|
|
81
|
-
method: "POST",
|
|
82
|
-
headers: { "Content-Type": "application/json" },
|
|
83
|
-
body: JSON.stringify({ texts }),
|
|
84
|
-
signal: AbortSignal.timeout(this.timeout),
|
|
85
|
-
});
|
|
86
|
-
if (!resp.ok) {
|
|
87
|
-
const errBody = await resp.text().catch(() => "(unreadable)");
|
|
88
|
-
this.logger?.warn(`${TAG} ${path} HTTP ${resp.status}: ${errBody.slice(0, 200)}`);
|
|
89
|
-
return [];
|
|
90
|
-
}
|
|
91
|
-
const json = (await resp.json());
|
|
92
|
-
return json.vectors ?? [];
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
// Mark unhealthy on connection errors
|
|
96
|
-
this._healthy = false;
|
|
97
|
-
this._lastHealthCheck = Date.now();
|
|
98
|
-
this.logger?.warn(`${TAG} ${path} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
99
|
-
return [];
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
// ============================
|
|
104
|
-
// Factory
|
|
105
|
-
// ============================
|
|
106
|
-
/**
|
|
107
|
-
* Create a BM25Client if BM25 is enabled in config.
|
|
108
|
-
* Returns undefined if disabled — callers should check before using.
|
|
109
|
-
*/
|
|
110
|
-
export function createBM25Client(config, logger) {
|
|
111
|
-
if (!config.enabled) {
|
|
112
|
-
logger?.info(`${TAG} BM25 sparse encoding disabled`);
|
|
113
|
-
return undefined;
|
|
114
|
-
}
|
|
115
|
-
logger?.info(`${TAG} BM25 client → ${config.serviceUrl}`);
|
|
116
|
-
return new BM25Client({ serviceUrl: config.serviceUrl, timeout: config.timeout }, logger);
|
|
117
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* BM25 Sparse Vector Encoding Client.
|
|
3
|
+
*
|
|
4
|
+
* HTTP client for the BM25 Python sidecar service (bm25_server.py).
|
|
5
|
+
* Used by TCVDB backend to generate sparse vectors for hybridSearch.
|
|
6
|
+
*
|
|
7
|
+
* Two operations:
|
|
8
|
+
* - `encodeTexts(texts)` — encode documents for upsert (TF-based)
|
|
9
|
+
* - `encodeQueries(texts)` — encode queries for search (IDF-based)
|
|
10
|
+
*
|
|
11
|
+
* Graceful degradation: if the sidecar is unreachable, all methods
|
|
12
|
+
* return empty arrays and `isHealthy()` returns false. Callers can
|
|
13
|
+
* check health to dynamically downgrade to pure semantic search.
|
|
14
|
+
*/
|
|
15
|
+
// ============================
|
|
16
|
+
// Implementation
|
|
17
|
+
// ============================
|
|
18
|
+
const TAG = "[memory-tdai][bm25-client]";
|
|
19
|
+
export class BM25Client {
|
|
20
|
+
baseUrl;
|
|
21
|
+
timeout;
|
|
22
|
+
logger;
|
|
23
|
+
/** Cached health status to avoid repeated checks on every call. */
|
|
24
|
+
_healthy;
|
|
25
|
+
_lastHealthCheck = 0;
|
|
26
|
+
static HEALTH_CHECK_INTERVAL_MS = 30_000; // re-check every 30s
|
|
27
|
+
constructor(config, logger) {
|
|
28
|
+
this.baseUrl = config.serviceUrl.replace(/\/+$/, "");
|
|
29
|
+
this.timeout = config.timeout;
|
|
30
|
+
this.logger = logger;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Encode document texts for upsert (TF-based BM25 scoring).
|
|
34
|
+
* Returns one SparseVector per input text.
|
|
35
|
+
* Returns empty array on error (non-throwing).
|
|
36
|
+
*/
|
|
37
|
+
async encodeTexts(texts) {
|
|
38
|
+
if (texts.length === 0)
|
|
39
|
+
return [];
|
|
40
|
+
return this._encode("/encode_texts", texts);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Encode query texts for search (IDF-based BM25 scoring).
|
|
44
|
+
* Returns one SparseVector per input text.
|
|
45
|
+
* Returns empty array on error (non-throwing).
|
|
46
|
+
*/
|
|
47
|
+
async encodeQueries(texts) {
|
|
48
|
+
if (texts.length === 0)
|
|
49
|
+
return [];
|
|
50
|
+
return this._encode("/encode_queries", texts);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if the BM25 sidecar is reachable.
|
|
54
|
+
* Result is cached for 30 seconds to avoid spamming health checks.
|
|
55
|
+
*/
|
|
56
|
+
async isHealthy() {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
if (this._healthy !== undefined &&
|
|
59
|
+
now - this._lastHealthCheck < BM25Client.HEALTH_CHECK_INTERVAL_MS) {
|
|
60
|
+
return this._healthy;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const resp = await fetch(`${this.baseUrl}/health`, {
|
|
64
|
+
signal: AbortSignal.timeout(3000),
|
|
65
|
+
});
|
|
66
|
+
this._healthy = resp.ok;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
this._healthy = false;
|
|
70
|
+
}
|
|
71
|
+
this._lastHealthCheck = now;
|
|
72
|
+
if (!this._healthy) {
|
|
73
|
+
this.logger?.warn(`${TAG} BM25 sidecar health check failed (${this.baseUrl})`);
|
|
74
|
+
}
|
|
75
|
+
return this._healthy;
|
|
76
|
+
}
|
|
77
|
+
// ── Internal ──────────────────────────────────────────────────
|
|
78
|
+
async _encode(path, texts) {
|
|
79
|
+
try {
|
|
80
|
+
const resp = await fetch(`${this.baseUrl}${path}`, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: { "Content-Type": "application/json" },
|
|
83
|
+
body: JSON.stringify({ texts }),
|
|
84
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
85
|
+
});
|
|
86
|
+
if (!resp.ok) {
|
|
87
|
+
const errBody = await resp.text().catch(() => "(unreadable)");
|
|
88
|
+
this.logger?.warn(`${TAG} ${path} HTTP ${resp.status}: ${errBody.slice(0, 200)}`);
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
const json = (await resp.json());
|
|
92
|
+
return json.vectors ?? [];
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
// Mark unhealthy on connection errors
|
|
96
|
+
this._healthy = false;
|
|
97
|
+
this._lastHealthCheck = Date.now();
|
|
98
|
+
this.logger?.warn(`${TAG} ${path} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// ============================
|
|
104
|
+
// Factory
|
|
105
|
+
// ============================
|
|
106
|
+
/**
|
|
107
|
+
* Create a BM25Client if BM25 is enabled in config.
|
|
108
|
+
* Returns undefined if disabled — callers should check before using.
|
|
109
|
+
*/
|
|
110
|
+
export function createBM25Client(config, logger) {
|
|
111
|
+
if (!config.enabled) {
|
|
112
|
+
logger?.info(`${TAG} BM25 sparse encoding disabled`);
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
logger?.info(`${TAG} BM25 client → ${config.serviceUrl}`);
|
|
116
|
+
return new BM25Client({ serviceUrl: config.serviceUrl, timeout: config.timeout }, logger);
|
|
117
|
+
}
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local BM25 Sparse Vector Encoder.
|
|
3
|
-
*
|
|
4
|
-
* Pure TypeScript replacement for the Python sidecar BM25 client.
|
|
5
|
-
* Uses @tencentdb-agent-memory/tcvdb-text package for tokenization (jieba-wasm) and BM25 encoding.
|
|
6
|
-
*
|
|
7
|
-
* Two operations (same contract as the old BM25Client):
|
|
8
|
-
* - `encodeTexts(texts)` — encode documents for upsert (TF-based)
|
|
9
|
-
* - `encodeQueries(texts)` — encode queries for search (IDF-based)
|
|
10
|
-
*/
|
|
11
|
-
import { BM25Encoder } from "@tencentdb-agent-memory/tcvdb-text";
|
|
12
|
-
const TAG = "[memory-tdai][bm25-local]";
|
|
13
|
-
// ============================
|
|
14
|
-
// Implementation
|
|
15
|
-
// ============================
|
|
16
|
-
export class BM25LocalEncoder {
|
|
17
|
-
encoder;
|
|
18
|
-
logger;
|
|
19
|
-
constructor(language = "zh", logger) {
|
|
20
|
-
this.logger = logger;
|
|
21
|
-
this.encoder = BM25Encoder.default(language);
|
|
22
|
-
logger?.debug?.(`${TAG} Initialized BM25 local encoder (language=${language})`);
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Encode document texts for upsert (TF-based BM25 scoring).
|
|
26
|
-
* Returns one SparseVector per input text.
|
|
27
|
-
*/
|
|
28
|
-
encodeTexts(texts) {
|
|
29
|
-
if (texts.length === 0)
|
|
30
|
-
return [];
|
|
31
|
-
try {
|
|
32
|
-
return this.encoder.encodeTexts(texts);
|
|
33
|
-
}
|
|
34
|
-
catch (err) {
|
|
35
|
-
this.logger?.warn(`${TAG} encodeTexts failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Encode query texts for search (IDF-based BM25 scoring).
|
|
41
|
-
* Returns one SparseVector per input text.
|
|
42
|
-
*/
|
|
43
|
-
encodeQueries(texts) {
|
|
44
|
-
if (texts.length === 0)
|
|
45
|
-
return [];
|
|
46
|
-
try {
|
|
47
|
-
return this.encoder.encodeQueries(texts);
|
|
48
|
-
}
|
|
49
|
-
catch (err) {
|
|
50
|
-
this.logger?.warn(`${TAG} encodeQueries failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// ============================
|
|
56
|
-
// Factory
|
|
57
|
-
// ============================
|
|
58
|
-
/**
|
|
59
|
-
* Create a BM25LocalEncoder if BM25 is enabled in config.
|
|
60
|
-
* Returns undefined if disabled — callers should check before using.
|
|
61
|
-
*/
|
|
62
|
-
export function createBM25Encoder(config, logger) {
|
|
63
|
-
if (!config.enabled) {
|
|
64
|
-
logger?.debug?.(`${TAG} BM25 sparse encoding disabled`);
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
return new BM25LocalEncoder(config.language ?? "zh", logger);
|
|
68
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Local BM25 Sparse Vector Encoder.
|
|
3
|
+
*
|
|
4
|
+
* Pure TypeScript replacement for the Python sidecar BM25 client.
|
|
5
|
+
* Uses @tencentdb-agent-memory/tcvdb-text package for tokenization (jieba-wasm) and BM25 encoding.
|
|
6
|
+
*
|
|
7
|
+
* Two operations (same contract as the old BM25Client):
|
|
8
|
+
* - `encodeTexts(texts)` — encode documents for upsert (TF-based)
|
|
9
|
+
* - `encodeQueries(texts)` — encode queries for search (IDF-based)
|
|
10
|
+
*/
|
|
11
|
+
import { BM25Encoder } from "@tencentdb-agent-memory/tcvdb-text";
|
|
12
|
+
const TAG = "[memory-tdai][bm25-local]";
|
|
13
|
+
// ============================
|
|
14
|
+
// Implementation
|
|
15
|
+
// ============================
|
|
16
|
+
export class BM25LocalEncoder {
|
|
17
|
+
encoder;
|
|
18
|
+
logger;
|
|
19
|
+
constructor(language = "zh", logger) {
|
|
20
|
+
this.logger = logger;
|
|
21
|
+
this.encoder = BM25Encoder.default(language);
|
|
22
|
+
logger?.debug?.(`${TAG} Initialized BM25 local encoder (language=${language})`);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Encode document texts for upsert (TF-based BM25 scoring).
|
|
26
|
+
* Returns one SparseVector per input text.
|
|
27
|
+
*/
|
|
28
|
+
encodeTexts(texts) {
|
|
29
|
+
if (texts.length === 0)
|
|
30
|
+
return [];
|
|
31
|
+
try {
|
|
32
|
+
return this.encoder.encodeTexts(texts);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
this.logger?.warn(`${TAG} encodeTexts failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Encode query texts for search (IDF-based BM25 scoring).
|
|
41
|
+
* Returns one SparseVector per input text.
|
|
42
|
+
*/
|
|
43
|
+
encodeQueries(texts) {
|
|
44
|
+
if (texts.length === 0)
|
|
45
|
+
return [];
|
|
46
|
+
try {
|
|
47
|
+
return this.encoder.encodeQueries(texts);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
this.logger?.warn(`${TAG} encodeQueries failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// ============================
|
|
56
|
+
// Factory
|
|
57
|
+
// ============================
|
|
58
|
+
/**
|
|
59
|
+
* Create a BM25LocalEncoder if BM25 is enabled in config.
|
|
60
|
+
* Returns undefined if disabled — callers should check before using.
|
|
61
|
+
*/
|
|
62
|
+
export function createBM25Encoder(config, logger) {
|
|
63
|
+
if (!config.enabled) {
|
|
64
|
+
logger?.debug?.(`${TAG} BM25 sparse encoding disabled`);
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
return new BM25LocalEncoder(config.language ?? "zh", logger);
|
|
68
|
+
}
|