@danielzfliu/memory 1.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/LICENSE +674 -0
- package/README.md +195 -0
- package/dist/embeddings.d.ts +8 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +28 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +9 -0
- package/dist/main.js.map +1 -0
- package/dist/rag.d.ts +10 -0
- package/dist/rag.d.ts.map +1 -0
- package/dist/rag.js +44 -0
- package/dist/rag.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +126 -0
- package/dist/server.js.map +1 -0
- package/dist/store.d.ts +16 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +132 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
- package/package_scripts/serve-chroma.js +17 -0
- package/package_scripts/serve-ollama.js +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Memory
|
|
2
|
+
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.
|
|
3
|
+
|
|
4
|
+
## Prerequisites
|
|
5
|
+
|
|
6
|
+
- **Node.js** ≥ 18
|
|
7
|
+
- **Ollama** running locally ([install](https://ollama.com))
|
|
8
|
+
- **ChromaDB** server running locally
|
|
9
|
+
|
|
10
|
+
### Start Ollama & pull models
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
ollama pull nomic-embed-text-v2-moe
|
|
14
|
+
ollama pull llama3.2
|
|
15
|
+
npm run ollama
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
or on a specific port:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm run ollama:port 11435
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Start ChromaDB
|
|
25
|
+
```bash
|
|
26
|
+
npm run db
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
or on a specific port:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm run db:port 9000
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**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:
|
|
36
|
+
```powershell
|
|
37
|
+
& "$env:APPDATA\Python\Python313\Scripts\chroma.exe" run --port 8000
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### REST API Server
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm run dev
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Server starts on `http://localhost:3000` by default (set `PORT` env var to change).
|
|
55
|
+
|
|
56
|
+
### API Endpoints
|
|
57
|
+
|
|
58
|
+
#### Add a piece
|
|
59
|
+
```bash
|
|
60
|
+
curl -X POST http://localhost:3000/pieces \
|
|
61
|
+
-H "Content-Type: application/json" \
|
|
62
|
+
-d '{"content": "TypeScript is a typed superset of JavaScript.", "tags": ["typescript", "programming"]}'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Get a piece by ID
|
|
66
|
+
```bash
|
|
67
|
+
curl http://localhost:3000/pieces/<id>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Update a piece
|
|
71
|
+
```bash
|
|
72
|
+
curl -X PUT http://localhost:3000/pieces/<id> \
|
|
73
|
+
-H "Content-Type: application/json" \
|
|
74
|
+
-d '{"content": "Updated content.", "tags": ["new-tag"]}'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### Delete a piece
|
|
78
|
+
```bash
|
|
79
|
+
curl -X DELETE http://localhost:3000/pieces/<id>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Semantic search
|
|
83
|
+
```bash
|
|
84
|
+
curl -X POST http://localhost:3000/query \
|
|
85
|
+
-H "Content-Type: application/json" \
|
|
86
|
+
-d '{"query": "What is TypeScript?", "topK": 5}'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
With tag filtering:
|
|
90
|
+
```bash
|
|
91
|
+
curl -X POST http://localhost:3000/query \
|
|
92
|
+
-H "Content-Type: application/json" \
|
|
93
|
+
-d '{"query": "What is TypeScript?", "tags": ["programming"], "topK": 5}'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### RAG query (retrieve + generate)
|
|
97
|
+
```bash
|
|
98
|
+
curl -X POST http://localhost:3000/rag \
|
|
99
|
+
-H "Content-Type: application/json" \
|
|
100
|
+
-d '{"query": "Explain TypeScript", "tags": ["programming"], "topK": 5}'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"answer": "Generated answer based on retrieved context...",
|
|
107
|
+
"sources": [
|
|
108
|
+
{
|
|
109
|
+
"piece": { "id": "...", "content": "...", "tags": ["..."] },
|
|
110
|
+
"score": 0.87
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Programmatic Usage (Library)
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { PieceStore, RagPipeline, MemoryConfig } from "memory";
|
|
120
|
+
|
|
121
|
+
async function main() {
|
|
122
|
+
const config: MemoryConfig = {
|
|
123
|
+
chromaUrl: "http://localhost:8000",
|
|
124
|
+
ollamaUrl: "http://localhost:11434",
|
|
125
|
+
embeddingModel: "nomic-embed-text-v2-moe",
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const store = new PieceStore(config);
|
|
129
|
+
await store.init();
|
|
130
|
+
|
|
131
|
+
await store.addPiece("TypeScript is a typed superset of JavaScript.", [
|
|
132
|
+
"typescript",
|
|
133
|
+
"programming",
|
|
134
|
+
]);
|
|
135
|
+
await store.addPiece("Python is great for data science.", [
|
|
136
|
+
"python",
|
|
137
|
+
"data-science",
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
const results = await store.queryPieces("typed languages", { topK: 5 });
|
|
141
|
+
console.log("results", results);
|
|
142
|
+
|
|
143
|
+
const filtered = await store.queryPieces("typed languages", {
|
|
144
|
+
tags: ["typescript"],
|
|
145
|
+
topK: 5,
|
|
146
|
+
});
|
|
147
|
+
console.log("filtered", filtered);
|
|
148
|
+
|
|
149
|
+
const rag = new RagPipeline(store, "http://localhost:11434", "llama3.2");
|
|
150
|
+
const answer = await rag.query("What is TypeScript?", {
|
|
151
|
+
tags: ["programming"],
|
|
152
|
+
});
|
|
153
|
+
console.log("answer", answer);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
main().catch((err) => {
|
|
157
|
+
console.error(err);
|
|
158
|
+
process.exit(1);
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Configuration (`MemoryConfig`)
|
|
163
|
+
|
|
164
|
+
| Option | Default | Description |
|
|
165
|
+
|--------|---------|-------------|
|
|
166
|
+
| `chromaUrl` | `http://localhost:8000` | ChromaDB server URL |
|
|
167
|
+
| `ollamaUrl` | `http://localhost:11434` | Ollama server URL |
|
|
168
|
+
| `embeddingModel` | `nomic-embed-text-v2-moe` | Ollama model for embeddings |
|
|
169
|
+
| `generationModel` | `llama3.2` | Ollama model for RAG generation |
|
|
170
|
+
| `collectionName` | `pieces` | ChromaDB collection name |
|
|
171
|
+
|
|
172
|
+
## Testing
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
npm test # run all tests
|
|
176
|
+
npm run test:watch # watch mode
|
|
177
|
+
npm run test:coverage # with coverage report
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Project Structure
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
src/
|
|
184
|
+
├── types.ts # Interfaces (MemoryConfig, Piece, QueryResult, etc.)
|
|
185
|
+
├── embeddings.ts # Ollama embedding client
|
|
186
|
+
├── store.ts # PieceStore — CRUD + semantic search + tag filtering
|
|
187
|
+
├── rag.ts # RAG pipeline — retrieve → prompt → generate
|
|
188
|
+
├── server.ts # Express REST API (app factory)
|
|
189
|
+
├── main.ts # Server entry point (starts listening)
|
|
190
|
+
└── index.ts # Library entry point (public exports)
|
|
191
|
+
tests/
|
|
192
|
+
├── helpers/ # Shared test fixtures (in-memory ChromaDB mock, etc.)
|
|
193
|
+
├── unit/ # Unit tests (embeddings, store, rag)
|
|
194
|
+
└── integration/ # API integration tests (supertest)
|
|
195
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class EmbeddingClient {
|
|
2
|
+
private readonly client;
|
|
3
|
+
private readonly model;
|
|
4
|
+
constructor(ollamaUrl: string, model: string);
|
|
5
|
+
embed(text: string): Promise<number[]>;
|
|
6
|
+
embedBatch(texts: string[]): Promise<number[][]>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=embeddings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAEA,qBAAa,eAAe;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAKtC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQtC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;CAOzD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EmbeddingClient = void 0;
|
|
4
|
+
const ollama_1 = require("ollama");
|
|
5
|
+
class EmbeddingClient {
|
|
6
|
+
client;
|
|
7
|
+
model;
|
|
8
|
+
constructor(ollamaUrl, model) {
|
|
9
|
+
this.client = new ollama_1.Ollama({ host: ollamaUrl });
|
|
10
|
+
this.model = model;
|
|
11
|
+
}
|
|
12
|
+
async embed(text) {
|
|
13
|
+
const response = await this.client.embed({
|
|
14
|
+
model: this.model,
|
|
15
|
+
input: text,
|
|
16
|
+
});
|
|
17
|
+
return response.embeddings[0];
|
|
18
|
+
}
|
|
19
|
+
async embedBatch(texts) {
|
|
20
|
+
const response = await this.client.embed({
|
|
21
|
+
model: this.model,
|
|
22
|
+
input: texts,
|
|
23
|
+
});
|
|
24
|
+
return response.embeddings;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.EmbeddingClient = EmbeddingClient;
|
|
28
|
+
//# sourceMappingURL=embeddings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":";;;AAAA,mCAAgC;AAEhC,MAAa,eAAe;IACP,MAAM,CAAS;IACf,KAAK,CAAS;IAE/B,YAAY,SAAiB,EAAE,KAAa;QACxC,IAAI,CAAC,MAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACpB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YACrC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI;SACd,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAe;QAC5B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YACrC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,KAAK;SACf,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,UAAU,CAAC;IAC/B,CAAC;CACJ;AAxBD,0CAwBC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { PieceStore } from "./store";
|
|
2
|
+
export { RagPipeline } from "./rag";
|
|
3
|
+
export { EmbeddingClient } from "./embeddings";
|
|
4
|
+
export { createServer } from "./server";
|
|
5
|
+
export { Piece, MemoryConfig, DEFAULT_MEMORY_CONFIG, QueryOptions, QueryResult, RagResult, } from "./types";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EACH,KAAK,EACL,YAAY,EACZ,qBAAqB,EACrB,YAAY,EACZ,WAAW,EACX,SAAS,GACZ,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_MEMORY_CONFIG = exports.createServer = exports.EmbeddingClient = exports.RagPipeline = exports.PieceStore = void 0;
|
|
4
|
+
var store_1 = require("./store");
|
|
5
|
+
Object.defineProperty(exports, "PieceStore", { enumerable: true, get: function () { return store_1.PieceStore; } });
|
|
6
|
+
var rag_1 = require("./rag");
|
|
7
|
+
Object.defineProperty(exports, "RagPipeline", { enumerable: true, get: function () { return rag_1.RagPipeline; } });
|
|
8
|
+
var embeddings_1 = require("./embeddings");
|
|
9
|
+
Object.defineProperty(exports, "EmbeddingClient", { enumerable: true, get: function () { return embeddings_1.EmbeddingClient; } });
|
|
10
|
+
var server_1 = require("./server");
|
|
11
|
+
Object.defineProperty(exports, "createServer", { enumerable: true, get: function () { return server_1.createServer; } });
|
|
12
|
+
var types_1 = require("./types");
|
|
13
|
+
Object.defineProperty(exports, "DEFAULT_MEMORY_CONFIG", { enumerable: true, get: function () { return types_1.DEFAULT_MEMORY_CONFIG; } });
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iCAAqC;AAA5B,mGAAA,UAAU,OAAA;AACnB,6BAAoC;AAA3B,kGAAA,WAAW,OAAA;AACpB,2CAA+C;AAAtC,6GAAA,eAAe,OAAA;AACxB,mCAAwC;AAA/B,sGAAA,YAAY,OAAA;AACrB,iCAOiB;AAJb,8GAAA,qBAAqB,OAAA"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const server_1 = require("./server");
|
|
4
|
+
const PORT = process.env.PORT ?? 3000;
|
|
5
|
+
const app = (0, server_1.createServer)();
|
|
6
|
+
app.listen(PORT, () => {
|
|
7
|
+
console.log(`Memory RAG server running on http://localhost:${PORT}`);
|
|
8
|
+
});
|
|
9
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,qCAAwC;AAExC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AACtC,MAAM,GAAG,GAAG,IAAA,qBAAY,GAAE,CAAC;AAC3B,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IAClB,OAAO,CAAC,GAAG,CAAC,iDAAiD,IAAI,EAAE,CAAC,CAAC;AACzE,CAAC,CAAC,CAAC"}
|
package/dist/rag.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PieceStore } from "./store";
|
|
2
|
+
import { QueryOptions, RagResult } from "./types";
|
|
3
|
+
export declare class RagPipeline {
|
|
4
|
+
private readonly store;
|
|
5
|
+
private readonly ollama;
|
|
6
|
+
private readonly model;
|
|
7
|
+
constructor(store: PieceStore, ollamaUrl: string, model: string);
|
|
8
|
+
query(query: string, options?: QueryOptions): Promise<RagResult>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=rag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rag.d.ts","sourceRoot":"","sources":["../src/rag.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAElD,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAMzD,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,SAAS,CAAC;CAuC7E"}
|
package/dist/rag.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RagPipeline = void 0;
|
|
4
|
+
const ollama_1 = require("ollama");
|
|
5
|
+
class RagPipeline {
|
|
6
|
+
store;
|
|
7
|
+
ollama;
|
|
8
|
+
model;
|
|
9
|
+
constructor(store, ollamaUrl, model) {
|
|
10
|
+
this.store = store;
|
|
11
|
+
this.ollama = new ollama_1.Ollama({ host: ollamaUrl });
|
|
12
|
+
this.model = model;
|
|
13
|
+
}
|
|
14
|
+
async query(query, options = {}) {
|
|
15
|
+
const sources = await this.store.queryPieces(query, options);
|
|
16
|
+
if (sources.length === 0) {
|
|
17
|
+
return {
|
|
18
|
+
answer: "I don't have enough context to answer this question. " +
|
|
19
|
+
"No relevant pieces were found in the knowledge base.",
|
|
20
|
+
sources: [],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const contextBlock = sources
|
|
24
|
+
.map((s, i) => `[${i + 1}] (tags: ${s.piece.tags.join(", ")})\n${s.piece.content}`)
|
|
25
|
+
.join("\n\n");
|
|
26
|
+
const systemPrompt = "You are a helpful assistant. Answer the user's question based on the provided context. " +
|
|
27
|
+
"If the context does not contain enough information, say so. " +
|
|
28
|
+
"Cite sources by their number when relevant.";
|
|
29
|
+
const userPrompt = `Context:\n${contextBlock}\n\nQuestion: ${query}`;
|
|
30
|
+
const response = await this.ollama.chat({
|
|
31
|
+
model: this.model,
|
|
32
|
+
messages: [
|
|
33
|
+
{ role: "system", content: systemPrompt },
|
|
34
|
+
{ role: "user", content: userPrompt },
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
answer: response.message.content,
|
|
39
|
+
sources,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.RagPipeline = RagPipeline;
|
|
44
|
+
//# sourceMappingURL=rag.js.map
|
package/dist/rag.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rag.js","sourceRoot":"","sources":["../src/rag.ts"],"names":[],"mappings":";;;AAAA,mCAAgC;AAIhC,MAAa,WAAW;IACH,KAAK,CAAa;IAClB,MAAM,CAAS;IACf,KAAK,CAAS;IAE/B,YAAY,KAAiB,EAAE,SAAiB,EAAE,KAAa;QAC3D,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAa,EAAE,UAAwB,EAAE;QACjD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAE7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO;gBACH,MAAM,EACF,uDAAuD;oBACvD,sDAAsD;gBAC1D,OAAO,EAAE,EAAE;aACd,CAAC;QACN,CAAC;QAED,MAAM,YAAY,GAAG,OAAO;aACvB,GAAG,CACA,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACL,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAC1E;aACA,IAAI,CAAC,MAAM,CAAC,CAAC;QAElB,MAAM,YAAY,GACd,yFAAyF;YACzF,8DAA8D;YAC9D,6CAA6C,CAAC;QAElD,MAAM,UAAU,GAAG,aAAa,YAAY,iBAAiB,KAAK,EAAE,CAAC;QAErE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YACpC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE;gBACN,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE;gBACzC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE;aACxC;SACJ,CAAC,CAAC;QAEH,OAAO;YACH,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO;YAChC,OAAO;SACV,CAAC;IACN,CAAC;CACJ;AAlDD,kCAkDC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAyB,MAAM,SAAS,CAAC;AAE9D,wBAAgB,YAAY,CAAC,MAAM,GAAE,YAAiB,+CAwHrD"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createServer = createServer;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const store_1 = require("./store");
|
|
9
|
+
const rag_1 = require("./rag");
|
|
10
|
+
const types_1 = require("./types");
|
|
11
|
+
function createServer(config = {}) {
|
|
12
|
+
const resolvedConfig = { ...types_1.DEFAULT_MEMORY_CONFIG, ...config };
|
|
13
|
+
const app = (0, express_1.default)();
|
|
14
|
+
app.use(express_1.default.json());
|
|
15
|
+
const store = new store_1.PieceStore(resolvedConfig);
|
|
16
|
+
const rag = new rag_1.RagPipeline(store, resolvedConfig.ollamaUrl, resolvedConfig.generationModel);
|
|
17
|
+
// Middleware to ensure store is initialized (uses a cached promise to
|
|
18
|
+
// avoid duplicate init() calls when concurrent requests arrive early)
|
|
19
|
+
let initPromise = null;
|
|
20
|
+
app.use(async (_req, res, next) => {
|
|
21
|
+
if (!initPromise) {
|
|
22
|
+
initPromise = store.init();
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
await initPromise;
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
initPromise = null; // allow retry on next request
|
|
29
|
+
res.status(503).json({
|
|
30
|
+
error: "Failed to connect to ChromaDB",
|
|
31
|
+
details: String(err),
|
|
32
|
+
});
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
next();
|
|
36
|
+
});
|
|
37
|
+
// POST /pieces — Add a piece
|
|
38
|
+
app.post("/pieces", async (req, res) => {
|
|
39
|
+
try {
|
|
40
|
+
const { content, tags } = req.body;
|
|
41
|
+
if (!content || typeof content !== "string") {
|
|
42
|
+
res.status(400).json({ error: "content (string) is required" });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const piece = await store.addPiece(content, tags ?? []);
|
|
46
|
+
res.status(201).json(piece);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
res.status(500).json({ error: String(err) });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// GET /pieces/:id — Get a piece by ID
|
|
53
|
+
app.get("/pieces/:id", async (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const { id } = req.params;
|
|
56
|
+
const piece = await store.getPiece(id);
|
|
57
|
+
if (!piece) {
|
|
58
|
+
res.status(404).json({ error: "Piece not found" });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
res.json(piece);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
res.status(500).json({ error: String(err) });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
// PUT /pieces/:id — Update a piece
|
|
68
|
+
app.put("/pieces/:id", async (req, res) => {
|
|
69
|
+
try {
|
|
70
|
+
const { id } = req.params;
|
|
71
|
+
const { content, tags } = req.body;
|
|
72
|
+
const piece = await store.updatePiece(id, content, tags);
|
|
73
|
+
if (!piece) {
|
|
74
|
+
res.status(404).json({ error: "Piece not found" });
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
res.json(piece);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
res.status(500).json({ error: String(err) });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
// DELETE /pieces/:id — Delete a piece
|
|
84
|
+
app.delete("/pieces/:id", async (req, res) => {
|
|
85
|
+
try {
|
|
86
|
+
const { id } = req.params;
|
|
87
|
+
await store.deletePiece(id);
|
|
88
|
+
res.status(204).send();
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
res.status(500).json({ error: String(err) });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// POST /query — Semantic search
|
|
95
|
+
app.post("/query", async (req, res) => {
|
|
96
|
+
try {
|
|
97
|
+
const { query, tags, topK } = req.body;
|
|
98
|
+
if (!query || typeof query !== "string") {
|
|
99
|
+
res.status(400).json({ error: "query (string) is required" });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const results = await store.queryPieces(query, { tags, topK });
|
|
103
|
+
res.json(results);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
res.status(500).json({ error: String(err) });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
// POST /rag — Full RAG query
|
|
110
|
+
app.post("/rag", async (req, res) => {
|
|
111
|
+
try {
|
|
112
|
+
const { query, tags, topK } = req.body;
|
|
113
|
+
if (!query || typeof query !== "string") {
|
|
114
|
+
res.status(400).json({ error: "query (string) is required" });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const result = await rag.query(query, { tags, topK });
|
|
118
|
+
res.json(result);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
res.status(500).json({ error: String(err) });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
return app;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Piece, MemoryConfig, QueryOptions, QueryResult } from "./types";
|
|
2
|
+
export declare class PieceStore {
|
|
3
|
+
private readonly chromaClient;
|
|
4
|
+
private readonly embeddingClient;
|
|
5
|
+
private collection;
|
|
6
|
+
private readonly config;
|
|
7
|
+
constructor(config?: MemoryConfig);
|
|
8
|
+
init(): Promise<void>;
|
|
9
|
+
private getCollection;
|
|
10
|
+
addPiece(content: string, tags: string[]): Promise<Piece>;
|
|
11
|
+
getPiece(id: string): Promise<Piece | null>;
|
|
12
|
+
deletePiece(id: string): Promise<void>;
|
|
13
|
+
updatePiece(id: string, content?: string, tags?: string[]): Promise<Piece | null>;
|
|
14
|
+
queryPieces(query: string, options?: QueryOptions): Promise<QueryResult[]>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,KAAK,EACL,YAAY,EAEZ,YAAY,EACZ,WAAW,EACd,MAAM,SAAS,CAAC;AAYjB,qBAAa,UAAU;IACnB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;gBAEpC,MAAM,GAAE,YAAiB;IAS/B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B,OAAO,CAAC,aAAa;IAOf,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;IAezD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAgB3C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtC,WAAW,CACb,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IA8BlB,WAAW,CACb,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,YAAiB,GAC3B,OAAO,CAAC,WAAW,EAAE,CAAC;CAiD5B"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PieceStore = void 0;
|
|
4
|
+
const chromadb_1 = require("chromadb");
|
|
5
|
+
const uuid_1 = require("uuid");
|
|
6
|
+
const embeddings_1 = require("./embeddings");
|
|
7
|
+
const types_1 = require("./types");
|
|
8
|
+
function toChromaMetadata(tags) {
|
|
9
|
+
return { tags };
|
|
10
|
+
}
|
|
11
|
+
function parseTags(metadata) {
|
|
12
|
+
return metadata?.tags ?? [];
|
|
13
|
+
}
|
|
14
|
+
class PieceStore {
|
|
15
|
+
chromaClient;
|
|
16
|
+
embeddingClient;
|
|
17
|
+
collection = null;
|
|
18
|
+
config;
|
|
19
|
+
constructor(config = {}) {
|
|
20
|
+
this.config = { ...types_1.DEFAULT_MEMORY_CONFIG, ...config };
|
|
21
|
+
this.chromaClient = new chromadb_1.ChromaClient({ path: this.config.chromaUrl });
|
|
22
|
+
this.embeddingClient = new embeddings_1.EmbeddingClient(this.config.ollamaUrl, this.config.embeddingModel);
|
|
23
|
+
}
|
|
24
|
+
async init() {
|
|
25
|
+
this.collection = await this.chromaClient.getOrCreateCollection({
|
|
26
|
+
name: this.config.collectionName,
|
|
27
|
+
metadata: { "hnsw:space": "cosine" },
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
getCollection() {
|
|
31
|
+
if (!this.collection) {
|
|
32
|
+
throw new Error("PieceStore not initialized. Call init() first.");
|
|
33
|
+
}
|
|
34
|
+
return this.collection;
|
|
35
|
+
}
|
|
36
|
+
async addPiece(content, tags) {
|
|
37
|
+
const collection = this.getCollection();
|
|
38
|
+
const id = (0, uuid_1.v4)();
|
|
39
|
+
const embedding = await this.embeddingClient.embed(content);
|
|
40
|
+
await collection.add({
|
|
41
|
+
ids: [id],
|
|
42
|
+
embeddings: [embedding],
|
|
43
|
+
documents: [content],
|
|
44
|
+
metadatas: [toChromaMetadata(tags)],
|
|
45
|
+
});
|
|
46
|
+
return { id, content, tags };
|
|
47
|
+
}
|
|
48
|
+
async getPiece(id) {
|
|
49
|
+
const collection = this.getCollection();
|
|
50
|
+
const result = await collection.get({
|
|
51
|
+
ids: [id],
|
|
52
|
+
include: [chromadb_1.IncludeEnum.Documents, chromadb_1.IncludeEnum.Metadatas],
|
|
53
|
+
});
|
|
54
|
+
if (!result.ids.length)
|
|
55
|
+
return null;
|
|
56
|
+
return {
|
|
57
|
+
id: result.ids[0],
|
|
58
|
+
content: result.documents[0] ?? "",
|
|
59
|
+
tags: parseTags(result.metadatas[0]),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async deletePiece(id) {
|
|
63
|
+
const collection = this.getCollection();
|
|
64
|
+
await collection.delete({ ids: [id] });
|
|
65
|
+
}
|
|
66
|
+
async updatePiece(id, content, tags) {
|
|
67
|
+
const collection = this.getCollection();
|
|
68
|
+
const existing = await this.getPiece(id);
|
|
69
|
+
if (!existing)
|
|
70
|
+
return null;
|
|
71
|
+
const newContent = content ?? existing.content;
|
|
72
|
+
const newTags = tags ?? existing.tags;
|
|
73
|
+
const updateData = {
|
|
74
|
+
ids: [id],
|
|
75
|
+
metadatas: [toChromaMetadata(newTags)],
|
|
76
|
+
};
|
|
77
|
+
if (content !== undefined) {
|
|
78
|
+
updateData.documents = [newContent];
|
|
79
|
+
updateData.embeddings = [
|
|
80
|
+
await this.embeddingClient.embed(newContent),
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
await collection.update(updateData);
|
|
84
|
+
return { id, content: newContent, tags: newTags };
|
|
85
|
+
}
|
|
86
|
+
async queryPieces(query, options = {}) {
|
|
87
|
+
const collection = this.getCollection();
|
|
88
|
+
const { tags, topK = 10 } = options;
|
|
89
|
+
const queryEmbedding = await this.embeddingClient.embed(query);
|
|
90
|
+
let whereClause;
|
|
91
|
+
if (tags && tags.length > 0) {
|
|
92
|
+
if (tags.length === 1) {
|
|
93
|
+
whereClause = { tags: { $contains: tags[0] } };
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
whereClause = {
|
|
97
|
+
$and: tags.map((tag) => ({
|
|
98
|
+
tags: { $contains: tag },
|
|
99
|
+
})),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const results = await collection.query({
|
|
104
|
+
queryEmbeddings: [queryEmbedding],
|
|
105
|
+
nResults: topK,
|
|
106
|
+
where: whereClause,
|
|
107
|
+
include: [
|
|
108
|
+
chromadb_1.IncludeEnum.Documents,
|
|
109
|
+
chromadb_1.IncludeEnum.Metadatas,
|
|
110
|
+
chromadb_1.IncludeEnum.Distances,
|
|
111
|
+
],
|
|
112
|
+
});
|
|
113
|
+
const queryResults = [];
|
|
114
|
+
const ids = results.ids[0] ?? [];
|
|
115
|
+
const documents = results.documents[0] ?? [];
|
|
116
|
+
const metadatas = results.metadatas[0] ?? [];
|
|
117
|
+
const distances = results.distances?.[0] ?? [];
|
|
118
|
+
for (let i = 0; i < ids.length; i++) {
|
|
119
|
+
queryResults.push({
|
|
120
|
+
piece: {
|
|
121
|
+
id: ids[i],
|
|
122
|
+
content: documents[i] ?? "",
|
|
123
|
+
tags: parseTags(metadatas[i]),
|
|
124
|
+
},
|
|
125
|
+
score: 1 - (distances[i] ?? 0), // cosine distance → similarity
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return queryResults;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
exports.PieceStore = PieceStore;
|
|
132
|
+
//# sourceMappingURL=store.js.map
|