@equationalapplications/expo-llm-wiki 2.2.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-CjlQ68X0.d.mts → WikiMemory-ChQmVyvA.d.mts} +13 -1
- package/dist/{WikiMemory-CjlQ68X0.d.ts → WikiMemory-ChQmVyvA.d.ts} +13 -1
- 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
|
|
@@ -137,6 +137,18 @@ declare class WikiMemory {
|
|
|
137
137
|
private _warnCrossEntityCollision;
|
|
138
138
|
constructor(db: SQLite.SQLiteDatabase, options: WikiOptions);
|
|
139
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
|
+
}>;
|
|
140
152
|
private formatSearchQuery;
|
|
141
153
|
read(entityId: string, query: string): Promise<MemoryBundle>;
|
|
142
154
|
getMemoryBundle(entityId: string): Promise<MemoryBundle>;
|
|
@@ -177,4 +189,4 @@ declare class WikiMemory {
|
|
|
177
189
|
}>;
|
|
178
190
|
}
|
|
179
191
|
|
|
180
|
-
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 };
|
|
@@ -137,6 +137,18 @@ declare class WikiMemory {
|
|
|
137
137
|
private _warnCrossEntityCollision;
|
|
138
138
|
constructor(db: SQLite.SQLiteDatabase, options: WikiOptions);
|
|
139
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
|
+
}>;
|
|
140
152
|
private formatSearchQuery;
|
|
141
153
|
read(entityId: string, query: string): Promise<MemoryBundle>;
|
|
142
154
|
getMemoryBundle(entityId: string): Promise<MemoryBundle>;
|
|
@@ -177,4 +189,4 @@ declare class WikiMemory {
|
|
|
177
189
|
}>;
|
|
178
190
|
}
|
|
179
191
|
|
|
180
|
-
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 };
|