@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,834 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hides the checkpointer's row format.
|
|
4
|
+
*
|
|
5
|
+
* A checkpoint is three kinds of row in its thread's partition — a light META
|
|
6
|
+
* row a listing reads, a heavy PAYLOAD row, and one WRITE row per pending
|
|
7
|
+
* write — and everything about how they are keyed, what they carry, and how a
|
|
8
|
+
* checkpoint and its writes become rows and come back is decided here: the
|
|
9
|
+
* sort-key segments and their order, the offset that lets the reserved negative
|
|
10
|
+
* write indices sort as plain strings, the `writeGroup` and `occurrence` that
|
|
11
|
+
* identify a write across calls, the recency-index keys a META row carries, and
|
|
12
|
+
* which rows a read admits as this adapter's own. Nothing else composes or
|
|
13
|
+
* splits a checkpointer key.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.MIN_ENCODABLE_WRITE_INDEX = exports.WRITE_GROUP_ATTRIBUTE = void 0;
|
|
17
|
+
exports.checkpointerPartitionPrefix = checkpointerPartitionPrefix;
|
|
18
|
+
exports.partitionKey = partitionKey;
|
|
19
|
+
exports.metaSortKey = metaSortKey;
|
|
20
|
+
exports.metaSortKeyPrefix = metaSortKeyPrefix;
|
|
21
|
+
exports.metaAnyNamespacePrefix = metaAnyNamespacePrefix;
|
|
22
|
+
exports.payloadSortKey = payloadSortKey;
|
|
23
|
+
exports.writeSortKey = writeSortKey;
|
|
24
|
+
exports.writeSortKeyBytes = writeSortKeyBytes;
|
|
25
|
+
exports.isCheckpointerSortKey = isCheckpointerSortKey;
|
|
26
|
+
exports.writeSortKeyPrefix = writeSortKeyPrefix;
|
|
27
|
+
exports.metaRowKey = metaRowKey;
|
|
28
|
+
exports.payloadRowKey = payloadRowKey;
|
|
29
|
+
exports.partitionQuery = partitionQuery;
|
|
30
|
+
exports.beginsWithQuery = beginsWithQuery;
|
|
31
|
+
exports.buildCheckpointRows = buildCheckpointRows;
|
|
32
|
+
exports.buildWriteRows = buildWriteRows;
|
|
33
|
+
exports.parseMetaRow = parseMetaRow;
|
|
34
|
+
exports.parseHeadRow = parseHeadRow;
|
|
35
|
+
exports.readCheckpoint = readCheckpoint;
|
|
36
|
+
exports.readMetadata = readMetadata;
|
|
37
|
+
exports.toPendingWrites = toPendingWrites;
|
|
38
|
+
exports.resolveWriteIndices = resolveWriteIndices;
|
|
39
|
+
exports.dropSupersededWrites = dropSupersededWrites;
|
|
40
|
+
exports.checkpointRowDescriptors = checkpointRowDescriptors;
|
|
41
|
+
exports.checkpointRowUnit = checkpointRowUnit;
|
|
42
|
+
exports.checkpointRowKind = checkpointRowKind;
|
|
43
|
+
exports.checkpointIndexTarget = checkpointIndexTarget;
|
|
44
|
+
const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
|
|
45
|
+
const clock_1 = require("../../shared/clock");
|
|
46
|
+
const codec_1 = require("../../shared/codec/codec");
|
|
47
|
+
const offloader_1 = require("../../shared/codec/s3/offloader");
|
|
48
|
+
const concurrency_1 = require("../../shared/concurrency");
|
|
49
|
+
const partition_delete_1 = require("../../shared/dynamodb/partition-delete");
|
|
50
|
+
const recency_index_1 = require("../../shared/dynamodb/recency-index");
|
|
51
|
+
const table_schema_1 = require("../../shared/dynamodb/table-schema");
|
|
52
|
+
const errors_1 = require("../../shared/errors/errors");
|
|
53
|
+
const truncate_1 = require("../../shared/logging/truncate");
|
|
54
|
+
const ulid_1 = require("../../shared/ulid");
|
|
55
|
+
/**
|
|
56
|
+
* The attribute a WRITE row carries the id of the `putWrites` call that wrote
|
|
57
|
+
* it in. A special write's compare-and-swap pins it, and a thread delete pins
|
|
58
|
+
* a WRITE row's delete to it; a per-call ULID is already unique, so it needs no
|
|
59
|
+
* separate revision attribute.
|
|
60
|
+
*/
|
|
61
|
+
exports.WRITE_GROUP_ATTRIBUTE = 'writeGroup';
|
|
62
|
+
/** Fixed digit width for the WRITE index so sort keys order numerically. */
|
|
63
|
+
const WRITE_INDEX_PAD_WIDTH = 10;
|
|
64
|
+
/**
|
|
65
|
+
* Added to every write index before padding so the special negative slots from
|
|
66
|
+
* `WRITES_IDX_MAP` (-1 ERROR .. -4 RESUME) encode as non-negative, sortable
|
|
67
|
+
* integers that order below positional (0+) writes.
|
|
68
|
+
*/
|
|
69
|
+
const WRITE_INDEX_OFFSET = 8;
|
|
70
|
+
/**
|
|
71
|
+
* The most negative write index the sort key can encode, `-WRITE_INDEX_OFFSET`.
|
|
72
|
+
* A static test pins it against the peer's `WRITES_IDX_MAP`, so a peer bump
|
|
73
|
+
* that adds a more negative special slot fails loudly instead of producing
|
|
74
|
+
* unsortable keys.
|
|
75
|
+
*/
|
|
76
|
+
exports.MIN_ENCODABLE_WRITE_INDEX = -WRITE_INDEX_OFFSET;
|
|
77
|
+
/** Sort-key kinds for the checkpoints table (the approved SK separation). */
|
|
78
|
+
var CheckpointRowKind;
|
|
79
|
+
(function (CheckpointRowKind) {
|
|
80
|
+
CheckpointRowKind["META"] = "META";
|
|
81
|
+
CheckpointRowKind["PAYLOAD"] = "PAYLOAD";
|
|
82
|
+
CheckpointRowKind["WRITE"] = "WRITE";
|
|
83
|
+
})(CheckpointRowKind || (CheckpointRowKind = {}));
|
|
84
|
+
/**
|
|
85
|
+
* Adapter tag prefixed to every checkpointer partition key. Without it a
|
|
86
|
+
* `thread_id` reused as a `sessionId` or a store namespace root — an ordinary
|
|
87
|
+
* design choice — put all three adapters' rows in one partition on a table
|
|
88
|
+
* shared via `DynamoDBFactory.createAll()`, where a partition-wide delete
|
|
89
|
+
* reached another adapter's data and composed sort keys could collide
|
|
90
|
+
* byte-for-byte. The three tags differ in their first character, so the key
|
|
91
|
+
* spaces are disjoint by construction.
|
|
92
|
+
*/
|
|
93
|
+
const ADAPTER_PARTITION_PREFIX = `${table_schema_1.ADAPTER_TAGS.checkpointer}${table_schema_1.KEY_SEPARATOR}`;
|
|
94
|
+
/**
|
|
95
|
+
* The tag every checkpointer partition key starts with.
|
|
96
|
+
*
|
|
97
|
+
* Accepts: nothing — the tag is fixed, and the function exists so no caller
|
|
98
|
+
* composes it by hand.
|
|
99
|
+
*
|
|
100
|
+
* Returns: the tag, for a table-wide `begins_with` over this adapter's rows.
|
|
101
|
+
*
|
|
102
|
+
* Throws: nothing.
|
|
103
|
+
*/
|
|
104
|
+
function checkpointerPartitionPrefix() {
|
|
105
|
+
return ADAPTER_PARTITION_PREFIX;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Partition key for a thread: the adapter tag plus the thread id.
|
|
109
|
+
*
|
|
110
|
+
* Accepts: `threadId` — normally validated, so it cannot contain the separator.
|
|
111
|
+
*
|
|
112
|
+
* Returns: the partition key. Total, like every key builder here: a row read
|
|
113
|
+
* from a shared table is *tested* against these, so a malformed value must
|
|
114
|
+
* compose a key that matches nothing rather than fail the read.
|
|
115
|
+
*
|
|
116
|
+
* Throws: nothing.
|
|
117
|
+
*/
|
|
118
|
+
function partitionKey(threadId) {
|
|
119
|
+
return `${ADAPTER_PARTITION_PREFIX}${threadId}`;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Sort key for a checkpoint's lightweight metadata item.
|
|
123
|
+
*
|
|
124
|
+
* Accepts: `checkpointNs` — possibly empty, which is the root namespace.
|
|
125
|
+
* `checkpointId` — the checkpoint's own id.
|
|
126
|
+
*
|
|
127
|
+
* Returns: the sort key. The namespace sits above the id so a namespace's
|
|
128
|
+
* checkpoints are contiguous and a `begins_with` selects exactly them.
|
|
129
|
+
*
|
|
130
|
+
* Throws: nothing.
|
|
131
|
+
*/
|
|
132
|
+
function metaSortKey(checkpointNs, checkpointId) {
|
|
133
|
+
return `${CheckpointRowKind.META}${table_schema_1.KEY_SEPARATOR}${checkpointNs}${table_schema_1.KEY_SEPARATOR}${checkpointId}`;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* `begins_with` prefix selecting every META item in one namespace.
|
|
137
|
+
*
|
|
138
|
+
* Accepts: `checkpointNs` — the namespace to scope to; empty scopes to the root
|
|
139
|
+
* namespace, not to every namespace.
|
|
140
|
+
*
|
|
141
|
+
* Returns: the prefix, separator-terminated, so the namespace `a` does not also
|
|
142
|
+
* select `ab`.
|
|
143
|
+
*
|
|
144
|
+
* Throws: nothing.
|
|
145
|
+
*/
|
|
146
|
+
function metaSortKeyPrefix(checkpointNs) {
|
|
147
|
+
return `${CheckpointRowKind.META}${table_schema_1.KEY_SEPARATOR}${checkpointNs}${table_schema_1.KEY_SEPARATOR}`;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* `begins_with` prefix selecting every META item of a thread.
|
|
151
|
+
*
|
|
152
|
+
* Accepts: nothing — the prefix is the same for every thread, because the
|
|
153
|
+
* thread is already the partition.
|
|
154
|
+
*
|
|
155
|
+
* Returns: the kind prefix alone, so the selection spans every namespace of the
|
|
156
|
+
* thread — what a `list` with no `checkpoint_ns` asks for.
|
|
157
|
+
*
|
|
158
|
+
* Throws: nothing.
|
|
159
|
+
*/
|
|
160
|
+
function metaAnyNamespacePrefix() {
|
|
161
|
+
return `${CheckpointRowKind.META}${table_schema_1.KEY_SEPARATOR}`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Sort key for a checkpoint's heavy payload item.
|
|
165
|
+
*
|
|
166
|
+
* Accepts: as {@link metaSortKey}.
|
|
167
|
+
*
|
|
168
|
+
* Returns: the sort key of the row holding the checkpoint itself, which is
|
|
169
|
+
* written before its META row and read only after one is found.
|
|
170
|
+
*
|
|
171
|
+
* Throws: nothing.
|
|
172
|
+
*/
|
|
173
|
+
function payloadSortKey(checkpointNs, checkpointId) {
|
|
174
|
+
return `${CheckpointRowKind.PAYLOAD}${table_schema_1.KEY_SEPARATOR}${checkpointNs}${table_schema_1.KEY_SEPARATOR}${checkpointId}`;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Sort key for a single pending write. The trailing `channel` segment is what
|
|
178
|
+
* keeps two *different* channels from ever occupying one row: without it, a
|
|
179
|
+
* retried task whose write mix changed could compute an index another channel
|
|
180
|
+
* already holds, and the first-write-wins guard — which cannot tell a
|
|
181
|
+
* genuine retry from an unrelated write — would silently discard it. The
|
|
182
|
+
* channel is appended verbatim as the final segment, so two sort keys collide
|
|
183
|
+
* only when their channels are byte-identical; `writeSortKeyPrefix` stops at
|
|
184
|
+
* the checkpoint id, ahead of this segment, so `begins_with` reads are
|
|
185
|
+
* unaffected.
|
|
186
|
+
*
|
|
187
|
+
* The composed length is not checked here: `parsePutWritesRequest` refuses a
|
|
188
|
+
* write whose key would pass the cap, measured by {@link writeSortKeyBytes},
|
|
189
|
+
* before anything is encoded.
|
|
190
|
+
*
|
|
191
|
+
* Accepts: `at` — the write's location: `at.index`, an integer (padding a
|
|
192
|
+
* fraction produced `00000009.5`, which no longer orders numerically);
|
|
193
|
+
* `at.channel`, separator-free, which `parseWriteChannel` establishes before
|
|
194
|
+
* any key is built; `at.checkpointNs`, `at.checkpointId` and `at.taskId`, the
|
|
195
|
+
* other parsed identifiers.
|
|
196
|
+
*
|
|
197
|
+
* Returns: the sort key, its index zero-padded to a fixed width so the special
|
|
198
|
+
* negative slots order below the positional ones.
|
|
199
|
+
*
|
|
200
|
+
* Throws: `VALIDATION` naming `index` for an index this encoding cannot
|
|
201
|
+
* represent.
|
|
202
|
+
*/
|
|
203
|
+
function writeSortKey(at) {
|
|
204
|
+
const offsetIndex = at.index + WRITE_INDEX_OFFSET;
|
|
205
|
+
if (!Number.isInteger(offsetIndex) ||
|
|
206
|
+
offsetIndex < 0 ||
|
|
207
|
+
offsetIndex.toString().length > WRITE_INDEX_PAD_WIDTH) {
|
|
208
|
+
throw (0, errors_1.validationError)(`write index ${at.index} is not an integer encodable at offset ${WRITE_INDEX_OFFSET} ` +
|
|
209
|
+
`with ${WRITE_INDEX_PAD_WIDTH} digits`, 'index');
|
|
210
|
+
}
|
|
211
|
+
const paddedIndex = offsetIndex.toString().padStart(WRITE_INDEX_PAD_WIDTH, '0');
|
|
212
|
+
return [
|
|
213
|
+
CheckpointRowKind.WRITE,
|
|
214
|
+
at.checkpointNs,
|
|
215
|
+
at.checkpointId,
|
|
216
|
+
at.taskId,
|
|
217
|
+
paddedIndex,
|
|
218
|
+
at.channel,
|
|
219
|
+
].join(table_schema_1.KEY_SEPARATOR);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* The UTF-8 length of the WRITE sort key a write would get, without refusing
|
|
223
|
+
* one over DynamoDB's cap.
|
|
224
|
+
*
|
|
225
|
+
* Accepts: `at` — the write's location without its index: `at.checkpointNs`,
|
|
226
|
+
* `at.checkpointId`, `at.taskId` and `at.channel`, the four segments of a
|
|
227
|
+
* WRITE sort key other than the index. The index is zero-padded to a fixed
|
|
228
|
+
* width, so the length is the same for every index a write can take, and
|
|
229
|
+
* index 0 stands for them all.
|
|
230
|
+
*
|
|
231
|
+
* Returns: the byte length DynamoDB measures the composed key by.
|
|
232
|
+
*
|
|
233
|
+
* Throws: nothing.
|
|
234
|
+
*/
|
|
235
|
+
function writeSortKeyBytes(at) {
|
|
236
|
+
return Buffer.byteLength(writeSortKey({ ...at, index: 0 }), 'utf8');
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Whether `sortKey` is one this adapter writes.
|
|
240
|
+
*
|
|
241
|
+
* Accepts: any sort key read from a thread's partition.
|
|
242
|
+
*
|
|
243
|
+
* Returns: whether it starts with one of this adapter's kind tags. A partition
|
|
244
|
+
* query carries no sort-key condition, so a partition-wide delete uses this to
|
|
245
|
+
* leave a row it does not own in place rather than deleting the whole partition
|
|
246
|
+
* blindly.
|
|
247
|
+
*
|
|
248
|
+
* Throws: nothing.
|
|
249
|
+
*/
|
|
250
|
+
function isCheckpointerSortKey(sortKey) {
|
|
251
|
+
return Object.values(CheckpointRowKind).some((kind) => sortKey.startsWith(`${kind}${table_schema_1.KEY_SEPARATOR}`));
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* `begins_with` prefix selecting every WRITE item of one checkpoint.
|
|
255
|
+
*
|
|
256
|
+
* Accepts: the namespace and checkpoint the writes belong to.
|
|
257
|
+
*
|
|
258
|
+
* Returns: the prefix, separator-terminated, so one checkpoint's writes never
|
|
259
|
+
* include another's whose id merely starts the same way.
|
|
260
|
+
*
|
|
261
|
+
* Throws: nothing.
|
|
262
|
+
*/
|
|
263
|
+
function writeSortKeyPrefix(checkpointNs, checkpointId) {
|
|
264
|
+
return `${CheckpointRowKind.WRITE}${table_schema_1.KEY_SEPARATOR}${checkpointNs}${table_schema_1.KEY_SEPARATOR}${checkpointId}${table_schema_1.KEY_SEPARATOR}`;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* The key of one checkpoint's META row.
|
|
268
|
+
*
|
|
269
|
+
* Accepts: `at` — the checkpoint's thread, namespace and id.
|
|
270
|
+
*
|
|
271
|
+
* Returns: the row's partition and sort key.
|
|
272
|
+
*
|
|
273
|
+
* Throws: nothing.
|
|
274
|
+
*/
|
|
275
|
+
function metaRowKey(at) {
|
|
276
|
+
return { PK: partitionKey(at.threadId), SK: metaSortKey(at.checkpointNs, at.checkpointId) };
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* The key of one checkpoint's PAYLOAD row.
|
|
280
|
+
*
|
|
281
|
+
* Accepts: `at` — the checkpoint's thread, namespace and id.
|
|
282
|
+
*
|
|
283
|
+
* Returns: the row's partition and sort key.
|
|
284
|
+
*
|
|
285
|
+
* Throws: nothing.
|
|
286
|
+
*/
|
|
287
|
+
function payloadRowKey(at) {
|
|
288
|
+
return { PK: partitionKey(at.threadId), SK: payloadSortKey(at.checkpointNs, at.checkpointId) };
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Query input selecting every item in a thread's partition.
|
|
292
|
+
*
|
|
293
|
+
* Accepts: the thread whose partition to read.
|
|
294
|
+
*
|
|
295
|
+
* Returns: the Query input, with no sort-key condition: it selects this
|
|
296
|
+
* adapter's META, PAYLOAD and WRITE rows and any row another adapter left in
|
|
297
|
+
* the partition — which is why every caller filters with
|
|
298
|
+
* `isCheckpointerSortKey`.
|
|
299
|
+
*
|
|
300
|
+
* Throws: nothing.
|
|
301
|
+
*/
|
|
302
|
+
function partitionQuery(tableName, partition, options = {}) {
|
|
303
|
+
const params = {
|
|
304
|
+
TableName: tableName,
|
|
305
|
+
KeyConditionExpression: '#pk = :pk',
|
|
306
|
+
ExpressionAttributeNames: { '#pk': table_schema_1.PARTITION_KEY_ATTRIBUTE },
|
|
307
|
+
ExpressionAttributeValues: { ':pk': partition },
|
|
308
|
+
};
|
|
309
|
+
if (options.consistent)
|
|
310
|
+
params.ConsistentRead = true;
|
|
311
|
+
return params;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Query input for a `begins_with` sort-key prefix.
|
|
315
|
+
*
|
|
316
|
+
* Accepts: `options.ascending` — sort-key order; newest-first is the default
|
|
317
|
+
* because that is what "the latest checkpoint" asks for. `options.limit` — rows
|
|
318
|
+
* DynamoDB evaluates per page, not a total. `options.consistent` — for a read
|
|
319
|
+
* whose answer a write depends on.
|
|
320
|
+
*
|
|
321
|
+
* Returns: the Query input.
|
|
322
|
+
*
|
|
323
|
+
* Throws: nothing.
|
|
324
|
+
*/
|
|
325
|
+
function beginsWithQuery(tableName, partition, skPrefix, options = {}) {
|
|
326
|
+
const bounded = options.beforeSortKey !== undefined;
|
|
327
|
+
const params = {
|
|
328
|
+
TableName: tableName,
|
|
329
|
+
KeyConditionExpression: bounded
|
|
330
|
+
? '#pk = :pk AND #sk BETWEEN :skPrefix AND :before'
|
|
331
|
+
: '#pk = :pk AND begins_with(#sk, :skPrefix)',
|
|
332
|
+
ExpressionAttributeNames: { '#pk': table_schema_1.PARTITION_KEY_ATTRIBUTE, '#sk': table_schema_1.SORT_KEY_ATTRIBUTE },
|
|
333
|
+
ExpressionAttributeValues: {
|
|
334
|
+
':pk': partition,
|
|
335
|
+
':skPrefix': skPrefix,
|
|
336
|
+
...(bounded ? { ':before': options.beforeSortKey } : {}),
|
|
337
|
+
},
|
|
338
|
+
ScanIndexForward: options.ascending ?? false,
|
|
339
|
+
};
|
|
340
|
+
// DynamoDB requires `Limit` to be at least 1 and rejects anything lower with
|
|
341
|
+
// a raw `ValidationException`. A caller asking for nothing is answered
|
|
342
|
+
// before a request is built (see `listCheckpoints`), so a non-positive value
|
|
343
|
+
// reaching here means no page size was intended.
|
|
344
|
+
if (options.limit !== undefined && options.limit >= 1)
|
|
345
|
+
params.Limit = options.limit;
|
|
346
|
+
if (options.consistent)
|
|
347
|
+
params.ConsistentRead = true;
|
|
348
|
+
return params;
|
|
349
|
+
}
|
|
350
|
+
function withTtl(item, ttlTimestamp) {
|
|
351
|
+
if (ttlTimestamp !== undefined)
|
|
352
|
+
item.ttl = ttlTimestamp;
|
|
353
|
+
return item;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Release the objects a build had already uploaded when a later payload of the
|
|
357
|
+
* same build threw.
|
|
358
|
+
*
|
|
359
|
+
* Each payload uploads as it encodes, well before the row that would name it is
|
|
360
|
+
* written, so a build that throws partway leaves the payloads it had already
|
|
361
|
+
* finished with nothing pointing at them. Deleting them unconditionally — with
|
|
362
|
+
* no read of the table first — is safe for two independent reasons.
|
|
363
|
+
*
|
|
364
|
+
* The descriptors released here never escape the builder: they are locals it
|
|
365
|
+
* surrenders only at its `return`, which a build that throws never reaches, so
|
|
366
|
+
* nothing anywhere has ever been handed one to copy onto a row. That holds
|
|
367
|
+
* whatever the keys look like. Second, and only as a backstop to it, every key
|
|
368
|
+
* ends in an `objectId` drawn for this call alone, so no row another call
|
|
369
|
+
* commits addresses the same object.
|
|
370
|
+
*
|
|
371
|
+
* Best-effort, and it never replaces the failure that caused it: a caller needs
|
|
372
|
+
* to see why its payload was refused, not why a cleanup could not finish.
|
|
373
|
+
*/
|
|
374
|
+
async function releaseUploads(context, uploaded, operation) {
|
|
375
|
+
if (!context.offloader)
|
|
376
|
+
return;
|
|
377
|
+
await (0, offloader_1.cleanUpS3Orphans)(context.offloader, {
|
|
378
|
+
keys: (0, codec_1.collectS3Keys)(uploaded),
|
|
379
|
+
operation,
|
|
380
|
+
logger: context.logger,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Names the objects of one `saver.put`. A ULID rather than a UUID because it
|
|
385
|
+
* sorts by time, which keeps a bucket listing of one checkpoint's objects
|
|
386
|
+
* readable.
|
|
387
|
+
*/
|
|
388
|
+
const nextPutObjectId = (0, ulid_1.createUlidFactory)();
|
|
389
|
+
/**
|
|
390
|
+
* Encode a checkpoint + metadata into its META and PAYLOAD rows.
|
|
391
|
+
*
|
|
392
|
+
* Each call draws one object id and uploads both offloaded payloads under it,
|
|
393
|
+
* below the row that points at each. A second put of the same checkpoint id — a
|
|
394
|
+
* put the caller re-issues after a lost response, or a repair tool re-writing a
|
|
395
|
+
* checkpoint — draws another, so it never names an object the first put
|
|
396
|
+
* uploaded, and the verification after a failed transaction can tell the two
|
|
397
|
+
* puts' rows apart by the key alone.
|
|
398
|
+
*
|
|
399
|
+
* Accepts: `request` — parsed by `parsePutRequest`: the address the rows are
|
|
400
|
+
* keyed by (its `checkpointId` is `checkpoint.id`), the parent, the checkpoint
|
|
401
|
+
* — every channel value it carries is stored; see `putCheckpoint` for why
|
|
402
|
+
* nothing is narrowed away — the metadata, and the signal that cancels the
|
|
403
|
+
* uploads. `ttlTimestamp` — stamped on both rows so they expire together.
|
|
404
|
+
*
|
|
405
|
+
* Returns: the META row (light: ids, metadata, index keys) and the PAYLOAD row
|
|
406
|
+
* (heavy: the checkpoint itself), which the caller writes in that order —
|
|
407
|
+
* payload first, so a META row never names a payload that is not there yet.
|
|
408
|
+
*
|
|
409
|
+
* Throws: `VALIDATION` naming `value` for a checkpoint the serializer cannot
|
|
410
|
+
* represent; `S3_OFFLOAD_FAILED` when an offloaded payload cannot be uploaded.
|
|
411
|
+
* Encoding precedes every write, so a checkpoint that cannot be stored never
|
|
412
|
+
* half-writes a thread — and a metadata payload refused after the checkpoint's
|
|
413
|
+
* own object has uploaded releases that object before the failure leaves here
|
|
414
|
+
* (see {@link releaseUploads}), so a refusal strands nothing either.
|
|
415
|
+
*/
|
|
416
|
+
async function buildCheckpointRows(context, request, ttlTimestamp) {
|
|
417
|
+
const { threadId, checkpointNs, checkpointId } = request.address;
|
|
418
|
+
const deps = (0, codec_1.codecDepsOf)(context, request.signal);
|
|
419
|
+
const pk = partitionKey(threadId);
|
|
420
|
+
const objectId = nextPutObjectId();
|
|
421
|
+
const checkpointDescriptor = await (0, codec_1.encodePayload)(request.checkpoint, deps, {
|
|
422
|
+
keyParts: [threadId, checkpointNs, checkpointId, 'checkpoint'],
|
|
423
|
+
objectId,
|
|
424
|
+
row: { pk, sk: payloadSortKey(checkpointNs, checkpointId) },
|
|
425
|
+
});
|
|
426
|
+
// The checkpoint's object is already uploaded by the time the metadata is
|
|
427
|
+
// encoded, so a metadata payload the serde refuses — or cannot represent —
|
|
428
|
+
// would otherwise strand it: the transaction that would have named it never
|
|
429
|
+
// goes out. See {@link releaseUploads} for why deleting it needs no check.
|
|
430
|
+
let metadataDescriptor;
|
|
431
|
+
try {
|
|
432
|
+
metadataDescriptor = await (0, codec_1.encodePayload)(request.metadata, deps, {
|
|
433
|
+
keyParts: [threadId, checkpointNs, checkpointId, 'metadata'],
|
|
434
|
+
objectId,
|
|
435
|
+
row: { pk, sk: metaSortKey(checkpointNs, checkpointId) },
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
catch (error) {
|
|
439
|
+
await releaseUploads(context, [checkpointDescriptor], 'put.encode');
|
|
440
|
+
throw error;
|
|
441
|
+
}
|
|
442
|
+
// The META row takes part in the recency index, so that a `saver.list`
|
|
443
|
+
// without a `thread_id` can stream checkpoints across threads from the index,
|
|
444
|
+
// newest first, instead of scanning the table, when `indexName` is set. The
|
|
445
|
+
// PAYLOAD and WRITE rows do not: nothing lists them across partitions, and
|
|
446
|
+
// indexing them would pay an extra write for an access pattern that does not
|
|
447
|
+
// exist.
|
|
448
|
+
const index = (0, recency_index_1.indexKeys)('CHKPT', checkpointId, (0, clock_1.nowIso)(), context.indexShards ?? recency_index_1.DEFAULT_INDEX_SHARDS);
|
|
449
|
+
const meta = {
|
|
450
|
+
PK: pk,
|
|
451
|
+
SK: metaSortKey(checkpointNs, checkpointId),
|
|
452
|
+
v: table_schema_1.ROW_FORMAT_VERSION,
|
|
453
|
+
...index,
|
|
454
|
+
threadId,
|
|
455
|
+
checkpointNs,
|
|
456
|
+
checkpointId,
|
|
457
|
+
metadata: metadataDescriptor,
|
|
458
|
+
};
|
|
459
|
+
if (request.parentCheckpointId !== undefined)
|
|
460
|
+
meta.parentCheckpointId = request.parentCheckpointId;
|
|
461
|
+
const payload = {
|
|
462
|
+
PK: pk,
|
|
463
|
+
SK: payloadSortKey(checkpointNs, checkpointId),
|
|
464
|
+
v: table_schema_1.ROW_FORMAT_VERSION,
|
|
465
|
+
checkpoint: checkpointDescriptor,
|
|
466
|
+
};
|
|
467
|
+
return { meta: withTtl(meta, ttlTimestamp), payload: withTtl(payload, ttlTimestamp) };
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Encode a task's pending writes into one row per write.
|
|
471
|
+
*
|
|
472
|
+
* Accepts: `request` — parsed by `parsePutWritesRequest`, so every channel is
|
|
473
|
+
* well-formed and every composed sort key fits before this runs, and a bad one
|
|
474
|
+
* costs no S3 object. `writeGroup` — unique per `putWrites` *call*, not per
|
|
475
|
+
* write, and stored on every row the call produces: it is what tells one
|
|
476
|
+
* call's writes apart from another's when `dropSupersededWrites` resolves
|
|
477
|
+
* first-write-wins, and it is the object id every offloaded write of the call
|
|
478
|
+
* is uploaded under. Two calls writing the same bytes for the same row
|
|
479
|
+
* therefore upload two objects, and each row names only its own call's.
|
|
480
|
+
* `ttlTimestamp` — stamped on every row.
|
|
481
|
+
*
|
|
482
|
+
* Returns: one row per write, special channels first, each carrying its
|
|
483
|
+
* `occurrence` so a channel emitted twice by one call keeps both values.
|
|
484
|
+
*
|
|
485
|
+
* Throws: `VALIDATION` naming `value`; `S3_OFFLOAD_FAILED`. A payload refused
|
|
486
|
+
* partway through releases the objects the earlier writes of the same call had
|
|
487
|
+
* already uploaded (see {@link releaseUploads}), so a build that throws
|
|
488
|
+
* returns the caller to where it started.
|
|
489
|
+
*/
|
|
490
|
+
async function buildWriteRows(context, request, writeGroup, ttlTimestamp) {
|
|
491
|
+
const { threadId, checkpointNs, checkpointId } = request.address;
|
|
492
|
+
const { taskId } = request;
|
|
493
|
+
const deps = (0, codec_1.codecDepsOf)(context, request.signal);
|
|
494
|
+
const pk = partitionKey(threadId);
|
|
495
|
+
const items = [];
|
|
496
|
+
// The writes upload one after another, so a payload refused at write N would
|
|
497
|
+
// otherwise strand writes 1..N-1's objects: this call returns no items and
|
|
498
|
+
// therefore writes no rows, leaving nothing that names them. See
|
|
499
|
+
// {@link releaseUploads} for why they are safe to delete unconditionally.
|
|
500
|
+
try {
|
|
501
|
+
for (const { channel, value, index, occurrence } of resolveWriteIndices(request.writes)) {
|
|
502
|
+
// `channel` is part of the key as well as the index: two channels can
|
|
503
|
+
// share an index (each channel's first occurrence is 0), so without it
|
|
504
|
+
// their uploads would collide on one S3 object within a single call.
|
|
505
|
+
const sk = writeSortKey({ checkpointNs, checkpointId, taskId, index, channel });
|
|
506
|
+
const descriptor = await (0, codec_1.encodePayload)(value, deps, {
|
|
507
|
+
keyParts: [threadId, checkpointNs, checkpointId, taskId, `write-${index}`, channel],
|
|
508
|
+
objectId: writeGroup,
|
|
509
|
+
row: { pk, sk },
|
|
510
|
+
});
|
|
511
|
+
const item = {
|
|
512
|
+
PK: pk,
|
|
513
|
+
SK: sk,
|
|
514
|
+
v: table_schema_1.ROW_FORMAT_VERSION,
|
|
515
|
+
taskId,
|
|
516
|
+
index,
|
|
517
|
+
channel,
|
|
518
|
+
// Shared by every row this call writes. Positions shift when a retried
|
|
519
|
+
// task's write mix changes, so a channel an earlier call already
|
|
520
|
+
// committed can land at a second index and be replayed twice; the group
|
|
521
|
+
// is what lets the read side tell that apart from a channel a single
|
|
522
|
+
// call legitimately wrote more than once.
|
|
523
|
+
writeGroup,
|
|
524
|
+
occurrence,
|
|
525
|
+
value: descriptor,
|
|
526
|
+
};
|
|
527
|
+
items.push(withTtl(item, ttlTimestamp));
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
catch (error) {
|
|
531
|
+
await releaseUploads(context, items.map((item) => item.value), 'putWrites.encode');
|
|
532
|
+
throw error;
|
|
533
|
+
}
|
|
534
|
+
return items;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Narrow a raw row to a {@link CheckpointMetaRow}.
|
|
538
|
+
*
|
|
539
|
+
* Accepts: `raw` — any row carrying the `META#` sort-key prefix, which on a
|
|
540
|
+
* shared table another writer can produce too.
|
|
541
|
+
*
|
|
542
|
+
* Returns: the item, or undefined for a row that merely shares the prefix, and
|
|
543
|
+
* for one whose own `threadId`/`checkpointNs`/`checkpointId` disagree with the
|
|
544
|
+
* DynamoDB key it was found at. The test is on the attributes a checkpoint must
|
|
545
|
+
* have, not on a cast: this is the one boundary where a row may not have been
|
|
546
|
+
* written by this adapter. A `metadata` of `null` is refused here, since
|
|
547
|
+
* dereferencing it later raised a raw `TypeError`.
|
|
548
|
+
*
|
|
549
|
+
* Throws: `FORMAT_UNSUPPORTED` for a row a newer version wrote — checked
|
|
550
|
+
* **before** the shape, as every other read of this package's rows checks it,
|
|
551
|
+
* so a row a newer release wrote is reported as newer rather than judged
|
|
552
|
+
* against attribute names it may no longer use. Skipping it would report a
|
|
553
|
+
* thread as shorter than it is.
|
|
554
|
+
*
|
|
555
|
+
* Guarantees: a row's attributes are bound to the partition it lives in. Those
|
|
556
|
+
* attributes name the S3 scope the row's payloads are read under and the thread
|
|
557
|
+
* the assembled tuple reports, so a writer confined to its own partition could
|
|
558
|
+
* otherwise hand back another tenant's offloaded payload under that tenant's
|
|
559
|
+
* `thread_id` — the same binding `parseStoreRow` makes for store items. The
|
|
560
|
+
* binding is judged under this release's rules, which is why it is judged only
|
|
561
|
+
* for a row this release can read.
|
|
562
|
+
*/
|
|
563
|
+
function parseMetaRow(raw) {
|
|
564
|
+
// The version first. A row a newer version wrote is not a foreign row to
|
|
565
|
+
// skip, and this release's names for its attributes are not that release's,
|
|
566
|
+
// so testing the shape first decides a row is foreign whenever a later
|
|
567
|
+
// format renamed what this one reads.
|
|
568
|
+
(0, table_schema_1.assertReadableRow)(raw, 'checkpoint');
|
|
569
|
+
const isCheckpoint = typeof raw.threadId === 'string' &&
|
|
570
|
+
typeof raw.checkpointId === 'string' &&
|
|
571
|
+
typeof raw.checkpointNs === 'string' &&
|
|
572
|
+
typeof raw.metadata === 'object' &&
|
|
573
|
+
raw.metadata !== null;
|
|
574
|
+
if (!isCheckpoint)
|
|
575
|
+
return undefined;
|
|
576
|
+
const item = raw;
|
|
577
|
+
const consistent = item.PK === partitionKey(item.threadId) &&
|
|
578
|
+
item.SK === metaSortKey(item.checkpointNs, item.checkpointId);
|
|
579
|
+
return consistent ? item : undefined;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Narrow a candidate head row, saying so when it is not one of ours.
|
|
583
|
+
*
|
|
584
|
+
* Accepts: `raw` — the row a newest-first read returned, or undefined when it
|
|
585
|
+
* returned none.
|
|
586
|
+
*
|
|
587
|
+
* Returns: the item, or undefined for an absent or foreign row — logged at
|
|
588
|
+
* `warn` in the second case, because a foreign row at the head of a thread is
|
|
589
|
+
* an operator's problem even though this read recovers from it.
|
|
590
|
+
*
|
|
591
|
+
* Throws: as {@link parseMetaRow}.
|
|
592
|
+
*
|
|
593
|
+
* Guarantees: a foreign row is skipped, never returned. Returning one made
|
|
594
|
+
* `assembleTuple` miss its payload and report the thread as empty, so LangGraph
|
|
595
|
+
* started a new run on top of the real history.
|
|
596
|
+
*/
|
|
597
|
+
function parseHeadRow(context, raw) {
|
|
598
|
+
if (raw === undefined)
|
|
599
|
+
return undefined;
|
|
600
|
+
const meta = parseMetaRow(raw);
|
|
601
|
+
if (!meta) {
|
|
602
|
+
context.logger.warn('getTuple: skipped a row that is not a checkpoint meta item', {
|
|
603
|
+
sortKey: (0, truncate_1.truncateForLog)(raw.SK),
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
return meta;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Decode the checkpoint stored in a PAYLOAD item.
|
|
610
|
+
*
|
|
611
|
+
* Accepts: `threadId` — the **caller's**, from the config, never the row's: it
|
|
612
|
+
* scopes which S3 object the row may point at, so it must come from the
|
|
613
|
+
* partition the caller asked for. A row that names an object outside that scope
|
|
614
|
+
* is refused by the codec rather than downloaded. `signal` — cancels the
|
|
615
|
+
* download an offloaded payload costs.
|
|
616
|
+
*
|
|
617
|
+
* Returns: the checkpoint.
|
|
618
|
+
*
|
|
619
|
+
* Throws: `PAYLOAD_CORRUPT` for bytes that cannot be decoded, `VALIDATION`
|
|
620
|
+
* for a descriptor pointing outside the row's scope and for a payload the
|
|
621
|
+
* configured serde refuses to reconstruct, and whatever the download throws.
|
|
622
|
+
*/
|
|
623
|
+
async function readCheckpoint(context, item, threadId, signal) {
|
|
624
|
+
return (0, codec_1.decodePayload)(item.checkpoint, (0, codec_1.codecDepsOf)(context, signal), [threadId]);
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Decode the metadata stored in a META item.
|
|
628
|
+
*
|
|
629
|
+
* Accepts: as {@link readCheckpoint}, for the metadata blob instead of the
|
|
630
|
+
* checkpoint.
|
|
631
|
+
*
|
|
632
|
+
* Returns: the metadata.
|
|
633
|
+
*
|
|
634
|
+
* Throws: as {@link readCheckpoint}.
|
|
635
|
+
*/
|
|
636
|
+
async function readMetadata(context, item, threadId, signal) {
|
|
637
|
+
return (0, codec_1.decodePayload)(item.metadata, (0, codec_1.codecDepsOf)(context, signal), [threadId]);
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Decode WRITE items into `[taskId, channel, value]` pending-write tuples.
|
|
641
|
+
*
|
|
642
|
+
* Accepts: `items` — one checkpoint's WRITE rows, in any order; empty is empty.
|
|
643
|
+
* `threadId` — the caller's, as in {@link readCheckpoint}. `signal` — cancels
|
|
644
|
+
* the downloads, all of which share it.
|
|
645
|
+
*
|
|
646
|
+
* Returns: the writes LangGraph replays, first-write-wins already resolved by
|
|
647
|
+
* `dropSupersededWrites`, in the order the surviving rows were read.
|
|
648
|
+
*
|
|
649
|
+
* Throws: whatever a decode throws — the first one, with the rest allowed to
|
|
650
|
+
* settle.
|
|
651
|
+
*
|
|
652
|
+
* Guarantees: payloads decode several at a time, so a checkpoint with many
|
|
653
|
+
* offloaded writes costs one round of downloads rather than one per write.
|
|
654
|
+
*/
|
|
655
|
+
async function toPendingWrites(context, items, threadId, signal) {
|
|
656
|
+
const deps = (0, codec_1.codecDepsOf)(context, signal);
|
|
657
|
+
const live = dropSupersededWrites(items);
|
|
658
|
+
const values = await (0, concurrency_1.mapWithConcurrency)(live, context.readConcurrency ?? concurrency_1.DEFAULT_READ_CONCURRENCY, (item) => (0, codec_1.decodePayload)(item.value, deps, [threadId]));
|
|
659
|
+
return live.map((item, index) => [
|
|
660
|
+
item.taskId,
|
|
661
|
+
item.channel,
|
|
662
|
+
values[index],
|
|
663
|
+
]);
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Assign every write in one `putWrites` call its sort-key index, in a single
|
|
667
|
+
* pass — nothing recomputes it downstream, so the deduped array's positions
|
|
668
|
+
* cannot disagree with the ones the caller's array produced.
|
|
669
|
+
*
|
|
670
|
+
* A regular write's index is its position in the caller's array, exactly as
|
|
671
|
+
* the reference `MemorySaver` computes it: that is what makes stored writes
|
|
672
|
+
* replay in the order the task emitted them. A special channel takes its
|
|
673
|
+
* fixed `WRITES_IDX_MAP` slot instead, and a later duplicate replaces an
|
|
674
|
+
* earlier one (last-write-wins, again matching the reference).
|
|
675
|
+
*
|
|
676
|
+
* Positions are not stable across calls, which is why the *sort key* also
|
|
677
|
+
* carries the channel and each call stamps its rows with a shared
|
|
678
|
+
* `writeGroup` — see {@link buildWriteRows} and `dropSupersededWrites`.
|
|
679
|
+
*
|
|
680
|
+
* `Object.hasOwn` guards WRITES_IDX_MAP's own `Object.prototype` chain — a
|
|
681
|
+
* channel literally named `constructor`/`toString`/etc. must be treated as
|
|
682
|
+
* regular, not resolve to an inherited function reference.
|
|
683
|
+
*
|
|
684
|
+
* Accepts: `writes` — one `putWrites` call's writes, already validated for
|
|
685
|
+
* channel shape; empty is empty.
|
|
686
|
+
*
|
|
687
|
+
* Returns: the special writes first, then the regular ones. A special channel
|
|
688
|
+
* appearing twice yields one entry (the last), a regular channel appearing
|
|
689
|
+
* twice yields two, distinguished by `occurrence`.
|
|
690
|
+
*
|
|
691
|
+
* Throws: nothing.
|
|
692
|
+
*
|
|
693
|
+
* Guarantees: within one call, `(channel, occurrence)` is unique — which is
|
|
694
|
+
* what lets `dropSupersededWrites` treat it as an identity across calls.
|
|
695
|
+
*/
|
|
696
|
+
function resolveWriteIndices(writes) {
|
|
697
|
+
const bySpecialIndex = new Map();
|
|
698
|
+
const regular = [];
|
|
699
|
+
const occurrences = new Map();
|
|
700
|
+
writes.forEach(([channel, value], positional) => {
|
|
701
|
+
if (Object.hasOwn(langgraph_checkpoint_1.WRITES_IDX_MAP, channel)) {
|
|
702
|
+
const index = langgraph_checkpoint_1.WRITES_IDX_MAP[channel];
|
|
703
|
+
// Last write wins per special channel, so a call holds exactly one.
|
|
704
|
+
bySpecialIndex.set(index, { channel, value, index, occurrence: 0 });
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const occurrence = occurrences.get(channel) ?? 0;
|
|
708
|
+
occurrences.set(channel, occurrence + 1);
|
|
709
|
+
regular.push({ channel, value, index: positional, occurrence });
|
|
710
|
+
});
|
|
711
|
+
return [...bySpecialIndex.values(), ...regular];
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Resolve a task's pending writes to one row per `(taskId, channel,
|
|
715
|
+
* occurrence)`, keeping the earliest `putWrites` call that wrote it.
|
|
716
|
+
*
|
|
717
|
+
* A regular write's index is its position in the caller's array, so a retried
|
|
718
|
+
* task whose write mix changed places an already-committed channel at a
|
|
719
|
+
* different index, where the first-write-wins guard cannot recognise it and a
|
|
720
|
+
* second row commits. Replaying both double-counts an accumulating channel.
|
|
721
|
+
* Each call stamps its rows with one `writeGroup`, and the earliest group per
|
|
722
|
+
* identity is the call that actually won.
|
|
723
|
+
*
|
|
724
|
+
* `occurrence` is part of the identity so a retry that legitimately emits a
|
|
725
|
+
* channel *more* often than the original keeps both values, which is what
|
|
726
|
+
* `MemorySaver` does when it keys first-write-wins on `(taskId, index)`.
|
|
727
|
+
*
|
|
728
|
+
* Accepts: `items` — the WRITE rows of one checkpoint, in any order; empty is
|
|
729
|
+
* empty. A row written before `writeGroup` or `occurrence` existed carries
|
|
730
|
+
* neither, and both are normalised at the edge rather than tested for.
|
|
731
|
+
*
|
|
732
|
+
* Returns: the rows to replay, in the order given. One per `(taskId, channel,
|
|
733
|
+
* occurrence)`: the row whose `writeGroup` sorts earliest, which is the call
|
|
734
|
+
* that actually won the first-write-wins guard.
|
|
735
|
+
*
|
|
736
|
+
* Throws: nothing.
|
|
737
|
+
*
|
|
738
|
+
* Guarantees: exactly one row survives per identity. Two rows could tie only by
|
|
739
|
+
* sharing a `writeGroup` as well, and one call assigns each of its channels a
|
|
740
|
+
* distinct `occurrence`, so within a call the identity is already unique.
|
|
741
|
+
*/
|
|
742
|
+
function dropSupersededWrites(items) {
|
|
743
|
+
const identity = (item) => JSON.stringify([item.taskId, item.channel, item.occurrence ?? 0]);
|
|
744
|
+
// The call a row belongs to, as something orderable. A row written before
|
|
745
|
+
// `writeGroup` existed carries none and is older than every row that does —
|
|
746
|
+
// the empty string sorts before any ULID.
|
|
747
|
+
//
|
|
748
|
+
// Keeping the raw `undefined` reversed first-write-wins across an upgrade: a
|
|
749
|
+
// `Map` cannot tell a key whose value is absent from one whose value *is*
|
|
750
|
+
// `undefined`, so the guard that checks "nothing recorded yet" fired again on
|
|
751
|
+
// the pre-upgrade row's own entry and let the next, newer row overwrite it.
|
|
752
|
+
// Normalising at the edge removes the ambiguity instead of testing for it.
|
|
753
|
+
const groupOf = (item) => item.writeGroup ?? '';
|
|
754
|
+
const earliestGroup = new Map();
|
|
755
|
+
for (const item of items) {
|
|
756
|
+
const id = identity(item);
|
|
757
|
+
const seen = earliestGroup.get(id);
|
|
758
|
+
const group = groupOf(item);
|
|
759
|
+
if (seen === undefined || group < seen)
|
|
760
|
+
earliestGroup.set(id, group);
|
|
761
|
+
}
|
|
762
|
+
return items.filter((item) => earliestGroup.get(identity(item)) === groupOf(item));
|
|
763
|
+
}
|
|
764
|
+
/** The attributes a checkpointer row can hold an offloaded payload under. */
|
|
765
|
+
const PAYLOAD_ATTRIBUTES = ['metadata', 'checkpoint', 'value'];
|
|
766
|
+
/**
|
|
767
|
+
* The offloaded payloads a checkpointer row references, each named by the
|
|
768
|
+
* attribute holding it, because a row is pinned through a document path over
|
|
769
|
+
* that name.
|
|
770
|
+
*
|
|
771
|
+
* Accepts: `row` — a row of this adapter's partition.
|
|
772
|
+
*
|
|
773
|
+
* Returns: the descriptors, each named by its attribute. An attribute the row
|
|
774
|
+
* leaves out and one it holds `null` in both name no payload, which is what
|
|
775
|
+
* `namedDescriptor` decides.
|
|
776
|
+
*
|
|
777
|
+
* Throws: nothing.
|
|
778
|
+
*/
|
|
779
|
+
function checkpointRowDescriptors(row) {
|
|
780
|
+
const named = [];
|
|
781
|
+
for (const attribute of PAYLOAD_ATTRIBUTES) {
|
|
782
|
+
const entry = (0, partition_delete_1.namedDescriptor)(row, attribute);
|
|
783
|
+
if (entry !== undefined)
|
|
784
|
+
named.push(entry);
|
|
785
|
+
}
|
|
786
|
+
return named;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* The checkpoint a row belongs to.
|
|
790
|
+
*
|
|
791
|
+
* Accepts: `row` — a row of this adapter's partition.
|
|
792
|
+
*
|
|
793
|
+
* Returns: the `ns#id` unit: the namespace and id its sort key carries in the
|
|
794
|
+
* same two segments whatever its kind, which is exact rather than hopeful
|
|
795
|
+
* because the separator is forbidden inside every segment.
|
|
796
|
+
*
|
|
797
|
+
* Throws: nothing.
|
|
798
|
+
*/
|
|
799
|
+
function checkpointRowUnit(row) {
|
|
800
|
+
return row.SK.split(table_schema_1.KEY_SEPARATOR).slice(1, 3).join(table_schema_1.KEY_SEPARATOR);
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* The row kind.
|
|
804
|
+
*
|
|
805
|
+
* Accepts: `row` — a row of this adapter's partition.
|
|
806
|
+
*
|
|
807
|
+
* Returns: the kind, which is the sort key's leading segment.
|
|
808
|
+
*
|
|
809
|
+
* Throws: nothing.
|
|
810
|
+
*/
|
|
811
|
+
function checkpointRowKind(row) {
|
|
812
|
+
return row.SK.split(table_schema_1.KEY_SEPARATOR)[0];
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Where a checkpointer row sits in the recency index, for a row written before
|
|
816
|
+
* the index existed.
|
|
817
|
+
*
|
|
818
|
+
* Accepts: `row` — any row of the table.
|
|
819
|
+
*
|
|
820
|
+
* Returns: a META row's identity — its checkpoint id, at {@link BACKFILLED_AT},
|
|
821
|
+
* because a META row records no time of its own — or `undefined` for any other
|
|
822
|
+
* row, this adapter's or not.
|
|
823
|
+
*
|
|
824
|
+
* Throws: nothing.
|
|
825
|
+
*/
|
|
826
|
+
function checkpointIndexTarget(row) {
|
|
827
|
+
const pk = typeof row.PK === 'string' ? row.PK : '';
|
|
828
|
+
const sk = typeof row.SK === 'string' ? row.SK : '';
|
|
829
|
+
if (!pk.startsWith(ADAPTER_PARTITION_PREFIX))
|
|
830
|
+
return undefined;
|
|
831
|
+
return sk.startsWith(metaAnyNamespacePrefix()) && typeof row.checkpointId === 'string'
|
|
832
|
+
? { tag: 'CHKPT', id: row.checkpointId, at: recency_index_1.BACKFILLED_AT }
|
|
833
|
+
: undefined;
|
|
834
|
+
}
|