@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/dist/index.d.cts
CHANGED
|
@@ -64,6 +64,7 @@ interface MemoryStoreInput {
|
|
|
64
64
|
sourceId?: string;
|
|
65
65
|
metadata?: Record<string, unknown>;
|
|
66
66
|
expiresAt?: Date;
|
|
67
|
+
createdAt?: Date;
|
|
67
68
|
onConflict?: "append";
|
|
68
69
|
/** Pre-assigned ID. When set, the storage adapter uses it instead of generating one. */
|
|
69
70
|
id?: string;
|
|
@@ -106,6 +107,7 @@ interface SummarizeOptions {
|
|
|
106
107
|
}
|
|
107
108
|
interface PruneStrategy {
|
|
108
109
|
type: "byAge" | "byImportance" | "byCount" | "byType" | "custom" | "compose";
|
|
110
|
+
actorId?: string;
|
|
109
111
|
maxAge?: number;
|
|
110
112
|
minImportance?: number;
|
|
111
113
|
maxPerActor?: number;
|
|
@@ -333,6 +335,13 @@ declare class DiskStorageAdapter implements StorageProvider {
|
|
|
333
335
|
private _readFile;
|
|
334
336
|
private _writeFile;
|
|
335
337
|
private _withWriteLock;
|
|
338
|
+
/**
|
|
339
|
+
* Acquires an exclusive cross-process lock for `actorId` via `open(path, "wx")`
|
|
340
|
+
* (atomic create-if-absent). Retries with jittered backoff while the lock is
|
|
341
|
+
* held elsewhere, and steals locks left behind by a crashed process once
|
|
342
|
+
* they're older than `staleMs`.
|
|
343
|
+
*/
|
|
344
|
+
private _acquireFileLock;
|
|
336
345
|
private _listActors;
|
|
337
346
|
private _buildIndex;
|
|
338
347
|
private _toExternal;
|
package/dist/index.d.ts
CHANGED
|
@@ -64,6 +64,7 @@ interface MemoryStoreInput {
|
|
|
64
64
|
sourceId?: string;
|
|
65
65
|
metadata?: Record<string, unknown>;
|
|
66
66
|
expiresAt?: Date;
|
|
67
|
+
createdAt?: Date;
|
|
67
68
|
onConflict?: "append";
|
|
68
69
|
/** Pre-assigned ID. When set, the storage adapter uses it instead of generating one. */
|
|
69
70
|
id?: string;
|
|
@@ -106,6 +107,7 @@ interface SummarizeOptions {
|
|
|
106
107
|
}
|
|
107
108
|
interface PruneStrategy {
|
|
108
109
|
type: "byAge" | "byImportance" | "byCount" | "byType" | "custom" | "compose";
|
|
110
|
+
actorId?: string;
|
|
109
111
|
maxAge?: number;
|
|
110
112
|
minImportance?: number;
|
|
111
113
|
maxPerActor?: number;
|
|
@@ -333,6 +335,13 @@ declare class DiskStorageAdapter implements StorageProvider {
|
|
|
333
335
|
private _readFile;
|
|
334
336
|
private _writeFile;
|
|
335
337
|
private _withWriteLock;
|
|
338
|
+
/**
|
|
339
|
+
* Acquires an exclusive cross-process lock for `actorId` via `open(path, "wx")`
|
|
340
|
+
* (atomic create-if-absent). Retries with jittered backoff while the lock is
|
|
341
|
+
* held elsewhere, and steals locks left behind by a crashed process once
|
|
342
|
+
* they're older than `staleMs`.
|
|
343
|
+
*/
|
|
344
|
+
private _acquireFileLock;
|
|
336
345
|
private _listActors;
|
|
337
346
|
private _buildIndex;
|
|
338
347
|
private _toExternal;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile, readdir, appendFile } from 'fs/promises';
|
|
1
|
+
import { mkdir, readFile, writeFile, rename, unlink, open, stat, readdir, appendFile } from 'fs/promises';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
|
|
4
4
|
// src/errors.ts
|
|
@@ -598,7 +598,7 @@ var MemoryStore = class {
|
|
|
598
598
|
}
|
|
599
599
|
async prune(strategy) {
|
|
600
600
|
await this._ensureInit();
|
|
601
|
-
const allMemories = await this.storage.retrieve({ limit: this.limits.pruneScan });
|
|
601
|
+
const allMemories = await this.storage.retrieve({ actorId: strategy.actorId, limit: this.limits.pruneScan });
|
|
602
602
|
const kept = this.pruner.execute(allMemories, strategy);
|
|
603
603
|
const keptIds = new Set(kept.map((k) => k.id));
|
|
604
604
|
const prunedIds = allMemories.filter((m) => !keptIds.has(m.id)).map((m) => m.id);
|
|
@@ -610,7 +610,7 @@ var MemoryStore = class {
|
|
|
610
610
|
}
|
|
611
611
|
async dryRunPrune(strategy) {
|
|
612
612
|
await this._ensureInit();
|
|
613
|
-
const allMemories = await this.storage.retrieve({ limit: this.limits.pruneScan });
|
|
613
|
+
const allMemories = await this.storage.retrieve({ actorId: strategy.actorId, limit: this.limits.pruneScan });
|
|
614
614
|
const kept = this.pruner.execute(allMemories, strategy);
|
|
615
615
|
const keptIds = new Set(kept.map((k) => k.id));
|
|
616
616
|
const wouldPrune = allMemories.filter((m) => !keptIds.has(m.id)).map((m) => m.id);
|
|
@@ -744,7 +744,7 @@ var InMemoryStorageAdapter = class {
|
|
|
744
744
|
sourceId: input.sourceId,
|
|
745
745
|
metadata: input.metadata ?? {},
|
|
746
746
|
expiresAt: input.expiresAt,
|
|
747
|
-
createdAt: now,
|
|
747
|
+
createdAt: input.createdAt ?? now,
|
|
748
748
|
_touchedAt: now
|
|
749
749
|
};
|
|
750
750
|
this.memories.set(memory.id, memory);
|
|
@@ -794,8 +794,11 @@ var InMemoryStorageAdapter = class {
|
|
|
794
794
|
results = results.filter((m) => m.tags?.some((t) => query.tags.includes(t)));
|
|
795
795
|
}
|
|
796
796
|
if (query.query) {
|
|
797
|
-
const
|
|
798
|
-
results = results.filter((m) =>
|
|
797
|
+
const terms = query.query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
798
|
+
results = results.filter((m) => {
|
|
799
|
+
const content = m.content.toLowerCase();
|
|
800
|
+
return terms.some((t) => content.includes(t));
|
|
801
|
+
});
|
|
799
802
|
}
|
|
800
803
|
if (query.createdAfter) {
|
|
801
804
|
results = results.filter((m) => m.createdAt >= query.createdAfter);
|
|
@@ -981,7 +984,19 @@ var MemStack = class {
|
|
|
981
984
|
if (snapshot.version !== 1) {
|
|
982
985
|
throw validationError(`Unsupported snapshot version: ${snapshot.version}`);
|
|
983
986
|
}
|
|
984
|
-
|
|
987
|
+
const memories = snapshot.memories.map((m, i) => {
|
|
988
|
+
const createdAt = typeof m.createdAt === "string" ? new Date(m.createdAt) : m.createdAt;
|
|
989
|
+
const expiresAt = typeof m.expiresAt === "string" ? new Date(m.expiresAt) : m.expiresAt;
|
|
990
|
+
const label = m.id ? `id: ${m.id}` : `index ${i}`;
|
|
991
|
+
if (createdAt && isNaN(createdAt.getTime())) {
|
|
992
|
+
throw validationError(`Invalid createdAt for memory (${label}): ${String(m.createdAt)}`);
|
|
993
|
+
}
|
|
994
|
+
if (expiresAt && isNaN(expiresAt.getTime())) {
|
|
995
|
+
throw validationError(`Invalid expiresAt for memory (${label}): ${String(m.expiresAt)}`);
|
|
996
|
+
}
|
|
997
|
+
return { ...m, createdAt, expiresAt };
|
|
998
|
+
});
|
|
999
|
+
await this.memory.storeBatch(memories);
|
|
985
1000
|
}
|
|
986
1001
|
async health() {
|
|
987
1002
|
const status = { storage: false, llm: false, embedding: false };
|
|
@@ -1068,7 +1083,7 @@ var DiskStorageAdapter = class {
|
|
|
1068
1083
|
sourceId: input.sourceId,
|
|
1069
1084
|
metadata: input.metadata ?? {},
|
|
1070
1085
|
expiresAt: input.expiresAt,
|
|
1071
|
-
createdAt: now,
|
|
1086
|
+
createdAt: input.createdAt ?? now,
|
|
1072
1087
|
_touchedAt: now.toISOString()
|
|
1073
1088
|
};
|
|
1074
1089
|
await this._withWriteLock(input.actorId, async () => {
|
|
@@ -1101,7 +1116,7 @@ var DiskStorageAdapter = class {
|
|
|
1101
1116
|
sourceId: input.sourceId,
|
|
1102
1117
|
metadata: input.metadata ?? {},
|
|
1103
1118
|
expiresAt: input.expiresAt,
|
|
1104
|
-
createdAt: now,
|
|
1119
|
+
createdAt: input.createdAt ?? now,
|
|
1105
1120
|
_touchedAt: now.toISOString()
|
|
1106
1121
|
};
|
|
1107
1122
|
all.push(memory);
|
|
@@ -1208,8 +1223,11 @@ var DiskStorageAdapter = class {
|
|
|
1208
1223
|
records = records.filter((r) => r.tags?.some((t) => query.tags.includes(t)));
|
|
1209
1224
|
}
|
|
1210
1225
|
if (query.query) {
|
|
1211
|
-
const
|
|
1212
|
-
records = records.filter((r) =>
|
|
1226
|
+
const terms = query.query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
1227
|
+
records = records.filter((r) => {
|
|
1228
|
+
const content = r.content.toLowerCase();
|
|
1229
|
+
return terms.some((t) => content.includes(t));
|
|
1230
|
+
});
|
|
1213
1231
|
}
|
|
1214
1232
|
results.push(...records);
|
|
1215
1233
|
}
|
|
@@ -1300,9 +1318,14 @@ var DiskStorageAdapter = class {
|
|
|
1300
1318
|
}
|
|
1301
1319
|
}
|
|
1302
1320
|
async _writeFile(actorId, records) {
|
|
1321
|
+
const filePath = this._filePath(actorId);
|
|
1322
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
1303
1323
|
try {
|
|
1304
|
-
await writeFile(
|
|
1324
|
+
await writeFile(tmpPath, JSON.stringify(records, null, 2), "utf-8");
|
|
1325
|
+
await rename(tmpPath, filePath);
|
|
1305
1326
|
} catch (err) {
|
|
1327
|
+
await unlink(tmpPath).catch(() => {
|
|
1328
|
+
});
|
|
1306
1329
|
throw storageError(`Failed to write memories for actor ${actorId}: ${err.message}`);
|
|
1307
1330
|
}
|
|
1308
1331
|
}
|
|
@@ -1314,15 +1337,57 @@ var DiskStorageAdapter = class {
|
|
|
1314
1337
|
});
|
|
1315
1338
|
this._writeLocks.set(actorId, next);
|
|
1316
1339
|
await prev;
|
|
1340
|
+
const releaseFileLock = await this._acquireFileLock(actorId);
|
|
1317
1341
|
try {
|
|
1318
1342
|
await fn();
|
|
1319
1343
|
} finally {
|
|
1344
|
+
await releaseFileLock();
|
|
1320
1345
|
resolve();
|
|
1321
1346
|
if (this._writeLocks.get(actorId) === next) {
|
|
1322
1347
|
this._writeLocks.delete(actorId);
|
|
1323
1348
|
}
|
|
1324
1349
|
}
|
|
1325
1350
|
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Acquires an exclusive cross-process lock for `actorId` via `open(path, "wx")`
|
|
1353
|
+
* (atomic create-if-absent). Retries with jittered backoff while the lock is
|
|
1354
|
+
* held elsewhere, and steals locks left behind by a crashed process once
|
|
1355
|
+
* they're older than `staleMs`.
|
|
1356
|
+
*/
|
|
1357
|
+
async _acquireFileLock(actorId) {
|
|
1358
|
+
const lockPath = `${this._filePath(actorId)}.lock`;
|
|
1359
|
+
const staleMs = 1e4;
|
|
1360
|
+
const timeoutMs = 3e4;
|
|
1361
|
+
const start = Date.now();
|
|
1362
|
+
while (true) {
|
|
1363
|
+
try {
|
|
1364
|
+
const handle = await open(lockPath, "wx");
|
|
1365
|
+
await handle.close();
|
|
1366
|
+
return async () => {
|
|
1367
|
+
await unlink(lockPath).catch(() => {
|
|
1368
|
+
});
|
|
1369
|
+
};
|
|
1370
|
+
} catch (err) {
|
|
1371
|
+
if (err.code !== "EEXIST") {
|
|
1372
|
+
throw storageError(`Failed to acquire lock for actor ${actorId}: ${err.message}`);
|
|
1373
|
+
}
|
|
1374
|
+
try {
|
|
1375
|
+
const st = await stat(lockPath);
|
|
1376
|
+
if (Date.now() - st.mtimeMs > staleMs) {
|
|
1377
|
+
await unlink(lockPath).catch(() => {
|
|
1378
|
+
});
|
|
1379
|
+
continue;
|
|
1380
|
+
}
|
|
1381
|
+
} catch {
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
if (Date.now() - start > timeoutMs) {
|
|
1385
|
+
throw storageError(`Timed out waiting for disk storage lock: ${lockPath}`);
|
|
1386
|
+
}
|
|
1387
|
+
await new Promise((r) => setTimeout(r, 15 + Math.random() * 35));
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1326
1391
|
async _listActors() {
|
|
1327
1392
|
try {
|
|
1328
1393
|
const entries = await readdir(this.dir);
|
|
@@ -1380,7 +1445,7 @@ var MarkdownStorageAdapter = class {
|
|
|
1380
1445
|
sourceId: input.sourceId,
|
|
1381
1446
|
metadata: input.metadata ?? {},
|
|
1382
1447
|
expiresAt: input.expiresAt,
|
|
1383
|
-
createdAt: now
|
|
1448
|
+
createdAt: input.createdAt ?? now
|
|
1384
1449
|
};
|
|
1385
1450
|
const filePath = this._filePath(input.actorId);
|
|
1386
1451
|
const block = this._formatBlock(memory);
|
|
@@ -1457,8 +1522,11 @@ var MarkdownStorageAdapter = class {
|
|
|
1457
1522
|
results = results.filter((m) => m.tags?.some((t) => query.tags.includes(t)));
|
|
1458
1523
|
}
|
|
1459
1524
|
if (query.query) {
|
|
1460
|
-
const
|
|
1461
|
-
results = results.filter((m) =>
|
|
1525
|
+
const terms = query.query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
1526
|
+
results = results.filter((m) => {
|
|
1527
|
+
const content = m.content.toLowerCase();
|
|
1528
|
+
return terms.some((t) => content.includes(t));
|
|
1529
|
+
});
|
|
1462
1530
|
}
|
|
1463
1531
|
switch (query.strategy) {
|
|
1464
1532
|
case "recent":
|
|
@@ -2009,7 +2077,7 @@ var PostgresStorageAdapter = class {
|
|
|
2009
2077
|
sourceId: input.sourceId,
|
|
2010
2078
|
metadata: input.metadata ?? {},
|
|
2011
2079
|
expiresAt: input.expiresAt,
|
|
2012
|
-
createdAt: new Date(now)
|
|
2080
|
+
createdAt: input.createdAt ?? new Date(now)
|
|
2013
2081
|
};
|
|
2014
2082
|
}
|
|
2015
2083
|
async storeBatch(inputs) {
|
|
@@ -2212,7 +2280,7 @@ var RedisStorageAdapter = class {
|
|
|
2212
2280
|
sourceId: input.sourceId,
|
|
2213
2281
|
metadata: input.metadata ?? {},
|
|
2214
2282
|
expiresAt: input.expiresAt,
|
|
2215
|
-
createdAt: now,
|
|
2283
|
+
createdAt: input.createdAt ?? now,
|
|
2216
2284
|
_touchedAt: now.toISOString()
|
|
2217
2285
|
};
|
|
2218
2286
|
await this.redis.set(this._memKey(memory.id), JSON.stringify(memory));
|
|
@@ -2236,7 +2304,7 @@ var RedisStorageAdapter = class {
|
|
|
2236
2304
|
sourceId: input.sourceId,
|
|
2237
2305
|
metadata: input.metadata ?? {},
|
|
2238
2306
|
expiresAt: input.expiresAt,
|
|
2239
|
-
createdAt: now,
|
|
2307
|
+
createdAt: input.createdAt ?? now,
|
|
2240
2308
|
_touchedAt: now.toISOString()
|
|
2241
2309
|
};
|
|
2242
2310
|
await this.redis.set(this._memKey(memory.id), JSON.stringify(memory));
|
|
@@ -2432,7 +2500,7 @@ var QdrantStorageAdapter = class {
|
|
|
2432
2500
|
sourceId: input.sourceId,
|
|
2433
2501
|
metadata: input.metadata ?? {},
|
|
2434
2502
|
expiresAt: input.expiresAt,
|
|
2435
|
-
createdAt: now
|
|
2503
|
+
createdAt: input.createdAt ?? now
|
|
2436
2504
|
};
|
|
2437
2505
|
const qdrantId = crypto.randomUUID();
|
|
2438
2506
|
await this.client.upsert(this.collectionName, {
|
|
@@ -2464,7 +2532,7 @@ var QdrantStorageAdapter = class {
|
|
|
2464
2532
|
sourceId: input.sourceId,
|
|
2465
2533
|
metadata: input.metadata ?? {},
|
|
2466
2534
|
expiresAt: input.expiresAt,
|
|
2467
|
-
createdAt: now
|
|
2535
|
+
createdAt: input.createdAt ?? now
|
|
2468
2536
|
};
|
|
2469
2537
|
results.push(memory);
|
|
2470
2538
|
const qid = crypto.randomUUID();
|
|
@@ -2642,10 +2710,13 @@ var Neo4jStorageAdapter = class {
|
|
|
2642
2710
|
sourceId: input.sourceId ?? null,
|
|
2643
2711
|
metadata: JSON.stringify(input.metadata ?? {}),
|
|
2644
2712
|
expiresAt: input.expiresAt?.toISOString() ?? null,
|
|
2645
|
-
createdAt: now,
|
|
2713
|
+
createdAt: input.createdAt?.toISOString() ?? now,
|
|
2646
2714
|
embedding: input.embedding ?? null
|
|
2647
2715
|
};
|
|
2648
|
-
const
|
|
2716
|
+
const updateProps = { ...props };
|
|
2717
|
+
if (!input.createdAt) {
|
|
2718
|
+
delete updateProps.createdAt;
|
|
2719
|
+
}
|
|
2649
2720
|
const session = this.driver.session({ database: this.database });
|
|
2650
2721
|
try {
|
|
2651
2722
|
await session.run(
|
|
@@ -2667,7 +2738,7 @@ var Neo4jStorageAdapter = class {
|
|
|
2667
2738
|
sourceId: input.sourceId,
|
|
2668
2739
|
metadata: input.metadata ?? {},
|
|
2669
2740
|
expiresAt: input.expiresAt,
|
|
2670
|
-
createdAt: new Date(now)
|
|
2741
|
+
createdAt: input.createdAt ?? new Date(now)
|
|
2671
2742
|
};
|
|
2672
2743
|
}
|
|
2673
2744
|
async storeBatch(inputs) {
|
|
@@ -2904,7 +2975,7 @@ var WeaviateStorageAdapter = class {
|
|
|
2904
2975
|
sourceId: input.sourceId,
|
|
2905
2976
|
metadata: input.metadata ?? {},
|
|
2906
2977
|
expiresAt: input.expiresAt,
|
|
2907
|
-
createdAt: now
|
|
2978
|
+
createdAt: input.createdAt ?? now
|
|
2908
2979
|
};
|
|
2909
2980
|
const wvId = await this.collection().data.insert({
|
|
2910
2981
|
id: crypto.randomUUID(),
|
|
@@ -3137,7 +3208,7 @@ var LanceDBStorageAdapter = class {
|
|
|
3137
3208
|
sourceId: input.sourceId,
|
|
3138
3209
|
metadata: input.metadata ?? {},
|
|
3139
3210
|
expiresAt: input.expiresAt,
|
|
3140
|
-
createdAt: now
|
|
3211
|
+
createdAt: input.createdAt ?? now
|
|
3141
3212
|
};
|
|
3142
3213
|
}
|
|
3143
3214
|
async storeBatch(inputs) {
|
|
@@ -3305,7 +3376,7 @@ var MongoDBStorageAdapter = class {
|
|
|
3305
3376
|
sourceId: input.sourceId,
|
|
3306
3377
|
metadata: input.metadata ?? {},
|
|
3307
3378
|
expiresAt: input.expiresAt,
|
|
3308
|
-
createdAt: now
|
|
3379
|
+
createdAt: input.createdAt ?? now
|
|
3309
3380
|
};
|
|
3310
3381
|
await this.collection.insertOne(this.toDoc(memory));
|
|
3311
3382
|
return memory;
|
|
@@ -3534,7 +3605,7 @@ var SQLiteStorageAdapter = class {
|
|
|
3534
3605
|
sourceId: input.sourceId,
|
|
3535
3606
|
metadata: input.metadata ?? {},
|
|
3536
3607
|
expiresAt: input.expiresAt,
|
|
3537
|
-
createdAt: new Date(now)
|
|
3608
|
+
createdAt: input.createdAt ?? new Date(now)
|
|
3538
3609
|
};
|
|
3539
3610
|
}
|
|
3540
3611
|
async storeBatch(inputs) {
|
|
@@ -3807,11 +3878,12 @@ var OpenAILLMAdapter = class {
|
|
|
3807
3878
|
try {
|
|
3808
3879
|
const parsed = JSON.parse(data);
|
|
3809
3880
|
const content = parsed.choices?.[0]?.delta?.content ?? "";
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3881
|
+
const usage = parsed.usage;
|
|
3882
|
+
if (usage) {
|
|
3883
|
+
promptTokens = usage.prompt_tokens;
|
|
3884
|
+
completionTokens = usage.completion_tokens;
|
|
3813
3885
|
}
|
|
3814
|
-
if (content) {
|
|
3886
|
+
if (content || usage) {
|
|
3815
3887
|
yield {
|
|
3816
3888
|
text: content,
|
|
3817
3889
|
tokens: { prompt: promptTokens, completion: completionTokens, total: promptTokens + completionTokens }
|
|
@@ -3867,7 +3939,15 @@ var AnthropicLLMAdapter = class {
|
|
|
3867
3939
|
};
|
|
3868
3940
|
} catch (err) {
|
|
3869
3941
|
if (err instanceof MemStackError) throw err;
|
|
3870
|
-
|
|
3942
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3943
|
+
const code = err?.code;
|
|
3944
|
+
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));
|
|
3945
|
+
if (isMissingSdk) {
|
|
3946
|
+
throw new MemStackError("LLM_ERROR", "Anthropic adapter requires @anthropic-ai/sdk. Install it: npm install @anthropic-ai/sdk", {
|
|
3947
|
+
retryable: false
|
|
3948
|
+
});
|
|
3949
|
+
}
|
|
3950
|
+
throw new MemStackError("LLM_ERROR", `Anthropic request failed: ${msg}`, {
|
|
3871
3951
|
retryable: true
|
|
3872
3952
|
});
|
|
3873
3953
|
}
|
|
@@ -4156,7 +4236,7 @@ var GroqLLMAdapter = class {
|
|
|
4156
4236
|
promptTokens = usage.prompt_tokens;
|
|
4157
4237
|
completionTokens = usage.completion_tokens;
|
|
4158
4238
|
}
|
|
4159
|
-
if (content) {
|
|
4239
|
+
if (content || usage) {
|
|
4160
4240
|
yield {
|
|
4161
4241
|
text: content,
|
|
4162
4242
|
tokens: { prompt: promptTokens, completion: completionTokens, total: promptTokens + completionTokens }
|