@equationalapplications/core-llm-wiki 2.6.0 → 3.0.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/README.md +171 -1
- package/dist/index.d.mts +77 -5
- package/dist/index.d.ts +77 -5
- package/dist/index.js +696 -219
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +696 -219
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,10 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Pure TypeScript business logic for LLM Wiki Memory.
|
|
4
4
|
|
|
5
|
+
> Inspired by [Andrej Karpathy's LLM Wiki memory spec](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f).
|
|
6
|
+
|
|
5
7
|
## Features
|
|
6
8
|
|
|
7
9
|
- **Platform-agnostic** — Zero runtime dependencies; works with any SQLite driver via the `SQLiteAdapter` interface
|
|
8
|
-
- **
|
|
10
|
+
- **Semantic search** — Vector embeddings via your LLM's `embed` function, ranked by cosine similarity
|
|
11
|
+
- **Keyword fallback** — MiniSearch in-memory index for offline/degraded scenarios when embeddings unavailable
|
|
12
|
+
- **Retrieval tuning** — Per-call overrides for `maxResults`, `preFilterLimit`, and `hybridWeight` blend
|
|
13
|
+
- **Full-featured memory** — Facts, tasks, events, maintenance jobs (librarian, heal, reembed, prune)
|
|
9
14
|
- **Type-safe** — Built with TypeScript, full type exports
|
|
10
15
|
|
|
11
16
|
## Installation
|
|
@@ -14,6 +19,131 @@ Pure TypeScript business logic for LLM Wiki Memory.
|
|
|
14
19
|
npm install @equationalapplications/core-llm-wiki
|
|
15
20
|
```
|
|
16
21
|
|
|
22
|
+
## Semantic Search with Embeddings
|
|
23
|
+
|
|
24
|
+
Provide an `embed` function in `llmProvider` to enable vector-based retrieval:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { WikiMemory } from '@equationalapplications/core-llm-wiki';
|
|
28
|
+
|
|
29
|
+
const wikiMemory = new WikiMemory(db, {
|
|
30
|
+
llmProvider: {
|
|
31
|
+
generateText: async ({ systemPrompt, userPrompt }) => {
|
|
32
|
+
// Your LLM call for extracting facts, tasks
|
|
33
|
+
return 'Model output';
|
|
34
|
+
},
|
|
35
|
+
embed: async (text: string) => {
|
|
36
|
+
// Your embedding service (e.g., OpenAI, Cohere, local)
|
|
37
|
+
const response = await fetch('https://your-app.example.com/api/embed', {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
body: JSON.stringify({ text }),
|
|
40
|
+
});
|
|
41
|
+
const { embedding } = await response.json();
|
|
42
|
+
return embedding; // number[]
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
await wikiMemory.setup();
|
|
48
|
+
|
|
49
|
+
// Query with semantic matching
|
|
50
|
+
const memory = await wikiMemory.read('user-123', 'What should I do this weekend?');
|
|
51
|
+
// Returns facts semantically similar to the query, not lexical matches
|
|
52
|
+
// E.g., fact "Saturday hiking trip" ranks high even though no lexical overlap
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
When `embed` is unavailable, `read()` silently falls back to MiniSearch keyword search. If an embedding attempt throws, `read()` falls back and calls `onRetrievalFallback` if provided:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
const wikiMemory = new WikiMemory(db, {
|
|
59
|
+
llmProvider: {
|
|
60
|
+
generateText: async () => { /* ... */ },
|
|
61
|
+
embed: undefined, // or throws on network error
|
|
62
|
+
},
|
|
63
|
+
onRetrievalFallback: (error) => {
|
|
64
|
+
console.warn('Embedding retrieval unavailable, using keyword search:', error);
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// read() returns MiniSearch results, onRetrievalFallback not called (embed absent is expected)
|
|
69
|
+
// read() returns MiniSearch results, onRetrievalFallback called (embed threw)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Configuration
|
|
73
|
+
|
|
74
|
+
All `WikiConfig` fields are optional:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const wikiMemory = new WikiMemory(db, {
|
|
78
|
+
llmProvider: { /* ... */ },
|
|
79
|
+
config: {
|
|
80
|
+
tablePrefix: 'llm_wiki_', // default: 'llm_wiki_'
|
|
81
|
+
maxResults: 10, // default: 10
|
|
82
|
+
autoLibrarianThreshold: 20, // default: 20 — events before librarian auto-runs
|
|
83
|
+
autoHealThreshold: 100, // default: 100 — events before heal auto-runs
|
|
84
|
+
maxChunkLength: 12000, // default: 12000 (char count per ingestDocument chunk)
|
|
85
|
+
chunkOverlap: 400, // default: 400 (overlap between chunks in characters)
|
|
86
|
+
chunkConcurrency: 1, // default: 1 (parallel LLM calls per ingestDocument)
|
|
87
|
+
pruneRetainSoftDeletedFor: 7, // default: 7 (days before hard-deleting soft-deleted facts)
|
|
88
|
+
pruneEventsAfter: 30, // default: 30 (days before hard-deleting old events)
|
|
89
|
+
orphanAfterDays: 30, // default: 30 (days before runHeal flags sourceless facts; null to disable)
|
|
90
|
+
staleInferredAfterDays: 60, // default: 60 (days before runHeal downgrades inferred facts; null to disable)
|
|
91
|
+
preFilterLimit: 50, // default: undefined — MiniSearch pre-filter before cosine scan; recommended for >500 facts
|
|
92
|
+
hybridWeight: 0.7, // default: undefined — blend semantic (1.0) ↔ keyword (0.0); pure semantic when unset
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Retrieval Tuning
|
|
98
|
+
|
|
99
|
+
Optimize `read()` performance and blend retrieval strategies:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const config = {
|
|
103
|
+
// Limit cosine similarity scoring to top-K MiniSearch keyword candidates
|
|
104
|
+
preFilterLimit: 50,
|
|
105
|
+
|
|
106
|
+
// Blend semantic and keyword scores (0.0 = pure keyword, 1.0 = pure semantic)
|
|
107
|
+
hybridWeight: 0.7,
|
|
108
|
+
|
|
109
|
+
// Max results returned per read
|
|
110
|
+
maxResults: 10,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const wikiMemory = new WikiMemory(db, {
|
|
114
|
+
config,
|
|
115
|
+
llmProvider: { /* ... */ },
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Per-call overrides (runtime controls for search dashboards, etc.)
|
|
119
|
+
const memory = await wikiMemory.read('user-123', 'my preferences', {
|
|
120
|
+
maxResults: 5,
|
|
121
|
+
preFilterLimit: 20,
|
|
122
|
+
hybridWeight: 0.5,
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Hybrid scoring blends:**
|
|
127
|
+
- `hybridWeight: 1.0` → pure semantic scoring among the candidates being scored; if `preFilterLimit` is set, semantic scoring is still limited to the top-K MiniSearch matches
|
|
128
|
+
- `hybridWeight: 0.5` → balanced semantic + keyword (50/50 blend)
|
|
129
|
+
- `hybridWeight: 0.0` → pure keyword ranking, skips `embed()` entirely (no LLM API cost)
|
|
130
|
+
|
|
131
|
+
**Pre-filtering optimization:**
|
|
132
|
+
When `preFilterLimit: 50` is set with 1000 facts, cosine similarity is computed only for the top 50 MiniSearch keyword matches, reducing O(N) scoring to O(50).
|
|
133
|
+
|
|
134
|
+
## Vector Cache
|
|
135
|
+
|
|
136
|
+
Parsed embedding vectors from full-scan `read()` calls are cached in memory, keyed by entity ID (max 16 entities, max 500 vectors per entity). This avoids redundant `Float32Array` parsing on repeated queries for the same entity. When the 16-entity limit is reached, the oldest-inserted entity is evicted to make room; if an entity exceeds 500 facts, its vectors are not cached at all for that read.
|
|
137
|
+
|
|
138
|
+
After heavy read workloads or on memory-constrained runtimes, you can release the entire cache explicitly:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// Release all cached embedding vectors
|
|
142
|
+
wikiMemory.clearVectorCache();
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The cache is also automatically invalidated on any mutation (`runLibrarian`, `runHeal`, `runPrune`, `runReembed`, `ingestDocument`, `importDump`, `forget`).
|
|
146
|
+
|
|
17
147
|
## Usage
|
|
18
148
|
|
|
19
149
|
```typescript
|
|
@@ -128,6 +258,46 @@ const adapter: SQLiteAdapter = {
|
|
|
128
258
|
};
|
|
129
259
|
```
|
|
130
260
|
|
|
261
|
+
## How It Works
|
|
262
|
+
|
|
263
|
+
```mermaid
|
|
264
|
+
flowchart TD
|
|
265
|
+
A["read(entityId, query)"] --> B{hybridWeight = 0?}
|
|
266
|
+
B -->|Yes| C["MiniSearch only<br/>(skip embed)"]
|
|
267
|
+
B -->|No| D{embed available?}
|
|
268
|
+
D -->|No| C
|
|
269
|
+
D -->|Yes| F["Embed query"]
|
|
270
|
+
F -->|throws| E["onRetrievalFallback<br/>callback"]
|
|
271
|
+
E --> C
|
|
272
|
+
F -->|succeeds| G{preFilterLimit<br/>active?}
|
|
273
|
+
G -->|Yes| H["MiniSearch pre-filter<br/>top K candidates"]
|
|
274
|
+
H --> I["Phase 1: Cosine score<br/>top K candidates"]
|
|
275
|
+
G -->|No| J["Phase 1: Cosine score<br/>all facts"]
|
|
276
|
+
J --> K["Cache vectors<br/>in-memory<br/>(full scan only)"]
|
|
277
|
+
K --> L{hybridWeight = 1?}
|
|
278
|
+
I --> L
|
|
279
|
+
L -->|Yes| M["Pure semantic<br/>ranking"]
|
|
280
|
+
L -->|No| N["Hybrid blend:<br/>semantic + keyword<br/>via MiniSearch"]
|
|
281
|
+
M --> O["Phase 2: Fetch full rows<br/>top maxResults"]
|
|
282
|
+
N --> O
|
|
283
|
+
C --> P["MiniSearch ranking"]
|
|
284
|
+
P --> O
|
|
285
|
+
O --> R["Track access"]
|
|
286
|
+
R --> Q["Return MemoryBundle"]
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The flowchart shows:
|
|
290
|
+
1. **Fast-path** when `hybridWeight = 0` (pure keyword, no embed cost)
|
|
291
|
+
2. **Fallback chain** when embed unavailable (MiniSearch silently) or throws (`onRetrievalFallback` callback, then MiniSearch)
|
|
292
|
+
3. **Pre-filtering** to limit cosine scoring to top-K keyword matches (O(N) → O(K))
|
|
293
|
+
4. **Two-phase SELECT**: phase 1 scores all/filtered facts with minimal columns, phase 2 fetches full rows for winners
|
|
294
|
+
5. **Hybrid scoring** to blend semantic and keyword rankings
|
|
295
|
+
6. **Vector caching** on full scans only; reads with `preFilterLimit` active skip cache population
|
|
296
|
+
|
|
131
297
|
## License
|
|
132
298
|
|
|
133
299
|
MIT
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
Made with ❤️ by Equational Applications LLC. [https://equationalapplications.com/](https://equationalapplications.com/)
|
package/dist/index.d.mts
CHANGED
|
@@ -28,6 +28,30 @@ interface WikiConfig {
|
|
|
28
28
|
maxChunkLength?: number;
|
|
29
29
|
chunkOverlap?: number;
|
|
30
30
|
chunkConcurrency?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Max MiniSearch candidates passed to cosine scoring.
|
|
33
|
+
* When set, MiniSearch pre-filters before the cosine scan.
|
|
34
|
+
* Only applies when embed is provided and succeeds.
|
|
35
|
+
* Default: undefined (full scan).
|
|
36
|
+
*/
|
|
37
|
+
preFilterLimit?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Hybrid blend weight (0.0–1.0).
|
|
40
|
+
* 0.0 = pure keyword (skips embed() entirely).
|
|
41
|
+
* 1.0 = pure semantic.
|
|
42
|
+
* Values outside [0,1] are clamped. Ignored when embed is absent or throws.
|
|
43
|
+
* Default: undefined (pure semantic when embed provided).
|
|
44
|
+
*/
|
|
45
|
+
hybridWeight?: number;
|
|
46
|
+
}
|
|
47
|
+
interface ReadOptions {
|
|
48
|
+
maxResults?: number;
|
|
49
|
+
/**
|
|
50
|
+
* undefined → use WikiConfig.preFilterLimit (or no pre-filter if also unset).
|
|
51
|
+
* null → explicitly disable a config-level preFilterLimit for this call.
|
|
52
|
+
*/
|
|
53
|
+
preFilterLimit?: number | null;
|
|
54
|
+
hybridWeight?: number;
|
|
31
55
|
}
|
|
32
56
|
interface WikiFact {
|
|
33
57
|
id: string;
|
|
@@ -41,6 +65,18 @@ interface WikiFact {
|
|
|
41
65
|
source_ref: string | null;
|
|
42
66
|
created_at: number;
|
|
43
67
|
updated_at: number;
|
|
68
|
+
/**
|
|
69
|
+
* Raw Float32Array bytes for the fact's embedding vector.
|
|
70
|
+
* Set when the fact was fetched via exportDump() with blob preservation.
|
|
71
|
+
* Accepted in importDump() as a real Uint8Array (in-memory round-trip),
|
|
72
|
+
* a Node.js Buffer JSON shape `{ type: 'Buffer', data: number[] }`,
|
|
73
|
+
* or a numeric-keyed plain object `{ 0: byte, 1: byte, ... }` produced
|
|
74
|
+
* by JSON.stringify(Uint8Array).
|
|
75
|
+
*/
|
|
76
|
+
embedding_blob?: Uint8Array | {
|
|
77
|
+
type: 'Buffer';
|
|
78
|
+
data: number[];
|
|
79
|
+
} | Record<string, number>;
|
|
44
80
|
last_accessed_at: number | null;
|
|
45
81
|
access_count: number;
|
|
46
82
|
deleted_at: number | null;
|
|
@@ -145,10 +181,22 @@ interface EntityStatus {
|
|
|
145
181
|
librarian: boolean;
|
|
146
182
|
heal: boolean;
|
|
147
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* All operations that can appear in a {@link WikiBusyError}.
|
|
186
|
+
*
|
|
187
|
+
* @remarks **Breaking change from v2.x** — the union previously only contained
|
|
188
|
+
* `'ingest' | 'librarian' | 'heal' | 'prune' | 'reembed'`. The values `'import'`
|
|
189
|
+
* and `'forget'` were added in v3.0. Exhaustive `switch` / narrowing on this type
|
|
190
|
+
* must be updated (or given a `default` arm) to compile without errors.
|
|
191
|
+
*/
|
|
192
|
+
type WikiBusyOperation = 'ingest' | 'librarian' | 'heal' | 'prune' | 'reembed' | 'import' | 'forget';
|
|
193
|
+
/**
|
|
194
|
+
* Thrown when a background mutator is already running for the requested entity.
|
|
195
|
+
*/
|
|
148
196
|
declare class WikiBusyError extends Error {
|
|
149
|
-
readonly operation:
|
|
197
|
+
readonly operation: WikiBusyOperation;
|
|
150
198
|
readonly entityId: string;
|
|
151
|
-
constructor(operation:
|
|
199
|
+
constructor(operation: WikiBusyOperation, entityId: string);
|
|
152
200
|
}
|
|
153
201
|
|
|
154
202
|
declare class WikiMemory {
|
|
@@ -159,6 +207,19 @@ declare class WikiMemory {
|
|
|
159
207
|
private activeIngestJobs;
|
|
160
208
|
private miniSearch;
|
|
161
209
|
private miniSearchEntryIdsByEntity;
|
|
210
|
+
/**
|
|
211
|
+
* Maximum number of entities whose parsed embedding vectors are held in
|
|
212
|
+
* memory. This cap is intentionally conservative so the cache remains safe
|
|
213
|
+
* on memory-constrained runtimes (e.g., mobile/Expo).
|
|
214
|
+
*/
|
|
215
|
+
private static readonly MAX_VECTOR_CACHE_ENTITIES;
|
|
216
|
+
/**
|
|
217
|
+
* Maximum number of fact vectors cached per entity. Keep this high enough to
|
|
218
|
+
* preserve the parsed-embedding reuse optimization for common mid-sized
|
|
219
|
+
* entities while still maintaining a bounded memory footprint.
|
|
220
|
+
*/
|
|
221
|
+
private static readonly MAX_VECTOR_CACHE_FACTS_PER_ENTITY;
|
|
222
|
+
private vectorCache;
|
|
162
223
|
private normalizeMiniSearchRow;
|
|
163
224
|
private rebuildMiniSearchIndex;
|
|
164
225
|
private storeEmbeddingDimension;
|
|
@@ -179,7 +240,12 @@ declare class WikiMemory {
|
|
|
179
240
|
private _pruneKey;
|
|
180
241
|
private _reembedKey;
|
|
181
242
|
private _globalReembedKey;
|
|
243
|
+
private _importKey;
|
|
244
|
+
private _globalImportKey;
|
|
245
|
+
private _forgetKey;
|
|
182
246
|
private _isReembedActive;
|
|
247
|
+
private _isImportActiveFor;
|
|
248
|
+
private _isForgetActiveFor;
|
|
183
249
|
/** Returns true if any maintenance job has the given operation suffix (e.g. ':prune'). */
|
|
184
250
|
private _isAnyMaintenanceActiveWithSuffix;
|
|
185
251
|
/** Returns true if any ingest job is active for the given entity. */
|
|
@@ -194,7 +260,7 @@ declare class WikiMemory {
|
|
|
194
260
|
tasks: number;
|
|
195
261
|
events: number;
|
|
196
262
|
}>;
|
|
197
|
-
read(entityId: string, query: string): Promise<MemoryBundle>;
|
|
263
|
+
read(entityId: string, query: string, options?: ReadOptions): Promise<MemoryBundle>;
|
|
198
264
|
getMemoryBundle(entityId: string): Promise<MemoryBundle>;
|
|
199
265
|
write(entityId: string, event: Omit<WikiEvent, 'id' | 'entity_id' | 'created_at'>): Promise<void>;
|
|
200
266
|
private runLibrarianThenMaybeHeal;
|
|
@@ -202,16 +268,22 @@ declare class WikiMemory {
|
|
|
202
268
|
private _doRunHeal;
|
|
203
269
|
runLibrarian(entityId: string): Promise<void>;
|
|
204
270
|
runHeal(entityId: string): Promise<void>;
|
|
205
|
-
runReembed(entityId?: string
|
|
271
|
+
runReembed(entityId?: string, opts?: {
|
|
272
|
+
force?: boolean;
|
|
273
|
+
skipExisting?: boolean;
|
|
274
|
+
}): Promise<{
|
|
206
275
|
embedded: number;
|
|
207
276
|
skipped: number;
|
|
277
|
+
failed: number;
|
|
208
278
|
}>;
|
|
209
279
|
getEntityStatus(entityId: string): EntityStatus;
|
|
280
|
+
clearVectorCache(): void;
|
|
210
281
|
private _getFullBundle;
|
|
211
282
|
exportDump(entityIds?: string[]): Promise<MemoryDump>;
|
|
212
283
|
importDump(dump: MemoryDump, opts?: {
|
|
213
284
|
merge?: boolean;
|
|
214
285
|
}): Promise<void>;
|
|
286
|
+
private _doImportEntity;
|
|
215
287
|
forget(entityId: string, params: {
|
|
216
288
|
entryId?: string;
|
|
217
289
|
taskId?: string;
|
|
@@ -243,4 +315,4 @@ declare function formatMemoryDump(dump: MemoryDump): FormattedMemoryDump;
|
|
|
243
315
|
|
|
244
316
|
declare function createWiki(db: SQLiteAdapter, options: WikiOptions): WikiMemory;
|
|
245
317
|
|
|
246
|
-
export { type EntityStatus, type ExtractedFact, type ExtractedTask, type FormatContextOptions, type FormattedMemoryDump, type LLMProvider, type MemoryBundle, type MemoryDump, type SQLiteAdapter, WikiBusyError, type WikiCheckpoint, type WikiConfig, type WikiEvent, type WikiFact, WikiMemory, type WikiOptions, type WikiTask, createWiki, formatContext, formatMemoryDump };
|
|
318
|
+
export { type EntityStatus, type ExtractedFact, type ExtractedTask, type FormatContextOptions, type FormattedMemoryDump, type LLMProvider, type MemoryBundle, type MemoryDump, type ReadOptions, type SQLiteAdapter, WikiBusyError, type WikiBusyOperation, type WikiCheckpoint, type WikiConfig, type WikiEvent, type WikiFact, WikiMemory, type WikiOptions, type WikiTask, createWiki, formatContext, formatMemoryDump };
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,30 @@ interface WikiConfig {
|
|
|
28
28
|
maxChunkLength?: number;
|
|
29
29
|
chunkOverlap?: number;
|
|
30
30
|
chunkConcurrency?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Max MiniSearch candidates passed to cosine scoring.
|
|
33
|
+
* When set, MiniSearch pre-filters before the cosine scan.
|
|
34
|
+
* Only applies when embed is provided and succeeds.
|
|
35
|
+
* Default: undefined (full scan).
|
|
36
|
+
*/
|
|
37
|
+
preFilterLimit?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Hybrid blend weight (0.0–1.0).
|
|
40
|
+
* 0.0 = pure keyword (skips embed() entirely).
|
|
41
|
+
* 1.0 = pure semantic.
|
|
42
|
+
* Values outside [0,1] are clamped. Ignored when embed is absent or throws.
|
|
43
|
+
* Default: undefined (pure semantic when embed provided).
|
|
44
|
+
*/
|
|
45
|
+
hybridWeight?: number;
|
|
46
|
+
}
|
|
47
|
+
interface ReadOptions {
|
|
48
|
+
maxResults?: number;
|
|
49
|
+
/**
|
|
50
|
+
* undefined → use WikiConfig.preFilterLimit (or no pre-filter if also unset).
|
|
51
|
+
* null → explicitly disable a config-level preFilterLimit for this call.
|
|
52
|
+
*/
|
|
53
|
+
preFilterLimit?: number | null;
|
|
54
|
+
hybridWeight?: number;
|
|
31
55
|
}
|
|
32
56
|
interface WikiFact {
|
|
33
57
|
id: string;
|
|
@@ -41,6 +65,18 @@ interface WikiFact {
|
|
|
41
65
|
source_ref: string | null;
|
|
42
66
|
created_at: number;
|
|
43
67
|
updated_at: number;
|
|
68
|
+
/**
|
|
69
|
+
* Raw Float32Array bytes for the fact's embedding vector.
|
|
70
|
+
* Set when the fact was fetched via exportDump() with blob preservation.
|
|
71
|
+
* Accepted in importDump() as a real Uint8Array (in-memory round-trip),
|
|
72
|
+
* a Node.js Buffer JSON shape `{ type: 'Buffer', data: number[] }`,
|
|
73
|
+
* or a numeric-keyed plain object `{ 0: byte, 1: byte, ... }` produced
|
|
74
|
+
* by JSON.stringify(Uint8Array).
|
|
75
|
+
*/
|
|
76
|
+
embedding_blob?: Uint8Array | {
|
|
77
|
+
type: 'Buffer';
|
|
78
|
+
data: number[];
|
|
79
|
+
} | Record<string, number>;
|
|
44
80
|
last_accessed_at: number | null;
|
|
45
81
|
access_count: number;
|
|
46
82
|
deleted_at: number | null;
|
|
@@ -145,10 +181,22 @@ interface EntityStatus {
|
|
|
145
181
|
librarian: boolean;
|
|
146
182
|
heal: boolean;
|
|
147
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* All operations that can appear in a {@link WikiBusyError}.
|
|
186
|
+
*
|
|
187
|
+
* @remarks **Breaking change from v2.x** — the union previously only contained
|
|
188
|
+
* `'ingest' | 'librarian' | 'heal' | 'prune' | 'reembed'`. The values `'import'`
|
|
189
|
+
* and `'forget'` were added in v3.0. Exhaustive `switch` / narrowing on this type
|
|
190
|
+
* must be updated (or given a `default` arm) to compile without errors.
|
|
191
|
+
*/
|
|
192
|
+
type WikiBusyOperation = 'ingest' | 'librarian' | 'heal' | 'prune' | 'reembed' | 'import' | 'forget';
|
|
193
|
+
/**
|
|
194
|
+
* Thrown when a background mutator is already running for the requested entity.
|
|
195
|
+
*/
|
|
148
196
|
declare class WikiBusyError extends Error {
|
|
149
|
-
readonly operation:
|
|
197
|
+
readonly operation: WikiBusyOperation;
|
|
150
198
|
readonly entityId: string;
|
|
151
|
-
constructor(operation:
|
|
199
|
+
constructor(operation: WikiBusyOperation, entityId: string);
|
|
152
200
|
}
|
|
153
201
|
|
|
154
202
|
declare class WikiMemory {
|
|
@@ -159,6 +207,19 @@ declare class WikiMemory {
|
|
|
159
207
|
private activeIngestJobs;
|
|
160
208
|
private miniSearch;
|
|
161
209
|
private miniSearchEntryIdsByEntity;
|
|
210
|
+
/**
|
|
211
|
+
* Maximum number of entities whose parsed embedding vectors are held in
|
|
212
|
+
* memory. This cap is intentionally conservative so the cache remains safe
|
|
213
|
+
* on memory-constrained runtimes (e.g., mobile/Expo).
|
|
214
|
+
*/
|
|
215
|
+
private static readonly MAX_VECTOR_CACHE_ENTITIES;
|
|
216
|
+
/**
|
|
217
|
+
* Maximum number of fact vectors cached per entity. Keep this high enough to
|
|
218
|
+
* preserve the parsed-embedding reuse optimization for common mid-sized
|
|
219
|
+
* entities while still maintaining a bounded memory footprint.
|
|
220
|
+
*/
|
|
221
|
+
private static readonly MAX_VECTOR_CACHE_FACTS_PER_ENTITY;
|
|
222
|
+
private vectorCache;
|
|
162
223
|
private normalizeMiniSearchRow;
|
|
163
224
|
private rebuildMiniSearchIndex;
|
|
164
225
|
private storeEmbeddingDimension;
|
|
@@ -179,7 +240,12 @@ declare class WikiMemory {
|
|
|
179
240
|
private _pruneKey;
|
|
180
241
|
private _reembedKey;
|
|
181
242
|
private _globalReembedKey;
|
|
243
|
+
private _importKey;
|
|
244
|
+
private _globalImportKey;
|
|
245
|
+
private _forgetKey;
|
|
182
246
|
private _isReembedActive;
|
|
247
|
+
private _isImportActiveFor;
|
|
248
|
+
private _isForgetActiveFor;
|
|
183
249
|
/** Returns true if any maintenance job has the given operation suffix (e.g. ':prune'). */
|
|
184
250
|
private _isAnyMaintenanceActiveWithSuffix;
|
|
185
251
|
/** Returns true if any ingest job is active for the given entity. */
|
|
@@ -194,7 +260,7 @@ declare class WikiMemory {
|
|
|
194
260
|
tasks: number;
|
|
195
261
|
events: number;
|
|
196
262
|
}>;
|
|
197
|
-
read(entityId: string, query: string): Promise<MemoryBundle>;
|
|
263
|
+
read(entityId: string, query: string, options?: ReadOptions): Promise<MemoryBundle>;
|
|
198
264
|
getMemoryBundle(entityId: string): Promise<MemoryBundle>;
|
|
199
265
|
write(entityId: string, event: Omit<WikiEvent, 'id' | 'entity_id' | 'created_at'>): Promise<void>;
|
|
200
266
|
private runLibrarianThenMaybeHeal;
|
|
@@ -202,16 +268,22 @@ declare class WikiMemory {
|
|
|
202
268
|
private _doRunHeal;
|
|
203
269
|
runLibrarian(entityId: string): Promise<void>;
|
|
204
270
|
runHeal(entityId: string): Promise<void>;
|
|
205
|
-
runReembed(entityId?: string
|
|
271
|
+
runReembed(entityId?: string, opts?: {
|
|
272
|
+
force?: boolean;
|
|
273
|
+
skipExisting?: boolean;
|
|
274
|
+
}): Promise<{
|
|
206
275
|
embedded: number;
|
|
207
276
|
skipped: number;
|
|
277
|
+
failed: number;
|
|
208
278
|
}>;
|
|
209
279
|
getEntityStatus(entityId: string): EntityStatus;
|
|
280
|
+
clearVectorCache(): void;
|
|
210
281
|
private _getFullBundle;
|
|
211
282
|
exportDump(entityIds?: string[]): Promise<MemoryDump>;
|
|
212
283
|
importDump(dump: MemoryDump, opts?: {
|
|
213
284
|
merge?: boolean;
|
|
214
285
|
}): Promise<void>;
|
|
286
|
+
private _doImportEntity;
|
|
215
287
|
forget(entityId: string, params: {
|
|
216
288
|
entryId?: string;
|
|
217
289
|
taskId?: string;
|
|
@@ -243,4 +315,4 @@ declare function formatMemoryDump(dump: MemoryDump): FormattedMemoryDump;
|
|
|
243
315
|
|
|
244
316
|
declare function createWiki(db: SQLiteAdapter, options: WikiOptions): WikiMemory;
|
|
245
317
|
|
|
246
|
-
export { type EntityStatus, type ExtractedFact, type ExtractedTask, type FormatContextOptions, type FormattedMemoryDump, type LLMProvider, type MemoryBundle, type MemoryDump, type SQLiteAdapter, WikiBusyError, type WikiCheckpoint, type WikiConfig, type WikiEvent, type WikiFact, WikiMemory, type WikiOptions, type WikiTask, createWiki, formatContext, formatMemoryDump };
|
|
318
|
+
export { type EntityStatus, type ExtractedFact, type ExtractedTask, type FormatContextOptions, type FormattedMemoryDump, type LLMProvider, type MemoryBundle, type MemoryDump, type ReadOptions, type SQLiteAdapter, WikiBusyError, type WikiBusyOperation, type WikiCheckpoint, type WikiConfig, type WikiEvent, type WikiFact, WikiMemory, type WikiOptions, type WikiTask, createWiki, formatContext, formatMemoryDump };
|