@utaba/deep-memory 0.14.0 → 0.16.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.
@@ -1,4 +1,4 @@
1
- import { S as StorageRepositoryConfig, c as StoredRepository, d as RepositoryFilter, e as PaginatedResult, f as StoredRepositorySummary, g as RepositoryUpdate, h as DeleteProgressCallback, i as RepositoryStats, M as MemoryVocabulary, j as PaginationOptions, V as VocabularyChangeRecord, k as StoredEntity, l as StoredEntityUpdate, m as StorageFindQuery, n as StoredRelationship, o as RelationshipQueryOptions, p as StorageExploreOptions, q as StorageNeighborhood, r as StoragePathOptions, s as StoragePathResult, t as StorageTimelineOptions, u as StorageTimelineResult, v as ExportChunk, I as ImportChunk, B as BulkImportOptions, w as BulkImportResult } from './portability-DWpZbxNn.cjs';
1
+ import { S as StorageRepositoryConfig, c as StoredRepository, d as RepositoryFilter, e as PaginatedResult, f as StoredRepositorySummary, g as RepositoryUpdate, h as DeleteProgressCallback, i as RepositoryStats, M as MemoryVocabulary, j as PaginationOptions, V as VocabularyChangeRecord, k as StoredEntity, l as StoredEntityUpdate, m as StorageFindQuery, n as StoredRelationship, o as RelationshipQueryOptions, p as StorageExploreOptions, q as StorageNeighborhood, r as StoragePathOptions, s as StoragePathResult, t as StorageTimelineOptions, u as StorageTimelineResult, v as ExportChunk, I as ImportChunk, B as BulkImportOptions, w as BulkImportResult } from './portability-P1hL-bOa.cjs';
2
2
 
3
3
  /** Result returned by ensureSchema describing what actions were taken. */
