@farukada/aws-langgraph-dynamodb-ts 0.9.0 → 1.0.0-rc.2
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 +1720 -154
- package/dist/backfill/backfill.d.ts +168 -0
- package/dist/backfill/backfill.js +393 -0
- package/dist/checkpointer/actions/delete-thread.d.ts +47 -6
- package/dist/checkpointer/actions/delete-thread.js +58 -21
- package/dist/checkpointer/actions/get-tuple.d.ts +29 -4
- package/dist/checkpointer/actions/get-tuple.js +44 -10
- package/dist/checkpointer/actions/list.d.ts +46 -4
- package/dist/checkpointer/actions/list.js +121 -66
- package/dist/checkpointer/actions/put-writes.d.ts +41 -9
- package/dist/checkpointer/actions/put-writes.js +62 -77
- package/dist/checkpointer/actions/put.d.ts +83 -4
- package/dist/checkpointer/actions/put.js +177 -25
- package/dist/checkpointer/internal/delta-history.d.ts +112 -0
- package/dist/checkpointer/internal/delta-history.js +252 -0
- package/dist/checkpointer/internal/listing.d.ts +149 -0
- package/dist/checkpointer/internal/listing.js +245 -0
- package/dist/checkpointer/internal/parse.d.ts +262 -0
- package/dist/checkpointer/internal/parse.js +372 -0
- package/dist/checkpointer/internal/pending-writes.d.ts +275 -0
- package/dist/checkpointer/internal/pending-writes.js +588 -0
- package/dist/checkpointer/internal/read.d.ts +130 -0
- package/dist/checkpointer/internal/read.js +264 -0
- package/dist/checkpointer/internal/rows.d.ts +571 -0
- package/dist/checkpointer/internal/rows.js +834 -0
- package/dist/checkpointer/internal/setup.d.ts +42 -19
- package/dist/checkpointer/internal/setup.js +65 -29
- package/dist/checkpointer/saver.d.ts +256 -16
- package/dist/checkpointer/saver.js +275 -29
- package/dist/checkpointer/types.d.ts +39 -39
- package/dist/checkpointer/types.js +10 -1
- package/dist/factory/factory.d.ts +134 -28
- package/dist/factory/factory.js +240 -21
- package/dist/factory/types.d.ts +76 -0
- package/dist/factory/types.js +10 -0
- package/dist/history/actions/add-messages.d.ts +31 -4
- package/dist/history/actions/add-messages.js +38 -58
- package/dist/history/actions/clear.d.ts +49 -6
- package/dist/history/actions/clear.js +66 -14
- package/dist/history/actions/get-messages.d.ts +54 -6
- package/dist/history/actions/get-messages.js +126 -43
- package/dist/history/actions/list-sessions.d.ts +52 -10
- package/dist/history/actions/list-sessions.js +139 -40
- package/dist/history/actions/reconcile-count.d.ts +42 -10
- package/dist/history/actions/reconcile-count.js +45 -45
- package/dist/history/chat-message-history.d.ts +220 -33
- package/dist/history/chat-message-history.js +240 -43
- package/dist/history/internal/append.d.ts +212 -0
- package/dist/history/internal/append.js +500 -0
- package/dist/history/internal/message-read.d.ts +84 -0
- package/dist/history/internal/message-read.js +204 -0
- package/dist/history/internal/parse.d.ts +153 -0
- package/dist/history/internal/parse.js +252 -0
- package/dist/history/internal/rows.d.ts +195 -0
- package/dist/history/internal/rows.js +250 -0
- package/dist/history/internal/session.d.ts +331 -0
- package/dist/history/internal/session.js +628 -0
- package/dist/history/internal/setup.d.ts +52 -17
- package/dist/history/internal/setup.js +92 -21
- package/dist/history/session-adapter.d.ts +102 -7
- package/dist/history/session-adapter.js +103 -9
- package/dist/history/types.d.ts +80 -29
- package/dist/history/types.js +10 -1
- package/dist/index.d.ts +42 -11
- package/dist/index.js +33 -12
- package/dist/shared/adapter.d.ts +135 -0
- package/dist/shared/adapter.js +143 -0
- package/dist/shared/clock.d.ts +51 -2
- package/dist/shared/clock.js +57 -2
- package/dist/shared/codec/codec.d.ts +288 -13
- package/dist/shared/codec/codec.js +416 -19
- package/dist/shared/codec/compression.d.ts +43 -7
- package/dist/shared/codec/compression.js +53 -13
- package/dist/shared/codec/json-serde.d.ts +76 -4
- package/dist/shared/codec/json-serde.js +181 -8
- package/dist/shared/codec/s3/client-types.d.ts +53 -0
- package/dist/shared/codec/s3/client-types.js +26 -0
- package/dist/shared/codec/s3/client.d.ts +43 -10
- package/dist/shared/codec/s3/client.js +82 -9
- package/dist/shared/codec/s3/config.d.ts +242 -11
- package/dist/shared/codec/s3/config.js +293 -11
- package/dist/shared/codec/s3/lifecycle.d.ts +164 -6
- package/dist/shared/codec/s3/lifecycle.js +335 -27
- package/dist/shared/codec/s3/offloader.d.ts +393 -18
- package/dist/shared/codec/s3/offloader.js +595 -37
- package/dist/shared/concurrency.d.ts +43 -0
- package/dist/shared/concurrency.js +78 -0
- package/dist/shared/dynamodb/abort.d.ts +47 -0
- package/dist/shared/dynamodb/abort.js +59 -0
- package/dist/shared/dynamodb/batch-write.d.ts +77 -14
- package/dist/shared/dynamodb/batch-write.js +146 -27
- package/dist/shared/dynamodb/cancellation.d.ts +121 -4
- package/dist/shared/dynamodb/cancellation.js +147 -3
- package/dist/shared/dynamodb/client.d.ts +162 -8
- package/dist/shared/dynamodb/client.js +153 -5
- package/dist/shared/dynamodb/idempotent-write.d.ts +551 -0
- package/dist/shared/dynamodb/idempotent-write.js +593 -0
- package/dist/shared/dynamodb/paginate.d.ts +105 -9
- package/dist/shared/dynamodb/paginate.js +175 -7
- package/dist/shared/dynamodb/partition-delete.d.ts +185 -14
- package/dist/shared/dynamodb/partition-delete.js +314 -44
- package/dist/shared/dynamodb/recency-index.d.ts +231 -0
- package/dist/shared/dynamodb/recency-index.js +377 -0
- package/dist/shared/dynamodb/retry.d.ts +276 -8
- package/dist/shared/dynamodb/retry.js +433 -23
- package/dist/shared/dynamodb/table-schema.d.ts +190 -0
- package/dist/shared/dynamodb/table-schema.js +209 -0
- package/dist/shared/errors/base-error.d.ts +184 -10
- package/dist/shared/errors/base-error.js +160 -14
- package/dist/shared/errors/boundary.d.ts +71 -0
- package/dist/shared/errors/boundary.js +143 -0
- package/dist/shared/errors/classify.d.ts +97 -0
- package/dist/shared/errors/classify.js +257 -0
- package/dist/shared/errors/error-code.d.ts +77 -2
- package/dist/shared/errors/error-code.js +83 -1
- package/dist/shared/errors/errors.d.ts +158 -59
- package/dist/shared/errors/errors.js +219 -92
- package/dist/shared/logging/logger.d.ts +69 -3
- package/dist/shared/logging/logger.js +97 -3
- package/dist/shared/logging/redaction.d.ts +92 -8
- package/dist/shared/logging/redaction.js +273 -17
- package/dist/shared/logging/secret-patterns.d.ts +149 -19
- package/dist/shared/logging/secret-patterns.js +188 -27
- package/dist/shared/logging/truncate.d.ts +197 -0
- package/dist/shared/logging/truncate.js +231 -0
- package/dist/shared/options.d.ts +59 -7
- package/dist/shared/options.js +9 -1
- package/dist/shared/ulid.d.ts +77 -7
- package/dist/shared/ulid.js +103 -8
- package/dist/shared/validation/collaborators.d.ts +141 -0
- package/dist/shared/validation/collaborators.js +188 -0
- package/dist/shared/validation/option-shape.d.ts +89 -0
- package/dist/shared/validation/option-shape.js +113 -0
- package/dist/shared/validation/options.d.ts +145 -0
- package/dist/shared/validation/options.js +328 -0
- package/dist/shared/validation/primitives.d.ts +288 -21
- package/dist/shared/validation/primitives.js +353 -50
- package/dist/shared/validation/ttl.d.ts +66 -10
- package/dist/shared/validation/ttl.js +113 -15
- package/dist/store/actions/list-namespaces.d.ts +76 -6
- package/dist/store/actions/list-namespaces.js +166 -24
- package/dist/store/actions/put.d.ts +33 -8
- package/dist/store/actions/put.js +53 -60
- package/dist/store/actions/reconcile-vector-index.d.ts +31 -10
- package/dist/store/actions/reconcile-vector-index.js +34 -15
- package/dist/store/actions/search.d.ts +34 -6
- package/dist/store/actions/search.js +56 -51
- package/dist/store/internal/batch-plan.d.ts +26 -0
- package/dist/store/internal/batch-plan.js +109 -0
- package/dist/store/internal/filter.d.ts +36 -3
- package/dist/store/internal/filter.js +66 -15
- package/dist/store/internal/get-item.d.ts +45 -0
- package/dist/store/internal/get-item.js +115 -0
- package/dist/store/internal/item-write.d.ts +230 -0
- package/dist/store/internal/item-write.js +463 -0
- package/dist/store/internal/parse.d.ts +225 -0
- package/dist/store/internal/parse.js +350 -0
- package/dist/store/internal/rows.d.ts +355 -0
- package/dist/store/internal/rows.js +447 -0
- package/dist/store/internal/semantic-search.d.ts +161 -6
- package/dist/store/internal/semantic-search.js +360 -18
- package/dist/store/internal/setup.d.ts +77 -20
- package/dist/store/internal/setup.js +178 -47
- package/dist/store/internal/table-search.d.ts +100 -0
- package/dist/store/internal/table-search.js +213 -0
- package/dist/store/internal/vector-index.d.ts +247 -0
- package/dist/store/internal/vector-index.js +546 -0
- package/dist/store/store.d.ts +270 -17
- package/dist/store/store.js +329 -38
- package/dist/store/types.d.ts +76 -26
- package/dist/store/types.js +13 -1
- package/dist/store/vector-backend.d.ts +64 -4
- package/dist/store/vector-backend.js +15 -1
- package/package.json +58 -36
- package/dist/checkpointer/actions/delete-thread.d.ts.map +0 -1
- package/dist/checkpointer/actions/delete-thread.js.map +0 -1
- package/dist/checkpointer/actions/get-tuple.d.ts.map +0 -1
- package/dist/checkpointer/actions/get-tuple.js.map +0 -1
- package/dist/checkpointer/actions/list.d.ts.map +0 -1
- package/dist/checkpointer/actions/list.js.map +0 -1
- package/dist/checkpointer/actions/put-writes.d.ts.map +0 -1
- package/dist/checkpointer/actions/put-writes.js.map +0 -1
- package/dist/checkpointer/actions/put.d.ts.map +0 -1
- package/dist/checkpointer/actions/put.js.map +0 -1
- package/dist/checkpointer/internal/assemble.d.ts +0 -10
- package/dist/checkpointer/internal/assemble.d.ts.map +0 -1
- package/dist/checkpointer/internal/assemble.js +0 -37
- package/dist/checkpointer/internal/assemble.js.map +0 -1
- package/dist/checkpointer/internal/configurable.d.ts +0 -13
- package/dist/checkpointer/internal/configurable.d.ts.map +0 -1
- package/dist/checkpointer/internal/configurable.js +0 -23
- package/dist/checkpointer/internal/configurable.js.map +0 -1
- package/dist/checkpointer/internal/fetch.d.ts +0 -10
- package/dist/checkpointer/internal/fetch.d.ts.map +0 -1
- package/dist/checkpointer/internal/fetch.js +0 -46
- package/dist/checkpointer/internal/fetch.js.map +0 -1
- package/dist/checkpointer/internal/filter-match.d.ts +0 -12
- package/dist/checkpointer/internal/filter-match.d.ts.map +0 -1
- package/dist/checkpointer/internal/filter-match.js +0 -14
- package/dist/checkpointer/internal/filter-match.js.map +0 -1
- package/dist/checkpointer/internal/item-reader.d.ts +0 -55
- package/dist/checkpointer/internal/item-reader.d.ts.map +0 -1
- package/dist/checkpointer/internal/item-reader.js +0 -88
- package/dist/checkpointer/internal/item-reader.js.map +0 -1
- package/dist/checkpointer/internal/item-writer.d.ts +0 -26
- package/dist/checkpointer/internal/item-writer.d.ts.map +0 -1
- package/dist/checkpointer/internal/item-writer.js +0 -92
- package/dist/checkpointer/internal/item-writer.js.map +0 -1
- package/dist/checkpointer/internal/keys.d.ts +0 -31
- package/dist/checkpointer/internal/keys.d.ts.map +0 -1
- package/dist/checkpointer/internal/keys.js +0 -87
- package/dist/checkpointer/internal/keys.js.map +0 -1
- package/dist/checkpointer/internal/query.d.ts +0 -20
- package/dist/checkpointer/internal/query.d.ts.map +0 -1
- package/dist/checkpointer/internal/query.js +0 -36
- package/dist/checkpointer/internal/query.js.map +0 -1
- package/dist/checkpointer/internal/setup.d.ts.map +0 -1
- package/dist/checkpointer/internal/setup.js.map +0 -1
- package/dist/checkpointer/internal/special-write-cas.d.ts +0 -30
- package/dist/checkpointer/internal/special-write-cas.d.ts.map +0 -1
- package/dist/checkpointer/internal/special-write-cas.js +0 -104
- package/dist/checkpointer/internal/special-write-cas.js.map +0 -1
- package/dist/checkpointer/internal/special-write-cleanup.d.ts +0 -24
- package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +0 -1
- package/dist/checkpointer/internal/special-write-cleanup.js +0 -47
- package/dist/checkpointer/internal/special-write-cleanup.js.map +0 -1
- package/dist/checkpointer/internal/special-write-verify.d.ts +0 -54
- package/dist/checkpointer/internal/special-write-verify.d.ts.map +0 -1
- package/dist/checkpointer/internal/special-write-verify.js +0 -65
- package/dist/checkpointer/internal/special-write-verify.js.map +0 -1
- package/dist/checkpointer/internal/validation.d.ts +0 -13
- package/dist/checkpointer/internal/validation.d.ts.map +0 -1
- package/dist/checkpointer/internal/validation.js +0 -30
- package/dist/checkpointer/internal/validation.js.map +0 -1
- package/dist/checkpointer/internal/write-guard.d.ts +0 -13
- package/dist/checkpointer/internal/write-guard.d.ts.map +0 -1
- package/dist/checkpointer/internal/write-guard.js +0 -39
- package/dist/checkpointer/internal/write-guard.js.map +0 -1
- package/dist/checkpointer/internal/write-index.d.ts +0 -37
- package/dist/checkpointer/internal/write-index.d.ts.map +0 -1
- package/dist/checkpointer/internal/write-index.js +0 -42
- package/dist/checkpointer/internal/write-index.js.map +0 -1
- package/dist/checkpointer/saver.d.ts.map +0 -1
- package/dist/checkpointer/saver.js.map +0 -1
- package/dist/checkpointer/types.d.ts.map +0 -1
- package/dist/checkpointer/types.js.map +0 -1
- package/dist/factory/factory.d.ts.map +0 -1
- package/dist/factory/factory.js.map +0 -1
- package/dist/history/actions/add-messages.d.ts.map +0 -1
- package/dist/history/actions/add-messages.js.map +0 -1
- package/dist/history/actions/clear.d.ts.map +0 -1
- package/dist/history/actions/clear.js.map +0 -1
- package/dist/history/actions/get-messages.d.ts.map +0 -1
- package/dist/history/actions/get-messages.js.map +0 -1
- package/dist/history/actions/list-sessions.d.ts.map +0 -1
- package/dist/history/actions/list-sessions.js.map +0 -1
- package/dist/history/actions/reconcile-count.d.ts.map +0 -1
- package/dist/history/actions/reconcile-count.js.map +0 -1
- package/dist/history/chat-message-history.d.ts.map +0 -1
- package/dist/history/chat-message-history.js.map +0 -1
- package/dist/history/internal/append-saga.d.ts +0 -20
- package/dist/history/internal/append-saga.d.ts.map +0 -1
- package/dist/history/internal/append-saga.js +0 -35
- package/dist/history/internal/append-saga.js.map +0 -1
- package/dist/history/internal/compensation.d.ts +0 -21
- package/dist/history/internal/compensation.d.ts.map +0 -1
- package/dist/history/internal/compensation.js +0 -84
- package/dist/history/internal/compensation.js.map +0 -1
- package/dist/history/internal/item-mapper.d.ts +0 -12
- package/dist/history/internal/item-mapper.d.ts.map +0 -1
- package/dist/history/internal/item-mapper.js +0 -33
- package/dist/history/internal/item-mapper.js.map +0 -1
- package/dist/history/internal/keys.d.ts +0 -17
- package/dist/history/internal/keys.d.ts.map +0 -1
- package/dist/history/internal/keys.js +0 -49
- package/dist/history/internal/keys.js.map +0 -1
- package/dist/history/internal/message-chunker.d.ts +0 -14
- package/dist/history/internal/message-chunker.d.ts.map +0 -1
- package/dist/history/internal/message-chunker.js +0 -68
- package/dist/history/internal/message-chunker.js.map +0 -1
- package/dist/history/internal/message-transaction.d.ts +0 -26
- package/dist/history/internal/message-transaction.d.ts.map +0 -1
- package/dist/history/internal/message-transaction.js +0 -60
- package/dist/history/internal/message-transaction.js.map +0 -1
- package/dist/history/internal/query.d.ts +0 -10
- package/dist/history/internal/query.d.ts.map +0 -1
- package/dist/history/internal/query.js +0 -31
- package/dist/history/internal/query.js.map +0 -1
- package/dist/history/internal/session-count.d.ts +0 -41
- package/dist/history/internal/session-count.d.ts.map +0 -1
- package/dist/history/internal/session-count.js +0 -109
- package/dist/history/internal/session-count.js.map +0 -1
- package/dist/history/internal/session-title.d.ts +0 -20
- package/dist/history/internal/session-title.d.ts.map +0 -1
- package/dist/history/internal/session-title.js +0 -44
- package/dist/history/internal/session-title.js.map +0 -1
- package/dist/history/internal/session-update.d.ts +0 -28
- package/dist/history/internal/session-update.d.ts.map +0 -1
- package/dist/history/internal/session-update.js +0 -70
- package/dist/history/internal/session-update.js.map +0 -1
- package/dist/history/internal/setup.d.ts.map +0 -1
- package/dist/history/internal/setup.js.map +0 -1
- package/dist/history/internal/title-generator.d.ts +0 -13
- package/dist/history/internal/title-generator.d.ts.map +0 -1
- package/dist/history/internal/title-generator.js +0 -25
- package/dist/history/internal/title-generator.js.map +0 -1
- package/dist/history/internal/ttl-anchor.d.ts +0 -25
- package/dist/history/internal/ttl-anchor.d.ts.map +0 -1
- package/dist/history/internal/ttl-anchor.js +0 -38
- package/dist/history/internal/ttl-anchor.js.map +0 -1
- package/dist/history/internal/validation.d.ts +0 -9
- package/dist/history/internal/validation.d.ts.map +0 -1
- package/dist/history/internal/validation.js +0 -16
- package/dist/history/internal/validation.js.map +0 -1
- package/dist/history/session-adapter.d.ts.map +0 -1
- package/dist/history/session-adapter.js.map +0 -1
- package/dist/history/types.d.ts.map +0 -1
- package/dist/history/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/shared/clock.d.ts.map +0 -1
- package/dist/shared/clock.js.map +0 -1
- package/dist/shared/codec/codec.d.ts.map +0 -1
- package/dist/shared/codec/codec.js.map +0 -1
- package/dist/shared/codec/compression.d.ts.map +0 -1
- package/dist/shared/codec/compression.js.map +0 -1
- package/dist/shared/codec/descriptor-keys.d.ts +0 -4
- package/dist/shared/codec/descriptor-keys.d.ts.map +0 -1
- package/dist/shared/codec/descriptor-keys.js +0 -14
- package/dist/shared/codec/descriptor-keys.js.map +0 -1
- package/dist/shared/codec/json-serde.d.ts.map +0 -1
- package/dist/shared/codec/json-serde.js.map +0 -1
- package/dist/shared/codec/s3/client.d.ts.map +0 -1
- package/dist/shared/codec/s3/client.js.map +0 -1
- package/dist/shared/codec/s3/config.d.ts.map +0 -1
- package/dist/shared/codec/s3/config.js.map +0 -1
- package/dist/shared/codec/s3/delete.d.ts +0 -8
- package/dist/shared/codec/s3/delete.d.ts.map +0 -1
- package/dist/shared/codec/s3/delete.js +0 -29
- package/dist/shared/codec/s3/delete.js.map +0 -1
- package/dist/shared/codec/s3/lifecycle.d.ts.map +0 -1
- package/dist/shared/codec/s3/lifecycle.js.map +0 -1
- package/dist/shared/codec/s3/offloader.d.ts.map +0 -1
- package/dist/shared/codec/s3/offloader.js.map +0 -1
- package/dist/shared/codec/s3/orphans.d.ts +0 -18
- package/dist/shared/codec/s3/orphans.d.ts.map +0 -1
- package/dist/shared/codec/s3/orphans.js +0 -58
- package/dist/shared/codec/s3/orphans.js.map +0 -1
- package/dist/shared/codec/s3/read-write.d.ts +0 -14
- package/dist/shared/codec/s3/read-write.d.ts.map +0 -1
- package/dist/shared/codec/s3/read-write.js +0 -43
- package/dist/shared/codec/s3/read-write.js.map +0 -1
- package/dist/shared/codec/s3/retry.d.ts +0 -5
- package/dist/shared/codec/s3/retry.d.ts.map +0 -1
- package/dist/shared/codec/s3/retry.js +0 -25
- package/dist/shared/codec/s3/retry.js.map +0 -1
- package/dist/shared/constants.d.ts +0 -64
- package/dist/shared/constants.d.ts.map +0 -1
- package/dist/shared/constants.js +0 -67
- package/dist/shared/constants.js.map +0 -1
- package/dist/shared/dynamodb/backoff.d.ts +0 -15
- package/dist/shared/dynamodb/backoff.d.ts.map +0 -1
- package/dist/shared/dynamodb/backoff.js +0 -48
- package/dist/shared/dynamodb/backoff.js.map +0 -1
- package/dist/shared/dynamodb/batch-write.d.ts.map +0 -1
- package/dist/shared/dynamodb/batch-write.js.map +0 -1
- package/dist/shared/dynamodb/cancellation.d.ts.map +0 -1
- package/dist/shared/dynamodb/cancellation.js.map +0 -1
- package/dist/shared/dynamodb/client.d.ts.map +0 -1
- package/dist/shared/dynamodb/client.js.map +0 -1
- package/dist/shared/dynamodb/conditional-put.d.ts +0 -51
- package/dist/shared/dynamodb/conditional-put.d.ts.map +0 -1
- package/dist/shared/dynamodb/conditional-put.js +0 -59
- package/dist/shared/dynamodb/conditional-put.js.map +0 -1
- package/dist/shared/dynamodb/drain-unprocessed.d.ts +0 -19
- package/dist/shared/dynamodb/drain-unprocessed.d.ts.map +0 -1
- package/dist/shared/dynamodb/drain-unprocessed.js +0 -44
- package/dist/shared/dynamodb/drain-unprocessed.js.map +0 -1
- package/dist/shared/dynamodb/paginate-core.d.ts +0 -22
- package/dist/shared/dynamodb/paginate-core.d.ts.map +0 -1
- package/dist/shared/dynamodb/paginate-core.js +0 -52
- package/dist/shared/dynamodb/paginate-core.js.map +0 -1
- package/dist/shared/dynamodb/paginate.d.ts.map +0 -1
- package/dist/shared/dynamodb/paginate.js.map +0 -1
- package/dist/shared/dynamodb/partition-delete.d.ts.map +0 -1
- package/dist/shared/dynamodb/partition-delete.js.map +0 -1
- package/dist/shared/dynamodb/retry-classifier.d.ts +0 -9
- package/dist/shared/dynamodb/retry-classifier.d.ts.map +0 -1
- package/dist/shared/dynamodb/retry-classifier.js +0 -87
- package/dist/shared/dynamodb/retry-classifier.js.map +0 -1
- package/dist/shared/dynamodb/retry.d.ts.map +0 -1
- package/dist/shared/dynamodb/retry.js.map +0 -1
- package/dist/shared/dynamodb/scan.d.ts +0 -15
- package/dist/shared/dynamodb/scan.d.ts.map +0 -1
- package/dist/shared/dynamodb/scan.js +0 -20
- package/dist/shared/dynamodb/scan.js.map +0 -1
- package/dist/shared/dynamodb/types.d.ts +0 -24
- package/dist/shared/dynamodb/types.d.ts.map +0 -1
- package/dist/shared/dynamodb/types.js +0 -3
- package/dist/shared/dynamodb/types.js.map +0 -1
- package/dist/shared/errors/base-error.d.ts.map +0 -1
- package/dist/shared/errors/base-error.js.map +0 -1
- package/dist/shared/errors/error-code.d.ts.map +0 -1
- package/dist/shared/errors/error-code.js.map +0 -1
- package/dist/shared/errors/errors.d.ts.map +0 -1
- package/dist/shared/errors/errors.js.map +0 -1
- package/dist/shared/errors/wrap-error.d.ts +0 -16
- package/dist/shared/errors/wrap-error.d.ts.map +0 -1
- package/dist/shared/errors/wrap-error.js +0 -30
- package/dist/shared/errors/wrap-error.js.map +0 -1
- package/dist/shared/logging/logger.d.ts.map +0 -1
- package/dist/shared/logging/logger.js.map +0 -1
- package/dist/shared/logging/redaction-walk.d.ts +0 -23
- package/dist/shared/logging/redaction-walk.d.ts.map +0 -1
- package/dist/shared/logging/redaction-walk.js +0 -92
- package/dist/shared/logging/redaction-walk.js.map +0 -1
- package/dist/shared/logging/redaction.d.ts.map +0 -1
- package/dist/shared/logging/redaction.js.map +0 -1
- package/dist/shared/logging/secret-patterns.d.ts.map +0 -1
- package/dist/shared/logging/secret-patterns.js.map +0 -1
- package/dist/shared/options.d.ts.map +0 -1
- package/dist/shared/options.js.map +0 -1
- package/dist/shared/ulid.d.ts.map +0 -1
- package/dist/shared/ulid.js.map +0 -1
- package/dist/shared/validation/primitives.d.ts.map +0 -1
- package/dist/shared/validation/primitives.js.map +0 -1
- package/dist/shared/validation/ttl.d.ts.map +0 -1
- package/dist/shared/validation/ttl.js.map +0 -1
- package/dist/store/actions/get.d.ts +0 -5
- package/dist/store/actions/get.d.ts.map +0 -1
- package/dist/store/actions/get.js +0 -35
- package/dist/store/actions/get.js.map +0 -1
- package/dist/store/actions/list-namespaces.d.ts.map +0 -1
- package/dist/store/actions/list-namespaces.js.map +0 -1
- package/dist/store/actions/put.d.ts.map +0 -1
- package/dist/store/actions/put.js.map +0 -1
- package/dist/store/actions/reconcile-vector-index.d.ts.map +0 -1
- package/dist/store/actions/reconcile-vector-index.js.map +0 -1
- package/dist/store/actions/search.d.ts.map +0 -1
- package/dist/store/actions/search.js.map +0 -1
- package/dist/store/internal/backend-search.d.ts +0 -5
- package/dist/store/internal/backend-search.d.ts.map +0 -1
- package/dist/store/internal/backend-search.js +0 -68
- package/dist/store/internal/backend-search.js.map +0 -1
- package/dist/store/internal/filter.d.ts.map +0 -1
- package/dist/store/internal/filter.js.map +0 -1
- package/dist/store/internal/index-reconcile.d.ts +0 -22
- package/dist/store/internal/index-reconcile.d.ts.map +0 -1
- package/dist/store/internal/index-reconcile.js +0 -105
- package/dist/store/internal/index-reconcile.js.map +0 -1
- package/dist/store/internal/index-sync.d.ts +0 -11
- package/dist/store/internal/index-sync.d.ts.map +0 -1
- package/dist/store/internal/index-sync.js +0 -26
- package/dist/store/internal/index-sync.js.map +0 -1
- package/dist/store/internal/item-mapper.d.ts +0 -25
- package/dist/store/internal/item-mapper.d.ts.map +0 -1
- package/dist/store/internal/item-mapper.js +0 -53
- package/dist/store/internal/item-mapper.js.map +0 -1
- package/dist/store/internal/keys.d.ts +0 -18
- package/dist/store/internal/keys.d.ts.map +0 -1
- package/dist/store/internal/keys.js +0 -42
- package/dist/store/internal/keys.js.map +0 -1
- package/dist/store/internal/namespace-match.d.ts +0 -12
- package/dist/store/internal/namespace-match.d.ts.map +0 -1
- package/dist/store/internal/namespace-match.js +0 -41
- package/dist/store/internal/namespace-match.js.map +0 -1
- package/dist/store/internal/overwrite-swap.d.ts +0 -33
- package/dist/store/internal/overwrite-swap.d.ts.map +0 -1
- package/dist/store/internal/overwrite-swap.js +0 -62
- package/dist/store/internal/overwrite-swap.js.map +0 -1
- package/dist/store/internal/persist.d.ts +0 -27
- package/dist/store/internal/persist.d.ts.map +0 -1
- package/dist/store/internal/persist.js +0 -59
- package/dist/store/internal/persist.js.map +0 -1
- package/dist/store/internal/query.d.ts +0 -6
- package/dist/store/internal/query.d.ts.map +0 -1
- package/dist/store/internal/query.js +0 -32
- package/dist/store/internal/query.js.map +0 -1
- package/dist/store/internal/ranker.d.ts +0 -13
- package/dist/store/internal/ranker.d.ts.map +0 -1
- package/dist/store/internal/ranker.js +0 -31
- package/dist/store/internal/ranker.js.map +0 -1
- package/dist/store/internal/read-existing.d.ts +0 -19
- package/dist/store/internal/read-existing.d.ts.map +0 -1
- package/dist/store/internal/read-existing.js +0 -29
- package/dist/store/internal/read-existing.js.map +0 -1
- package/dist/store/internal/score-direction.d.ts +0 -32
- package/dist/store/internal/score-direction.d.ts.map +0 -1
- package/dist/store/internal/score-direction.js +0 -39
- package/dist/store/internal/score-direction.js.map +0 -1
- package/dist/store/internal/search-filter.d.ts +0 -4
- package/dist/store/internal/search-filter.d.ts.map +0 -1
- package/dist/store/internal/search-filter.js +0 -11
- package/dist/store/internal/search-filter.js.map +0 -1
- package/dist/store/internal/semantic-search.d.ts.map +0 -1
- package/dist/store/internal/semantic-search.js.map +0 -1
- package/dist/store/internal/setup.d.ts.map +0 -1
- package/dist/store/internal/setup.js.map +0 -1
- package/dist/store/internal/validation.d.ts +0 -13
- package/dist/store/internal/validation.d.ts.map +0 -1
- package/dist/store/internal/validation.js +0 -35
- package/dist/store/internal/validation.js.map +0 -1
- package/dist/store/internal/write-verify.d.ts +0 -37
- package/dist/store/internal/write-verify.d.ts.map +0 -1
- package/dist/store/internal/write-verify.js +0 -68
- package/dist/store/internal/write-verify.js.map +0 -1
- package/dist/store/store.d.ts.map +0 -1
- package/dist/store/store.js.map +0 -1
- package/dist/store/types.d.ts.map +0 -1
- package/dist/store/types.js.map +0 -1
- package/dist/store/vector-backend.d.ts.map +0 -1
- package/dist/store/vector-backend.js.map +0 -1
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hides the vector backend as a copy of the items' embeddings.
|
|
4
|
+
*
|
|
5
|
+
* When a store is given a `VectorBackend`, every item's embedding is kept there
|
|
6
|
+
* as well as the item in the table, and the table is the truth. When the copy
|
|
7
|
+
* is written (after the row commits), when an entry is dropped (only once a
|
|
8
|
+
* fresh read finds the row gone), how a backend's answer is resolved back to
|
|
9
|
+
* the canonical items and in which direction its scores run, and how the copy
|
|
10
|
+
* is repaired against the table are decided here, and nothing else calls the
|
|
11
|
+
* backend.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.hasVectorBackend = hasVectorBackend;
|
|
15
|
+
exports.itemVector = itemVector;
|
|
16
|
+
exports.syncItemVector = syncItemVector;
|
|
17
|
+
exports.dropVectorWhenGone = dropVectorWhenGone;
|
|
18
|
+
exports.collectReconcileTargets = collectReconcileTargets;
|
|
19
|
+
exports.pushEmbeddings = pushEmbeddings;
|
|
20
|
+
exports.selectOrphans = selectOrphans;
|
|
21
|
+
exports.pruneOrphans = pruneOrphans;
|
|
22
|
+
exports.reconcileVectors = reconcileVectors;
|
|
23
|
+
exports.searchViaBackend = searchViaBackend;
|
|
24
|
+
exports.toRelevanceScores = toRelevanceScores;
|
|
25
|
+
const clock_1 = require("../../shared/clock");
|
|
26
|
+
const concurrency_1 = require("../../shared/concurrency");
|
|
27
|
+
const idempotent_write_1 = require("../../shared/dynamodb/idempotent-write");
|
|
28
|
+
const paginate_1 = require("../../shared/dynamodb/paginate");
|
|
29
|
+
const retry_1 = require("../../shared/dynamodb/retry");
|
|
30
|
+
const table_schema_1 = require("../../shared/dynamodb/table-schema");
|
|
31
|
+
const base_error_1 = require("../../shared/errors/base-error");
|
|
32
|
+
const errors_1 = require("../../shared/errors/errors");
|
|
33
|
+
const truncate_1 = require("../../shared/logging/truncate");
|
|
34
|
+
const filter_1 = require("./filter");
|
|
35
|
+
const get_item_1 = require("./get-item");
|
|
36
|
+
const parse_1 = require("./parse");
|
|
37
|
+
const rows_1 = require("./rows");
|
|
38
|
+
const semantic_search_1 = require("./semantic-search");
|
|
39
|
+
/**
|
|
40
|
+
* Whether the store keeps a vector copy.
|
|
41
|
+
*
|
|
42
|
+
* Accepts: `context` — the store's.
|
|
43
|
+
*
|
|
44
|
+
* Returns: `true`, narrowing `context`, when both `index` and `vectorBackend`
|
|
45
|
+
* are configured.
|
|
46
|
+
*
|
|
47
|
+
* Throws: nothing.
|
|
48
|
+
*/
|
|
49
|
+
function hasVectorBackend(context) {
|
|
50
|
+
return context.index !== undefined && context.vectorBackend !== undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The one vector a put gives the backend for its item.
|
|
54
|
+
*
|
|
55
|
+
* Accepts: `op` — the parsed put. Its `index` is `false` to embed nothing, a
|
|
56
|
+
* field list to embed those fields, absent to embed the configured ones.
|
|
57
|
+
*
|
|
58
|
+
* Returns: the joined embedding, or `undefined` when the store keeps no vector
|
|
59
|
+
* copy, the put indexes nothing, or the value has no text to embed.
|
|
60
|
+
*
|
|
61
|
+
* Throws: `VALIDATION` naming `index.dims` when the model returns a vector of
|
|
62
|
+
* another length; whatever `embedDocuments` throws.
|
|
63
|
+
*/
|
|
64
|
+
async function itemVector(context, op) {
|
|
65
|
+
if (!context.vectorBackend || op.index === false)
|
|
66
|
+
return undefined;
|
|
67
|
+
return (0, semantic_search_1.embedValue)(context, op.value, op.index);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Write an item's entry in the vector copy after its row has committed: the
|
|
71
|
+
* vector when there is one, a delete when there is none. Best-effort sync of
|
|
72
|
+
* one item's embedding to the vector backend after the canonical DynamoDB
|
|
73
|
+
* write has already succeeded.
|
|
74
|
+
*
|
|
75
|
+
* Accepts: `address` — the item's. `embedding` — from {@link itemVector}:
|
|
76
|
+
* present upserts it; absent deletes any vector the backend still holds, which
|
|
77
|
+
* is what a re-put with no indexable text, an `index: false` put and a delete
|
|
78
|
+
* all mean.
|
|
79
|
+
*
|
|
80
|
+
* Returns: nothing, in both the synced and the failed case; without a backend,
|
|
81
|
+
* nothing is done.
|
|
82
|
+
*
|
|
83
|
+
* Throws: nothing. The canonical item is already committed, so failing here
|
|
84
|
+
* would report a put that in fact succeeded; the drift is logged at `warn` and
|
|
85
|
+
* `reconcileVectorIndex` repairs it.
|
|
86
|
+
*/
|
|
87
|
+
async function syncItemVector(context, address, embedding) {
|
|
88
|
+
const backend = context.vectorBackend;
|
|
89
|
+
if (backend === undefined)
|
|
90
|
+
return;
|
|
91
|
+
try {
|
|
92
|
+
if (embedding)
|
|
93
|
+
await backend.upsert(address.namespace, address.key, embedding);
|
|
94
|
+
else
|
|
95
|
+
await backend.delete(address.namespace, address.key);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// The name, not the message: a backend's error text is not an identifier.
|
|
99
|
+
// Bounded all the same — the name is the backend's own and nothing this
|
|
100
|
+
// package ran checked its length, and `message` is bounded where
|
|
101
|
+
// `redactedMessage` relays it, so relaying the name whole would split what
|
|
102
|
+
// is one value. The literal does not name a method, because both
|
|
103
|
+
// `store.put` and `store.delete` reach here and reporting a failed delete
|
|
104
|
+
// as a failed put sends an operator to the wrong call site; `operation`
|
|
105
|
+
// carries which one.
|
|
106
|
+
context.logger.warn('store vector-index sync failed; reconcileVectorIndex will repair', {
|
|
107
|
+
namespace: address.namespace,
|
|
108
|
+
key: address.key,
|
|
109
|
+
operation: embedding ? 'upsert' : 'delete',
|
|
110
|
+
reason: (0, truncate_1.truncateForLog)((0, base_error_1.failureLabel)(error)),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Drop the item's vector, but only on a fresh read that finds no row at the
|
|
116
|
+
* key.
|
|
117
|
+
*
|
|
118
|
+
* The question is deliberately **not** "did this call remove the row" — that
|
|
119
|
+
* one is true in exactly the interleaving that goes wrong. It is "does the key
|
|
120
|
+
* hold a row *now*", which a racing put that recreated it and a
|
|
121
|
+
* compare-and-swap that left it alone both answer the same way, and which costs
|
|
122
|
+
* a point read of this library's own table rather than anything the backend has
|
|
123
|
+
* to offer. The reconciler asks the same question before pruning a vector, so
|
|
124
|
+
* the delete path and the reconciler are equally careful.
|
|
125
|
+
*
|
|
126
|
+
* A read that itself fails answers "not confirmed" and keeps the vector: a
|
|
127
|
+
* stale vector for a deleted item, which `reconcileVectorIndex` removes, rather
|
|
128
|
+
* than a missing one for a live item, which is the defect this exists for.
|
|
129
|
+
*
|
|
130
|
+
* Accepts: `address` — the deleted item's; the row read is derived from it.
|
|
131
|
+
*
|
|
132
|
+
* Returns: nothing; without a backend, nothing is read or dropped. A row still
|
|
133
|
+
* at the key keeps its vector and logs one `info`.
|
|
134
|
+
*
|
|
135
|
+
* Throws: nothing the backend throws (see {@link syncItemVector}); whatever the
|
|
136
|
+
* confirmation read throws that `isRowAbsent` does not answer as "not
|
|
137
|
+
* confirmed".
|
|
138
|
+
*/
|
|
139
|
+
async function dropVectorWhenGone(context, address) {
|
|
140
|
+
if (context.vectorBackend === undefined)
|
|
141
|
+
return;
|
|
142
|
+
const key = (0, rows_1.itemRowKey)(address);
|
|
143
|
+
if (!(await (0, idempotent_write_1.isRowAbsent)(context, key))) {
|
|
144
|
+
context.logger.info('store.delete: kept a vector whose item was not confirmed gone', {
|
|
145
|
+
namespace: address.namespace,
|
|
146
|
+
key: address.key,
|
|
147
|
+
});
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
await syncItemVector(context, address, undefined);
|
|
151
|
+
}
|
|
152
|
+
/** Stable, collision-free identity for a (namespace, key) pair. */
|
|
153
|
+
function refIdentity(namespace, key) {
|
|
154
|
+
return JSON.stringify([...namespace, key]);
|
|
155
|
+
}
|
|
156
|
+
/** Decode the buffered rows with the same bounded concurrency the search path uses. */
|
|
157
|
+
async function drainPending(context, pending, live, signal) {
|
|
158
|
+
if (pending.length === 0)
|
|
159
|
+
return;
|
|
160
|
+
const batch = pending.splice(0, pending.length);
|
|
161
|
+
const items = await (0, concurrency_1.mapWithConcurrency)(batch, context.readConcurrency ?? concurrency_1.DEFAULT_READ_CONCURRENCY, (record) => (0, rows_1.readStoreItem)(context, record, signal));
|
|
162
|
+
batch.forEach((record, index) => {
|
|
163
|
+
live.push({
|
|
164
|
+
namespace: record.namespace,
|
|
165
|
+
key: record.key,
|
|
166
|
+
value: items[index].value,
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Enumerate the live (unexpired) items under `prefix` and recompute their
|
|
172
|
+
* embeddings.
|
|
173
|
+
*
|
|
174
|
+
* Accepts: `prefix` — at least one element, since the enumeration is one
|
|
175
|
+
* partition's Query; reconciling is always scoped. `signal` — aborts between
|
|
176
|
+
* pages.
|
|
177
|
+
*
|
|
178
|
+
* Returns: one target per live item, each with the embedding the item's current
|
|
179
|
+
* value produces — `undefined` when it yields no indexable text, which is the
|
|
180
|
+
* evidence that lets {@link pruneOrphans} drop a vector that has gone stale.
|
|
181
|
+
*
|
|
182
|
+
* Throws: whatever the reads, the decodes and the embeddings model throw; a
|
|
183
|
+
* failed embedding rejects the whole reconcile, because a skipped item would
|
|
184
|
+
* leave the live set and {@link selectOrphans} would prune its still-valid
|
|
185
|
+
* vector. `RESULT_TRUNCATED` when `maxScanItems` is reached while rows
|
|
186
|
+
* remain — reconciling from a partial view would prune live vectors.
|
|
187
|
+
*
|
|
188
|
+
* Guarantees: rows are decoded in bounded batches, so a namespace of offloaded
|
|
189
|
+
* items costs neither one round-trip at a time nor every payload in memory at
|
|
190
|
+
* once.
|
|
191
|
+
*/
|
|
192
|
+
async function collectReconcileTargets(context, prefix, signal) {
|
|
193
|
+
const now = (0, clock_1.nowSeconds)();
|
|
194
|
+
const live = [];
|
|
195
|
+
const pending = [];
|
|
196
|
+
const batchLimit = context.readConcurrency ?? concurrency_1.DEFAULT_READ_CONCURRENCY;
|
|
197
|
+
const source = (0, paginate_1.paginateQuery)({
|
|
198
|
+
retry: (0, retry_1.retryFor)(context, signal),
|
|
199
|
+
signal,
|
|
200
|
+
client: context.client,
|
|
201
|
+
params: (0, table_schema_1.withoutExpired)((0, rows_1.scopedQuery)(context.tableName, prefix), now),
|
|
202
|
+
maxItems: context.maxScanItems,
|
|
203
|
+
});
|
|
204
|
+
for await (const raw of source) {
|
|
205
|
+
const record = (0, rows_1.parseWholeStoreRow)(raw);
|
|
206
|
+
if (!record) {
|
|
207
|
+
context.logger.warn('reconcileVectorIndex: skipped a row that is not a store item', {
|
|
208
|
+
sortKey: (0, truncate_1.truncateForLog)(raw.SK),
|
|
209
|
+
});
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if ((0, table_schema_1.isExpiredRow)(record, now) || !(0, rows_1.namespaceMatchesPrefix)(record.namespace, prefix))
|
|
213
|
+
continue;
|
|
214
|
+
pending.push(record);
|
|
215
|
+
if (pending.length >= batchLimit)
|
|
216
|
+
await drainPending(context, pending, live, signal);
|
|
217
|
+
}
|
|
218
|
+
await drainPending(context, pending, live, signal);
|
|
219
|
+
const embeddings = await (0, semantic_search_1.embedValues)(context, live.map((entry) => entry.value));
|
|
220
|
+
return live.map((entry, i) => ({
|
|
221
|
+
namespace: entry.namespace,
|
|
222
|
+
key: entry.key,
|
|
223
|
+
embedding: embeddings[i],
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Re-push every live embedding to the backend.
|
|
228
|
+
*
|
|
229
|
+
* Accepts: `targets` — a target with no embedding is not pushed; its vector is
|
|
230
|
+
* {@link pruneOrphans}' business, not an upsert of nothing.
|
|
231
|
+
*
|
|
232
|
+
* Returns: how many vectors were upserted.
|
|
233
|
+
*
|
|
234
|
+
* Throws: whatever the backend throws. The upserts are issued one at a time on
|
|
235
|
+
* purpose: this is a bulk repair against a third-party index, and the run has
|
|
236
|
+
* no latency budget worth a thundering herd.
|
|
237
|
+
*/
|
|
238
|
+
async function pushEmbeddings(backend, targets) {
|
|
239
|
+
let upserted = 0;
|
|
240
|
+
for (const target of targets) {
|
|
241
|
+
if (!target.embedding)
|
|
242
|
+
continue;
|
|
243
|
+
await backend.upsert(target.namespace, target.key, target.embedding);
|
|
244
|
+
upserted += 1;
|
|
245
|
+
}
|
|
246
|
+
return upserted;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Refs the backend holds that the live set does not account for.
|
|
250
|
+
*
|
|
251
|
+
* Accepts: `backendRefs` — whatever the backend lists under the prefix.
|
|
252
|
+
* `live` — the snapshot; a target with no embedding does not account for a
|
|
253
|
+
* vector, since its item no longer produces one.
|
|
254
|
+
*
|
|
255
|
+
* Returns: the candidates to prune — candidates, not conclusions: the snapshot
|
|
256
|
+
* and the backend listing are not one point in time (see {@link pruneOrphans}).
|
|
257
|
+
*
|
|
258
|
+
* Throws: nothing.
|
|
259
|
+
*/
|
|
260
|
+
function selectOrphans(backendRefs, live) {
|
|
261
|
+
const liveKeys = new Set(live
|
|
262
|
+
.filter((target) => target.embedding !== undefined)
|
|
263
|
+
.map((target) => refIdentity(target.namespace, target.key)));
|
|
264
|
+
return backendRefs.filter((ref) => !liveKeys.has(refIdentity(ref.namespace, ref.key)));
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* True when a candidate's canonical item is confirmed absent right now. The
|
|
268
|
+
* live-set snapshot and this read are not one point in time, so an item
|
|
269
|
+
* written between them looks orphaned even though it is live — and deleting
|
|
270
|
+
* its vector would silently drop a just-written item out of semantic search.
|
|
271
|
+
*/
|
|
272
|
+
async function confirmedGone(context, ref) {
|
|
273
|
+
return (0, idempotent_write_1.isRowAbsent)(context, (0, rows_1.itemRowKey)(ref));
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Delete backend vectors with no canonical item.
|
|
277
|
+
*
|
|
278
|
+
* Accepts: `live` — the snapshot {@link collectReconcileTargets} took.
|
|
279
|
+
* `backend.listKeys` — optional; a backend that cannot enumerate its own keys
|
|
280
|
+
* cannot be pruned, which is reported rather than silently skipped.
|
|
281
|
+
*
|
|
282
|
+
* Returns: how many vectors were deleted.
|
|
283
|
+
*
|
|
284
|
+
* Throws: whatever the backend and the confirmation read throw.
|
|
285
|
+
*
|
|
286
|
+
* Guarantees: a vector is deleted only on evidence that its item is gone — the
|
|
287
|
+
* snapshot saw the item and it yields no embedding, or a fresh
|
|
288
|
+
* strongly-consistent read finds no row at all. An item written between the
|
|
289
|
+
* snapshot and the listing looks orphaned and is kept, so reconciling never
|
|
290
|
+
* drops a just-written item out of semantic search.
|
|
291
|
+
*/
|
|
292
|
+
async function pruneOrphans(context, backend, prefix, live) {
|
|
293
|
+
if (!backend.listKeys) {
|
|
294
|
+
context.logger.info('reconcileVectorIndex prune skipped: backend has no listKeys', {
|
|
295
|
+
prefix: (0, truncate_1.truncateLabelsForLog)(prefix),
|
|
296
|
+
});
|
|
297
|
+
return 0;
|
|
298
|
+
}
|
|
299
|
+
const candidates = selectOrphans(await backend.listKeys(prefix), live);
|
|
300
|
+
// Every item the snapshot actually saw, embedded or not. A candidate in here
|
|
301
|
+
// is prunable on the evidence already gathered — its item exists but yields
|
|
302
|
+
// no embedding (its indexable text became empty), so its vector really is
|
|
303
|
+
// stale. Only a candidate the snapshot never saw at all is ambiguous.
|
|
304
|
+
const observed = new Set(live.map((target) => refIdentity(target.namespace, target.key)));
|
|
305
|
+
let pruned = 0;
|
|
306
|
+
for (const ref of candidates) {
|
|
307
|
+
const seen = observed.has(refIdentity(ref.namespace, ref.key));
|
|
308
|
+
if (!seen && !(await confirmedGone(context, ref))) {
|
|
309
|
+
// The ref is a consumer backend's answer, bounded by nothing this package ran.
|
|
310
|
+
context.logger.info('reconcileVectorIndex: kept a vector whose item reappeared', {
|
|
311
|
+
namespace: (0, truncate_1.truncateLabelsForLog)(ref.namespace),
|
|
312
|
+
key: (0, truncate_1.truncateForLog)(ref.key),
|
|
313
|
+
});
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
await backend.delete(ref.namespace, ref.key);
|
|
317
|
+
pruned += 1;
|
|
318
|
+
}
|
|
319
|
+
return pruned;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Repair the vector copy against the items under a prefix: re-push every live
|
|
323
|
+
* embedding, then prune entries whose item is confirmed gone.
|
|
324
|
+
*
|
|
325
|
+
* Accepts: `prefix` — parsed. `signal` — cancels the table read.
|
|
326
|
+
*
|
|
327
|
+
* Returns: how many entries were upserted and pruned.
|
|
328
|
+
*
|
|
329
|
+
* Throws: whatever the table read, the embeddings model or the backend throws.
|
|
330
|
+
*/
|
|
331
|
+
async function reconcileVectors(context, prefix, signal) {
|
|
332
|
+
const targets = await collectReconcileTargets(context, prefix, signal);
|
|
333
|
+
const upserted = await pushEmbeddings(context.vectorBackend, targets);
|
|
334
|
+
const pruned = await pruneOrphans(context, context.vectorBackend, prefix, targets);
|
|
335
|
+
return { upserted, pruned };
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Warn when a backend's own ordering disagrees with its scores. A backend
|
|
339
|
+
* returning a raw distance still hands back nearest-first, so the order looks
|
|
340
|
+
* right while every score means the opposite of what {@link VectorMatch.score}
|
|
341
|
+
* promises — ascending scores are that exact signature. Results are never
|
|
342
|
+
* reordered on this basis: a correctly-scored backend's order is authoritative.
|
|
343
|
+
*/
|
|
344
|
+
function warnOnNonDescendingScores(context, matches, namespacePrefix) {
|
|
345
|
+
const descending = matches.every((match, position) => position === 0 || matches[position - 1].score >= match.score);
|
|
346
|
+
if (descending)
|
|
347
|
+
return;
|
|
348
|
+
context.logger.warn('search: vectorBackend returned ascending scores; VectorMatch.score must be a relevance ' +
|
|
349
|
+
'(higher is better), not a distance — results are forwarded in the order the backend gave', { namespacePrefix: (0, truncate_1.truncateLabelsForLog)(namespacePrefix) });
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* The address a backend match names, parsed, or `undefined` — logged — when it
|
|
353
|
+
* names one this store cannot form. A backend returning a namespace element
|
|
354
|
+
* that holds the reserved separator would otherwise turn a whole search into a
|
|
355
|
+
* `VALIDATION` error over one bad key. The address is checked here rather than
|
|
356
|
+
* read back off the error `getItem` raises: the read raises a `VALIDATION` error
|
|
357
|
+
* of its own for a payload it cannot honour — an offloaded row read with no
|
|
358
|
+
* `s3` configured, a descriptor that is not one, an `s3Key` outside the row's
|
|
359
|
+
* path — and those are reads that did not happen, not items that are not
|
|
360
|
+
* there, so telling them apart by code alone dropped them as well.
|
|
361
|
+
*
|
|
362
|
+
* The line quotes the address the backend gave, bounded: this fires in the
|
|
363
|
+
* branch where `parseStoreAddress` refused it, one line per bad match, and
|
|
364
|
+
* nothing this package ran bounded either the labels or how many of them there
|
|
365
|
+
* are. The `reason` goes through the same cap, though it is the only one of
|
|
366
|
+
* these that cannot exceed it: what this `catch` binds is always
|
|
367
|
+
* `parseStoreAddress`'s own `VALIDATION` error, whose code is a literal of this
|
|
368
|
+
* package's. It is cut anyway so the rule reads the same at every site that
|
|
369
|
+
* names a failure — a name and a message are the two halves of what the
|
|
370
|
+
* failure was, and `message` is bounded where `redactedMessage` relays it —
|
|
371
|
+
* and so that a later refusal thrown from somewhere else does not arrive
|
|
372
|
+
* unbounded because this one site was reasoned about individually.
|
|
373
|
+
*/
|
|
374
|
+
function addressOf(context, match) {
|
|
375
|
+
try {
|
|
376
|
+
return (0, parse_1.parseStoreAddress)(match.namespace, match.key);
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
context.logger.warn('search: skipped an unusable vectorBackend match', {
|
|
380
|
+
namespace: (0, truncate_1.truncateLabelsForLog)(match.namespace),
|
|
381
|
+
key: (0, truncate_1.truncateForLog)(match.key),
|
|
382
|
+
reason: (0, truncate_1.truncateForLog)((0, base_error_1.failureLabel)(error)),
|
|
383
|
+
});
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Read the canonical item a backend match points at, or `null` when the match
|
|
389
|
+
* names an address this store cannot form (see {@link addressOf}).
|
|
390
|
+
*
|
|
391
|
+
* Every failure of the read itself reaches the caller. A throttled or cancelled
|
|
392
|
+
* read says nothing about whether the item is there, so treating it as an
|
|
393
|
+
* absent match handed back a page silently one item short, with only a `warn`
|
|
394
|
+
* the default logger never prints to say so — while the in-DynamoDB path fails
|
|
395
|
+
* the same search outright.
|
|
396
|
+
*/
|
|
397
|
+
async function fetchMatch(context, match, signal) {
|
|
398
|
+
const address = addressOf(context, match);
|
|
399
|
+
if (address === undefined)
|
|
400
|
+
return null;
|
|
401
|
+
return (0, get_item_1.getItem)(context, address, signal);
|
|
402
|
+
}
|
|
403
|
+
/** Stable, collision-free identity for a match, so one call reads each item once. */
|
|
404
|
+
function matchIdentity(match) {
|
|
405
|
+
return JSON.stringify([match.namespace, match.key]);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Read the canonical item behind every match this call has not read yet, into
|
|
409
|
+
* `fetched`.
|
|
410
|
+
*
|
|
411
|
+
* Each round asks the backend for a larger `topK`, and the answer *contains*
|
|
412
|
+
* the previous round's matches, so re-reading them cost one DynamoDB read — and
|
|
413
|
+
* one S3 download for an offloaded item — per match per round. Remembering the
|
|
414
|
+
* reads bounds a whole search at one read per distinct match. The same map also
|
|
415
|
+
* covers a backend that returns one key twice in a single round.
|
|
416
|
+
*
|
|
417
|
+
* What a caller trades for it: an item changed between two rounds is answered
|
|
418
|
+
* from the first read. A search is not a transaction and the rounds are
|
|
419
|
+
* milliseconds apart, so the alternative — re-reading to catch a write that may
|
|
420
|
+
* as well have landed a moment later — buys nothing.
|
|
421
|
+
*/
|
|
422
|
+
async function fetchUnseen(context, scoped, fetched, signal) {
|
|
423
|
+
const unseen = new Map();
|
|
424
|
+
for (const match of scoped) {
|
|
425
|
+
const identity = matchIdentity(match);
|
|
426
|
+
if (!fetched.has(identity))
|
|
427
|
+
unseen.set(identity, match);
|
|
428
|
+
}
|
|
429
|
+
const pending = [...unseen.values()];
|
|
430
|
+
const items = await (0, concurrency_1.mapWithConcurrency)(pending, context.readConcurrency ?? concurrency_1.DEFAULT_READ_CONCURRENCY, (match) => fetchMatch(context, match, signal));
|
|
431
|
+
pending.forEach((match, index) => fetched.set(matchIdentity(match), items[index]));
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Rank a search through a configured vector backend.
|
|
435
|
+
*
|
|
436
|
+
* Accepts: `context` — the store's, with the backend searched and the index
|
|
437
|
+
* whose embeddings model embeds the query. `search.query` — non-empty; the
|
|
438
|
+
* caller checks that before choosing this path. `search.offset`/`search.limit`
|
|
439
|
+
* — the page, whose end (`offset + limit`) must fit within
|
|
440
|
+
* `maxSearchCandidates`, since that many matches have to be fetched to fill it.
|
|
441
|
+
* `search.filter` — applied to the canonical item, not to whatever the backend
|
|
442
|
+
* stored, so a filter is never answered from a stale vector.
|
|
443
|
+
*
|
|
444
|
+
* Returns: the page's items in the backend's order, each carrying the relevance
|
|
445
|
+
* score for its vector. Fewer than `limit` items means the backend has no more
|
|
446
|
+
* matches under the prefix, not that the page was cut short.
|
|
447
|
+
*
|
|
448
|
+
* Throws: `VALIDATION` naming `index.dims` when the query embeds to a
|
|
449
|
+
* different width than the index declares, and naming `maxSearchCandidates`
|
|
450
|
+
* either for a page larger than the cap or when the filter leaves the page short
|
|
451
|
+
* at the cap — the same answer the in-DynamoDB ranker gives, rather than a
|
|
452
|
+
* silently short page. Whatever a canonical read throws — a read that did not
|
|
453
|
+
* happen is not an item that is not there — since a match this store cannot
|
|
454
|
+
* address is dropped before its read rather than caught after it (see
|
|
455
|
+
* {@link addressOf}). Whatever the embeddings model and the backend throw.
|
|
456
|
+
*
|
|
457
|
+
* Guarantees: DynamoDB stays canonical. A match whose item has since been
|
|
458
|
+
* deleted or lies outside the prefix is dropped and the search asks the backend
|
|
459
|
+
* for more, so a stale or over-broad index costs results only in latency. A
|
|
460
|
+
* match whose read *fails* is not dropped: the page a caller receives is never
|
|
461
|
+
* shorter than the matches that exist, which is what the in-DynamoDB path
|
|
462
|
+
* already promises. Items are fetched with the same bounded concurrency as the
|
|
463
|
+
* in-DynamoDB path, and each distinct match is read once for the whole call
|
|
464
|
+
* however many rounds it takes (see {@link fetchUnseen}).
|
|
465
|
+
*/
|
|
466
|
+
async function searchViaBackend(context, search, signal) {
|
|
467
|
+
const backend = context.vectorBackend;
|
|
468
|
+
const index = context.index;
|
|
469
|
+
const { offset, limit } = search;
|
|
470
|
+
const queryVector = await index.embeddings.embedQuery(search.query);
|
|
471
|
+
(0, semantic_search_1.assertVectorDims)(index, queryVector, 'query');
|
|
472
|
+
const need = offset + limit;
|
|
473
|
+
if (need > context.maxSearchCandidates) {
|
|
474
|
+
throw (0, errors_1.validationError)(`Requested page (offset ${offset} + limit ${limit} = ${need}) exceeds maxSearchCandidates ` +
|
|
475
|
+
`(${context.maxSearchCandidates}); narrow the page or raise maxSearchCandidates`, 'maxSearchCandidates');
|
|
476
|
+
}
|
|
477
|
+
let topK = Math.min(need, context.maxSearchCandidates);
|
|
478
|
+
let results;
|
|
479
|
+
const fetched = new Map();
|
|
480
|
+
for (;;) {
|
|
481
|
+
const matches = toRelevanceScores(await backend.query(search.namespacePrefix, queryVector, topK), context.vectorScoreDirection);
|
|
482
|
+
warnOnNonDescendingScores(context, matches, search.namespacePrefix);
|
|
483
|
+
const scoped = matches.filter((match) => (0, rows_1.namespaceMatchesPrefix)(match.namespace, search.namespacePrefix));
|
|
484
|
+
await fetchUnseen(context, scoped, fetched, signal);
|
|
485
|
+
results = [];
|
|
486
|
+
for (const match of scoped) {
|
|
487
|
+
const item = fetched.get(matchIdentity(match));
|
|
488
|
+
if (item && (0, filter_1.passesFilter)(item, search.filter))
|
|
489
|
+
results.push({ ...item, score: match.score });
|
|
490
|
+
}
|
|
491
|
+
if (results.length >= need || matches.length < topK)
|
|
492
|
+
break;
|
|
493
|
+
if (topK >= context.maxSearchCandidates) {
|
|
494
|
+
// The backend still holds matches, but the filter left the page short at the cap: the same answer the in-DB ranker gives, not a silently short page.
|
|
495
|
+
throw (0, errors_1.validationError)(`Semantic search collected ${results.length} of ${need} matches within maxSearchCandidates ` +
|
|
496
|
+
`(${context.maxSearchCandidates}); narrow the filter or raise maxSearchCandidates`, 'maxSearchCandidates');
|
|
497
|
+
}
|
|
498
|
+
topK = Math.min(topK * 2, context.maxSearchCandidates);
|
|
499
|
+
}
|
|
500
|
+
return results;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Normalise a backend's matches to the relevance direction upstream
|
|
504
|
+
* `SearchItem.score` documents — "higher scores indicate better matches",
|
|
505
|
+
* typically a cosine similarity between -1 and 1.
|
|
506
|
+
*
|
|
507
|
+
* A distance is converted by **negation** rather than `1 / (1 + d)`: negation
|
|
508
|
+
* is monotone over the whole real line, needs no non-negativity precondition,
|
|
509
|
+
* and is exactly invertible, so the original distance is simply `-score`. A
|
|
510
|
+
* reciprocal would silently imply a different `(0, 1]` scale and is undefined
|
|
511
|
+
* at `d === -1`. Negative scores are already in range for this contract, so
|
|
512
|
+
* nothing is lost by producing them.
|
|
513
|
+
*
|
|
514
|
+
* Results are re-sorted after conversion so a backend that returns its matches
|
|
515
|
+
* in some other order still ranks correctly. Anything that is not `'distance'`
|
|
516
|
+
* is returned untouched — its own order stays authoritative, exactly as before.
|
|
517
|
+
*
|
|
518
|
+
* The test is for `'distance'` rather than against `'relevance'` on purpose:
|
|
519
|
+
* converting is the destructive branch, so only the exact value that asks for
|
|
520
|
+
* it may reach it. Testing the other way round made every unrecognised
|
|
521
|
+
* string — `'Distance'`, a typo, a value read from a config file — reverse the
|
|
522
|
+
* ranking silently, with no error and no warning (the ascending-score warning
|
|
523
|
+
* lives downstream of this conversion, so it could never fire). `setUpStore`
|
|
524
|
+
* rejects such a value outright; this keeps the failure harmless for any
|
|
525
|
+
* caller that reaches the function some other way.
|
|
526
|
+
*
|
|
527
|
+
* Accepts: `matches` — in any order, including empty. `direction` — only the
|
|
528
|
+
* exact string `'distance'` converts; every other value, recognised or not,
|
|
529
|
+
* passes the matches through untouched.
|
|
530
|
+
*
|
|
531
|
+
* Returns: matches whose `score` follows the relevance direction upstream
|
|
532
|
+
* documents, highest first. Converting re-sorts, so a backend that returned its
|
|
533
|
+
* matches in some other order still ranks correctly.
|
|
534
|
+
*
|
|
535
|
+
* Throws: nothing.
|
|
536
|
+
*
|
|
537
|
+
* Guarantees: the conversion is exactly invertible — the original distance is
|
|
538
|
+
* `-score` — so nothing about the backend's answer is lost.
|
|
539
|
+
*/
|
|
540
|
+
function toRelevanceScores(matches, direction) {
|
|
541
|
+
if (direction !== 'distance')
|
|
542
|
+
return matches;
|
|
543
|
+
return matches
|
|
544
|
+
.map((match) => ({ ...match, score: -match.score }))
|
|
545
|
+
.sort((left, right) => right.score - left.score);
|
|
546
|
+
}
|