@equationalapplications/expo-llm-wiki 2.1.0 → 2.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/README.md +122 -12
- package/dist/{WikiMemory-BDVn-TJf.d.mts → WikiMemory-ChQmVyvA.d.mts} +29 -3
- package/dist/{WikiMemory-BDVn-TJf.d.ts → WikiMemory-ChQmVyvA.d.ts} +29 -3
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +324 -39
- package/dist/index.mjs +323 -39
- package/dist/react/index.d.mts +30 -3
- package/dist/react/index.d.ts +30 -3
- package/dist/react/index.js +57 -3
- package/dist/react/index.mjs +56 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,36 +19,46 @@ Offline-first, SQLite-backed memory for LLM apps built with Expo. Handles FTS5 s
|
|
|
19
19
|
## How It Works
|
|
20
20
|
|
|
21
21
|
```mermaid
|
|
22
|
-
flowchart
|
|
23
|
-
subgraph API
|
|
22
|
+
flowchart TB
|
|
23
|
+
subgraph API["API Layer"]
|
|
24
24
|
direction TB
|
|
25
25
|
write["write(event)"]
|
|
26
26
|
ingest["ingestDocument()"]
|
|
27
27
|
librarian["runLibrarian()"]
|
|
28
28
|
heal["runHeal()"]
|
|
29
|
+
read["read(entityId, query)"]
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
subgraph
|
|
32
|
+
subgraph LLMLayer["LLM Provider"]
|
|
33
|
+
LLM["LLMProvider.generateText()"]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
subgraph SQLiteLayer["SQLite Database"]
|
|
32
37
|
direction TB
|
|
33
38
|
events[(events)]
|
|
34
|
-
entries[("entries
|
|
39
|
+
entries[("entries<br/>(facts)")]
|
|
35
40
|
tasks[(tasks)]
|
|
36
41
|
end
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Bundle(["MemoryBundle\nfacts · tasks · events"])
|
|
43
|
+
subgraph ReadPath["Read Path"]
|
|
44
|
+
FTS5(["FTS5 search"])
|
|
45
|
+
Bundle(["MemoryBundle<br/>facts · tasks · events"])
|
|
46
|
+
end
|
|
43
47
|
|
|
48
|
+
%% Write paths
|
|
44
49
|
write --> events
|
|
45
50
|
events -. "≥ threshold" .-> librarian
|
|
51
|
+
|
|
52
|
+
%% LLM calls
|
|
46
53
|
librarian --> LLM
|
|
47
54
|
heal --> LLM
|
|
48
55
|
ingest --> LLM
|
|
56
|
+
|
|
57
|
+
%% Database writes
|
|
49
58
|
LLM --> entries
|
|
50
59
|
LLM --> tasks
|
|
51
|
-
|
|
60
|
+
|
|
61
|
+
%% Read path
|
|
52
62
|
read --> FTS5
|
|
53
63
|
FTS5 --> entries
|
|
54
64
|
entries --> Bundle
|
|
@@ -56,6 +66,7 @@ flowchart LR
|
|
|
56
66
|
events --> Bundle
|
|
57
67
|
```
|
|
58
68
|
|
|
69
|
+
|
|
59
70
|
## Installation
|
|
60
71
|
|
|
61
72
|
In your Expo project:
|
|
@@ -97,6 +108,8 @@ const wiki = createWiki(db, {
|
|
|
97
108
|
maxChunkLength: 6000, // optional, default: 6000 (char count, not bytes)
|
|
98
109
|
chunkOverlap: 400, // optional, default: 400 (overlap between chunks in characters)
|
|
99
110
|
chunkConcurrency: 1, // optional, default: 1 (parallel LLM calls per ingestDocument)
|
|
111
|
+
pruneRetainSoftDeletedFor: 7, // optional, default: 7 (days before hard-deleting soft-deleted rows)
|
|
112
|
+
pruneEventsAfter: 30, // optional, default: 30 (days before hard-deleting old events)
|
|
100
113
|
},
|
|
101
114
|
});
|
|
102
115
|
|
|
@@ -162,6 +175,34 @@ await wiki.runLibrarian('entity-123');
|
|
|
162
175
|
await wiki.runHeal('entity-123');
|
|
163
176
|
```
|
|
164
177
|
|
|
178
|
+
### Format Context
|
|
179
|
+
|
|
180
|
+
Convert a `MemoryBundle` into a string ready for LLM prompt injection:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { formatContext } from 'expo-llm-wiki';
|
|
184
|
+
|
|
185
|
+
const bundle = await wiki.read('entity-123', 'weekend plans');
|
|
186
|
+
const context = formatContext(bundle, {
|
|
187
|
+
format: 'markdown', // 'markdown' (default) | 'plain'
|
|
188
|
+
maxFacts: 10, // default 10
|
|
189
|
+
maxTasks: 10, // default 10
|
|
190
|
+
maxEvents: 10, // default 10
|
|
191
|
+
includeConfidence: true, // default true — appends (certain/inferred/tentative)
|
|
192
|
+
includeTags: true, // default true — appends [tag1, tag2]
|
|
193
|
+
factWeights: {
|
|
194
|
+
confidence: 1.0, // default 1.0
|
|
195
|
+
accessCount: 0.3, // default 0.3 — log(1 + access_count) * weight
|
|
196
|
+
recency: 0.5, // default 0.5 — decays over 30d
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Inject into your system prompt:
|
|
201
|
+
const systemPrompt = `You are a helpful assistant.\n\n${context}`;
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Facts are ranked by a weighted score combining confidence tier, access frequency, and recency. Returns an empty string for an empty bundle.
|
|
205
|
+
|
|
165
206
|
### Forget
|
|
166
207
|
|
|
167
208
|
```typescript
|
|
@@ -176,6 +217,38 @@ await wiki.forget('entity-123', { clearAll: true }); // wipe entity
|
|
|
176
217
|
|
|
177
218
|
Throws `Error` if `sourceRef` or `sourceHash` is provided but invalid. Soft-deletes are idempotent — calling again with the same parameters returns `{ deleted: { entries: 0; tasks: 0 } }`.
|
|
178
219
|
|
|
220
|
+
### Check for Changes
|
|
221
|
+
|
|
222
|
+
Skip re-ingest if a document's content hasn't changed since the last ingest:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const changed = await wiki.hasChanged('entity-123', 'preferences.md', sha256(content));
|
|
226
|
+
if (changed) {
|
|
227
|
+
await wiki.ingestDocument('entity-123', { sourceRef: 'preferences.md', sourceHash: sha256(content), documentChunk: content });
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Returns `true` if the document has never been ingested, all prior ingest results were forgotten, or the stored hash differs from the supplied one. Returns `false` if the stored hash matches exactly.
|
|
232
|
+
|
|
233
|
+
Throws `Error` if `sourceRef` or `sourceHash` is invalid (same rules as `ingestDocument`).
|
|
234
|
+
|
|
235
|
+
### Prune (Hard Delete)
|
|
236
|
+
|
|
237
|
+
Hard-delete aged soft-deleted entries/tasks and old events to reclaim storage:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
const result = await wiki.runPrune('entity-123', {
|
|
241
|
+
retainSoftDeletedFor: 7, // days — hard-delete entries/tasks soft-deleted > 7d ago; null to skip
|
|
242
|
+
retainEventsFor: 30, // days since created_at — hard-delete old events; null to skip
|
|
243
|
+
vacuum: false, // set true to VACUUM (slow on mobile, rewrites entire DB)
|
|
244
|
+
});
|
|
245
|
+
// result: { entries: number; tasks: number; events: number }
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Defaults: `retainSoftDeletedFor = config.pruneRetainSoftDeletedFor ?? 7`, `retainEventsFor = config.pruneEventsAfter ?? 30`, `vacuum = false`.
|
|
249
|
+
|
|
250
|
+
Throws `WikiBusyError` if librarian, heal, ingest, or another prune is in-flight for the same entity. `ingestDocument`, `runLibrarian`, and `runHeal` reciprocally throw `WikiBusyError` if a prune is in-flight.
|
|
251
|
+
|
|
179
252
|
---
|
|
180
253
|
|
|
181
254
|
## React / Expo Component API
|
|
@@ -223,10 +296,10 @@ await execute('entity-123', {
|
|
|
223
296
|
|
|
224
297
|
### `useWikiMaintenance()`
|
|
225
298
|
|
|
226
|
-
Shared `isPending` — true if
|
|
299
|
+
Shared `isPending` — true if any operation is in-flight. See [extended form below](#usewikimaintenance-extended) for `runPrune`:
|
|
227
300
|
|
|
228
301
|
```typescript
|
|
229
|
-
const { runLibrarian, runHeal, isPending, error } = useWikiMaintenance();
|
|
302
|
+
const { runLibrarian, runHeal, runPrune, isPending, error } = useWikiMaintenance();
|
|
230
303
|
|
|
231
304
|
await runLibrarian('entity-123');
|
|
232
305
|
await runHeal('entity-123');
|
|
@@ -257,6 +330,43 @@ const result = await execute('entity-123', { entryId: 'fact_abc' });
|
|
|
257
330
|
// result.deleted.entries — rows soft-deleted
|
|
258
331
|
```
|
|
259
332
|
|
|
333
|
+
### `useWikiHasChanged()`
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
const { execute, lastResult, isPending, error } = useWikiHasChanged();
|
|
337
|
+
// lastResult: boolean | null
|
|
338
|
+
|
|
339
|
+
const changed = await execute('entity-123', 'preferences.md', sha256(content));
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### `useWikiMaintenance()` (extended)
|
|
343
|
+
|
|
344
|
+
`runPrune` is now available alongside `runLibrarian` and `runHeal`. Shared `isPending` is true if any operation is in-flight. `lastResult` is a discriminated union — check `.operation` to narrow the type:
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
const { runLibrarian, runHeal, runPrune, lastResult, isPending, error } = useWikiMaintenance();
|
|
348
|
+
|
|
349
|
+
await runLibrarian('entity-123');
|
|
350
|
+
// lastResult: { operation: 'librarian', result: void }
|
|
351
|
+
|
|
352
|
+
await runHeal('entity-123');
|
|
353
|
+
// lastResult: { operation: 'heal', result: void }
|
|
354
|
+
|
|
355
|
+
const counts = await runPrune('entity-123', { retainSoftDeletedFor: 7, retainEventsFor: 30 });
|
|
356
|
+
// counts: { entries: number; tasks: number; events: number }
|
|
357
|
+
// lastResult: { operation: 'prune', result: { entries: number; tasks: number; events: number } }
|
|
358
|
+
|
|
359
|
+
if (lastResult?.operation === 'prune') {
|
|
360
|
+
console.log(lastResult.result.entries); // type-safe access to prune counts
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
The exported `MaintenanceResult` type can be imported for typed consumers:
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
import type { MaintenanceResult } from 'expo-llm-wiki/react';
|
|
368
|
+
```
|
|
369
|
+
|
|
260
370
|
All mutation hooks follow the same pattern (`TResult` is specific per hook):
|
|
261
371
|
|
|
262
372
|
```typescript
|
|
@@ -4,6 +4,7 @@ interface WikiConfig {
|
|
|
4
4
|
tablePrefix?: string;
|
|
5
5
|
maxFtsResults?: number;
|
|
6
6
|
pruneEventsAfter?: number;
|
|
7
|
+
pruneRetainSoftDeletedFor?: number;
|
|
7
8
|
autoLibrarianThreshold?: number;
|
|
8
9
|
autoHealThreshold?: number;
|
|
9
10
|
orphanAfterDays?: number | null;
|
|
@@ -101,15 +102,28 @@ interface FormattedMemoryDump {
|
|
|
101
102
|
content: string;
|
|
102
103
|
}>;
|
|
103
104
|
}
|
|
105
|
+
interface FormatContextOptions {
|
|
106
|
+
format?: 'markdown' | 'plain';
|
|
107
|
+
maxFacts?: number;
|
|
108
|
+
maxTasks?: number;
|
|
109
|
+
maxEvents?: number;
|
|
110
|
+
includeConfidence?: boolean;
|
|
111
|
+
includeTags?: boolean;
|
|
112
|
+
factWeights?: {
|
|
113
|
+
confidence?: number;
|
|
114
|
+
accessCount?: number;
|
|
115
|
+
recency?: number;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
104
118
|
interface EntityStatus {
|
|
105
119
|
ingesting: boolean;
|
|
106
120
|
librarian: boolean;
|
|
107
121
|
heal: boolean;
|
|
108
122
|
}
|
|
109
123
|
declare class WikiBusyError extends Error {
|
|
110
|
-
readonly operation: 'ingest' | 'librarian' | 'heal';
|
|
124
|
+
readonly operation: 'ingest' | 'librarian' | 'heal' | 'prune';
|
|
111
125
|
readonly entityId: string;
|
|
112
|
-
constructor(operation: 'ingest' | 'librarian' | 'heal', entityId: string);
|
|
126
|
+
constructor(operation: 'ingest' | 'librarian' | 'heal' | 'prune', entityId: string);
|
|
113
127
|
}
|
|
114
128
|
|
|
115
129
|
declare class WikiMemory {
|
|
@@ -123,6 +137,18 @@ declare class WikiMemory {
|
|
|
123
137
|
private _warnCrossEntityCollision;
|
|
124
138
|
constructor(db: SQLite.SQLiteDatabase, options: WikiOptions);
|
|
125
139
|
setup(): Promise<void>;
|
|
140
|
+
hasChanged(entityId: string, sourceRef: string, sourceHash: string): Promise<boolean>;
|
|
141
|
+
private _pruneKey;
|
|
142
|
+
private _validatePruneDuration;
|
|
143
|
+
runPrune(entityId: string, options?: {
|
|
144
|
+
retainSoftDeletedFor?: number | null;
|
|
145
|
+
retainEventsFor?: number | null;
|
|
146
|
+
vacuum?: boolean;
|
|
147
|
+
}): Promise<{
|
|
148
|
+
entries: number;
|
|
149
|
+
tasks: number;
|
|
150
|
+
events: number;
|
|
151
|
+
}>;
|
|
126
152
|
private formatSearchQuery;
|
|
127
153
|
read(entityId: string, query: string): Promise<MemoryBundle>;
|
|
128
154
|
getMemoryBundle(entityId: string): Promise<MemoryBundle>;
|
|
@@ -163,4 +189,4 @@ declare class WikiMemory {
|
|
|
163
189
|
}>;
|
|
164
190
|
}
|
|
165
191
|
|
|
166
|
-
export { type EntityStatus as E, type FormattedMemoryDump as F, type LLMProvider as L, type MemoryDump as M, type WikiOptions as W,
|
|
192
|
+
export { type EntityStatus as E, type FormattedMemoryDump as F, type LLMProvider as L, type MemoryDump as M, type WikiOptions as W, type MemoryBundle as a, type FormatContextOptions as b, WikiMemory as c, type ExtractedFact as d, type ExtractedTask as e, WikiBusyError as f, type WikiCheckpoint as g, type WikiConfig as h, type WikiEvent as i, type WikiFact as j, type WikiTask as k };
|
|
@@ -4,6 +4,7 @@ interface WikiConfig {
|
|
|
4
4
|
tablePrefix?: string;
|
|
5
5
|
maxFtsResults?: number;
|
|
6
6
|
pruneEventsAfter?: number;
|
|
7
|
+
pruneRetainSoftDeletedFor?: number;
|
|
7
8
|
autoLibrarianThreshold?: number;
|
|
8
9
|
autoHealThreshold?: number;
|
|
9
10
|
orphanAfterDays?: number | null;
|
|
@@ -101,15 +102,28 @@ interface FormattedMemoryDump {
|
|
|
101
102
|
content: string;
|
|
102
103
|
}>;
|
|
103
104
|
}
|
|
105
|
+
interface FormatContextOptions {
|
|
106
|
+
format?: 'markdown' | 'plain';
|
|
107
|
+
maxFacts?: number;
|
|
108
|
+
maxTasks?: number;
|
|
109
|
+
maxEvents?: number;
|
|
110
|
+
includeConfidence?: boolean;
|
|
111
|
+
includeTags?: boolean;
|
|
112
|
+
factWeights?: {
|
|
113
|
+
confidence?: number;
|
|
114
|
+
accessCount?: number;
|
|
115
|
+
recency?: number;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
104
118
|
interface EntityStatus {
|
|
105
119
|
ingesting: boolean;
|
|
106
120
|
librarian: boolean;
|
|
107
121
|
heal: boolean;
|
|
108
122
|
}
|
|
109
123
|
declare class WikiBusyError extends Error {
|
|
110
|
-
readonly operation: 'ingest' | 'librarian' | 'heal';
|
|
124
|
+
readonly operation: 'ingest' | 'librarian' | 'heal' | 'prune';
|
|
111
125
|
readonly entityId: string;
|
|
112
|
-
constructor(operation: 'ingest' | 'librarian' | 'heal', entityId: string);
|
|
126
|
+
constructor(operation: 'ingest' | 'librarian' | 'heal' | 'prune', entityId: string);
|
|
113
127
|
}
|
|
114
128
|
|
|
115
129
|
declare class WikiMemory {
|
|
@@ -123,6 +137,18 @@ declare class WikiMemory {
|
|
|
123
137
|
private _warnCrossEntityCollision;
|
|
124
138
|
constructor(db: SQLite.SQLiteDatabase, options: WikiOptions);
|
|
125
139
|
setup(): Promise<void>;
|
|
140
|
+
hasChanged(entityId: string, sourceRef: string, sourceHash: string): Promise<boolean>;
|
|
141
|
+
private _pruneKey;
|
|
142
|
+
private _validatePruneDuration;
|
|
143
|
+
runPrune(entityId: string, options?: {
|
|
144
|
+
retainSoftDeletedFor?: number | null;
|
|
145
|
+
retainEventsFor?: number | null;
|
|
146
|
+
vacuum?: boolean;
|
|
147
|
+
}): Promise<{
|
|
148
|
+
entries: number;
|
|
149
|
+
tasks: number;
|
|
150
|
+
events: number;
|
|
151
|
+
}>;
|
|
126
152
|
private formatSearchQuery;
|
|
127
153
|
read(entityId: string, query: string): Promise<MemoryBundle>;
|
|
128
154
|
getMemoryBundle(entityId: string): Promise<MemoryBundle>;
|
|
@@ -163,4 +189,4 @@ declare class WikiMemory {
|
|
|
163
189
|
}>;
|
|
164
190
|
}
|
|
165
191
|
|
|
166
|
-
export { type EntityStatus as E, type FormattedMemoryDump as F, type LLMProvider as L, type MemoryDump as M, type WikiOptions as W,
|
|
192
|
+
export { type EntityStatus as E, type FormattedMemoryDump as F, type LLMProvider as L, type MemoryDump as M, type WikiOptions as W, type MemoryBundle as a, type FormatContextOptions as b, WikiMemory as c, type ExtractedFact as d, type ExtractedTask as e, WikiBusyError as f, type WikiCheckpoint as g, type WikiConfig as h, type WikiEvent as i, type WikiFact as j, type WikiTask as k };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as SQLite from 'expo-sqlite';
|
|
2
|
-
import { M as MemoryDump, F as FormattedMemoryDump, W as WikiOptions,
|
|
3
|
-
export { E as EntityStatus,
|
|
2
|
+
import { M as MemoryDump, F as FormattedMemoryDump, a as MemoryBundle, b as FormatContextOptions, W as WikiOptions, c as WikiMemory } from './WikiMemory-ChQmVyvA.mjs';
|
|
3
|
+
export { E as EntityStatus, d as ExtractedFact, e as ExtractedTask, L as LLMProvider, f as WikiBusyError, g as WikiCheckpoint, h as WikiConfig, i as WikiEvent, j as WikiFact, k as WikiTask } from './WikiMemory-ChQmVyvA.mjs';
|
|
4
4
|
|
|
5
5
|
declare function formatMemoryDump(dump: MemoryDump): FormattedMemoryDump;
|
|
6
6
|
|
|
7
|
+
declare function formatContext(bundle: MemoryBundle, options?: FormatContextOptions): string;
|
|
8
|
+
|
|
7
9
|
declare function createWiki(db: SQLite.SQLiteDatabase, options: WikiOptions): WikiMemory;
|
|
8
10
|
|
|
9
|
-
export { FormattedMemoryDump, MemoryDump, WikiMemory, WikiOptions, createWiki, formatMemoryDump };
|
|
11
|
+
export { FormatContextOptions, FormattedMemoryDump, MemoryBundle, MemoryDump, WikiMemory, WikiOptions, createWiki, formatContext, formatMemoryDump };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as SQLite from 'expo-sqlite';
|
|
2
|
-
import { M as MemoryDump, F as FormattedMemoryDump, W as WikiOptions,
|
|
3
|
-
export { E as EntityStatus,
|
|
2
|
+
import { M as MemoryDump, F as FormattedMemoryDump, a as MemoryBundle, b as FormatContextOptions, W as WikiOptions, c as WikiMemory } from './WikiMemory-ChQmVyvA.js';
|
|
3
|
+
export { E as EntityStatus, d as ExtractedFact, e as ExtractedTask, L as LLMProvider, f as WikiBusyError, g as WikiCheckpoint, h as WikiConfig, i as WikiEvent, j as WikiFact, k as WikiTask } from './WikiMemory-ChQmVyvA.js';
|
|
4
4
|
|
|
5
5
|
declare function formatMemoryDump(dump: MemoryDump): FormattedMemoryDump;
|
|
6
6
|
|
|
7
|
+
declare function formatContext(bundle: MemoryBundle, options?: FormatContextOptions): string;
|
|
8
|
+
|
|
7
9
|
declare function createWiki(db: SQLite.SQLiteDatabase, options: WikiOptions): WikiMemory;
|
|
8
10
|
|
|
9
|
-
export { FormattedMemoryDump, MemoryDump, WikiMemory, WikiOptions, createWiki, formatMemoryDump };
|
|
11
|
+
export { FormatContextOptions, FormattedMemoryDump, MemoryBundle, MemoryDump, WikiMemory, WikiOptions, createWiki, formatContext, formatMemoryDump };
|