mnemocyte 0.2.0 → 0.4.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 +292 -27
- package/dist/embedders/openai.d.mts +1 -1
- package/dist/embedders/openai.d.mts.map +1 -1
- package/dist/embedders/openai.mjs +24 -9
- package/dist/embedders/openai.mjs.map +1 -1
- package/dist/{errors-BG3-mNur.mjs → errors-B_7ZdjEO.mjs} +4 -3
- package/dist/errors-B_7ZdjEO.mjs.map +1 -0
- package/dist/index.d.mts +11 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1724 -1092
- package/dist/index.mjs.map +1 -1
- package/dist/{types-DXEGcc47.d.mts → types-DH0inEay.d.mts} +166 -52
- package/dist/types-DH0inEay.d.mts.map +1 -0
- package/migrations/0000_initial.sql.template +2 -1
- package/migrations/0002_add_embedding_model.sql +15 -0
- package/migrations/meta/0002_snapshot.json +315 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +4 -3
- package/dist/errors-BG3-mNur.mjs.map +0 -1
- package/dist/types-DXEGcc47.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ logs, and duplicate-consolidation tools on top of your existing infrastructure.
|
|
|
16
16
|
- Hybrid recall with vector similarity, lexical matching, recency, confidence,
|
|
17
17
|
access count, and importance.
|
|
18
18
|
- Prompt-ready context output in Markdown, plain text, or XML.
|
|
19
|
-
- Provider retries,
|
|
19
|
+
- Provider retries, per-attempt timeouts, and caller `AbortSignal` cancellation.
|
|
20
20
|
- Pruning, duplicate detection, audit logs, and experimental consolidation.
|
|
21
21
|
|
|
22
22
|
Mnemocyte is ESM-only. Use `import`, not CommonJS `require`.
|
|
@@ -66,10 +66,16 @@ await client.close();
|
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
When `databaseUrl` is omitted, Mnemocyte uses the in-memory backend.
|
|
69
|
+
That backend is intended for development, tests, and prototyping. For
|
|
70
|
+
persistent or larger workloads, use Postgres.
|
|
69
71
|
|
|
70
72
|
## Embedder
|
|
71
73
|
|
|
72
74
|
Mnemocyte accepts any embedder that returns one vector per input text.
|
|
75
|
+
Each vector must match `embedder.dimensions`, contain only finite numbers, and
|
|
76
|
+
have at least one nonzero component. Invalid provider output, including an
|
|
77
|
+
exact zero-norm vector, fails with `MnemocyteError` code `"EMBEDDING"` before
|
|
78
|
+
storage or vector comparison. Tiny nonzero vectors remain valid.
|
|
73
79
|
|
|
74
80
|
```ts
|
|
75
81
|
const client = createMnemocyte({
|
|
@@ -99,7 +105,11 @@ const embedder = openaiEmbedder({
|
|
|
99
105
|
explicitly when you use a different key source. The helper uses `fetch`
|
|
100
106
|
directly and does not depend on the OpenAI SDK; plain `import "mnemocyte"` also
|
|
101
107
|
stays provider-free. The direct subpath `mnemocyte/embedders/openai` is also
|
|
102
|
-
supported for consumers that prefer provider-specific imports.
|
|
108
|
+
supported for consumers that prefer provider-specific imports. Provider
|
|
109
|
+
responses must contain exactly one uniquely indexed array embedding per input;
|
|
110
|
+
malformed response shapes fail with `MnemocyteError` code `"EMBEDDING"`.
|
|
111
|
+
Dimension and numeric-component validation remains at the shared embedder
|
|
112
|
+
boundary.
|
|
103
113
|
|
|
104
114
|
## Postgres
|
|
105
115
|
|
|
@@ -113,11 +123,16 @@ const client = createMnemocyte({
|
|
|
113
123
|
});
|
|
114
124
|
```
|
|
115
125
|
|
|
126
|
+
`databaseUrl` must use the `postgres:` or `postgresql:` protocol. Other URL
|
|
127
|
+
protocols reject synchronously with `MnemocyteError` code `"CONFIG"`; detailed
|
|
128
|
+
host, database, and credential validation remains the driver's responsibility.
|
|
129
|
+
|
|
116
130
|
The package includes:
|
|
117
131
|
|
|
118
132
|
```txt
|
|
119
133
|
migrations/0000_initial.sql
|
|
120
134
|
migrations/0001_add_mnemocyte_meta.sql
|
|
135
|
+
migrations/0002_add_embedding_model.sql
|
|
121
136
|
migrations/0000_initial.sql.template
|
|
122
137
|
migrations/render-initial.mjs
|
|
123
138
|
```
|
|
@@ -128,16 +143,19 @@ order:
|
|
|
128
143
|
```txt
|
|
129
144
|
migrations/0000_initial.sql
|
|
130
145
|
migrations/0001_add_mnemocyte_meta.sql
|
|
146
|
+
migrations/0002_add_embedding_model.sql
|
|
131
147
|
```
|
|
132
148
|
|
|
133
149
|
For an existing 0.1.x Postgres install, apply
|
|
134
|
-
`migrations/0001_add_mnemocyte_meta.sql`
|
|
135
|
-
|
|
150
|
+
`migrations/0001_add_mnemocyte_meta.sql` and then
|
|
151
|
+
`migrations/0002_add_embedding_model.sql`. Existing 0.2.x or 0.3.x installs
|
|
152
|
+
only need `0002_add_embedding_model.sql`.
|
|
136
153
|
|
|
137
154
|
For a different embedding dimension on a fresh installation, render an explicit
|
|
138
155
|
initial migration from the template and apply that rendered file instead of the
|
|
139
156
|
default `0000_initial.sql`. The rendered initial migration includes the
|
|
140
|
-
matching `mnemocyte_meta`
|
|
157
|
+
matching `mnemocyte_meta` dimensions and the installation-model column, so it
|
|
158
|
+
does not also need `0001` or `0002`.
|
|
141
159
|
|
|
142
160
|
```bash
|
|
143
161
|
node node_modules/mnemocyte/migrations/render-initial.mjs --dimensions 768 --out migrations/0000_initial.768.sql
|
|
@@ -149,14 +167,41 @@ Inside this repository, the equivalent development shortcut is:
|
|
|
149
167
|
pnpm migration:render -- --dimensions 768 --out migrations/0000_initial.768.sql
|
|
150
168
|
```
|
|
151
169
|
|
|
152
|
-
The Postgres backend supports one embedding dimension per
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
`
|
|
170
|
+
The Postgres backend supports one embedding model and dimension per
|
|
171
|
+
installation. Before writes, recall, or duplicate scans, Mnemocyte reads
|
|
172
|
+
`mnemocyte_meta` and validates both values before calling the embedder or
|
|
173
|
+
comparing vectors. An empty installation atomically records the configured
|
|
174
|
+
model on its first embedding-dependent operation. Migration `0002` records the
|
|
175
|
+
single model already present in historical rows when that value is
|
|
176
|
+
unambiguous.
|
|
177
|
+
|
|
178
|
+
A configured model or dimension mismatch throws `MnemocyteError` code
|
|
179
|
+
`"CONFIG"`. If historical rows contain multiple model values, the installation
|
|
180
|
+
model remains unset and embedding-dependent operations throw `"MIGRATION"`
|
|
181
|
+
until an operator re-embeds or removes the mixed rows and explicitly records
|
|
182
|
+
the intended installation model. Non-embedding recovery operations such as
|
|
183
|
+
`stats`, `forget`, and pruning remain available.
|
|
184
|
+
|
|
185
|
+
Inspect mixed history before repairing it:
|
|
186
|
+
|
|
187
|
+
```sql
|
|
188
|
+
SELECT embedding_model, count(*)
|
|
189
|
+
FROM mnemocyte_memories
|
|
190
|
+
GROUP BY embedding_model;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Only after every retained row belongs to the intended vector space, record that
|
|
194
|
+
model explicitly:
|
|
195
|
+
|
|
196
|
+
```sql
|
|
197
|
+
UPDATE mnemocyte_meta
|
|
198
|
+
SET embedding_model = 'your-model-id'
|
|
199
|
+
WHERE key = 'installation';
|
|
200
|
+
```
|
|
157
201
|
|
|
158
|
-
Use the embedding dimension documented for your
|
|
159
|
-
matching schema, and keep one
|
|
202
|
+
Use the model identifier and embedding dimension documented for your embedder,
|
|
203
|
+
render or apply a matching schema, and keep one vector space per Mnemocyte
|
|
204
|
+
installation.
|
|
160
205
|
|
|
161
206
|
The migration creates the bundled HNSW pgvector index
|
|
162
207
|
`mnemocyte_memories_embedding_hnsw_idx` for cosine search. HNSW is approximate:
|
|
@@ -191,6 +236,44 @@ await client.remember({
|
|
|
191
236
|
});
|
|
192
237
|
```
|
|
193
238
|
|
|
239
|
+
Memory metadata is persisted JSON value data. Use the exported `JsonObject`
|
|
240
|
+
and `JsonValue` types; unsupported values such as `undefined`, functions,
|
|
241
|
+
bigints, non-finite numbers, class instances, or cyclic objects are rejected
|
|
242
|
+
with `MnemocyteError` code `"VALIDATION"`. Mnemocyte deep-clones metadata when
|
|
243
|
+
writing and reading, so later mutations do not change stored state.
|
|
244
|
+
|
|
245
|
+
JavaScript callers receive the same runtime domain checks as TypeScript
|
|
246
|
+
callers: `type` and `importance` must be known values, tags must be an array of
|
|
247
|
+
strings, `source` must be a string when supplied, and `expiresAt` must be a
|
|
248
|
+
valid `Date`. Invalid values reject with `"VALIDATION"` before embedding or
|
|
249
|
+
storage. Mutable tags, metadata, and expiration dates are snapshotted when
|
|
250
|
+
`remember` or `rememberMany` is invoked, so later caller mutations cannot
|
|
251
|
+
change the pending write.
|
|
252
|
+
|
|
253
|
+
When observability is configured, snapshot and validation failures follow the
|
|
254
|
+
same operation lifecycle as provider or storage failures: one `"start"` event
|
|
255
|
+
and one `"error"` event carrying the thrown value. Snapshotting remains
|
|
256
|
+
synchronous before any awaited `onEvent` hook.
|
|
257
|
+
|
|
258
|
+
For batches, cancellation belongs to the whole operation:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
const abortController = new AbortController();
|
|
262
|
+
|
|
263
|
+
const memories = await client.rememberMany({
|
|
264
|
+
inputs: [
|
|
265
|
+
{ entityId: "user_123", content: "Likes concise answers." },
|
|
266
|
+
{ entityId: "user_123", content: "Uses TypeScript." },
|
|
267
|
+
],
|
|
268
|
+
signal: abortController.signal,
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The positional `rememberMany(inputs)` form remains as a deprecated pre-v1
|
|
273
|
+
compatibility overload and treats its first item's signal as the batch signal.
|
|
274
|
+
New code should use `{ inputs, signal }`; individual items in the object form do
|
|
275
|
+
not have cancellation signals.
|
|
276
|
+
|
|
194
277
|
### `recall`
|
|
195
278
|
|
|
196
279
|
```ts
|
|
@@ -202,6 +285,11 @@ const memories = await client.recall({
|
|
|
202
285
|
});
|
|
203
286
|
```
|
|
204
287
|
|
|
288
|
+
Successful recall results include the access metadata written by that call:
|
|
289
|
+
`accessCount`, `lastAccessedAt`, and `updatedAt` match stored state when the
|
|
290
|
+
promise resolves. Ranking and score explanations use the count from before the
|
|
291
|
+
current recall so the access update cannot change its own ordering.
|
|
292
|
+
|
|
205
293
|
### `buildContext`
|
|
206
294
|
|
|
207
295
|
```ts
|
|
@@ -213,6 +301,36 @@ const context = await client.buildContext({
|
|
|
213
301
|
});
|
|
214
302
|
```
|
|
215
303
|
|
|
304
|
+
Plain-text context uses a deterministic `=` fence longer than any `=` run in
|
|
305
|
+
the query, rendered metadata, or included memory content. The resulting
|
|
306
|
+
`MEMORY N START/END` boundaries cannot be reproduced by untrusted content.
|
|
307
|
+
Markdown continues to choose a content-safe backtick fence, and XML continues
|
|
308
|
+
to escape content.
|
|
309
|
+
|
|
310
|
+
When supplied, `maxTokens` must be a positive integer or `buildContext` rejects
|
|
311
|
+
with `"VALIDATION"`. Omitting it keeps the default token-budget path. Returned
|
|
312
|
+
context always fits the supplied counter and budget; when an extremely small
|
|
313
|
+
budget cannot fit the full truncation marker, the result is the longest marker
|
|
314
|
+
fragment that fits or an empty string.
|
|
315
|
+
|
|
316
|
+
### `forget` and `forgetAll`
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
await client.forget({
|
|
320
|
+
entityId: "user_123",
|
|
321
|
+
memoryId: "mem_old",
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
await client.forgetAll({ entityId: "user_123" });
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
A memory cannot be deleted while another memory's `supersededBy` still points
|
|
328
|
+
to it as a consolidation survivor. `forget` and `forgetAll` reject with
|
|
329
|
+
`"CONFLICT"` before deleting anything in that case. This also applies when
|
|
330
|
+
`forgetAll` selects both the survivor and its superseded dependent. Delete the
|
|
331
|
+
superseded memories first; deleting a loser, or any memory with no dependents,
|
|
332
|
+
continues to work normally.
|
|
333
|
+
|
|
216
334
|
### `prune`
|
|
217
335
|
|
|
218
336
|
```ts
|
|
@@ -228,6 +346,23 @@ await client.prune({
|
|
|
228
346
|
});
|
|
229
347
|
```
|
|
230
348
|
|
|
349
|
+
Prune selectors are validated and normalized before either backend is called.
|
|
350
|
+
Dates must be valid `Date` instances, enum values must be known, arrays must
|
|
351
|
+
contain valid values, and boolean fields must be actual booleans. `false`
|
|
352
|
+
selector flags and empty selector arrays do not count toward the required
|
|
353
|
+
selector; malformed or selector-free input rejects with `"VALIDATION"` without
|
|
354
|
+
deleting anything. Tag selectors are trimmed and deduplicated.
|
|
355
|
+
|
|
356
|
+
A dry run may preview a survivor among its matches. A non-dry-run prune rejects
|
|
357
|
+
with `"CONFLICT"` if any matching memory still has consolidation dependents,
|
|
358
|
+
and the entire prune batch remains unchanged; unrelated matching rows are not
|
|
359
|
+
partially deleted.
|
|
360
|
+
|
|
361
|
+
When audit logging is enabled, every successful non-dry prune writes one
|
|
362
|
+
best-effort `"memory.pruned"` event per affected entity, including global
|
|
363
|
+
prunes without an `entityId`. Dry runs and zero-deletion runs do not write prune
|
|
364
|
+
events.
|
|
365
|
+
|
|
231
366
|
### `findDuplicates`
|
|
232
367
|
|
|
233
368
|
```ts
|
|
@@ -238,6 +373,10 @@ const pairs = await client.findDuplicates({
|
|
|
238
373
|
});
|
|
239
374
|
```
|
|
240
375
|
|
|
376
|
+
The in-memory implementation compares memory pairs quadratically. It degrades
|
|
377
|
+
noticeably once an entity grows beyond roughly a few thousand memories, so use
|
|
378
|
+
the Postgres backend for duplicate detection beyond that scale.
|
|
379
|
+
|
|
241
380
|
### `listAuditLog`
|
|
242
381
|
|
|
243
382
|
```ts
|
|
@@ -250,8 +389,30 @@ const events = await client.listAuditLog({
|
|
|
250
389
|
entityId: "user_123",
|
|
251
390
|
limit: 100,
|
|
252
391
|
});
|
|
392
|
+
|
|
393
|
+
const last = events.at(-1);
|
|
394
|
+
const nextPage = last
|
|
395
|
+
? await client.listAuditLog({
|
|
396
|
+
entityId: "user_123",
|
|
397
|
+
limit: 100,
|
|
398
|
+
beforeCursor: { timestamp: last.timestamp, id: last.id },
|
|
399
|
+
})
|
|
400
|
+
: [];
|
|
253
401
|
```
|
|
254
402
|
|
|
403
|
+
Audit entries are ordered newest first by timestamp and event ID. The
|
|
404
|
+
experimental composite `beforeCursor` / `afterCursor` fields provide stable
|
|
405
|
+
pagination when multiple events share a timestamp. The existing `before` and
|
|
406
|
+
`after` fields remain strict timestamp filters; they exclude all events at the
|
|
407
|
+
boundary timestamp and should not be used as the sole cursor when complete
|
|
408
|
+
tie-safe pagination is required.
|
|
409
|
+
|
|
410
|
+
Ordinary audit writes are best-effort: an audit storage failure does not fail
|
|
411
|
+
the primary client operation, so `listAuditLog` returns only events that were
|
|
412
|
+
successfully persisted. Postgres consolidation is the exception; its
|
|
413
|
+
`"memory.superseded"` events share the consolidation transaction and commit or
|
|
414
|
+
roll back with it.
|
|
415
|
+
|
|
255
416
|
### `experimental.consolidate`
|
|
256
417
|
|
|
257
418
|
```ts
|
|
@@ -262,6 +423,18 @@ await client.experimental.consolidate({
|
|
|
262
423
|
});
|
|
263
424
|
```
|
|
264
425
|
|
|
426
|
+
Consolidation records each loser's `supersededBy` reference to the survivor.
|
|
427
|
+
Retrying the same loser with that same survivor is an idempotent no-op. If any
|
|
428
|
+
requested loser already points to a different survivor, the call rejects with
|
|
429
|
+
`"CONFLICT"`. Mixed batches are atomic: no newly eligible loser is changed, no
|
|
430
|
+
tags are merged, and no consolidation audit event is written when any item
|
|
431
|
+
conflicts. Concurrent calls follow the same target-specific rule in both
|
|
432
|
+
backends.
|
|
433
|
+
|
|
434
|
+
The survivor cannot later be deleted while any such reference remains:
|
|
435
|
+
`forget`, `forgetAll`, and matching non-dry-run `prune` calls reject with
|
|
436
|
+
`"CONFLICT"`. Deleting a superseded loser remains allowed.
|
|
437
|
+
|
|
265
438
|
The `experimental` namespace may change before v1.0.
|
|
266
439
|
|
|
267
440
|
## Provider Resilience
|
|
@@ -278,6 +451,12 @@ const client = createMnemocyte({
|
|
|
278
451
|
});
|
|
279
452
|
```
|
|
280
453
|
|
|
454
|
+
Provider resilience values are validated synchronously when the client is
|
|
455
|
+
created. Timeouts and delays must be finite non-negative numbers, retries must
|
|
456
|
+
be a non-negative integer, and `shouldRetry` must be a function when supplied;
|
|
457
|
+
invalid values throw `"CONFIG"`. A `maxDelayMs` below `baseDelayMs` remains
|
|
458
|
+
valid and is normalized up to the base delay.
|
|
459
|
+
|
|
281
460
|
Pass `signal` to cancel an operation:
|
|
282
461
|
|
|
283
462
|
```ts
|
|
@@ -290,6 +469,17 @@ await client.remember({
|
|
|
290
469
|
});
|
|
291
470
|
```
|
|
292
471
|
|
|
472
|
+
Pre-aborted signals reject with `"ABORTED"` before storage work begins.
|
|
473
|
+
In-memory maintenance scans check cancellation cooperatively. For Postgres,
|
|
474
|
+
standalone prune, duplicate-search, and audit-log queries request postgres.js
|
|
475
|
+
query cancellation while they are in flight.
|
|
476
|
+
|
|
477
|
+
Postgres consolidation has a transaction-specific boundary: cancellation is
|
|
478
|
+
checked between mutation steps and immediately before the transaction callback
|
|
479
|
+
returns. If a statement is already in flight, it may finish before the next
|
|
480
|
+
check throws and rolls the transaction back. An abort after the final check,
|
|
481
|
+
including while commit is in flight, may still leave the mutation committed.
|
|
482
|
+
|
|
293
483
|
Common `MnemocyteError` codes:
|
|
294
484
|
|
|
295
485
|
```txt
|
|
@@ -300,6 +490,7 @@ VALIDATION
|
|
|
300
490
|
CONFIG
|
|
301
491
|
DB
|
|
302
492
|
NOT_FOUND
|
|
493
|
+
CONFLICT
|
|
303
494
|
MIGRATION
|
|
304
495
|
```
|
|
305
496
|
|
|
@@ -324,15 +515,21 @@ const client = createMnemocyte({
|
|
|
324
515
|
});
|
|
325
516
|
```
|
|
326
517
|
|
|
518
|
+
Retrieval weights must be finite and non-negative, with a non-zero effective
|
|
519
|
+
total after defaults are applied. `recencyHalfLifeDays` and `accessSaturation`
|
|
520
|
+
must be positive finite numbers, and `candidateMultiplier` must be an integer
|
|
521
|
+
of at least 1. Invalid tuning is rejected at client construction with
|
|
522
|
+
`MnemocyteError` code `"CONFIG"`.
|
|
523
|
+
|
|
327
524
|
## Roadmap
|
|
328
525
|
|
|
329
526
|
Planned larger milestones:
|
|
330
527
|
|
|
331
|
-
- `MemoryStore`
|
|
528
|
+
- public `MemoryStore` adapter surface
|
|
332
529
|
- `drizzleStore(db)` for caller-owned Drizzle clients
|
|
333
530
|
- `@mnemocyte/mcp`
|
|
334
531
|
|
|
335
|
-
See [ROADMAP.md](./ROADMAP.md).
|
|
532
|
+
See [ROADMAP.md](./docs/ROADMAP.md).
|
|
336
533
|
|
|
337
534
|
## Pre-v1 Notes
|
|
338
535
|
|
|
@@ -340,6 +537,9 @@ Current behavior:
|
|
|
340
537
|
|
|
341
538
|
- `createMnemocyte()` selects either the in-memory backend or the
|
|
342
539
|
Postgres/pgvector backend.
|
|
540
|
+
- Both backends run through an internal `MemoryStore` boundary and shared
|
|
541
|
+
client orchestration; `MemoryStore` is not exported as a public adapter API
|
|
542
|
+
yet.
|
|
343
543
|
- Postgres schema setup is explicit; the client does not create tables or
|
|
344
544
|
indexes for you.
|
|
345
545
|
- `findDuplicates`, audit-log workflows, and `experimental.consolidate` are
|
|
@@ -347,34 +547,99 @@ Current behavior:
|
|
|
347
547
|
|
|
348
548
|
Known limitations before v1:
|
|
349
549
|
|
|
350
|
-
- The
|
|
351
|
-
|
|
352
|
-
- Configured provider timeouts
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
- Postgres dimension metadata
|
|
356
|
-
in one database are intentionally
|
|
550
|
+
- The internal `MemoryStore` boundary is not a public adapter API yet.
|
|
551
|
+
`drizzleStore(db)` is the planned first public store adapter.
|
|
552
|
+
- Configured provider timeouts abort the per-attempt `AbortSignal`; the
|
|
553
|
+
underlying provider request only stops promptly when the embedder honors that
|
|
554
|
+
signal.
|
|
555
|
+
- Postgres model and dimension metadata are installation-wide. Mixed embedding
|
|
556
|
+
vector spaces in one database are intentionally unsupported.
|
|
357
557
|
|
|
358
558
|
Planned v1 direction:
|
|
359
559
|
|
|
360
|
-
-
|
|
361
|
-
|
|
362
|
-
|
|
560
|
+
- Keep storage adapters behind `MemoryStore` while shared core code owns
|
|
561
|
+
validation, embedding, scoring, observability, resilience, and context
|
|
562
|
+
building.
|
|
363
563
|
- Keep root `mnemocyte` imports provider-SDK-free.
|
|
364
564
|
- Keep migrations, dimensions, and production index choices explicit.
|
|
365
565
|
|
|
366
566
|
## Development
|
|
367
567
|
|
|
568
|
+
### Prerequisites
|
|
569
|
+
|
|
570
|
+
- Node.js 22.18 or newer. CI tests the minimum 22.18 release and Node 24.
|
|
571
|
+
- pnpm 11.1.1, matching the `packageManager` field in `package.json`.
|
|
572
|
+
- Postgres with pgvector only when you want to run the integration suite.
|
|
573
|
+
|
|
574
|
+
Clone the repository and install its locked dependencies:
|
|
575
|
+
|
|
576
|
+
```bash
|
|
577
|
+
git clone https://github.com/Meenic/mnemocyte.git
|
|
578
|
+
cd mnemocyte
|
|
579
|
+
pnpm install --frozen-lockfile
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
Mnemocyte is a library, not a standalone server. Use the quick-start example
|
|
583
|
+
from a consuming TypeScript project, or run the repository build in watch mode
|
|
584
|
+
while developing:
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
pnpm dev
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
Create the publishable ESM artifacts in `dist/` with:
|
|
591
|
+
|
|
592
|
+
```bash
|
|
593
|
+
pnpm build
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Validation
|
|
597
|
+
|
|
598
|
+
Run the same main gates used by CI before committing:
|
|
599
|
+
|
|
368
600
|
```bash
|
|
369
601
|
pnpm checktypes
|
|
370
602
|
pnpm lint
|
|
371
603
|
pnpm test
|
|
372
|
-
pnpm run test:integration
|
|
373
604
|
pnpm run pack:check
|
|
374
605
|
```
|
|
375
606
|
|
|
376
|
-
`test
|
|
607
|
+
`pnpm test` builds the package, runs unit behavior, verifies package exports,
|
|
608
|
+
and type-checks the public declarations. `pnpm lint` is a read-only formatting,
|
|
609
|
+
lint, and import-order check; use `pnpm lint:fix` to apply its safe fixes. The
|
|
610
|
+
Postgres suite is separate:
|
|
611
|
+
|
|
612
|
+
```bash
|
|
613
|
+
DATABASE_URL=postgres://... pnpm run test:integration
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
On PowerShell, set the environment variable for the current process first:
|
|
617
|
+
|
|
618
|
+
```powershell
|
|
619
|
+
$env:DATABASE_URL = "postgres://..."
|
|
620
|
+
pnpm run test:integration
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
`test:integration` skips when `DATABASE_URL` is not set. The database must have
|
|
624
|
+
pgvector available; the test applies the bundled migrations.
|
|
625
|
+
|
|
626
|
+
### Where changes belong
|
|
627
|
+
|
|
628
|
+
- Public exports and types: `src/index.ts`, `src/types.ts`, and `src/errors.ts`.
|
|
629
|
+
- Shared client behavior: `src/memory/client-core.ts`; backend mechanics stay
|
|
630
|
+
in `src/memory/in-memory.ts` or `src/memory/postgres.ts`.
|
|
631
|
+
- Postgres schema and SQL: `src/db/schema.ts` and `src/db/queries/`. Schema
|
|
632
|
+
changes require an explicit file under `migrations/`.
|
|
633
|
+
- Provider helpers: `src/embedders/`; keep the root package provider-SDK-free.
|
|
634
|
+
- Retrieval and context rendering: `src/retrieval/` and `src/context/`.
|
|
635
|
+
- Tests: mirror the affected responsibility under `test/`; package boundary
|
|
636
|
+
checks live under `test/package/`, and real Postgres coverage under
|
|
637
|
+
`test/integration/`.
|
|
638
|
+
|
|
639
|
+
See [AGENTS.md](./AGENTS.md) for repository rules and the validation/release
|
|
640
|
+
policy. Read [ARCHITECTURE.md](./docs/ARCHITECTURE.md) before changing module
|
|
641
|
+
boundaries or the public surface.
|
|
377
642
|
|
|
378
643
|
## Architecture
|
|
379
644
|
|
|
380
|
-
See [ARCHITECTURE.md](./ARCHITECTURE.md).
|
|
645
|
+
See [ARCHITECTURE.md](./docs/ARCHITECTURE.md).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.mts","names":[],"sources":["../../src/embedders/openai.ts"],"mappings":";;;UAciB,qBAAA;;AAAjB;;WAIU,MAAA;EAJ4B;;;EAAA,SAQ5B,KAAA;EAUA;;AAAO;
|
|
1
|
+
{"version":3,"file":"openai.d.mts","names":[],"sources":["../../src/embedders/openai.ts"],"mappings":";;;UAciB,qBAAA;;AAAjB;;WAIU,MAAA;EAJ4B;;;EAAA,SAQ5B,KAAA;EAUA;;AAAO;AAoIjB;EApIU,SALA,UAAA;;;;;WAKA,OAAA;AAAA;AAoI8D;;;;AAAA,iBAAxD,cAAA,CAAe,OAAA,EAAS,qBAAA,GAAwB,QAAQ"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as MnemocyteError } from "../errors-
|
|
1
|
+
import { t as MnemocyteError } from "../errors-B_7ZdjEO.mjs";
|
|
2
2
|
//#region src/embedders/openai.ts
|
|
3
3
|
const KNOWN_MODEL_DIMENSIONS = {
|
|
4
4
|
"text-embedding-3-small": 1536,
|
|
@@ -6,6 +6,28 @@ const KNOWN_MODEL_DIMENSIONS = {
|
|
|
6
6
|
"text-embedding-ada-002": 1536
|
|
7
7
|
};
|
|
8
8
|
const CONFIGURABLE_DIMENSION_MODELS = new Set(["text-embedding-3-small", "text-embedding-3-large"]);
|
|
9
|
+
function isRecord(value) {
|
|
10
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function validateEmbeddingResponse(body, expectedCount) {
|
|
13
|
+
if (!isRecord(body) || !Array.isArray(body.data)) throw new MnemocyteError("OpenAI returned malformed embedding response data.", "EMBEDDING");
|
|
14
|
+
if (body.data.length !== expectedCount) throw new MnemocyteError("OpenAI returned an unexpected number of embeddings.", "EMBEDDING");
|
|
15
|
+
const indices = /* @__PURE__ */ new Set();
|
|
16
|
+
const items = [];
|
|
17
|
+
for (const item of body.data) {
|
|
18
|
+
if (!isRecord(item)) throw new MnemocyteError("OpenAI returned a malformed embedding item.", "EMBEDDING");
|
|
19
|
+
const { embedding, index } = item;
|
|
20
|
+
if (typeof index !== "number" || !Number.isInteger(index) || index < 0 || index >= expectedCount) throw new MnemocyteError("OpenAI returned an embedding with an invalid index.", "EMBEDDING");
|
|
21
|
+
if (indices.has(index)) throw new MnemocyteError("OpenAI returned a duplicate embedding index.", "EMBEDDING");
|
|
22
|
+
if (!Array.isArray(embedding)) throw new MnemocyteError("OpenAI returned an embedding that is not an array.", "EMBEDDING");
|
|
23
|
+
indices.add(index);
|
|
24
|
+
items.push({
|
|
25
|
+
embedding,
|
|
26
|
+
index
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return items;
|
|
30
|
+
}
|
|
9
31
|
function assertDimensions(dimensions) {
|
|
10
32
|
if (!Number.isInteger(dimensions) || dimensions < 1) throw new MnemocyteError("openaiEmbedder dimensions must be a positive integer.", "CONFIG");
|
|
11
33
|
}
|
|
@@ -61,14 +83,7 @@ function openaiEmbedder(options) {
|
|
|
61
83
|
...embedOptions?.signal === void 0 ? {} : { signal: embedOptions.signal }
|
|
62
84
|
});
|
|
63
85
|
if (!response.ok) throw Object.assign(/* @__PURE__ */ new Error(`OpenAI embeddings request failed with status ${response.status}.`), { status: response.status });
|
|
64
|
-
|
|
65
|
-
const embeddings = Array.from({ length: texts.length });
|
|
66
|
-
for (const item of body.data) {
|
|
67
|
-
if (!Number.isInteger(item.index) || item.index < 0 || item.index >= texts.length) throw new MnemocyteError("OpenAI returned an embedding with an invalid index.", "EMBEDDING");
|
|
68
|
-
embeddings[item.index] = item.embedding;
|
|
69
|
-
}
|
|
70
|
-
if (embeddings.some((embedding) => embedding === void 0)) throw new MnemocyteError("OpenAI returned fewer embeddings than requested.", "EMBEDDING");
|
|
71
|
-
return embeddings;
|
|
86
|
+
return validateEmbeddingResponse(await response.json(), texts.length).sort((left, right) => left.index - right.index).map((item) => item.embedding);
|
|
72
87
|
}
|
|
73
88
|
};
|
|
74
89
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.mjs","names":[],"sources":["../../src/embedders/openai.ts"],"sourcesContent":["import { MnemocyteError } from \"../errors.js\";\nimport type { Embedder } from \"../types.js\";\n\nconst KNOWN_MODEL_DIMENSIONS: Record<string, number> = {\n\t\"text-embedding-3-small\": 1536,\n\t\"text-embedding-3-large\": 3072,\n\t\"text-embedding-ada-002\": 1536,\n};\n\nconst CONFIGURABLE_DIMENSION_MODELS = new Set([\n\t\"text-embedding-3-small\",\n\t\"text-embedding-3-large\",\n]);\n\nexport interface OpenAIEmbedderOptions {\n\t/**\n\t * OpenAI API key. Defaults to `process.env.OPENAI_API_KEY`.\n\t */\n\treadonly apiKey?: string;\n\t/**\n\t * OpenAI embedding model name.\n\t */\n\treadonly model: string;\n\t/**\n\t * Output dimensions. Required for unknown/future model names and accepted\n\t * only for OpenAI models that support configurable dimensions.\n\t */\n\treadonly dimensions?: number;\n\t/**\n\t * Override the OpenAI API base URL. Intended for tests and compatible\n\t * deployments; defaults to `https://api.openai.com/v1`.\n\t */\n\treadonly baseUrl?: string;\n}\n\ninterface OpenAIEmbeddingItem {\n\treadonly embedding: number[];\n\treadonly index: number;\n}\n\
|
|
1
|
+
{"version":3,"file":"openai.mjs","names":[],"sources":["../../src/embedders/openai.ts"],"sourcesContent":["import { MnemocyteError } from \"../errors.js\";\nimport type { Embedder } from \"../types.js\";\n\nconst KNOWN_MODEL_DIMENSIONS: Record<string, number> = {\n\t\"text-embedding-3-small\": 1536,\n\t\"text-embedding-3-large\": 3072,\n\t\"text-embedding-ada-002\": 1536,\n};\n\nconst CONFIGURABLE_DIMENSION_MODELS = new Set([\n\t\"text-embedding-3-small\",\n\t\"text-embedding-3-large\",\n]);\n\nexport interface OpenAIEmbedderOptions {\n\t/**\n\t * OpenAI API key. Defaults to `process.env.OPENAI_API_KEY`.\n\t */\n\treadonly apiKey?: string;\n\t/**\n\t * OpenAI embedding model name.\n\t */\n\treadonly model: string;\n\t/**\n\t * Output dimensions. Required for unknown/future model names and accepted\n\t * only for OpenAI models that support configurable dimensions.\n\t */\n\treadonly dimensions?: number;\n\t/**\n\t * Override the OpenAI API base URL. Intended for tests and compatible\n\t * deployments; defaults to `https://api.openai.com/v1`.\n\t */\n\treadonly baseUrl?: string;\n}\n\ninterface OpenAIEmbeddingItem {\n\treadonly embedding: number[];\n\treadonly index: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction validateEmbeddingResponse(\n\tbody: unknown,\n\texpectedCount: number,\n): OpenAIEmbeddingItem[] {\n\tif (!isRecord(body) || !Array.isArray(body.data)) {\n\t\tthrow new MnemocyteError(\n\t\t\t\"OpenAI returned malformed embedding response data.\",\n\t\t\t\"EMBEDDING\",\n\t\t);\n\t}\n\tif (body.data.length !== expectedCount) {\n\t\tthrow new MnemocyteError(\n\t\t\t\"OpenAI returned an unexpected number of embeddings.\",\n\t\t\t\"EMBEDDING\",\n\t\t);\n\t}\n\n\tconst indices = new Set<number>();\n\tconst items: OpenAIEmbeddingItem[] = [];\n\tfor (const item of body.data) {\n\t\tif (!isRecord(item)) {\n\t\t\tthrow new MnemocyteError(\n\t\t\t\t\"OpenAI returned a malformed embedding item.\",\n\t\t\t\t\"EMBEDDING\",\n\t\t\t);\n\t\t}\n\t\tconst { embedding, index } = item;\n\t\tif (\n\t\t\ttypeof index !== \"number\" ||\n\t\t\t!Number.isInteger(index) ||\n\t\t\tindex < 0 ||\n\t\t\tindex >= expectedCount\n\t\t) {\n\t\t\tthrow new MnemocyteError(\n\t\t\t\t\"OpenAI returned an embedding with an invalid index.\",\n\t\t\t\t\"EMBEDDING\",\n\t\t\t);\n\t\t}\n\t\tif (indices.has(index)) {\n\t\t\tthrow new MnemocyteError(\n\t\t\t\t\"OpenAI returned a duplicate embedding index.\",\n\t\t\t\t\"EMBEDDING\",\n\t\t\t);\n\t\t}\n\t\tif (!Array.isArray(embedding)) {\n\t\t\tthrow new MnemocyteError(\n\t\t\t\t\"OpenAI returned an embedding that is not an array.\",\n\t\t\t\t\"EMBEDDING\",\n\t\t\t);\n\t\t}\n\t\tindices.add(index);\n\t\titems.push({ embedding: embedding as number[], index });\n\t}\n\treturn items;\n}\n\nfunction assertDimensions(dimensions: number): void {\n\tif (!Number.isInteger(dimensions) || dimensions < 1) {\n\t\tthrow new MnemocyteError(\n\t\t\t\"openaiEmbedder dimensions must be a positive integer.\",\n\t\t\t\"CONFIG\",\n\t\t);\n\t}\n}\n\nfunction resolveApiKey(apiKey: string | undefined): string {\n\tconst resolved = apiKey ?? process.env.OPENAI_API_KEY;\n\tif (!resolved || resolved.trim().length === 0) {\n\t\tthrow new MnemocyteError(\n\t\t\t\"openaiEmbedder requires an API key. Pass apiKey or set OPENAI_API_KEY.\",\n\t\t\t\"CONFIG\",\n\t\t);\n\t}\n\treturn resolved;\n}\n\nfunction resolveDimensions(options: OpenAIEmbedderOptions): {\n\tembedderDimensions: number;\n\trequestDimensions?: number;\n} {\n\tconst defaultDimensions = KNOWN_MODEL_DIMENSIONS[options.model];\n\tif (defaultDimensions === undefined) {\n\t\tif (options.dimensions === undefined) {\n\t\t\tthrow new MnemocyteError(\n\t\t\t\t`openaiEmbedder requires dimensions for unknown model \"${options.model}\".`,\n\t\t\t\t\"CONFIG\",\n\t\t\t);\n\t\t}\n\t\tassertDimensions(options.dimensions);\n\t\treturn {\n\t\t\tembedderDimensions: options.dimensions,\n\t\t\trequestDimensions: options.dimensions,\n\t\t};\n\t}\n\n\tif (\n\t\toptions.dimensions !== undefined &&\n\t\t!CONFIGURABLE_DIMENSION_MODELS.has(options.model)\n\t) {\n\t\tthrow new MnemocyteError(\n\t\t\t`openaiEmbedder does not support a dimensions override for \"${options.model}\".`,\n\t\t\t\"CONFIG\",\n\t\t);\n\t}\n\n\tif (options.dimensions !== undefined) {\n\t\tassertDimensions(options.dimensions);\n\t\treturn {\n\t\t\tembedderDimensions: options.dimensions,\n\t\t\trequestDimensions: options.dimensions,\n\t\t};\n\t}\n\n\treturn { embedderDimensions: defaultDimensions };\n}\n\n/**\n * Create an OpenAI-backed {@link Embedder}. This adapter uses `fetch`\n * directly so provider SDKs never become part of Mnemocyte's dependency graph.\n */\nexport function openaiEmbedder(options: OpenAIEmbedderOptions): Embedder {\n\tconst apiKey = resolveApiKey(options.apiKey);\n\tconst { embedderDimensions, requestDimensions } = resolveDimensions(options);\n\tconst baseUrl = options.baseUrl ?? \"https://api.openai.com/v1\";\n\n\treturn {\n\t\tmodel: options.model,\n\t\tdimensions: embedderDimensions,\n\t\tasync embed(texts, embedOptions) {\n\t\t\tconst response = await fetch(`${baseUrl}/embeddings`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tauthorization: `Bearer ${apiKey}`,\n\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: options.model,\n\t\t\t\t\tinput: [...texts],\n\t\t\t\t\tencoding_format: \"float\",\n\t\t\t\t\t...(requestDimensions === undefined\n\t\t\t\t\t\t? {}\n\t\t\t\t\t\t: { dimensions: requestDimensions }),\n\t\t\t\t}),\n\t\t\t\t...(embedOptions?.signal === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { signal: embedOptions.signal }),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow Object.assign(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t`OpenAI embeddings request failed with status ${response.status}.`,\n\t\t\t\t\t),\n\t\t\t\t\t{ status: response.status },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst items = validateEmbeddingResponse(\n\t\t\t\tawait response.json(),\n\t\t\t\ttexts.length,\n\t\t\t);\n\t\t\treturn items\n\t\t\t\t.sort((left, right) => left.index - right.index)\n\t\t\t\t.map((item) => item.embedding);\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -38,8 +38,9 @@ var MnemocyteError = class extends Error {
|
|
|
38
38
|
/**
|
|
39
39
|
* Type guard for {@link MnemocyteError}.
|
|
40
40
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
41
|
+
* This guard uses `instanceof` and recognizes errors created by the same loaded
|
|
42
|
+
* Mnemocyte class. It does not recognize structurally similar errors or errors
|
|
43
|
+
* created by another installed copy of the package.
|
|
43
44
|
*
|
|
44
45
|
* @param error - Value to test (typically a caught `unknown`).
|
|
45
46
|
* @returns `true` if `error` is a {@link MnemocyteError}.
|
|
@@ -50,4 +51,4 @@ function isMnemocyteError(error) {
|
|
|
50
51
|
//#endregion
|
|
51
52
|
export { isMnemocyteError as n, MnemocyteError as t };
|
|
52
53
|
|
|
53
|
-
//# sourceMappingURL=errors-
|
|
54
|
+
//# sourceMappingURL=errors-B_7ZdjEO.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors-B_7ZdjEO.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Stable, machine-readable error code attached to every {@link MnemocyteError}.\n *\n * - `\"CONFIG\"` — invalid client configuration (e.g. missing or malformed embedder).\n * - `\"VALIDATION\"` — invalid argument passed to a client method.\n * - `\"DB\"` — Postgres or driver-level failure.\n * - `\"EMBEDDING\"` — embedder threw or returned an invalid vector.\n * - `\"NOT_FOUND\"` — referenced memory or entity does not exist.\n * - `\"CONFLICT\"` — the requested mutation conflicts with stored relationships.\n * - `\"MIGRATION\"` — schema/migration check failed against the target database.\n * - `\"TIMEOUT\"` — a provider call exceeded its configured timeout.\n * - `\"ABORTED\"` — the operation was cancelled via an `AbortSignal`.\n */\nexport type MnemocyteErrorCode =\n\t| \"CONFIG\"\n\t| \"VALIDATION\"\n\t| \"DB\"\n\t| \"EMBEDDING\"\n\t| \"NOT_FOUND\"\n\t| \"CONFLICT\"\n\t| \"MIGRATION\"\n\t| \"TIMEOUT\"\n\t| \"ABORTED\";\n\n/**\n * Error type thrown by every public Mnemocyte API.\n *\n * Use the {@link MnemocyteError.code} property — not the message — to branch\n * on failure modes, or call {@link isMnemocyteError} as a type guard when\n * working with `unknown` errors.\n *\n * @example\n * ```ts\n * try {\n * await client.recall({ entityId: \"u1\", query: \"hi\" });\n * } catch (err) {\n * if (isMnemocyteError(err) && err.code === \"DB\") {\n * // handle database failure\n * }\n * throw err;\n * }\n * ```\n */\nexport class MnemocyteError extends Error {\n\t/** Stable, machine-readable failure code. See {@link MnemocyteErrorCode}. */\n\treadonly code: MnemocyteErrorCode;\n\t/** Underlying cause, if any (e.g. an original driver or embedder error). */\n\toverride readonly cause?: unknown;\n\n\t/**\n\t * @param message - Human-readable error message.\n\t * @param code - Stable failure code; see {@link MnemocyteErrorCode}.\n\t * @param cause - Optional underlying cause (driver error, network error, etc.).\n\t */\n\tconstructor(message: string, code: MnemocyteErrorCode, cause?: unknown) {\n\t\tsuper(message);\n\t\tthis.name = \"MnemocyteError\";\n\t\tthis.code = code;\n\t\tthis.cause = cause;\n\t}\n}\n\n/**\n * Type guard for {@link MnemocyteError}.\n *\n * This guard uses `instanceof` and recognizes errors created by the same loaded\n * Mnemocyte class. It does not recognize structurally similar errors or errors\n * created by another installed copy of the package.\n *\n * @param error - Value to test (typically a caught `unknown`).\n * @returns `true` if `error` is a {@link MnemocyteError}.\n */\nexport function isMnemocyteError(error: unknown): error is MnemocyteError {\n\treturn error instanceof MnemocyteError;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2CA,IAAa,iBAAb,cAAoC,MAAM;;CAEzC;;CAEA;;;;;;CAOA,YAAY,SAAiB,MAA0B,OAAiB;EACvE,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,QAAQ;CACd;AACD;;;;;;;;;;;AAYA,SAAgB,iBAAiB,OAAyC;CACzE,OAAO,iBAAiB;AACzB"}
|