@memstack/core 0.5.0 → 0.7.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 +25 -8
- package/dist/index.cjs +112 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +113 -33
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
> The open-source memory layer for AI agents — store, retrieve, summarize, and prune.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@memstack/core)
|
|
6
|
+
[](https://github.com/isiomaC/memstack/actions/workflows/ci.yml)
|
|
6
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://gitmcp.io/isiomaC/memstack)
|
|
7
9
|
|
|
8
10
|
```bash
|
|
9
11
|
npm install @memstack/core
|
|
@@ -671,7 +673,7 @@ new OpenAIEmbeddingAdapter({ apiKey: "...", baseURL: "https://api.voyageai.com/v
|
|
|
671
673
|
|
|
672
674
|
### Storage Adapters
|
|
673
675
|
|
|
674
|
-
MemStack ships with **
|
|
676
|
+
MemStack ships with **12 production-ready storage adapters (6 experimental)** — every major backend, zero peer dependencies, all client-injected.
|
|
675
677
|
|
|
676
678
|
### Production (e2e verified against real instances)
|
|
677
679
|
|
|
@@ -687,6 +689,7 @@ MemStack ships with **11 production-ready storage adapters (7 experimental)**
|
|
|
687
689
|
| Adapter | Backend | Vector search |
|
|
688
690
|
|---|---|---|
|
|
689
691
|
| `PostgresStorageAdapter` | PostgreSQL + pgvector | HNSW native |
|
|
692
|
+
| `SQLiteStorageAdapter` | SQLite (better-sqlite3) | Cosine in-memory |
|
|
690
693
|
|
|
691
694
|
**Vector databases:**
|
|
692
695
|
| Adapter | Backend |
|
|
@@ -712,7 +715,6 @@ Available via direct source import. Not yet in the barrel export — uncomment i
|
|
|
712
715
|
|
|
713
716
|
| Adapter | Backend | Blocker |
|
|
714
717
|
|---|---|---|
|
|
715
|
-
| `SQLiteStorageAdapter` | SQLite (better-sqlite3) | Native binary for Node 24 |
|
|
716
718
|
| `TursoStorageAdapter` | Turso (libsql) | Cloud-only (needs Turso account) |
|
|
717
719
|
| `ChromaStorageAdapter` | ChromaDB | Embedding function dependency |
|
|
718
720
|
| `PineconeStorageAdapter` | Pinecone | Cloud-only (needs API key) |
|
|
@@ -781,7 +783,6 @@ class MyStorage implements StorageProvider {
|
|
|
781
783
|
| Neo4j | Neo4j vector index | No | ✅ Production |
|
|
782
784
|
| Hybrid | Delegates to cache/durable | If durable supports | ✅ Production |
|
|
783
785
|
| SQLite | Cosine in-memory | Yes | ✅ Production |
|
|
784
|
-
| Hybrid | Delegates to cache/durable | If durable supports | ✅ Production |
|
|
785
786
|
|
|
786
787
|
---
|
|
787
788
|
|
|
@@ -797,18 +798,28 @@ const ms = new MemStack({
|
|
|
797
798
|
embedding?: EmbeddingProvider, // Optional — for semantic search
|
|
798
799
|
storage?: StorageProvider, // Optional — defaults to InMemoryStorageAdapter
|
|
799
800
|
defaults?: {
|
|
800
|
-
summarizationThreshold?: number, // Auto-summarize every N
|
|
801
|
+
summarizationThreshold?: number, // Auto-summarize every N process() calls. Default: 100
|
|
801
802
|
embedOnStore?: boolean, // Auto-embed on store(). Default: true
|
|
802
|
-
pruneStrategy?: PruneStrategy, // Auto-prune
|
|
803
|
+
pruneStrategy?: PruneStrategy, // Auto-prune during process() (throttled). Default: disabled
|
|
804
|
+
pruneInterval?: number, // Run auto-prune every N process() calls. Default: 100
|
|
805
|
+
autoImportance?: boolean, // LLM-score importance in process() when not provided. Default: false
|
|
806
|
+
autoTags?: boolean, // LLM-extract tags in process() when not provided. Default: false
|
|
807
|
+
summarizationPrompt?: string, // Custom prompt for the summarizer
|
|
803
808
|
},
|
|
804
809
|
hooks?: {
|
|
805
810
|
onMemoryStored?: (memory: Memory) => void;
|
|
806
811
|
onMemoryPruned?: (ids: string[]) => void;
|
|
807
812
|
onSummaryCreated?: (summary: Memory, deletedCount: number) => void;
|
|
813
|
+
onError?: (error: Error, context: string) => void;
|
|
808
814
|
},
|
|
809
815
|
});
|
|
810
816
|
```
|
|
811
817
|
|
|
818
|
+
> **Auto-behaviors run inside `process()`, not `store()`.** `process()` tracks a
|
|
819
|
+
> per-actor call count: summarization fires every `summarizationThreshold` calls,
|
|
820
|
+
> and pruning fires every `pruneInterval` calls (when `pruneStrategy` is set).
|
|
821
|
+
> `store()` is the low-level write and never triggers these.
|
|
822
|
+
|
|
812
823
|
### Memory Subsystem
|
|
813
824
|
|
|
814
825
|
All methods accessible via `ms.memory.*`:
|
|
@@ -857,6 +868,8 @@ const data = JSON.parse(fs.readFileSync("state.json", "utf-8"));
|
|
|
857
868
|
await ms2.import(data);
|
|
858
869
|
```
|
|
859
870
|
|
|
871
|
+
Each memory's original `createdAt` is preserved on import, so `export` → `import` is a lossless round-trip — safe for backups and cross-backend migration (e.g. disk → Postgres). All storage adapters honor a `createdAt` supplied on `store()`/`storeBatch()`; when omitted, they default to the current time.
|
|
872
|
+
|
|
860
873
|
### Health & Close
|
|
861
874
|
|
|
862
875
|
```typescript
|
|
@@ -874,14 +887,17 @@ await ms.close(); // graceful shutdown
|
|
|
874
887
|
const ms = new MemStack({
|
|
875
888
|
llm: new OpenAILLMAdapter({ apiKey: "..." }),
|
|
876
889
|
|
|
877
|
-
// Defaults control auto-behavior
|
|
890
|
+
// Defaults control auto-behavior (all applied during process())
|
|
878
891
|
defaults: {
|
|
879
|
-
summarizationThreshold: 50, // Summarize every 50
|
|
892
|
+
summarizationThreshold: 50, // Summarize every 50 process() calls (default: 100)
|
|
880
893
|
embedOnStore: false, // Don't auto-embed — saves API costs
|
|
881
|
-
pruneStrategy: { // Auto-clean
|
|
894
|
+
pruneStrategy: { // Auto-clean during process(), throttled by pruneInterval
|
|
882
895
|
type: "byAge",
|
|
883
896
|
maxAge: 90 * 86400000, // 90 days
|
|
884
897
|
},
|
|
898
|
+
pruneInterval: 100, // Run the prune check every 100 process() calls (default: 100)
|
|
899
|
+
autoImportance: true, // Let the LLM score importance when you don't pass one
|
|
900
|
+
autoTags: true, // Let the LLM extract tags when you don't pass any
|
|
885
901
|
},
|
|
886
902
|
|
|
887
903
|
// Hooks for observability
|
|
@@ -889,6 +905,7 @@ const ms = new MemStack({
|
|
|
889
905
|
onMemoryStored: (m) => logger.debug("memory:stored", { id: m.id, actor: m.actorId }),
|
|
890
906
|
onMemoryPruned: (ids) => logger.info("memory:pruned", { count: ids.length }),
|
|
891
907
|
onSummaryCreated: (summary, n) => logger.info("memory:summarized", { count: n }),
|
|
908
|
+
onError: (err, context) => logger.error("memory:error", { context, message: err.message }),
|
|
892
909
|
},
|
|
893
910
|
});
|
|
894
911
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -600,7 +600,7 @@ var MemoryStore = class {
|
|
|
600
600
|
}
|
|
601
601
|
async prune(strategy) {
|
|
602
602
|
await this._ensureInit();
|
|
603
|
-
const allMemories = await this.storage.retrieve({ limit: this.limits.pruneScan });
|
|
603
|
+
const allMemories = await this.storage.retrieve({ actorId: strategy.actorId, limit: this.limits.pruneScan });
|
|
604
604
|
const kept = this.pruner.execute(allMemories, strategy);
|
|
605
605
|
const keptIds = new Set(kept.map((k) => k.id));
|
|
606
606
|
const prunedIds = allMemories.filter((m) => !keptIds.has(m.id)).map((m) => m.id);
|
|
@@ -612,7 +612,7 @@ var MemoryStore = class {
|
|
|
612
612
|
}
|
|
613
613
|
async dryRunPrune(strategy) {
|
|
614
614
|
await this._ensureInit();
|
|
615
|
-
const allMemories = await this.storage.retrieve({ limit: this.limits.pruneScan });
|
|
615
|
+
const allMemories = await this.storage.retrieve({ actorId: strategy.actorId, limit: this.limits.pruneScan });
|
|
616
616
|
const kept = this.pruner.execute(allMemories, strategy);
|
|
617
617
|
const keptIds = new Set(kept.map((k) => k.id));
|
|
618
618
|
const wouldPrune = allMemories.filter((m) => !keptIds.has(m.id)).map((m) => m.id);
|
|
@@ -746,7 +746,7 @@ var InMemoryStorageAdapter = class {
|
|
|
746
746
|
sourceId: input.sourceId,
|
|
747
747
|
metadata: input.metadata ?? {},
|
|
748
748
|
expiresAt: input.expiresAt,
|
|
749
|
-
createdAt: now,
|
|
749
|
+
createdAt: input.createdAt ?? now,
|
|
750
750
|
_touchedAt: now
|
|
751
751
|
};
|
|
752
752
|
this.memories.set(memory.id, memory);
|
|
@@ -796,8 +796,11 @@ var InMemoryStorageAdapter = class {
|
|
|
796
796
|
results = results.filter((m) => m.tags?.some((t) => query.tags.includes(t)));
|
|
797
797
|
}
|
|
798
798
|
if (query.query) {
|
|
799
|
-
const
|
|
800
|
-
results = results.filter((m) =>
|
|
799
|
+
const terms = query.query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
800
|
+
results = results.filter((m) => {
|
|
801
|
+
const content = m.content.toLowerCase();
|
|
802
|
+
return terms.some((t) => content.includes(t));
|
|
803
|
+
});
|
|
801
804
|
}
|
|
802
805
|
if (query.createdAfter) {
|
|
803
806
|
results = results.filter((m) => m.createdAt >= query.createdAfter);
|
|
@@ -983,7 +986,19 @@ var MemStack = class {
|
|
|
983
986
|
if (snapshot.version !== 1) {
|
|
984
987
|
throw validationError(`Unsupported snapshot version: ${snapshot.version}`);
|
|
985
988
|
}
|
|
986
|
-
|
|
989
|
+
const memories = snapshot.memories.map((m, i) => {
|
|
990
|
+
const createdAt = typeof m.createdAt === "string" ? new Date(m.createdAt) : m.createdAt;
|
|
991
|
+
const expiresAt = typeof m.expiresAt === "string" ? new Date(m.expiresAt) : m.expiresAt;
|
|
992
|
+
const label = m.id ? `id: ${m.id}` : `index ${i}`;
|
|
993
|
+
if (createdAt && isNaN(createdAt.getTime())) {
|
|
994
|
+
throw validationError(`Invalid createdAt for memory (${label}): ${String(m.createdAt)}`);
|
|
995
|
+
}
|
|
996
|
+
if (expiresAt && isNaN(expiresAt.getTime())) {
|
|
997
|
+
throw validationError(`Invalid expiresAt for memory (${label}): ${String(m.expiresAt)}`);
|
|
998
|
+
}
|
|
999
|
+
return { ...m, createdAt, expiresAt };
|
|
1000
|
+
});
|
|
1001
|
+
await this.memory.storeBatch(memories);
|
|
987
1002
|
}
|
|
988
1003
|
async health() {
|
|
989
1004
|
const status = { storage: false, llm: false, embedding: false };
|
|
@@ -1070,7 +1085,7 @@ var DiskStorageAdapter = class {
|
|
|
1070
1085
|
sourceId: input.sourceId,
|
|
1071
1086
|
metadata: input.metadata ?? {},
|
|
1072
1087
|
expiresAt: input.expiresAt,
|
|
1073
|
-
createdAt: now,
|
|
1088
|
+
createdAt: input.createdAt ?? now,
|
|
1074
1089
|
_touchedAt: now.toISOString()
|
|
1075
1090
|
};
|
|
1076
1091
|
await this._withWriteLock(input.actorId, async () => {
|
|
@@ -1103,7 +1118,7 @@ var DiskStorageAdapter = class {
|
|
|
1103
1118
|
sourceId: input.sourceId,
|
|
1104
1119
|
metadata: input.metadata ?? {},
|
|
1105
1120
|
expiresAt: input.expiresAt,
|
|
1106
|
-
createdAt: now,
|
|
1121
|
+
createdAt: input.createdAt ?? now,
|
|
1107
1122
|
_touchedAt: now.toISOString()
|
|
1108
1123
|
};
|
|
1109
1124
|
all.push(memory);
|
|
@@ -1210,8 +1225,11 @@ var DiskStorageAdapter = class {
|
|
|
1210
1225
|
records = records.filter((r) => r.tags?.some((t) => query.tags.includes(t)));
|
|
1211
1226
|
}
|
|
1212
1227
|
if (query.query) {
|
|
1213
|
-
const
|
|
1214
|
-
records = records.filter((r) =>
|
|
1228
|
+
const terms = query.query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
1229
|
+
records = records.filter((r) => {
|
|
1230
|
+
const content = r.content.toLowerCase();
|
|
1231
|
+
return terms.some((t) => content.includes(t));
|
|
1232
|
+
});
|
|
1215
1233
|
}
|
|
1216
1234
|
results.push(...records);
|
|
1217
1235
|
}
|
|
@@ -1302,9 +1320,14 @@ var DiskStorageAdapter = class {
|
|
|
1302
1320
|
}
|
|
1303
1321
|
}
|
|
1304
1322
|
async _writeFile(actorId, records) {
|
|
1323
|
+
const filePath = this._filePath(actorId);
|
|
1324
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
1305
1325
|
try {
|
|
1306
|
-
await promises.writeFile(
|
|
1326
|
+
await promises.writeFile(tmpPath, JSON.stringify(records, null, 2), "utf-8");
|
|
1327
|
+
await promises.rename(tmpPath, filePath);
|
|
1307
1328
|
} catch (err) {
|
|
1329
|
+
await promises.unlink(tmpPath).catch(() => {
|
|
1330
|
+
});
|
|
1308
1331
|
throw storageError(`Failed to write memories for actor ${actorId}: ${err.message}`);
|
|
1309
1332
|
}
|
|
1310
1333
|
}
|
|
@@ -1316,15 +1339,57 @@ var DiskStorageAdapter = class {
|
|
|
1316
1339
|
});
|
|
1317
1340
|
this._writeLocks.set(actorId, next);
|
|
1318
1341
|
await prev;
|
|
1342
|
+
const releaseFileLock = await this._acquireFileLock(actorId);
|
|
1319
1343
|
try {
|
|
1320
1344
|
await fn();
|
|
1321
1345
|
} finally {
|
|
1346
|
+
await releaseFileLock();
|
|
1322
1347
|
resolve();
|
|
1323
1348
|
if (this._writeLocks.get(actorId) === next) {
|
|
1324
1349
|
this._writeLocks.delete(actorId);
|
|
1325
1350
|
}
|
|
1326
1351
|
}
|
|
1327
1352
|
}
|
|
1353
|
+
/**
|
|
1354
|
+
* Acquires an exclusive cross-process lock for `actorId` via `open(path, "wx")`
|
|
1355
|
+
* (atomic create-if-absent). Retries with jittered backoff while the lock is
|
|
1356
|
+
* held elsewhere, and steals locks left behind by a crashed process once
|
|
1357
|
+
* they're older than `staleMs`.
|
|
1358
|
+
*/
|
|
1359
|
+
async _acquireFileLock(actorId) {
|
|
1360
|
+
const lockPath = `${this._filePath(actorId)}.lock`;
|
|
1361
|
+
const staleMs = 1e4;
|
|
1362
|
+
const timeoutMs = 3e4;
|
|
1363
|
+
const start = Date.now();
|
|
1364
|
+
while (true) {
|
|
1365
|
+
try {
|
|
1366
|
+
const handle = await promises.open(lockPath, "wx");
|
|
1367
|
+
await handle.close();
|
|
1368
|
+
return async () => {
|
|
1369
|
+
await promises.unlink(lockPath).catch(() => {
|
|
1370
|
+
});
|
|
1371
|
+
};
|
|
1372
|
+
} catch (err) {
|
|
1373
|
+
if (err.code !== "EEXIST") {
|
|
1374
|
+
throw storageError(`Failed to acquire lock for actor ${actorId}: ${err.message}`);
|
|
1375
|
+
}
|
|
1376
|
+
try {
|
|
1377
|
+
const st = await promises.stat(lockPath);
|
|
1378
|
+
if (Date.now() - st.mtimeMs > staleMs) {
|
|
1379
|
+
await promises.unlink(lockPath).catch(() => {
|
|
1380
|
+
});
|
|
1381
|
+
continue;
|
|
1382
|
+
}
|
|
1383
|
+
} catch {
|
|
1384
|
+
continue;
|
|
1385
|
+
}
|
|
1386
|
+
if (Date.now() - start > timeoutMs) {
|
|
1387
|
+
throw storageError(`Timed out waiting for disk storage lock: ${lockPath}`);
|
|
1388
|
+
}
|
|
1389
|
+
await new Promise((r) => setTimeout(r, 15 + Math.random() * 35));
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1328
1393
|
async _listActors() {
|
|
1329
1394
|
try {
|
|
1330
1395
|
const entries = await promises.readdir(this.dir);
|
|
@@ -1382,7 +1447,7 @@ var MarkdownStorageAdapter = class {
|
|
|
1382
1447
|
sourceId: input.sourceId,
|
|
1383
1448
|
metadata: input.metadata ?? {},
|
|
1384
1449
|
expiresAt: input.expiresAt,
|
|
1385
|
-
createdAt: now
|
|
1450
|
+
createdAt: input.createdAt ?? now
|
|
1386
1451
|
};
|
|
1387
1452
|
const filePath = this._filePath(input.actorId);
|
|
1388
1453
|
const block = this._formatBlock(memory);
|
|
@@ -1459,8 +1524,11 @@ var MarkdownStorageAdapter = class {
|
|
|
1459
1524
|
results = results.filter((m) => m.tags?.some((t) => query.tags.includes(t)));
|
|
1460
1525
|
}
|
|
1461
1526
|
if (query.query) {
|
|
1462
|
-
const
|
|
1463
|
-
results = results.filter((m) =>
|
|
1527
|
+
const terms = query.query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
1528
|
+
results = results.filter((m) => {
|
|
1529
|
+
const content = m.content.toLowerCase();
|
|
1530
|
+
return terms.some((t) => content.includes(t));
|
|
1531
|
+
});
|
|
1464
1532
|
}
|
|
1465
1533
|
switch (query.strategy) {
|
|
1466
1534
|
case "recent":
|
|
@@ -2011,7 +2079,7 @@ var PostgresStorageAdapter = class {
|
|
|
2011
2079
|
sourceId: input.sourceId,
|
|
2012
2080
|
metadata: input.metadata ?? {},
|
|
2013
2081
|
expiresAt: input.expiresAt,
|
|
2014
|
-
createdAt: new Date(now)
|
|
2082
|
+
createdAt: input.createdAt ?? new Date(now)
|
|
2015
2083
|
};
|
|
2016
2084
|
}
|
|
2017
2085
|
async storeBatch(inputs) {
|
|
@@ -2214,7 +2282,7 @@ var RedisStorageAdapter = class {
|
|
|
2214
2282
|
sourceId: input.sourceId,
|
|
2215
2283
|
metadata: input.metadata ?? {},
|
|
2216
2284
|
expiresAt: input.expiresAt,
|
|
2217
|
-
createdAt: now,
|
|
2285
|
+
createdAt: input.createdAt ?? now,
|
|
2218
2286
|
_touchedAt: now.toISOString()
|
|
2219
2287
|
};
|
|
2220
2288
|
await this.redis.set(this._memKey(memory.id), JSON.stringify(memory));
|
|
@@ -2238,7 +2306,7 @@ var RedisStorageAdapter = class {
|
|
|
2238
2306
|
sourceId: input.sourceId,
|
|
2239
2307
|
metadata: input.metadata ?? {},
|
|
2240
2308
|
expiresAt: input.expiresAt,
|
|
2241
|
-
createdAt: now,
|
|
2309
|
+
createdAt: input.createdAt ?? now,
|
|
2242
2310
|
_touchedAt: now.toISOString()
|
|
2243
2311
|
};
|
|
2244
2312
|
await this.redis.set(this._memKey(memory.id), JSON.stringify(memory));
|
|
@@ -2434,7 +2502,7 @@ var QdrantStorageAdapter = class {
|
|
|
2434
2502
|
sourceId: input.sourceId,
|
|
2435
2503
|
metadata: input.metadata ?? {},
|
|
2436
2504
|
expiresAt: input.expiresAt,
|
|
2437
|
-
createdAt: now
|
|
2505
|
+
createdAt: input.createdAt ?? now
|
|
2438
2506
|
};
|
|
2439
2507
|
const qdrantId = crypto.randomUUID();
|
|
2440
2508
|
await this.client.upsert(this.collectionName, {
|
|
@@ -2466,7 +2534,7 @@ var QdrantStorageAdapter = class {
|
|
|
2466
2534
|
sourceId: input.sourceId,
|
|
2467
2535
|
metadata: input.metadata ?? {},
|
|
2468
2536
|
expiresAt: input.expiresAt,
|
|
2469
|
-
createdAt: now
|
|
2537
|
+
createdAt: input.createdAt ?? now
|
|
2470
2538
|
};
|
|
2471
2539
|
results.push(memory);
|
|
2472
2540
|
const qid = crypto.randomUUID();
|
|
@@ -2644,10 +2712,13 @@ var Neo4jStorageAdapter = class {
|
|
|
2644
2712
|
sourceId: input.sourceId ?? null,
|
|
2645
2713
|
metadata: JSON.stringify(input.metadata ?? {}),
|
|
2646
2714
|
expiresAt: input.expiresAt?.toISOString() ?? null,
|
|
2647
|
-
createdAt: now,
|
|
2715
|
+
createdAt: input.createdAt?.toISOString() ?? now,
|
|
2648
2716
|
embedding: input.embedding ?? null
|
|
2649
2717
|
};
|
|
2650
|
-
const
|
|
2718
|
+
const updateProps = { ...props };
|
|
2719
|
+
if (!input.createdAt) {
|
|
2720
|
+
delete updateProps.createdAt;
|
|
2721
|
+
}
|
|
2651
2722
|
const session = this.driver.session({ database: this.database });
|
|
2652
2723
|
try {
|
|
2653
2724
|
await session.run(
|
|
@@ -2669,7 +2740,7 @@ var Neo4jStorageAdapter = class {
|
|
|
2669
2740
|
sourceId: input.sourceId,
|
|
2670
2741
|
metadata: input.metadata ?? {},
|
|
2671
2742
|
expiresAt: input.expiresAt,
|
|
2672
|
-
createdAt: new Date(now)
|
|
2743
|
+
createdAt: input.createdAt ?? new Date(now)
|
|
2673
2744
|
};
|
|
2674
2745
|
}
|
|
2675
2746
|
async storeBatch(inputs) {
|
|
@@ -2906,7 +2977,7 @@ var WeaviateStorageAdapter = class {
|
|
|
2906
2977
|
sourceId: input.sourceId,
|
|
2907
2978
|
metadata: input.metadata ?? {},
|
|
2908
2979
|
expiresAt: input.expiresAt,
|
|
2909
|
-
createdAt: now
|
|
2980
|
+
createdAt: input.createdAt ?? now
|
|
2910
2981
|
};
|
|
2911
2982
|
const wvId = await this.collection().data.insert({
|
|
2912
2983
|
id: crypto.randomUUID(),
|
|
@@ -3139,7 +3210,7 @@ var LanceDBStorageAdapter = class {
|
|
|
3139
3210
|
sourceId: input.sourceId,
|
|
3140
3211
|
metadata: input.metadata ?? {},
|
|
3141
3212
|
expiresAt: input.expiresAt,
|
|
3142
|
-
createdAt: now
|
|
3213
|
+
createdAt: input.createdAt ?? now
|
|
3143
3214
|
};
|
|
3144
3215
|
}
|
|
3145
3216
|
async storeBatch(inputs) {
|
|
@@ -3307,7 +3378,7 @@ var MongoDBStorageAdapter = class {
|
|
|
3307
3378
|
sourceId: input.sourceId,
|
|
3308
3379
|
metadata: input.metadata ?? {},
|
|
3309
3380
|
expiresAt: input.expiresAt,
|
|
3310
|
-
createdAt: now
|
|
3381
|
+
createdAt: input.createdAt ?? now
|
|
3311
3382
|
};
|
|
3312
3383
|
await this.collection.insertOne(this.toDoc(memory));
|
|
3313
3384
|
return memory;
|
|
@@ -3536,7 +3607,7 @@ var SQLiteStorageAdapter = class {
|
|
|
3536
3607
|
sourceId: input.sourceId,
|
|
3537
3608
|
metadata: input.metadata ?? {},
|
|
3538
3609
|
expiresAt: input.expiresAt,
|
|
3539
|
-
createdAt: new Date(now)
|
|
3610
|
+
createdAt: input.createdAt ?? new Date(now)
|
|
3540
3611
|
};
|
|
3541
3612
|
}
|
|
3542
3613
|
async storeBatch(inputs) {
|
|
@@ -3809,11 +3880,12 @@ var OpenAILLMAdapter = class {
|
|
|
3809
3880
|
try {
|
|
3810
3881
|
const parsed = JSON.parse(data);
|
|
3811
3882
|
const content = parsed.choices?.[0]?.delta?.content ?? "";
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3883
|
+
const usage = parsed.usage;
|
|
3884
|
+
if (usage) {
|
|
3885
|
+
promptTokens = usage.prompt_tokens;
|
|
3886
|
+
completionTokens = usage.completion_tokens;
|
|
3815
3887
|
}
|
|
3816
|
-
if (content) {
|
|
3888
|
+
if (content || usage) {
|
|
3817
3889
|
yield {
|
|
3818
3890
|
text: content,
|
|
3819
3891
|
tokens: { prompt: promptTokens, completion: completionTokens, total: promptTokens + completionTokens }
|
|
@@ -3869,7 +3941,15 @@ var AnthropicLLMAdapter = class {
|
|
|
3869
3941
|
};
|
|
3870
3942
|
} catch (err) {
|
|
3871
3943
|
if (err instanceof MemStackError) throw err;
|
|
3872
|
-
|
|
3944
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3945
|
+
const code = err?.code;
|
|
3946
|
+
const isMissingSdk = msg.includes("@anthropic-ai/sdk") && (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || /cannot find (package|module)/i.test(msg) || /failed to (load|resolve)/i.test(msg));
|
|
3947
|
+
if (isMissingSdk) {
|
|
3948
|
+
throw new MemStackError("LLM_ERROR", "Anthropic adapter requires @anthropic-ai/sdk. Install it: npm install @anthropic-ai/sdk", {
|
|
3949
|
+
retryable: false
|
|
3950
|
+
});
|
|
3951
|
+
}
|
|
3952
|
+
throw new MemStackError("LLM_ERROR", `Anthropic request failed: ${msg}`, {
|
|
3873
3953
|
retryable: true
|
|
3874
3954
|
});
|
|
3875
3955
|
}
|
|
@@ -4158,7 +4238,7 @@ var GroqLLMAdapter = class {
|
|
|
4158
4238
|
promptTokens = usage.prompt_tokens;
|
|
4159
4239
|
completionTokens = usage.completion_tokens;
|
|
4160
4240
|
}
|
|
4161
|
-
if (content) {
|
|
4241
|
+
if (content || usage) {
|
|
4162
4242
|
yield {
|
|
4163
4243
|
text: content,
|
|
4164
4244
|
tokens: { prompt: promptTokens, completion: completionTokens, total: promptTokens + completionTokens }
|