@dudousxd/nestjs-agent-rag 0.14.1 → 0.14.3

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/dist/index.d.cts CHANGED
@@ -281,8 +281,8 @@ interface LexicalVectorStore extends VectorStore {
281
281
  /**
282
282
  * An **optional** capability: swap all of a document's chunks safely under concurrent calls, so two
283
283
  * writers can't leave a mix of versions behind. Postgres also makes the swap atomic for searches and
284
- * crashes; Redis can't, and only guarantees the end state. {@link replaceDocumentChunks} falls back to
285
- * `remove` plus `upsert` for the others.
284
+ * crashes, and so does Redis up to a size limit (see `RedisVectorStore.replaceDocument`).
285
+ * {@link replaceDocumentChunks} falls back to `remove` plus `upsert` for the others.
286
286
  */
287
287
  interface AtomicReplaceVectorStore extends VectorStore {
288
288
  replaceDocument(documentId: string, records: VectorRecord[]): Promise<void>;
@@ -407,8 +407,9 @@ declare function describeSharedSource(legs: readonly unknown[]): Omit<RetrievalD
407
407
  * (`removeWhere` refusing an empty filter, an empty-array filter deleting nothing) are behavioural,
408
408
  * not incidental to Redis.
409
409
  */
410
- declare class MemoryVectorStore implements VectorStore {
410
+ declare class MemoryVectorStore implements AtomicReplaceVectorStore {
411
411
  private readonly records;
412
+ private readonly replacing;
412
413
  /**
413
414
  * Telemetry self-description. No `collection`: there is no namespace to name — the corpus IS this
414
415
  * instance, and two `MemoryVectorStore`s in one process are two corpora with nothing to tell them
@@ -416,7 +417,11 @@ declare class MemoryVectorStore implements VectorStore {
416
417
  */
417
418
  describeRetrieval(): RetrievalDescriptor;
418
419
  upsert(records: VectorRecord[]): Promise<void>;
420
+ /** A replace and a removal of one document take turns, in the order they were called. */
421
+ replaceDocument(documentId: string, records: VectorRecord[]): Promise<void>;
419
422
  remove(documentId: string): Promise<void>;
423
+ private inTurn;
424
+ private deleteChunks;
420
425
  /**
421
426
  * Merge `patch` into every chunk of `documentId`, leaving `text` and `embedding` untouched. The
422
427
  * record is *replaced* rather than mutated: `upsert` stores the caller's own object, and patching
@@ -878,18 +883,28 @@ declare class RedisVectorStore implements LexicalVectorStore, AtomicReplaceVecto
878
883
  private reconcileSchema;
879
884
  private createIndex;
880
885
  upsert(records: VectorRecord[]): Promise<void>;
886
+ private chunkFields;
881
887
  /**
882
- * Replaces a document's chunks, one writer at a time per document. Redis can't swap them in one step
883
- * without scanning the keyspace inside a script, so a search may briefly see old and new chunks
884
- * together, but concurrent calls can't leave them mixed. The new chunks go in before the old ones
885
- * come out, so a crash leaves the document searchable and the next call repairs it.
888
+ * Replaces a document's chunks, one writer at a time per document. The swap is one script, so a search
889
+ * sees the old version or the new one, and a crash leaves the old one. The keys to drop are found
890
+ * first, under the lock, because a script can't scan the keyspace.
891
+ *
892
+ * A document past {@link SWAP_MAX_CHUNKS} would block Redis for too long that way. Its new chunks go in
893
+ * first and the old ones come out after, so a search may briefly see both, and a crash leaves the
894
+ * document searchable for the next call to repair.
886
895
  */
887
896
  replaceDocument(documentId: string, records: VectorRecord[]): Promise<void>;
897
+ private swapChunks;
898
+ private lockKey;
888
899
  /**
889
- * A lease, so a crashed writer can't block the document forever. It is renewed while the work runs,
890
- * which keeps a large document from outliving it.
900
+ * A lease on each document, so a crashed writer can't block one forever. They are renewed while the
901
+ * work runs, which keeps a large document from outliving them.
902
+ *
903
+ * Several documents are locked in sorted order, without duplicates. Every other caller either holds
904
+ * one lock or takes them in that same order, so no two calls can wait on each other.
891
905
  */
892
- private withDocumentLock;
906
+ private withDocumentLocks;
907
+ private acquireLock;
893
908
  /**
894
909
  * Single-document deletion, by `SCAN … MATCH`.
895
910
  *
@@ -990,6 +1005,9 @@ declare class RedisVectorStore implements LexicalVectorStore, AtomicReplaceVecto
990
1005
  *
991
1006
  * It deletes by key, exactly as `remove` does, so it is correct on chunks written by any version of
992
1007
  * this library — there is no field it needs the corpus to already carry.
1008
+ *
1009
+ * It holds every document's lock for the whole scan, so it waits for a `replaceDocument` in progress
1010
+ * and one can't start partway through. That adds two commands per document to the one scan.
993
1011
  */
994
1012
  removeMany(documentIds: string[]): Promise<void>;
995
1013
  /**
@@ -1015,6 +1033,10 @@ declare class RedisVectorStore implements LexicalVectorStore, AtomicReplaceVecto
1015
1033
  * from the index, so paging forward would skip their replacements. If a page comes back non-empty
1016
1034
  * but `DEL` reports nothing removed (keys expired between query and delete, or the index is stale),
1017
1035
  * the loop stops rather than spinning on results it can never consume.
1036
+ *
1037
+ * The documents aren't known until a page comes back, so each page locks the documents it touches, one
1038
+ * page at a time, and asks again with `INKEYS` once it holds them. A `replaceDocument` that finished in
1039
+ * between may have dropped those chunks, or rewritten them to no longer match.
1018
1040
  */
1019
1041
  removeWhere(filter: Record<string, unknown>): Promise<number>;
1020
1042
  /**
@@ -1711,9 +1733,10 @@ declare class RagIndex<Row, Scope extends RagScopeDefinition<Row>> {
1711
1733
  search(query: string, options: RagSearchOptions<Scope>): Promise<Passage[]>;
1712
1734
  /**
1713
1735
  * Replaces the row's chunks and returns how many were written. Embeddings come first: if the
1714
- * provider fails, the previous chunks are still there. The swap is one transaction on Postgres. Redis
1715
- * adds the new chunks before dropping the old, so a search can briefly see both. On a store without
1716
- * `replaceDocument`, a crash mid-swap loses the document until the next `index`.
1736
+ * provider fails, the previous chunks are still there. The swap is one transaction on Postgres and one
1737
+ * script on Redis, where a document too large for that adds the new chunks before dropping the old, so
1738
+ * a search can briefly see both. On a store without `replaceDocument`, a crash mid-swap loses the
1739
+ * document until the next `index`.
1717
1740
  */
1718
1741
  index(row: Row): Promise<number>;
1719
1742
  /** Takes the id itself once the row is gone. */
package/dist/index.d.ts CHANGED
@@ -281,8 +281,8 @@ interface LexicalVectorStore extends VectorStore {
281
281
  /**
282
282
  * An **optional** capability: swap all of a document's chunks safely under concurrent calls, so two
283
283
  * writers can't leave a mix of versions behind. Postgres also makes the swap atomic for searches and
284
- * crashes; Redis can't, and only guarantees the end state. {@link replaceDocumentChunks} falls back to
285
- * `remove` plus `upsert` for the others.
284
+ * crashes, and so does Redis up to a size limit (see `RedisVectorStore.replaceDocument`).
285
+ * {@link replaceDocumentChunks} falls back to `remove` plus `upsert` for the others.
286
286
  */
287
287
  interface AtomicReplaceVectorStore extends VectorStore {
288
288
  replaceDocument(documentId: string, records: VectorRecord[]): Promise<void>;
@@ -407,8 +407,9 @@ declare function describeSharedSource(legs: readonly unknown[]): Omit<RetrievalD
407
407
  * (`removeWhere` refusing an empty filter, an empty-array filter deleting nothing) are behavioural,
408
408
  * not incidental to Redis.
409
409
  */
410
- declare class MemoryVectorStore implements VectorStore {
410
+ declare class MemoryVectorStore implements AtomicReplaceVectorStore {
411
411
  private readonly records;
412
+ private readonly replacing;
412
413
  /**
413
414
  * Telemetry self-description. No `collection`: there is no namespace to name — the corpus IS this
414
415
  * instance, and two `MemoryVectorStore`s in one process are two corpora with nothing to tell them
@@ -416,7 +417,11 @@ declare class MemoryVectorStore implements VectorStore {
416
417
  */
417
418
  describeRetrieval(): RetrievalDescriptor;
418
419
  upsert(records: VectorRecord[]): Promise<void>;
420
+ /** A replace and a removal of one document take turns, in the order they were called. */
421
+ replaceDocument(documentId: string, records: VectorRecord[]): Promise<void>;
419
422
  remove(documentId: string): Promise<void>;
423
+ private inTurn;
424
+ private deleteChunks;
420
425
  /**
421
426
  * Merge `patch` into every chunk of `documentId`, leaving `text` and `embedding` untouched. The
422
427
  * record is *replaced* rather than mutated: `upsert` stores the caller's own object, and patching
@@ -878,18 +883,28 @@ declare class RedisVectorStore implements LexicalVectorStore, AtomicReplaceVecto
878
883
  private reconcileSchema;
879
884
  private createIndex;
880
885
  upsert(records: VectorRecord[]): Promise<void>;
886
+ private chunkFields;
881
887
  /**
882
- * Replaces a document's chunks, one writer at a time per document. Redis can't swap them in one step
883
- * without scanning the keyspace inside a script, so a search may briefly see old and new chunks
884
- * together, but concurrent calls can't leave them mixed. The new chunks go in before the old ones
885
- * come out, so a crash leaves the document searchable and the next call repairs it.
888
+ * Replaces a document's chunks, one writer at a time per document. The swap is one script, so a search
889
+ * sees the old version or the new one, and a crash leaves the old one. The keys to drop are found
890
+ * first, under the lock, because a script can't scan the keyspace.
891
+ *
892
+ * A document past {@link SWAP_MAX_CHUNKS} would block Redis for too long that way. Its new chunks go in
893
+ * first and the old ones come out after, so a search may briefly see both, and a crash leaves the
894
+ * document searchable for the next call to repair.
886
895
  */
887
896
  replaceDocument(documentId: string, records: VectorRecord[]): Promise<void>;
897
+ private swapChunks;
898
+ private lockKey;
888
899
  /**
889
- * A lease, so a crashed writer can't block the document forever. It is renewed while the work runs,
890
- * which keeps a large document from outliving it.
900
+ * A lease on each document, so a crashed writer can't block one forever. They are renewed while the
901
+ * work runs, which keeps a large document from outliving them.
902
+ *
903
+ * Several documents are locked in sorted order, without duplicates. Every other caller either holds
904
+ * one lock or takes them in that same order, so no two calls can wait on each other.
891
905
  */
892
- private withDocumentLock;
906
+ private withDocumentLocks;
907
+ private acquireLock;
893
908
  /**
894
909
  * Single-document deletion, by `SCAN … MATCH`.
895
910
  *
@@ -990,6 +1005,9 @@ declare class RedisVectorStore implements LexicalVectorStore, AtomicReplaceVecto
990
1005
  *
991
1006
  * It deletes by key, exactly as `remove` does, so it is correct on chunks written by any version of
992
1007
  * this library — there is no field it needs the corpus to already carry.
1008
+ *
1009
+ * It holds every document's lock for the whole scan, so it waits for a `replaceDocument` in progress
1010
+ * and one can't start partway through. That adds two commands per document to the one scan.
993
1011
  */
994
1012
  removeMany(documentIds: string[]): Promise<void>;
995
1013
  /**
@@ -1015,6 +1033,10 @@ declare class RedisVectorStore implements LexicalVectorStore, AtomicReplaceVecto
1015
1033
  * from the index, so paging forward would skip their replacements. If a page comes back non-empty
1016
1034
  * but `DEL` reports nothing removed (keys expired between query and delete, or the index is stale),
1017
1035
  * the loop stops rather than spinning on results it can never consume.
1036
+ *
1037
+ * The documents aren't known until a page comes back, so each page locks the documents it touches, one
1038
+ * page at a time, and asks again with `INKEYS` once it holds them. A `replaceDocument` that finished in
1039
+ * between may have dropped those chunks, or rewritten them to no longer match.
1018
1040
  */
1019
1041
  removeWhere(filter: Record<string, unknown>): Promise<number>;
1020
1042
  /**
@@ -1711,9 +1733,10 @@ declare class RagIndex<Row, Scope extends RagScopeDefinition<Row>> {
1711
1733
  search(query: string, options: RagSearchOptions<Scope>): Promise<Passage[]>;
1712
1734
  /**
1713
1735
  * Replaces the row's chunks and returns how many were written. Embeddings come first: if the
1714
- * provider fails, the previous chunks are still there. The swap is one transaction on Postgres. Redis
1715
- * adds the new chunks before dropping the old, so a search can briefly see both. On a store without
1716
- * `replaceDocument`, a crash mid-swap loses the document until the next `index`.
1736
+ * provider fails, the previous chunks are still there. The swap is one transaction on Postgres and one
1737
+ * script on Redis, where a document too large for that adds the new chunks before dropping the old, so
1738
+ * a search can briefly see both. On a store without `replaceDocument`, a crash mid-swap loses the
1739
+ * document until the next `index`.
1717
1740
  */
1718
1741
  index(row: Row): Promise<number>;
1719
1742
  /** Takes the id itself once the row is gone. */