4
4
  interface EnsureSchemaResult {
@@ -1,4 +1,4 @@
1
- import { S as StorageRepositoryConfig, c as StoredRepository, d as RepositoryFilter, e as PaginatedResult, f as StoredRepositorySummary, g as RepositoryUpdate, h as DeleteProgressCallback, i as RepositoryStats, M as MemoryVocabulary, j as PaginationOptions, V as VocabularyChangeRecord, k as StoredEntity, l as StoredEntityUpdate, m as StorageFindQuery, n as StoredRelationship, o as RelationshipQueryOptions, p as StorageExploreOptions, q as StorageNeighborhood, r as StoragePathOptions, s as StoragePathResult, t as StorageTimelineOptions, u as StorageTimelineResult, v as ExportChunk, I as ImportChunk, B as BulkImportOptions, w as BulkImportResult } from './portability-DWpZbxNn.js';
1
+ import { S as StorageRepositoryConfig, c as StoredRepository, d as RepositoryFilter, e as PaginatedResult, f as StoredRepositorySummary, g as RepositoryUpdate, h as DeleteProgressCallback, i as RepositoryStats, M as MemoryVocabulary, j as PaginationOptions, V as VocabularyChangeRecord, k as StoredEntity, l as StoredEntityUpdate, m as StorageFindQuery, n as StoredRelationship, o as RelationshipQueryOptions, p as StorageExploreOptions, q as StorageNeighborhood, r as StoragePathOptions, s as StoragePathResult, t as StorageTimelineOptions, u as StorageTimelineResult, v as ExportChunk, I as ImportChunk, B as BulkImportOptions, w as BulkImportResult } from './portability-P1hL-bOa.js';
2
2
 
3
3
  /** Result returned by ensureSchema describing what actions were taken. */
4
4
  interface EnsureSchemaResult {
package/dist/index.cjs CHANGED
@@ -39,6 +39,7 @@ __export(src_exports, {
39
39
  InvalidInputError: () => InvalidInputError,
40
40
  MemoryRepository: () => MemoryRepository,
41
41
  NoOpEmbeddingProvider: () => NoOpEmbeddingProvider,
42
+ OperationAbortedError: () => OperationAbortedError,
42
43
  OperationCancelledError: () => OperationCancelledError,
43
44
  ProviderError: () => ProviderError,
44
45
  RelationshipConstraintError: () => RelationshipConstraintError,
@@ -225,6 +226,18 @@ var OperationCancelledError = class extends DeepMemoryError {
225
226
  this.reason = reason;
226
227
  }
227
228
  };
229
+ var OperationAbortedError = class extends DeepMemoryError {
230
+ operation;
231
+ constructor(operation) {
232
+ super(
233
+ "OPERATION_ABORTED",
234
+ `${operation} aborted by caller`,
235
+ `The caller's AbortSignal was triggered. Partial state from completed batches has been retained.`
236
+ );
237
+ this.name = "OperationAbortedError";
238
+ this.operation = operation;
239
+ }
240
+ };
228
241
  var EmbeddingProviderRequiredError = class extends DeepMemoryError {
229
242
  constructor() {
230
243
  super(
@@ -881,10 +894,15 @@ var EntityManager = class {
881
894
  attempt++;
882
895
  if (attempt > maxRetries) {
883
896
  const errorMsg = err instanceof Error ? err.message : String(err);
897
+ const failureMessage = `embedBatch failed after ${maxRetries} retries: ${errorMsg}`;
898
+ const failed = entries.map(([id]) => ({ entityId: id, error: failureMessage }));
899
+ for (const { entityId, error } of failed) {
900
+ await options?.onItemFailed?.(entityId, error);
901
+ }
884
902
  return {
885
903
  processed: 0,
886
904
  failed: entries.length,
887
- errors: entries.map(([id]) => ({ entityId: id, error: `embedBatch failed after ${maxRetries} retries: ${errorMsg}` })),
905
+ errors: failed,
888
906
  modelId: this.embedding.modelId(),
889
907
  dimensions: this.embedding.dimensions()
890
908
  };
@@ -907,7 +925,9 @@ var EntityManager = class {
907
925
  });
908
926
  processed++;
909
927
  } catch (err) {
910
- errors.push({ entityId: id, error: err instanceof Error ? err.message : String(err) });
928
+ const errorMessage = err instanceof Error ? err.message : String(err);
929
+ errors.push({ entityId: id, error: errorMessage });
930
+ await options?.onItemFailed?.(id, errorMessage);
911
931
  }
912
932
  }
913
933
  return {
@@ -932,6 +952,7 @@ var EntityManager = class {
932
952
  }
933
953
  const batchSize = options?.batchSize ?? 50;
934
954
  const errorThreshold = options?.errorThresholdToAbort;
955
+ const signal = options?.signal;
935
956
  const firstPage = await this.storage.findEntities(this.repositoryId, { limit: 1, offset: 0 });
936
957
  const total = firstPage.total;
937
958
  let totalProcessed = 0;
@@ -939,6 +960,9 @@ var EntityManager = class {
939
960
  const allErrors = [];
940
961
  let offset = 0;
941
962
  while (offset < total) {
963
+ if (signal?.aborted) {
964
+ throw new OperationAbortedError("reembedAll");
965
+ }
942
966
  const page = await this.storage.findEntities(this.repositoryId, {
943
967
  limit: batchSize,
944
968
  offset
@@ -946,17 +970,21 @@ var EntityManager = class {
946
970
  if (page.items.length === 0) break;
947
971
  const ids = page.items.map((e) => e.id);
948
972
  const result = await this.reembedEntities(ids, {
949
- maxRetries: options?.maxRetries
973
+ maxRetries: options?.maxRetries,
974
+ onItemFailed: options?.onItemFailed
950
975
  });
951
976
  totalProcessed += result.processed;
952
977
  totalFailed += result.failed;
953
978
  allErrors.push(...result.errors);
954
- options?.onProgress?.(totalProcessed, total, totalFailed);
979
+ await options?.onProgress?.(totalProcessed, total, totalFailed);
955
980
  if (errorThreshold !== void 0 && totalFailed >= errorThreshold) {
956
981
  allErrors.push({ entityId: "", error: `Aborted: error threshold of ${errorThreshold} reached (${totalFailed} failures)` });
957
982
  break;
958
983
  }
959
984
  offset += page.items.length;
985
+ if (signal?.aborted) {
986
+ throw new OperationAbortedError("reembedAll");
987
+ }
960
988
  const delayMs = options?.delayBetweenBatchesMs ?? 0;
961
989
  if (delayMs > 0 && offset < total) {
962
990
  await new Promise((resolve) => {
@@ -2323,7 +2351,13 @@ var MemoryRepository = class {
2323
2351
  * the repository's embedding configuration is updated to the new values before
2324
2352
  * re-embedding, so all subsequent entity writes use the new model/dimensionality.
2325
2353
  *
2326
- * Processes in batches with optional rate limiting. Emits reembed:* events for progress tracking.
2354
+ * Processes in batches with optional rate limiting.
2355
+ *
2356
+ * Progress is delivered via the optional `onProgress` callback. Most callers
2357
+ * should use {@link DeepMemory.reembedAll} instead, which wires this callback
2358
+ * to the global event bus so `reembed:*` events are reachable via
2359
+ * `DeepMemory.on()`.
2360
+ *
2327
2361
  * Updates repository metadata with the new model ID and dimensions on completion.
2328
2362
  */
2329
2363
  async reembedAll(options) {
@@ -2358,46 +2392,26 @@ var MemoryRepository = class {
2358
2392
  }
2359
2393
  });
2360
2394
  }
2361
- const stats = await this.getStats();
2362
- await this.eventBus.emit("reembed:started", {
2363
- repositoryId: this.repositoryId,
2364
- totalEntities: stats.entityCount
2395
+ const result = await this.entityManager.reembedAll({
2396
+ batchSize: options?.batchSize,
2397
+ maxRetries: options?.maxRetries,
2398
+ errorThresholdToAbort: options?.errorThresholdToAbort,
2399
+ delayBetweenBatchesMs: options?.delayBetweenBatchesMs,
2400
+ signal: options?.signal,
2401
+ onProgress: async (processed, total, failed) => {
2402
+ await options?.onProgress?.({ processed, totalEntities: total, failed });
2403
+ },
2404
+ onItemFailed: async (entityId, error) => {
2405
+ await options?.onItemFailed?.({ entityId, error });
2406
+ }
2365
2407
  });
2366
- try {
2367
- const result = await this.entityManager.reembedAll({
2368
- batchSize: options?.batchSize,
2369
- maxRetries: options?.maxRetries,
2370
- errorThresholdToAbort: options?.errorThresholdToAbort,
2371
- delayBetweenBatchesMs: options?.delayBetweenBatchesMs,
2372
- onProgress: async (processed, total, failed) => {
2373
- await this.eventBus.emit("reembed:progress", {
2374
- repositoryId: this.repositoryId,
2375
- processed,
2376
- totalEntities: total,
2377
- failed
2378
- });
2379
- }
2380
- });
2381
- await this.storage.updateRepository(this.repositoryId, {
2382
- metadata: {
2383
- embeddingModelId: result.modelId,
2384
- embeddingDimensions: result.dimensions
2385
- }
2386
- });
2387
- await this.eventBus.emit("reembed:completed", {
2388
- repositoryId: this.repositoryId,
2389
- processed: result.processed,
2390
- failed: result.failed,
2391
- modelId: result.modelId
2392
- });
2393
- return result;
2394
- } catch (err) {
2395
- await this.eventBus.emit("reembed:failed", {
2396
- repositoryId: this.repositoryId,
2397
- error: err instanceof Error ? err.message : String(err)
2398
- });
2399
- throw err;
2400
- }
2408
+ await this.storage.updateRepository(this.repositoryId, {
2409
+ metadata: {
2410
+ embeddingModelId: result.modelId,
2411
+ embeddingDimensions: result.dimensions
2412
+ }
2413
+ });
2414
+ return result;
2401
2415
  }
2402
2416
  // ─── Relationships ─────────────────────────────────────────────────
2403
2417
  async createRelationships(inputs) {
@@ -3511,7 +3525,7 @@ var EventBus = class {
3511
3525
  };
3512
3526
 
3513
3527
  // src/portability/RepositoryExporter.ts
3514
- var LIBRARY_VERSION = true ? "0.14.0" : "0.1.0";
3528
+ var LIBRARY_VERSION = true ? "0.16.0" : "0.1.0";
3515
3529
  var RepositoryExporter = class {
3516
3530
  storage;
3517
3531
  provenance;
@@ -3865,11 +3879,20 @@ var RepositoryImporter = class {
3865
3879
  storage;
3866
3880
  actorId;
3867
3881
  onProgress;
3882
+ onItemFailed;
3883
+ signal;
3868
3884
  migrationEngine = new MigrationEngine();
3869
3885
  constructor(config) {
3870
3886
  this.storage = config.storage;
3871
3887
  this.actorId = config.actorId;
3872
3888
  this.onProgress = config.onProgress;
3889
+ this.onItemFailed = config.onItemFailed;
3890
+ this.signal = config.signal;
3891
+ }
3892
+ throwIfAborted() {
3893
+ if (this.signal?.aborted) {
3894
+ throw new OperationAbortedError("import");
3895
+ }
3873
3896
  }
3874
3897
  /**
3875
3898
  * Import an export archive according to the provided options.
@@ -3945,6 +3968,7 @@ var RepositoryImporter = class {
3945
3968
  let chunksCompleted = 0;
3946
3969
  const adaptiveConcurrencyHandle = options.bulk?.adaptiveConcurrencyHandle ?? {};
3947
3970
  for await (const chunk of chunks) {
3971
+ this.throwIfAborted();
3948
3972
  const bulkResult = await this.storage.importBulk(target.repositoryId, [chunk], {
3949
3973
  ...options.bulk,
3950
3974
  skipExistenceCheck: true,
@@ -3959,6 +3983,8 @@ var RepositoryImporter = class {
3959
3983
  message: `Failed to import ${e.item}: ${e.error}`,
3960
3984
  id: e.item
3961
3985
  });
3986
+ const itemType = chunk.entities?.some((x) => x.id === e.item) ? "entity" : chunk.relationships?.some((x) => x.id === e.item) ? "relationship" : "entity";
3987
+ await this.onItemFailed?.({ itemId: e.item, itemType, error: e.error });
3962
3988
  }
3963
3989
  await this.onProgress?.({
3964
3990
  repositoryId: target.repositoryId,
@@ -4026,6 +4052,7 @@ var RepositoryImporter = class {
4026
4052
  let relationshipsSkipped = 0;
4027
4053
  let chunksCompleted = 0;
4028
4054
  for await (const chunk of chunks) {
4055
+ this.throwIfAborted();
4029
4056
  if (chunk.entities) {
4030
4057
  for (const entity of chunk.entities) {
4031
4058
  let existing = await this.storage.getEntity(repositoryId, entity.id);
@@ -4094,11 +4121,13 @@ var RepositoryImporter = class {
4094
4121
  const targetExists = await this.storage.getEntity(repositoryId, rel.targetEntityId);
4095
4122
  if (!sourceExists || !targetExists) {
4096
4123
  relationshipsSkipped++;
4124
+ const errorMsg = `source or target entity missing`;
4097
4125
  warnings.push({
4098
4126
  code: "relationship_orphaned",
4099
- message: `Relationship "${rel.id}" skipped \u2014 source or target entity missing`,
4127
+ message: `Relationship "${rel.id}" skipped \u2014 ${errorMsg}`,
4100
4128
  relationshipId: rel.id
4101
4129
  });
4130
+ await this.onItemFailed?.({ itemId: rel.id, itemType: "relationship", error: errorMsg });
4102
4131
  } else {
4103
4132
  try {
4104
4133
  await this.storage.createRelationship(repositoryId, rel);
@@ -4444,22 +4473,37 @@ var DeepMemory = class {
4444
4473
  const importer = new RepositoryImporter({
4445
4474
  storage: this.storage,
4446
4475
  actorId: this.provenance.getContext().actorId,
4447
- onProgress: (progress) => this.globalEventBus.emit("import:progress", progress)
4476
+ onProgress: (progress) => this.globalEventBus.emit("import:progress", progress),
4477
+ onItemFailed: (failure) => this.globalEventBus.emit("import:item-failed", {
4478
+ repositoryId: options.target.repositoryId,
4479
+ itemId: failure.itemId,
4480
+ itemType: failure.itemType,
4481
+ error: failure.error
4482
+ }),
4483
+ signal: options.signal
4448
4484
  });
4449
- const result = await importer.import(archive, options);
4450
- if (result.success) {
4451
- await this.globalEventBus.emit("import:completed", {
4452
- repositoryId: result.repositoryId,
4453
- entitiesImported: result.statistics.entitiesImported,
4454
- relationshipsImported: result.statistics.relationshipsImported
4455
- });
4456
- } else {
4485
+ try {
4486
+ const result = await importer.import(archive, options);
4487
+ if (result.success) {
4488
+ await this.globalEventBus.emit("import:completed", {
4489
+ repositoryId: result.repositoryId,
4490
+ entitiesImported: result.statistics.entitiesImported,
4491
+ relationshipsImported: result.statistics.relationshipsImported
4492
+ });
4493
+ } else {
4494
+ await this.globalEventBus.emit("import:failed", {
4495
+ repositoryId: result.repositoryId,
4496
+ error: result.warnings.map((w) => w.message).join("; ")
4497
+ });
4498
+ }
4499
+ return result;
4500
+ } catch (err) {
4457
4501
  await this.globalEventBus.emit("import:failed", {
4458
- repositoryId: result.repositoryId,
4459
- error: result.warnings.map((w) => w.message).join("; ")
4502
+ repositoryId: options.target.repositoryId,
4503
+ error: err instanceof Error ? err.message : String(err)
4460
4504
  });
4505
+ throw err;
4461
4506
  }
4462
- return result;
4463
4507
  }
4464
4508
  /**
4465
4509
  * Stream a repository export as an async generator.
@@ -4528,23 +4572,88 @@ var DeepMemory = class {
4528
4572
  const importer = new RepositoryImporter({
4529
4573
  storage: this.storage,
4530
4574
  actorId: this.provenance.getContext().actorId,
4531
- onProgress: (progress) => this.globalEventBus.emit("import:progress", progress)
4575
+ onProgress: (progress) => this.globalEventBus.emit("import:progress", progress),
4576
+ onItemFailed: (failure) => this.globalEventBus.emit("import:item-failed", {
4577
+ repositoryId: options.target.repositoryId,
4578
+ itemId: failure.itemId,
4579
+ itemType: failure.itemType,
4580
+ error: failure.error
4581
+ }),
4582
+ signal: options.signal
4532
4583
  });
4533
4584
  await this.globalEventBus.emit("import:started", { repositoryId: options.target.repositoryId });
4534
- const result = await importer.importStream(header, chunks, options);
4535
- if (result.success) {
4536
- await this.globalEventBus.emit("import:completed", {
4537
- repositoryId: result.repositoryId,
4538
- entitiesImported: result.statistics.entitiesImported,
4539
- relationshipsImported: result.statistics.relationshipsImported
4540
- });
4541
- } else {
4585
+ try {
4586
+ const result = await importer.importStream(header, chunks, options);
4587
+ if (result.success) {
4588
+ await this.globalEventBus.emit("import:completed", {
4589
+ repositoryId: result.repositoryId,
4590
+ entitiesImported: result.statistics.entitiesImported,
4591
+ relationshipsImported: result.statistics.relationshipsImported
4592
+ });
4593
+ } else {
4594
+ await this.globalEventBus.emit("import:failed", {
4595
+ repositoryId: result.repositoryId,
4596
+ error: result.warnings.map((w) => w.message).join("; ")
4597
+ });
4598
+ }
4599
+ return result;
4600
+ } catch (err) {
4542
4601
  await this.globalEventBus.emit("import:failed", {
4543
- repositoryId: result.repositoryId,
4544
- error: result.warnings.map((w) => w.message).join("; ")
4602
+ repositoryId: options.target.repositoryId,
4603
+ error: err instanceof Error ? err.message : String(err)
4545
4604
  });
4605
+ throw err;
4606
+ }
4607
+ }
4608
+ /**
4609
+ * Re-embed all entities in a repository, emitting `reembed:*` events on the
4610
+ * global event bus (reachable via {@link DeepMemory.on}).
4611
+ *
4612
+ * Mirrors the lifecycle of {@link importRepositoryStream}: emits
4613
+ * `reembed:started` up front, `reembed:progress` after every batch,
4614
+ * and either `reembed:completed` or `reembed:failed` at the end.
4615
+ *
4616
+ * `options.signal` is honoured at batch boundaries — any vectors already
4617
+ * written by completed batches are retained, and `reembed:failed` is emitted
4618
+ * with the abort error message.
4619
+ */
4620
+ async reembedAll(repositoryId, options) {
4621
+ await this.ensureInitialized();
4622
+ const repo = await this.openRepository(repositoryId);
4623
+ const stats = await repo.getStats();
4624
+ await this.globalEventBus.emit("reembed:started", {
4625
+ repositoryId,
4626
+ totalEntities: stats.entityCount
4627
+ });
4628
+ try {
4629
+ const result = await repo.reembedAll({
4630
+ ...options,
4631
+ onProgress: (progress) => this.globalEventBus.emit("reembed:progress", {
4632
+ repositoryId,
4633
+ processed: progress.processed,
4634
+ totalEntities: progress.totalEntities,
4635
+ failed: progress.failed
4636
+ }),
4637
+ onItemFailed: (failure) => this.globalEventBus.emit("reembed:item-failed", {
4638
+ repositoryId,
4639
+ entityId: failure.entityId,
4640
+ error: failure.error
4641
+ })
4642
+ });
4643
+ await this.globalEventBus.emit("reembed:completed", {
4644
+ repositoryId,
4645
+ processed: result.processed,
4646
+ failed: result.failed,
4647
+ modelId: result.modelId
4648
+ });
4649
+ return result;
4650
+ } catch (err) {
4651
+ await this.globalEventBus.emit("reembed:failed", {
4652
+ repositoryId,
4653
+ error: err instanceof Error ? err.message : String(err)
4654
+ });
4655
+ throw err;
4546
4656
  }
4547
- return result;
4548
4657
  }
4549
4658
  /** Subscribe to global events */
4550
4659
  on(event, handler) {
@@ -5646,6 +5755,7 @@ var NoOpEmbeddingProvider = class {
5646
5755
  InvalidInputError,
5647
5756
  MemoryRepository,
5648
5757
  NoOpEmbeddingProvider,
5758
+ OperationAbortedError,
5649
5759
  OperationCancelledError,
5650
5760
  ProviderError,
5651
5761
  RelationshipConstraintError,