@danielzfliu/memory 1.0.2 → 2.1.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 CHANGED
@@ -1,266 +1,363 @@
1
- [![npm version](https://img.shields.io/npm/v/@danielzfliu/memory.svg)](https://www.npmjs.com/package/@danielzfliu/memory)
2
-
3
- # Memory
4
-
5
- A fully local Node.js library and REST API for storing, searching, and querying tagged text pieces using ChromaDB for vector storage and Ollama for embeddings + generation.
6
-
7
- Two ways to use Memory:
8
-
9
- - REST API Server — Clone the repo and run a standalone HTTP server with CRUD, semantic search, and RAG endpoints.
10
- - npm PackageInstall `@danielzfliu/memory` in your own project and use the classes directly, or embed the Express server in your app.
11
-
12
- ---
13
-
14
- ## Prerequisites
15
-
16
- - Node.js ≥ 18
17
- - Ollama running locally ([install](https://ollama.com))
18
- - ChromaDB server running locally
19
-
20
- Pull the required models:
21
- ```bash
22
- ollama pull nomic-embed-text-v2-moe
23
- ollama pull llama3.2
24
- ```
25
-
26
- ---
27
-
28
- ## Option A: REST API Server
29
-
30
- Use this option to run Memory as a standalone HTTP service.
31
-
32
- ### 1. Setup
33
-
34
- ```bash
35
- git clone https://github.com/DanielZFLiu/memory.git
36
- cd memory
37
- npm install
38
- ```
39
-
40
- ### 2. Start external services
41
-
42
- The repo includes convenience scripts for starting Ollama and ChromaDB:
43
-
44
- ```bash
45
- npm run ollama # start Ollama on default port 11434
46
- npm run ollama:port -- 11435 # start Ollama on a custom port
47
-
48
- npm run db # start ChromaDB on default port 8000
49
- npm run db:port -- 9000 # start ChromaDB on a custom port
50
- ```
51
-
52
- ### 3. Start the server
53
-
54
- ```bash
55
- npm run dev
56
- ```
57
-
58
- Server starts on `http://localhost:3000` by default (set `PORT` env var to change).
59
-
60
- ### API Endpoints
61
-
62
- #### Add a piece
63
- ```bash
64
- curl -X POST http://localhost:3000/pieces \
65
- -H "Content-Type: application/json" \
66
- -d '{"content": "TypeScript is a typed superset of JavaScript.", "tags": ["typescript", "programming"]}'
67
- ```
68
-
69
- #### Get a piece by ID
70
- ```bash
71
- curl http://localhost:3000/pieces/<id>
72
- ```
73
-
74
- #### Update a piece
75
- ```bash
76
- curl -X PUT http://localhost:3000/pieces/<id> \
77
- -H "Content-Type: application/json" \
78
- -d '{"content": "Updated content.", "tags": ["new-tag"]}'
79
- ```
80
-
81
- #### Delete a piece
82
- ```bash
83
- curl -X DELETE http://localhost:3000/pieces/<id>
84
- ```
85
-
86
- #### Semantic search
87
- ```bash
88
- curl -X POST http://localhost:3000/query \
89
- -H "Content-Type: application/json" \
90
- -d '{"query": "What is TypeScript?", "topK": 5}'
91
- ```
92
-
93
- With tag filtering:
94
- ```bash
95
- curl -X POST http://localhost:3000/query \
96
- -H "Content-Type: application/json" \
97
- -d '{"query": "What is TypeScript?", "tags": ["programming"], "topK": 5}'
98
- ```
99
-
100
- #### RAG query (retrieve + generate)
101
- ```bash
102
- curl -X POST http://localhost:3000/rag \
103
- -H "Content-Type: application/json" \
104
- -d '{"query": "Explain TypeScript", "tags": ["programming"], "topK": 5}'
105
- ```
106
-
107
- Returns:
108
- ```json
109
- {
110
- "answer": "Generated answer based on retrieved context...",
111
- "sources": [
112
- {
113
- "piece": { "id": "...", "content": "...", "tags": ["..."] },
114
- "score": 0.87
115
- }
116
- ]
117
- }
118
- ```
119
-
120
- ---
121
-
122
- ## Option B: npm Package
123
-
124
- Use this option to integrate Memory into your own Node.js/TypeScript project.
125
-
126
- ### 1. Install
127
-
128
- ```bash
129
- npm install @danielzfliu/memory
130
- ```
131
-
132
- ### 2. Start external services
133
-
134
- You are responsible for running Ollama and ChromaDB yourself:
135
-
136
- ```bash
137
- ollama serve # default port 11434
138
- chroma run --port 8000 # default port 8000
139
- ```
140
-
141
- **Windows note:** If `chroma` is not recognized, the `Scripts` directory may not be on your PATH. Either add it (e.g. `%APPDATA%\Python\Python3xx\Scripts`) or run the executable directly:
142
- ```powershell
143
- & "$env:APPDATA\Python\Python313\Scripts\chroma.exe" run --port 8000
144
- ```
145
-
146
- ### 3. Programmatic usage
147
-
148
- #### Using PieceStore and RagPipeline directly
149
-
150
- ```typescript
151
- import { PieceStore, RagPipeline, MemoryConfig } from "@danielzfliu/memory";
152
-
153
- async function main() {
154
- const config: MemoryConfig = {
155
- chromaUrl: "http://localhost:8000",
156
- ollamaUrl: "http://localhost:11434",
157
- embeddingModel: "nomic-embed-text-v2-moe",
158
- };
159
-
160
- // Store: CRUD + semantic search
161
- const store = new PieceStore(config);
162
- await store.init();
163
-
164
- await store.addPiece("TypeScript is a typed superset of JavaScript.", [
165
- "typescript",
166
- "programming",
167
- ]);
168
- await store.addPiece("Python is great for data science.", [
169
- "python",
170
- "data-science",
171
- ]);
172
-
173
- const results = await store.queryPieces("typed languages", { topK: 5 });
174
- console.log("results", results);
175
-
176
- const filtered = await store.queryPieces("typed languages", {
177
- tags: ["typescript"],
178
- topK: 5,
179
- });
180
- console.log("filtered", filtered);
181
-
182
- // RAG: retrieve relevant pieces generate an answer via Ollama
183
- const rag = new RagPipeline(store, config.ollamaUrl!, "llama3.2");
184
- const answer = await rag.query("What is TypeScript?", {
185
- tags: ["programming"],
186
- });
187
- console.log("answer", answer);
188
- }
189
-
190
- main().catch((err) => {
191
- console.error(err);
192
- process.exit(1);
193
- });
194
- ```
195
-
196
- #### Embedding the REST API in your own Express app
197
-
198
- `createServer` returns a configured Express app you can mount or extend:
199
-
200
- ```typescript
201
- import { createServer } from "@danielzfliu/memory";
202
-
203
- const app = createServer({
204
- chromaUrl: "http://localhost:8000",
205
- ollamaUrl: "http://localhost:11434",
206
- });
207
-
208
- app.listen(4000, () => console.log("Running on :4000"));
209
- ```
210
-
211
- ### Exports
212
-
213
- | Export | Description |
214
- |--------|-------------|
215
- | `PieceStore` | CRUD + semantic search over tagged text pieces |
216
- | `RagPipeline` | Retrieve-then-generate pipeline using `PieceStore` + Ollama |
217
- | `EmbeddingClient` | Low-level Ollama embedding wrapper |
218
- | `createServer` | Express app factory with all REST endpoints pre-configured |
219
- | `MemoryConfig` | Configuration interface (all fields optional with defaults) |
220
- | `DEFAULT_MEMORY_CONFIG` | The default values for `MemoryConfig` |
221
- | `Piece` | `{ id, content, tags }` |
222
- | `QueryOptions` | `{ tags?, topK? }` |
223
- | `QueryResult` | `{ piece, score }` |
224
- | `RagResult` | `{ answer, sources }` |
225
-
226
- ---
227
-
228
- ## Configuration (`MemoryConfig`)
229
-
230
- All fields are optional. Defaults are applied automatically.
231
-
232
- | Option | Default | Description |
233
- |--------|---------|-------------|
234
- | `chromaUrl` | `http://localhost:8000` | ChromaDB server URL |
235
- | `ollamaUrl` | `http://localhost:11434` | Ollama server URL |
236
- | `embeddingModel` | `nomic-embed-text-v2-moe` | Ollama model for embeddings |
237
- | `generationModel` | `llama3.2` | Ollama model for RAG generation (used by `createServer`) |
238
- | `collectionName` | `pieces` | ChromaDB collection name |
239
-
240
- > **Note:** `generationModel` is only used by `createServer`. When constructing `RagPipeline` directly, you pass the model name to its constructor.
241
-
242
- ## Testing
243
-
244
- ```bash
245
- npm test # run all tests
246
- npm run test:watch # watch mode
247
- npm run test:coverage # with coverage report
248
- ```
249
-
250
- ## Project Structure
251
-
252
- ```
253
- src/
254
- ├── types.ts # Interfaces (MemoryConfig, Piece, QueryResult, etc.)
255
- ├── embeddings.ts # Ollama embedding client
256
- ├── store.ts # PieceStore — CRUD + semantic search + tag filtering
257
- ├── rag.ts # RAG pipeline retrieve prompt → generate
258
- ├── server.ts # Express REST API (app factory)
259
- ├── main.ts # Server entry point (starts listening)
260
- └── index.ts # Library entry point (public exports)
261
- tests/
262
- ├── helpers/ # Shared test fixtures (in-memory ChromaDB mock, etc.)
263
- ├── unit/ # Unit tests (embeddings, store, rag)
264
- ├── manual/ # Manual api tests (used by ai agents with access to terminal)
265
- └── integration/ # API integration tests (supertest)
266
- ```
1
+ [![npm version](https://img.shields.io/npm/v/@danielzfliu/memory.svg)](https://www.npmjs.com/package/@danielzfliu/memory)
2
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-red.svg)](LICENSE)
3
+
4
+ # Memory
5
+
6
+ A fully local MCP server and Node.js library for storing, semantically searching, and querying tagged/titled text with ChromaDB (vector storage) and Ollama (embeddings and generation).
7
+
8
+ Three ways to use Memory:
9
+
10
+ - MCP ServerRun Memory as a Model Context Protocol server over stdio and expose memory tools to MCP-compatible clients.
11
+ - npm Package — Install `@danielzfliu/memory` in your own project and use the classes directly (store, embeddings, RAG, and MCP server class).
12
+ - REST API Server — Run the standalone HTTP server with CRUD, semantic search, and RAG endpoints.
13
+
14
+ ---
15
+
16
+ ## Prerequisites
17
+
18
+ - **Node.js** 18
19
+ - **Ollama** running locally
20
+ - **ChromaDB** server running locally
21
+
22
+ ### Setting up Ollama
23
+
24
+ Install Ollama ([install](https://ollama.com)) and pull the default models:
25
+
26
+ ```bash
27
+ ollama pull nomic-embed-text-v2-moe:latest
28
+ ollama pull gemma3:latest
29
+ ```
30
+
31
+ Then run:
32
+ ```bash
33
+ npm run ollama # start Ollama on default port 11434
34
+ npm run ollama:port -- 11435 # start Ollama on a custom port
35
+ ```
36
+
37
+ ### Setting up ChromaDB
38
+
39
+ **Option 1: Docker**
40
+
41
+ The repo includes a Docker Compose file that runs ChromaDB and stores its data in `./chroma/`.
42
+
43
+ ```bash
44
+ npm run docker:up # start ChromaDB on port 8000
45
+ npm run docker:logs # view logs
46
+ npm run docker:down # stop ChromaDB
47
+ ```
48
+
49
+ **Option 2: pip**
50
+
51
+ ```bash
52
+ pip install chromadb
53
+ chroma run --port 8000 # start ChromaDB on port 8000
54
+ ```
55
+
56
+ Note: You may need to add Python's Scripts folder to your PATH after installing.
57
+
58
+ ---
59
+
60
+ ## Option A: MCP Server
61
+
62
+ Use this option to run Memory as a standalone MCP server.
63
+
64
+ ### 1. Setup
65
+
66
+ ```bash
67
+ git clone https://github.com/DanielZFLiu/memory.git
68
+ cd memory
69
+ npm install
70
+ ```
71
+
72
+ ### 2. Build and run the MCP server
73
+
74
+ ```bash
75
+ npm run build
76
+ node ./dist/main.js
77
+ ```
78
+
79
+ Memory MCP communicates over stdio, so it does not bind an HTTP port.
80
+
81
+ ### MCP Client Configuration
82
+
83
+ #### Claude Desktop (example)
84
+
85
+ ```json
86
+ {
87
+ "mcpServers": {
88
+ "memory": {
89
+ "command": "npx",
90
+ "args": ["-y", "@danielzfliu/memory"]
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ If you are running from a local clone instead of npm:
97
+
98
+ ```json
99
+ {
100
+ "mcpServers": {
101
+ "memory": {
102
+ "command": "node",
103
+ "args": ["c:/path/to/memory/dist/main.js"]
104
+ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ ### MCP Tools
110
+
111
+ | Tool | Description |
112
+ |------|-------------|
113
+ | `add_piece` | Add a new piece with optional title and tags |
114
+ | `get_piece` | Retrieve a piece by id |
115
+ | `update_piece` | Update piece content, title, and/or tags (`title: null` clears title) |
116
+ | `delete_piece` | Delete a piece by id |
117
+ | `query_pieces` | Semantic search over content, plus title when present. Supports hybrid search (vector + keyword via RRF). |
118
+ | `rag_query` | Retrieve + generate answer with citations using content and title context. Supports hybrid search. |
119
+ | `list_collections` | List all collection names in the memory store |
120
+ | `delete_collection` | Delete an entire collection and all its pieces |
121
+
122
+ All piece-level tools accept an optional `collection` parameter to target a specific collection instead of the default. This allows multiple agents to use isolated memory stores.
123
+
124
+ ---
125
+
126
+ ## Option B: npm Package
127
+
128
+ Use this option to integrate Memory into your own Node.js/TypeScript project.
129
+
130
+ ### 1. Install
131
+
132
+ ```bash
133
+ npm install @danielzfliu/memory
134
+ ```
135
+
136
+ ### 2. Programmatic usage
137
+
138
+ #### Using PieceStore and RagPipeline directly
139
+
140
+ ```typescript
141
+ import { PieceStore, RagPipeline, MemoryConfig } from "@danielzfliu/memory";
142
+
143
+ async function main() {
144
+ const config: MemoryConfig = {
145
+ chromaUrl: "http://localhost:8000",
146
+ ollamaUrl: "http://localhost:11434",
147
+ embeddingModel: "nomic-embed-text-v2-moe:latest",
148
+ };
149
+
150
+ // Store: CRUD + semantic search
151
+ const store = new PieceStore(config);
152
+ await store.init();
153
+
154
+ await store.addPiece(
155
+ "TypeScript is a typed superset of JavaScript.",
156
+ ["typescript", "programming"],
157
+ "TypeScript overview",
158
+ );
159
+ await store.addPiece("Python is great for data science.", [
160
+ "python",
161
+ "data-science",
162
+ ]);
163
+
164
+ const results = await store.queryPieces("typed languages", { topK: 5 });
165
+ console.log("results", results);
166
+
167
+ const filtered = await store.queryPieces("typed languages", {
168
+ tags: ["typescript"],
169
+ topK: 5,
170
+ });
171
+ console.log("filtered", filtered);
172
+
173
+ // Hybrid search: combines vector similarity with keyword matching via RRF
174
+ const hybrid = await store.queryPieces("typed languages", {
175
+ topK: 5,
176
+ useHybridSearch: true,
177
+ });
178
+ console.log("hybrid", hybrid);
179
+
180
+ // RAG: retrieve relevant pieces → generate an answer via Ollama
181
+ const rag = new RagPipeline(store, config.ollamaUrl!, "gemma3:latest");
182
+ const answer = await rag.query("What is TypeScript?", {
183
+ tags: ["programming"],
184
+ });
185
+ console.log("answer", answer);
186
+ }
187
+
188
+ main().catch((err) => {
189
+ console.error(err);
190
+ process.exit(1);
191
+ });
192
+ ```
193
+
194
+ #### Embedding the REST API in your own Express app
195
+
196
+ `createServer` returns a configured Express app you can mount or extend:
197
+
198
+ ```typescript
199
+ import { createServer } from "@danielzfliu/memory";
200
+
201
+ const app = createServer({
202
+ chromaUrl: "http://localhost:8000",
203
+ ollamaUrl: "http://localhost:11434",
204
+ });
205
+
206
+ app.listen(4000, () => console.log("Running on :4000"));
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Option C: REST API Server
212
+
213
+ Use this option to run Memory as a standalone HTTP service.
214
+
215
+ ### 1. Setup
216
+
217
+ ```bash
218
+ git clone https://github.com/DanielZFLiu/memory.git
219
+ cd memory
220
+ npm install
221
+ ```
222
+
223
+ ### 2. Start the REST server
224
+
225
+ ```bash
226
+ npm run dev:http
227
+ ```
228
+
229
+ Server starts on `http://localhost:3000` by default (set `PORT` env var to change).
230
+
231
+ ### API Endpoints
232
+
233
+ #### Add a piece
234
+ ```bash
235
+ curl -X POST http://localhost:3000/pieces \
236
+ -H "Content-Type: application/json" \
237
+ -d '{"title": "TypeScript overview", "content": "TypeScript is a typed superset of JavaScript.", "tags": ["typescript", "programming"]}'
238
+ ```
239
+
240
+ With a specific collection:
241
+ ```bash
242
+ curl -X POST http://localhost:3000/pieces \
243
+ -H "Content-Type: application/json" \
244
+ -d '{"content": "Agent-specific memory.", "tags": ["agent"], "collection": "agent-alice"}'
245
+ ```
246
+
247
+ #### Get a piece by ID
248
+ ```bash
249
+ curl http://localhost:3000/pieces/<id>
250
+ curl http://localhost:3000/pieces/<id>?collection=agent-alice
251
+ ```
252
+
253
+ #### Update a piece
254
+ ```bash
255
+ curl -X PUT http://localhost:3000/pieces/<id> \
256
+ -H "Content-Type: application/json" \
257
+ -d '{"title": "Updated title", "content": "Updated content.", "tags": ["new-tag"]}'
258
+ ```
259
+
260
+ Set `title` to `null` to clear it.
261
+
262
+ #### Delete a piece
263
+ ```bash
264
+ curl -X DELETE http://localhost:3000/pieces/<id>
265
+ curl -X DELETE http://localhost:3000/pieces/<id>?collection=agent-alice
266
+ ```
267
+
268
+ #### Semantic search
269
+ ```bash
270
+ curl -X POST http://localhost:3000/query \
271
+ -H "Content-Type: application/json" \
272
+ -d '{"query": "What is TypeScript?", "topK": 5}'
273
+ ```
274
+
275
+ With tag filtering:
276
+ ```bash
277
+ curl -X POST http://localhost:3000/query \
278
+ -H "Content-Type: application/json" \
279
+ -d '{"query": "What is TypeScript?", "tags": ["programming"], "topK": 5}'
280
+ ```
281
+
282
+ With hybrid search (vector + keyword via Reciprocal Rank Fusion):
283
+ ```bash
284
+ curl -X POST http://localhost:3000/query \
285
+ -H "Content-Type: application/json" \
286
+ -d '{"query": "What is TypeScript?", "topK": 5, "useHybridSearch": true}'
287
+ ```
288
+
289
+ #### RAG query (retrieve + generate)
290
+ ```bash
291
+ curl -X POST http://localhost:3000/rag \
292
+ -H "Content-Type: application/json" \
293
+ -d '{"query": "Explain TypeScript", "tags": ["programming"], "topK": 5}'
294
+ ```
295
+
296
+ Returns:
297
+ ```json
298
+ {
299
+ "answer": "Generated answer based on retrieved context...",
300
+ "sources": [
301
+ {
302
+ "piece": { "id": "...", "title": "...", "content": "...", "tags": ["..."] },
303
+ "score": 0.87
304
+ }
305
+ ]
306
+ }
307
+ ```
308
+
309
+ #### List collections
310
+ ```bash
311
+ curl http://localhost:3000/collections
312
+ ```
313
+
314
+ #### Delete a collection
315
+ ```bash
316
+ curl -X DELETE http://localhost:3000/collections/agent-alice
317
+ ```
318
+
319
+ > **Multi-collection:** All piece and query endpoints accept an optional `collection` parameter (in the request body for POST/PUT, as a query string for GET/DELETE) to target a specific collection. Omitting it uses the default collection.
320
+
321
+ ---
322
+
323
+ ## Exports
324
+
325
+ | Export | Description |
326
+ |--------|-------------|
327
+ | `PieceStore` | CRUD + semantic search over tagged text pieces |
328
+ | `RagPipeline` | Retrieve-then-generate pipeline using `PieceStore` + Ollama |
329
+ | `EmbeddingClient` | Low-level Ollama embedding wrapper |
330
+ | `MemoryMcpServer` | MCP server class (stdio transport) exposing memory tools |
331
+ | `createServer` | Express app factory with all REST endpoints pre-configured |
332
+ | `MemoryConfig` | Configuration interface (all fields optional with defaults) |
333
+ | `DEFAULT_MEMORY_CONFIG` | The default values for `MemoryConfig` |
334
+ | `Piece` | `{ id, content, title?, tags }` |
335
+ | `QueryOptions` | `{ tags?, topK?, useHybridSearch? }` |
336
+ | `QueryResult` | `{ piece, score }` |
337
+ | `RagResult` | `{ answer, sources }` |
338
+
339
+ ---
340
+
341
+ ## Configuration (`MemoryConfig`)
342
+
343
+ All fields are optional. Defaults are applied automatically.
344
+
345
+ | Option | Default | Description |
346
+ |--------|---------|-------------|
347
+ | `chromaUrl` | `http://localhost:8000` | ChromaDB server URL |
348
+ | `ollamaUrl` | `http://localhost:11434` | Ollama server URL |
349
+ | `embeddingModel` | `nomic-embed-text-v2-moe:latest` | Ollama model for embeddings |
350
+ | `generationModel` | `gemma3:latest` | Ollama model for RAG generation |
351
+ | `collectionName` | `pieces` | ChromaDB collection name |
352
+
353
+ > **Note:** `generationModel` is used by `createServer` and `MemoryMcpServer`. When constructing `RagPipeline` directly, you pass the model name to its constructor.
354
+
355
+ Environment variables with the names above can override these defaults at runtime.
356
+
357
+ ## Testing
358
+
359
+ ```bash
360
+ npm test # run all tests
361
+ npm run test:watch # watch mode
362
+ npm run test:coverage # with coverage report
363
+ ```
@@ -0,0 +1,4 @@
1
+ import { MemoryConfig } from "./types";
2
+ export declare const DEFAULT_MEMORY_CONFIG: Required<MemoryConfig>;
3
+ export declare function resolveConfig(config?: MemoryConfig): Required<MemoryConfig>;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,YAAY,CAMxD,CAAC;AAqBF,wBAAgB,aAAa,CAAC,MAAM,GAAE,YAAiB,GAAG,QAAQ,CAAC,YAAY,CAAC,CAuB/E"}