@equationalapplications/expo-llm-wiki 2.5.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.
Files changed (2) hide show
  1. package/README.md +191 -0
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -2,11 +2,22 @@
2
2
 
3
3
  Expo/React Native adapter for @equationalapplications/core-llm-wiki, powered by `expo-sqlite`.
4
4
 
5
+ [![npm version](https://img.shields.io/npm/v/%40equationalapplications%2Fexpo-llm-wiki?label=npm)](https://www.npmjs.com/package/@equationalapplications/expo-llm-wiki)
6
+ [![npm downloads](https://img.shields.io/npm/dm/%40equationalapplications%2Fexpo-llm-wiki?label=downloads)](https://www.npmjs.com/package/@equationalapplications/expo-llm-wiki)
7
+ [![bundlephobia](https://img.shields.io/bundlephobia/minzip/%40equationalapplications%2Fexpo-llm-wiki?label=gzip)](https://bundlephobia.com/package/@equationalapplications/expo-llm-wiki)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
9
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
10
+
11
+ > Inspired by [Andrej Karpathy's LLM Wiki memory spec](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f).
12
+
5
13
  ## Features
6
14
 
7
15
  - **Expo-ready** — Pre-configured for React Native + Expo
8
16
  - **Built on `expo-sqlite`** — Stable, well-supported SQLite driver
17
+ - **Semantic search** — Vector embeddings via `embed` function, with MiniSearch fallback
18
+ - **Retrieval tuning** — Per-call overrides for search behavior (pre-filter, hybrid blend)
9
19
  - **React hooks** — `WikiProvider`, `useMemoryRead`, and all other hooks are re-exported directly from `@equationalapplications/expo-llm-wiki`
20
+ - **Full-featured memory** — Facts, tasks, events, maintenance jobs (librarian, heal, reembed, prune)
10
21
 
11
22
  ## Installation
12
23
 
@@ -15,6 +26,113 @@ npx expo install expo-sqlite
15
26
  npm install @equationalapplications/expo-llm-wiki
16
27
  ```
17
28
 
29
+ ## Semantic Search
30
+
31
+ Enable vector-based retrieval by providing an `embed` function:
32
+
33
+ ```typescript
34
+ import { createWiki } from '@equationalapplications/expo-llm-wiki';
35
+ import { openDatabaseSync } from 'expo-sqlite';
36
+
37
+ const db = openDatabaseSync('wiki.db');
38
+
39
+ const wiki = createWiki(db, {
40
+ config: {
41
+ // Optimize retrieval for large memory stores
42
+ preFilterLimit: 50, // Limit cosine scoring to top-50 keyword matches
43
+ hybridWeight: 0.7, // Blend semantic (0.7) + keyword (0.3)
44
+ },
45
+ llmProvider: {
46
+ generateText: async ({ systemPrompt, userPrompt }) => {
47
+ // Your LLM call — must return the model output as a string
48
+ return 'Model output';
49
+ },
50
+ embed: async (text: string) => {
51
+ // Your embedding service (e.g., OpenAI, Cohere)
52
+ // Use an absolute URL — React Native / Expo apps do not have a browser
53
+ // origin to resolve relative URLs against on device or simulator.
54
+ const response = await fetch('https://your-api.example.com/api/embed', {
55
+ method: 'POST',
56
+ body: JSON.stringify({ text })
57
+ });
58
+ const { embedding } = await response.json();
59
+ return embedding; // number[]
60
+ },
61
+ },
62
+ onRetrievalFallback: (error) => {
63
+ console.warn('Embedding unavailable, using keyword search:', error);
64
+ },
65
+ });
66
+
67
+ await wiki.setup();
68
+
69
+ // Semantic query
70
+ const memory = await wiki.read('user-123', 'what activities should I do this weekend?');
71
+ // Matches facts like "Saturday hiking trip" even with no lexical overlap
72
+
73
+ // Per-call overrides
74
+ const fasterSearch = await wiki.read('user-123', 'activities', {
75
+ maxResults: 5,
76
+ preFilterLimit: 20, // Tighter pre-filter for speed
77
+ hybridWeight: 0.5, // More keyword weight
78
+ });
79
+ ```
80
+
81
+ ## Configuration
82
+
83
+ All `WikiConfig` fields are optional:
84
+
85
+ ```typescript
86
+ const wiki = createWiki(db, {
87
+ llmProvider: { /* ... */ },
88
+ config: {
89
+ tablePrefix: 'llm_wiki_', // default: 'llm_wiki_'
90
+ maxResults: 10, // default: 10
91
+ autoLibrarianThreshold: 20, // default: 20 — events before librarian auto-runs
92
+ autoHealThreshold: 100, // default: 100 — events before heal auto-runs
93
+ maxChunkLength: 12000, // default: 12000 (char count per ingestDocument chunk)
94
+ chunkOverlap: 400, // default: 400 (overlap between chunks in characters)
95
+ chunkConcurrency: 1, // default: 1 (parallel LLM calls per ingestDocument)
96
+ pruneRetainSoftDeletedFor: 7, // default: 7 (days before hard-deleting soft-deleted facts)
97
+ pruneEventsAfter: 30, // default: 30 (days before hard-deleting old events)
98
+ orphanAfterDays: 30, // default: 30 (days before runHeal flags sourceless facts; null to disable)
99
+ staleInferredAfterDays: 60, // default: 60 (days before runHeal downgrades inferred facts; null to disable)
100
+ preFilterLimit: 50, // default: undefined — MiniSearch pre-filter before cosine scan; recommended for >500 facts
101
+ hybridWeight: 0.7, // default: undefined — blend semantic (1.0) ↔ keyword (0.0); pure semantic when unset
102
+ },
103
+ });
104
+ ```
105
+
106
+ ## Retrieval Tuning
107
+
108
+ Optimize `read()` performance and blend retrieval strategies:
109
+
110
+ ```typescript
111
+ const config = {
112
+ // Limit cosine similarity scoring to top-K MiniSearch keyword candidates
113
+ preFilterLimit: 50,
114
+
115
+ // Blend semantic and keyword scores (0.0 = pure keyword, 1.0 = pure semantic)
116
+ hybridWeight: 0.7,
117
+
118
+ // Max results returned per read
119
+ maxResults: 10,
120
+ };
121
+
122
+ const wiki = createWiki(db, {
123
+ config,
124
+ llmProvider: { /* ... */ },
125
+ });
126
+ ```
127
+
128
+ **Hybrid scoring blends:**
129
+ - `hybridWeight: 1.0` → pure semantic ranking among the candidates being scored; if `preFilterLimit` is set, semantic scoring is still limited to the top-K MiniSearch matches
130
+ - `hybridWeight: 0.5` → balanced semantic + keyword (50/50 blend)
131
+ - `hybridWeight: 0.0` → pure keyword ranking, skips `embed()` entirely (no LLM API cost)
132
+
133
+ **Pre-filtering optimization:**
134
+ 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).
135
+
18
136
  ## Usage
19
137
 
20
138
  ```typescript
@@ -64,6 +182,79 @@ export function UserProfile({ userId }: { userId: string }) {
64
182
  }
65
183
  ```
66
184
 
185
+ ## Component Lifecycle
186
+
187
+ ```mermaid
188
+ flowchart TD
189
+ A["<WikiProvider wiki={wiki}>"] --> B["App Components"]
190
+ B --> C{"Use Hook?"}
191
+ C -->|"useMemoryRead(entityId, query, options?)"| D["[Read Memory]"]
192
+ C -->|"useWikiWrite()"| E["[Write Memory]"]
193
+ C -->|"useWikiIngest()"| F["[Ingest Document]"]
194
+ C -->|"useWikiForget()"| G["[Delete Memory]"]
195
+ C -->|"useWikiMaintenance()"| H["[Run Jobs]"]
196
+ D --> I{"entityId, query, wiki,<br/>or ReadOptions changed?"}
197
+ I -->|"Yes"| J["Auto-refetch"]
198
+ I -->|"No"| K["Return cached data"]
199
+ J --> L["Trigger read()"]
200
+ L --> M["Embed query<br/>if embed available"]
201
+ M --> N["Phase 1: Score facts<br/>Phase 2: Fetch winners"]
202
+ N --> O["Update component state"]
203
+ O --> P["Re-render with data"]
204
+ E --> Q["Execute write()"]
205
+ F --> Q
206
+ G --> Q
207
+ H --> Q
208
+ Q --> R["Write completes"]
209
+ ```
210
+
211
+ **Data flow:**
212
+ 1. **Wrap app** with `<WikiProvider wiki={wiki}>` — provides wiki context
213
+ 2. **Use hooks** in components — access memory reactively
214
+ 3. **Read operations** auto-refetch when `entityId`, `query`, `wiki`, or `ReadOptions` values change; call `refetch()` to refresh manually
215
+ 4. **Write operations** (write, ingest, forget, maintenance) do not automatically re-trigger `useMemoryRead`; call `refetch()` after a write to refresh read results
216
+ 5. **Re-render** with new data flowing back to UI
217
+
218
+ ## Retrieval Engine Internals
219
+
220
+ ```mermaid
221
+ flowchart TD
222
+ A["read(entityId, query)"] --> B{hybridWeight = 0?}
223
+ B -->|Yes| C["MiniSearch only<br/>(skip embed)"]
224
+ B -->|No| D{embed available?}
225
+ D -->|No| C
226
+ D -->|Yes| F["Embed query"]
227
+ F -->|throws| E["onRetrievalFallback<br/>callback"]
228
+ E --> C
229
+ F -->|succeeds| G{preFilterLimit<br/>active?}
230
+ G -->|Yes| H["MiniSearch pre-filter<br/>top K candidates"]
231
+ H --> I["Phase 1: Cosine score<br/>top K candidates"]
232
+ G -->|No| J["Phase 1: Cosine score<br/>all facts"]
233
+ J --> K["Cache vectors<br/>in-memory<br/>(full scan only)"]
234
+ K --> L{hybridWeight = 1?}
235
+ I --> L
236
+ L -->|Yes| M["Pure semantic<br/>ranking"]
237
+ L -->|No| N["Hybrid blend:<br/>semantic + keyword<br/>via MiniSearch"]
238
+ M --> O["Phase 2: Fetch full rows<br/>top maxResults"]
239
+ N --> O
240
+ C --> P["MiniSearch ranking"]
241
+ P --> O
242
+ O --> R["Track access"]
243
+ R --> Q["Return MemoryBundle"]
244
+ ```
245
+
246
+ The flowchart shows:
247
+ 1. **Fast-path** when `hybridWeight = 0` (pure keyword, no embed cost)
248
+ 2. **Fallback chain** when embed unavailable (MiniSearch silently) or throws (`onRetrievalFallback` callback, then MiniSearch)
249
+ 3. **Pre-filtering** to limit cosine scoring to top-K keyword matches (O(N) → O(K))
250
+ 4. **Two-phase SELECT**: phase 1 scores all/filtered facts with minimal columns, phase 2 fetches full rows for winners
251
+ 5. **Hybrid scoring** to blend semantic and keyword rankings
252
+ 6. **Vector caching** on full scans only; reads with `preFilterLimit` active skip cache population
253
+
67
254
  ## License
68
255
 
69
256
  MIT
257
+
258
+ ---
259
+
260
+ Made with ❤️ by Equational Applications LLC. [https://equationalapplications.com/](https://equationalapplications.com/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equationalapplications/expo-llm-wiki",
3
- "version": "2.5.0",
3
+ "version": "3.0.0",
4
4
  "description": "Expo/React Native adapter for @equationalapplications/core-llm-wiki.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -28,8 +28,8 @@
28
28
  "registry": "https://registry.npmjs.org"
29
29
  },
30
30
  "dependencies": {
31
- "@equationalapplications/core-llm-wiki": "2.5.0",
32
- "@equationalapplications/react-llm-wiki": "2.5.0"
31
+ "@equationalapplications/core-llm-wiki": "3.0.0",
32
+ "@equationalapplications/react-llm-wiki": "3.0.0"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "expo-sqlite": "^14.0.0 || ^15.0.0 || ^55.0.0",