@farukada/aws-langgraph-dynamodb-ts 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -9
- package/dist/checkpointer/actions/delete-thread.d.ts +3 -4
- package/dist/checkpointer/actions/delete-thread.d.ts.map +1 -1
- package/dist/checkpointer/actions/delete-thread.js +21 -41
- package/dist/checkpointer/actions/delete-thread.js.map +1 -1
- package/dist/checkpointer/actions/list.d.ts.map +1 -1
- package/dist/checkpointer/actions/list.js +31 -4
- package/dist/checkpointer/actions/list.js.map +1 -1
- package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
- package/dist/checkpointer/actions/put-writes.js +14 -24
- package/dist/checkpointer/actions/put-writes.js.map +1 -1
- package/dist/checkpointer/internal/item-reader.d.ts +31 -0
- package/dist/checkpointer/internal/item-reader.d.ts.map +1 -1
- package/dist/checkpointer/internal/item-reader.js +49 -1
- package/dist/checkpointer/internal/item-reader.js.map +1 -1
- package/dist/checkpointer/internal/item-writer.d.ts +0 -8
- package/dist/checkpointer/internal/item-writer.d.ts.map +1 -1
- package/dist/checkpointer/internal/item-writer.js +15 -21
- package/dist/checkpointer/internal/item-writer.js.map +1 -1
- package/dist/checkpointer/internal/keys.d.ts +19 -3
- package/dist/checkpointer/internal/keys.d.ts.map +1 -1
- package/dist/checkpointer/internal/keys.js +41 -6
- package/dist/checkpointer/internal/keys.js.map +1 -1
- package/dist/checkpointer/internal/validation.d.ts +8 -4
- package/dist/checkpointer/internal/validation.d.ts.map +1 -1
- package/dist/checkpointer/internal/validation.js +12 -10
- package/dist/checkpointer/internal/validation.js.map +1 -1
- package/dist/checkpointer/internal/write-guard.d.ts +21 -0
- package/dist/checkpointer/internal/write-guard.d.ts.map +1 -0
- package/dist/checkpointer/internal/write-guard.js +48 -0
- package/dist/checkpointer/internal/write-guard.js.map +1 -0
- package/dist/checkpointer/internal/write-index.d.ts +29 -0
- package/dist/checkpointer/internal/write-index.d.ts.map +1 -0
- package/dist/checkpointer/internal/write-index.js +38 -0
- package/dist/checkpointer/internal/write-index.js.map +1 -0
- package/dist/checkpointer/types.d.ts +2 -0
- package/dist/checkpointer/types.d.ts.map +1 -1
- package/dist/history/actions/add-messages.d.ts.map +1 -1
- package/dist/history/actions/add-messages.js +24 -4
- package/dist/history/actions/add-messages.js.map +1 -1
- package/dist/history/actions/clear.d.ts +4 -6
- package/dist/history/actions/clear.d.ts.map +1 -1
- package/dist/history/actions/clear.js +18 -33
- package/dist/history/actions/clear.js.map +1 -1
- package/dist/history/actions/get-messages.d.ts +2 -1
- package/dist/history/actions/get-messages.d.ts.map +1 -1
- package/dist/history/actions/get-messages.js +28 -2
- package/dist/history/actions/get-messages.js.map +1 -1
- package/dist/history/actions/list-sessions.d.ts +2 -1
- package/dist/history/actions/list-sessions.d.ts.map +1 -1
- package/dist/history/actions/list-sessions.js +21 -2
- package/dist/history/actions/list-sessions.js.map +1 -1
- package/dist/history/actions/reconcile-count.d.ts.map +1 -1
- package/dist/history/actions/reconcile-count.js +2 -2
- package/dist/history/actions/reconcile-count.js.map +1 -1
- package/dist/history/internal/append-saga.d.ts.map +1 -1
- package/dist/history/internal/append-saga.js +2 -78
- package/dist/history/internal/append-saga.js.map +1 -1
- package/dist/history/internal/compensation.d.ts +21 -0
- package/dist/history/internal/compensation.d.ts.map +1 -0
- package/dist/history/internal/compensation.js +84 -0
- package/dist/history/internal/compensation.js.map +1 -0
- package/dist/history/internal/keys.d.ts +9 -1
- package/dist/history/internal/keys.d.ts.map +1 -1
- package/dist/history/internal/keys.js +21 -4
- package/dist/history/internal/keys.js.map +1 -1
- package/dist/history/internal/message-chunker.d.ts.map +1 -1
- package/dist/history/internal/message-chunker.js +13 -5
- package/dist/history/internal/message-chunker.js.map +1 -1
- package/dist/history/internal/session-count.d.ts +21 -0
- package/dist/history/internal/session-count.d.ts.map +1 -1
- package/dist/history/internal/session-count.js +51 -0
- package/dist/history/internal/session-count.js.map +1 -1
- package/dist/history/internal/session-title.d.ts +20 -0
- package/dist/history/internal/session-title.d.ts.map +1 -0
- package/dist/history/internal/session-title.js +44 -0
- package/dist/history/internal/session-title.js.map +1 -0
- package/dist/history/internal/setup.d.ts +2 -1
- package/dist/history/internal/setup.d.ts.map +1 -1
- package/dist/history/internal/setup.js +1 -0
- package/dist/history/internal/setup.js.map +1 -1
- package/dist/history/internal/title-generator.d.ts +8 -2
- package/dist/history/internal/title-generator.d.ts.map +1 -1
- package/dist/history/internal/title-generator.js +12 -3
- package/dist/history/internal/title-generator.js.map +1 -1
- package/dist/history/internal/validation.d.ts +9 -0
- package/dist/history/internal/validation.d.ts.map +1 -0
- package/dist/history/internal/validation.js +16 -0
- package/dist/history/internal/validation.js.map +1 -0
- package/dist/history/types.d.ts +12 -0
- package/dist/history/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/shared/codec/s3/offloader.d.ts +7 -1
- package/dist/shared/codec/s3/offloader.d.ts.map +1 -1
- package/dist/shared/codec/s3/offloader.js +14 -1
- package/dist/shared/codec/s3/offloader.js.map +1 -1
- package/dist/shared/dynamodb/partition-delete.d.ts +33 -0
- package/dist/shared/dynamodb/partition-delete.d.ts.map +1 -0
- package/dist/shared/dynamodb/partition-delete.js +70 -0
- package/dist/shared/dynamodb/partition-delete.js.map +1 -0
- package/dist/shared/dynamodb/retry-classifier.d.ts.map +1 -1
- package/dist/shared/dynamodb/retry-classifier.js +9 -1
- package/dist/shared/dynamodb/retry-classifier.js.map +1 -1
- package/dist/shared/logging/redaction-walk.d.ts +23 -0
- package/dist/shared/logging/redaction-walk.d.ts.map +1 -0
- package/dist/shared/logging/redaction-walk.js +92 -0
- package/dist/shared/logging/redaction-walk.js.map +1 -0
- package/dist/shared/logging/redaction.d.ts +22 -17
- package/dist/shared/logging/redaction.d.ts.map +1 -1
- package/dist/shared/logging/redaction.js +24 -48
- package/dist/shared/logging/redaction.js.map +1 -1
- package/dist/shared/logging/secret-patterns.d.ts +41 -0
- package/dist/shared/logging/secret-patterns.d.ts.map +1 -0
- package/dist/shared/logging/secret-patterns.js +74 -0
- package/dist/shared/logging/secret-patterns.js.map +1 -0
- package/dist/shared/validation/primitives.d.ts +9 -1
- package/dist/shared/validation/primitives.d.ts.map +1 -1
- package/dist/shared/validation/primitives.js +13 -0
- package/dist/shared/validation/primitives.js.map +1 -1
- package/dist/store/actions/get.d.ts.map +1 -1
- package/dist/store/actions/get.js +15 -1
- package/dist/store/actions/get.js.map +1 -1
- package/dist/store/actions/list-namespaces.d.ts.map +1 -1
- package/dist/store/actions/list-namespaces.js +1 -0
- package/dist/store/actions/list-namespaces.js.map +1 -1
- package/dist/store/actions/put.d.ts.map +1 -1
- package/dist/store/actions/put.js +20 -2
- package/dist/store/actions/put.js.map +1 -1
- package/dist/store/actions/search.d.ts.map +1 -1
- package/dist/store/actions/search.js +4 -35
- package/dist/store/actions/search.js.map +1 -1
- package/dist/store/internal/backend-search.d.ts +5 -0
- package/dist/store/internal/backend-search.d.ts.map +1 -0
- package/dist/store/internal/backend-search.js +67 -0
- package/dist/store/internal/backend-search.js.map +1 -0
- package/dist/store/internal/filter.d.ts.map +1 -1
- package/dist/store/internal/filter.js +26 -5
- package/dist/store/internal/filter.js.map +1 -1
- package/dist/store/internal/index-reconcile.d.ts.map +1 -1
- package/dist/store/internal/index-reconcile.js +33 -3
- package/dist/store/internal/index-reconcile.js.map +1 -1
- package/dist/store/internal/keys.d.ts +1 -1
- package/dist/store/internal/keys.d.ts.map +1 -1
- package/dist/store/internal/keys.js +8 -2
- package/dist/store/internal/keys.js.map +1 -1
- package/dist/store/internal/persist.d.ts.map +1 -1
- package/dist/store/internal/persist.js +1 -5
- package/dist/store/internal/persist.js.map +1 -1
- package/dist/store/internal/search-filter.d.ts +4 -0
- package/dist/store/internal/search-filter.d.ts.map +1 -0
- package/dist/store/internal/search-filter.js +11 -0
- package/dist/store/internal/search-filter.js.map +1 -0
- package/dist/store/internal/setup.d.ts +11 -1
- package/dist/store/internal/setup.d.ts.map +1 -1
- package/dist/store/internal/setup.js +17 -1
- package/dist/store/internal/setup.js.map +1 -1
- package/dist/store/internal/validation.d.ts +8 -2
- package/dist/store/internal/validation.d.ts.map +1 -1
- package/dist/store/internal/validation.js +15 -6
- package/dist/store/internal/validation.js.map +1 -1
- package/dist/store/internal/write-verify.d.ts +12 -0
- package/dist/store/internal/write-verify.d.ts.map +1 -1
- package/dist/store/internal/write-verify.js +27 -0
- package/dist/store/internal/write-verify.js.map +1 -1
- package/dist/store/vector-backend.d.ts +17 -0
- package/dist/store/vector-backend.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ npm install @langchain/aws # e.g. Bedrock Titan embeddings
|
|
|
57
57
|
|
|
58
58
|
## Table schema
|
|
59
59
|
|
|
60
|
-
Every adapter uses the **same simple key schema**: a string partition key `PK`, a string sort key `SK`, and an optional Number `ttl` attribute for expiry.
|
|
60
|
+
Every adapter uses the **same simple key schema**: a string partition key `PK`, a string sort key `SK`, and an optional Number `ttl` attribute for expiry. **A single table can back all three adapters**, or you can use a separate table per adapter — your choice via the `tableName` option.
|
|
61
61
|
|
|
62
62
|
| Attribute | Type | Role |
|
|
63
63
|
| --- | --- | --- |
|
|
@@ -67,9 +67,13 @@ Every adapter uses the **same simple key schema**: a string partition key `PK`,
|
|
|
67
67
|
|
|
68
68
|
How each adapter lays out keys (informational — you don't manage this):
|
|
69
69
|
|
|
70
|
-
- **Checkpointer** — `PK =
|
|
71
|
-
- **Store** — `PK =
|
|
72
|
-
- **Chat history** — `PK =
|
|
70
|
+
- **Checkpointer** — `PK = CHKPT#<thread_id>`; `SK` = `META#<ns>#<checkpoint_id>` (metadata), `PAYLOAD#<ns>#<checkpoint_id>` (checkpoint), `WRITE#<ns>#<checkpoint_id>#<task>#<idx>#<channel>` (pending writes).
|
|
71
|
+
- **Store** — `PK = STORE#<namespace[0]>` (the scope root); `SK = <namespace[1..]>#<key>`. This makes a scoped prefix search a native `Query` (`PK = root AND begins_with(SK, …)`); only a rootless "search everything" falls back to a `Scan`.
|
|
72
|
+
- **Chat history** — `PK = HIST#<sessionId>`; one item per message at `SK = HISTORY#MSG#<ULID>` (ordered, append-only) plus one `SK = HISTORY#SESSION` metadata item.
|
|
73
|
+
|
|
74
|
+
**Why the key spaces cannot collide.** Each adapter tags its partition key with its own prefix, and those three tags differ in their very first character, so no `CHKPT#…` can ever equal a `STORE#…` or `HIST#…` — whatever identifiers you pass. That matters because reusing one id across adapters (a "conversation id" used as both a `thread_id` and a `sessionId`) is an entirely ordinary design: without the tags it put unrelated adapters' rows in one partition, where `deleteThread()`/`history.clear()` would delete each other's data and identically-composed sort keys could silently overwrite one another.
|
|
75
|
+
|
|
76
|
+
Two further guards back that up, for a table holding hand-written rows or rows written before an upgrade: `deleteThread()`/`clear()` delete only rows whose sort key belongs to the calling adapter and log anything they leave in place, and every read narrows a row's shape before decoding it rather than trusting the key it was found at.
|
|
73
77
|
|
|
74
78
|
## Quick start
|
|
75
79
|
|
|
@@ -189,8 +193,9 @@ All adapters share a common base. Provide **either** a prebuilt `client` (which
|
|
|
189
193
|
| `compression` | `CompressionConfig` | all | `{ enabled, minSizeBytes?, level?, maxDecompressedBytes? }` |
|
|
190
194
|
| `s3` | `S3OffloadConfig` | all | offload large payloads to S3 (see below) |
|
|
191
195
|
| `serde` | `SerializerProtocol` | all | serializer override (checkpointer defaults to LangGraph's; store/history to JSON) |
|
|
196
|
+
| `onCorruptMessage` | `'skip' \| 'throw'` | history only | what `getMessages` does with an item it cannot decode (default `skip`: drop it, log at `error`, return the rest) |
|
|
192
197
|
| `index` | `IndexConfig` | store only | `{ dims, embeddings, fields? }` for semantic search |
|
|
193
|
-
| `vectorBackend` | `VectorBackend` | store only | delegate similarity search to an external index; DynamoDB keeps the canonical item |
|
|
198
|
+
| `vectorBackend` | `VectorBackend` | store only | delegate similarity search to an external index; DynamoDB keeps the canonical item. **Requires `index`** — constructing a store with one and not the other throws |
|
|
194
199
|
| `maxSearchCandidates` | `number` | store only | cap for the in-DB ranker before it errors (default 1000) |
|
|
195
200
|
| `maxScanItems` | `number` | store only | cap on items scanned into memory during a plain (non-semantic) `search()` before it errors (default 10000, the shared in-memory cap used by every paginated read) |
|
|
196
201
|
|
|
@@ -202,7 +207,7 @@ When `keyPrefix` is omitted, each adapter defaults to its own sub-prefix under t
|
|
|
202
207
|
|
|
203
208
|
**Gzip compression** — set `compression: { enabled: true }`. Payloads at or above `minSizeBytes` (default 1 KB) are gzipped transparently; decompression auto-detects on read and is guarded against decompression-bomb expansion (`maxDecompressedBytes`, default 50 MiB).
|
|
204
209
|
|
|
205
|
-
**S3 offloading** — set `s3: { bucketName }`. Any serialized payload at or above `thresholdBytes` (default 350 KB) is written to S3, with only a reference stored in DynamoDB; reads rehydrate transparently. Requires the optional `@aws-sdk/client-s3` peer. Deleting a checkpoint thread / chat session also best-effort deletes its offloaded objects. When a `ttl` is also configured, call `ensureS3LifecycleRule()` once (e.g. during deployment) to best-effort install a matching S3 lifecycle expiration rule (logged, never fatal) — this is opt-in rather than automatic, since it requires the broader `s3:PutLifecycleConfiguration` bucket-level permission and is not safe to fire on every adapter construction. If you configure `ttl` + `s3` but never call it, nothing reclaims objects that best-effort cleanup misses (a failed delete, or a write that lost the checkpointer's first-write-wins race) — they stay in the bucket until you remove them or add a lifecycle rule yourself. The store has an analogous, smaller-scale race: two concurrent `put`s to the same key that both read the previous descriptor before either commits each upload to their own distinct (nonced) key and each then try to clean up that same previous descriptor — the loser's own upload becomes an orphan (bounded at one per race), the same class of leak, reclaimed the same way.
|
|
210
|
+
**S3 offloading** — set `s3: { bucketName }`. Any serialized payload at or above `thresholdBytes` (default 350 KB) is written to S3, with only a reference stored in DynamoDB; reads rehydrate transparently. Requires the optional `@aws-sdk/client-s3` peer. Deleting a checkpoint thread / chat session also best-effort deletes its offloaded objects. When a `ttl` is also configured, call `ensureS3LifecycleRule()` once (e.g. during deployment) to best-effort install a matching S3 lifecycle expiration rule (logged, never fatal) — this is opt-in rather than automatic, since it requires the broader `s3:PutLifecycleConfiguration` bucket-level permission and is not safe to fire on every adapter construction. If you configure `ttl` + `s3` but never call it, nothing reclaims objects that best-effort cleanup misses (a failed delete, or a write that lost the checkpointer's first-write-wins race) — they stay in the bucket until you remove them or add a lifecycle rule yourself. The store has an analogous, smaller-scale race: two concurrent `put`s to the same key that both read the previous descriptor before either commits each upload to their own distinct (nonced) key and each then try to clean up that same previous descriptor — the loser's own upload becomes an orphan (bounded at one per race), the same class of leak, reclaimed the same way. The checkpointer's *special* writes (`__error__`, `__interrupt__`, `__resume__`, `__scheduled__`) overwrite their row in place and carry the identical race: two genuinely concurrent `putWrites` calls to the same special channel that both read "no existing object" before either commits each orphan the loser's own upload. Same bound (one per race), same reclamation — best-effort cleanup, then `ensureS3LifecycleRule()` or your own lifecycle rule.
|
|
206
211
|
|
|
207
212
|
**TTL expiry** — set `ttl: { days }` or `ttl: { seconds }`. The `ttl` attribute is written as a Unix-epoch-seconds timestamp; enable DynamoDB TTL on the `ttl` attribute for automatic deletion. Chat history anchors a single **uniform whole-conversation TTL** on the session's metadata row, shared by every message: normally it's set once, at session creation, via `if_not_exists`; but if the previously-stored anchor is ever found missing or already expired (DynamoDB's own TTL sweep can lag up to ~48h), the next append heals it with a plain overwrite instead of staying stuck. Every message written at any point in time shares whatever the current anchor is; expired messages are also filtered out on read. If the append that triggers a stale-anchor heal is itself later rolled back (a later chunk in the same call failed), the healed ttl is not reverted — the session simply keeps the fresher, never-shorter expiry rather than risk regressing a value a concurrent legitimate extension may have since written; this is a deliberate, self-healing tradeoff, not a bug.
|
|
208
213
|
|
|
@@ -251,7 +256,18 @@ const logger: Logger = {
|
|
|
251
256
|
const store = new DynamoDBStore({ tableName: 'langgraph', logger: redactLogger(logger) });
|
|
252
257
|
```
|
|
253
258
|
|
|
254
|
-
`redactLogger` wraps a logger so secret-looking fields (access keys, tokens, passwords, …) are replaced with `[REDACTED]` in structured log arguments. `redactSecrets` exposes the same redaction for arbitrary objects.
|
|
259
|
+
`redactLogger` wraps a logger so secret-looking fields (access keys, tokens, passwords, …) are replaced with `[REDACTED]` in structured log arguments. It also scans **string values, including an error's `message` and `stack`**, for recognisable credential shapes — AWS access key ids, `Bearer` tokens, JWTs, and `password=`/`token=` assignments — replacing just the matched substring so the text stays readable. Pass `extraKeys` to add field names and `extraValuePatterns` to add shapes. `redactSecrets` exposes the same redaction for arbitrary objects.
|
|
260
|
+
|
|
261
|
+
**What the library logs.** Nothing at all until you inject a logger — that is deliberate, a library should not write to your console uninvited. Once one is attached, the events worth alerting on are:
|
|
262
|
+
|
|
263
|
+
| Level | Event |
|
|
264
|
+
| --- | --- |
|
|
265
|
+
| `info` | `deleteThread` / `history.clear` completing, with rows deleted and rows skipped |
|
|
266
|
+
| `warn` | a row left in place by a delete because it belongs to another adapter |
|
|
267
|
+
| `warn` | a row skipped on read because its shape is not this adapter's (`store.get`, checkpointer `list`) |
|
|
268
|
+
| `warn` | a pending-write guard rejection whose existing row holds an unexpected channel |
|
|
269
|
+
| `warn` | a `vectorBackend` returning ascending scores, or a match this store cannot address |
|
|
270
|
+
| `error` | a chat message skipped because it could not be decoded, with its sort key |
|
|
255
271
|
|
|
256
272
|
## Infrastructure setup
|
|
257
273
|
|
|
@@ -315,6 +331,33 @@ When S3 offloading is enabled, on the bucket/objects: `s3:GetObject`, `s3:PutObj
|
|
|
315
331
|
|
|
316
332
|
## Migrating from earlier versions
|
|
317
333
|
|
|
334
|
+
**0.7.x → 0.8.0**: **every adapter's partition key is now adapter-tagged** —
|
|
335
|
+
`PK = <thread_id>` → `CHKPT#<thread_id>`, `PK = <namespace[0]>` →
|
|
336
|
+
`STORE#<namespace[0]>`, `PK = <sessionId>` → `HIST#<sessionId>` — and the
|
|
337
|
+
checkpointer's pending-write sort key gains a trailing channel segment
|
|
338
|
+
(`WRITE#<ns>#<id>#<task>#<idx>` → `…#<idx>#<channel>`).
|
|
339
|
+
|
|
340
|
+
This closes two real defects on a table shared via `createAll()`. Reusing one
|
|
341
|
+
identifier across adapters — a "conversation id" used as both a `thread_id`
|
|
342
|
+
and a `sessionId`, an entirely ordinary choice — put unrelated adapters' rows
|
|
343
|
+
in a single partition, where `deleteThread()` silently deleted the chat
|
|
344
|
+
history (and vice versa), and identically-composed sort keys let one adapter
|
|
345
|
+
overwrite another's item or hand its payload back on read. The channel segment
|
|
346
|
+
separately stops a retried task whose write mix changed from silently losing a
|
|
347
|
+
write; a per-call write group, also stored on each row, keeps such a retry from
|
|
348
|
+
replaying a channel twice. See the CHANGELOG for the full mechanism.
|
|
349
|
+
|
|
350
|
+
**Data written by 0.7.x is not found after upgrading**, for all three
|
|
351
|
+
adapters. Back up and recreate the table before upgrading.
|
|
352
|
+
|
|
353
|
+
Three behaviour changes are also worth checking before you upgrade:
|
|
354
|
+
constructing a `DynamoDBStore` with a `vectorBackend` but no `index` now
|
|
355
|
+
throws instead of silently returning unranked results; `getMessages` skips and
|
|
356
|
+
logs an undecodable message instead of failing the whole read (pass
|
|
357
|
+
`onCorruptMessage: 'throw'` to keep the old behaviour); and the store's
|
|
358
|
+
`$gt`/`$gte`/`$lt`/`$lte` filters no longer coerce across types, so a stored
|
|
359
|
+
`'10'` no longer matches `{ $gt: 5 }`.
|
|
360
|
+
|
|
318
361
|
**0.6.x → 0.7.0**: chat-history's sort keys now carry a `HISTORY#` item-kind
|
|
319
362
|
tag — `SK = SESSION` → `SK = HISTORY#SESSION`, `SK = MSG#<ULID>` →
|
|
320
363
|
`SK = HISTORY#MSG#<ULID>`. This closes a real key collision on a table
|
|
@@ -339,9 +382,9 @@ unaffected.
|
|
|
339
382
|
|
|
340
383
|
## Production notes
|
|
341
384
|
|
|
342
|
-
- **Sharing one table** across all three adapters is supported —
|
|
385
|
+
- **Sharing one table** across all three adapters is supported — adapter-tagged partition keys make the key spaces provably disjoint (see [Table schema](#table-schema)), and table-wide reads filter to their own items. Checkpointer, chat-history, and *scoped* store reads are all partition-scoped (`Query`/`GetItem`).
|
|
343
386
|
- **Scoped reads are `Query`s.** `store.search`/`store.listNamespaces` with a concrete namespace prefix and `history.getMessages` are native `Query`s. Only a rootless `store.search([])` / unprefixed `listNamespaces` and `history.listSessions` fall back to `Scan` (cost scales with table size) — keep those rare or use a dedicated table. `listSessions` accepts an optional `{ maxIterations }` override for tables where non-session rows dominate the scan.
|
|
344
|
-
- **Hot partitions.** The store's partition key is `namespace[0]
|
|
387
|
+
- **Hot partitions.** The store's partition key is `STORE#<namespace[0]>` and chat history's is `HIST#<sessionId>` — the adapter tag is constant, so throughput still concentrates on the identifier you choose. A single partition tops out around ~1000 WCU / 3000 RCU, so avoid funneling very high write throughput through one tenant/session id; spread load across scope roots (e.g. include a tenant id as `namespace[0]`).
|
|
345
388
|
- **Very large vector corpora** outgrow the in-DB ranker (`maxSearchCandidates`). Configure a `vectorBackend` (OpenSearch, pgvector, …) — the library keeps DynamoDB as the source of truth and only delegates similarity ranking.
|
|
346
389
|
- **TTL deletion timing** is governed by DynamoDB (typically within 48 h of expiry) and S3 lifecycle expiry is day-granular — the library writes the correct expiry timestamp (and filters expired chat messages on read) but does not guarantee instant deletion. The matching S3 lifecycle rule is not written automatically: it is installed only when you call `ensureS3LifecycleRule()`.
|
|
347
390
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { CheckpointerContext } from '../internal/setup';
|
|
2
2
|
/**
|
|
3
3
|
* Delete every checkpoint, payload, and write for a thread (all share the
|
|
4
|
-
* thread's partition), best-effort deleting any offloaded S3 objects.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* silently truncated at the in-memory page caps.
|
|
4
|
+
* thread's partition), best-effort deleting any offloaded S3 objects. Rows
|
|
5
|
+
* this adapter does not own are left in place and logged, so a shared-table
|
|
6
|
+
* partition holding a foreign row is never collaterally wiped.
|
|
8
7
|
*/
|
|
9
8
|
export declare function deleteThread(context: CheckpointerContext, threadId: string): Promise<void>;
|
|
10
9
|
//# sourceMappingURL=delete-thread.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete-thread.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/delete-thread.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"delete-thread.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/delete-thread.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAY7D;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhG"}
|
|
@@ -1,55 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.deleteThread = deleteThread;
|
|
4
|
-
const
|
|
5
|
-
const orphans_1 = require("../../shared/codec/s3/orphans");
|
|
6
|
-
const constants_1 = require("../../shared/constants");
|
|
7
|
-
const batch_write_1 = require("../../shared/dynamodb/batch-write");
|
|
8
|
-
const paginate_1 = require("../../shared/dynamodb/paginate");
|
|
9
|
-
const primitives_1 = require("../../shared/validation/primitives");
|
|
4
|
+
const partition_delete_1 = require("../../shared/dynamodb/partition-delete");
|
|
10
5
|
const keys_1 = require("../internal/keys");
|
|
11
6
|
const query_1 = require("../internal/query");
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/** Delete the buffered keys and best-effort clean their S3 objects, then clear it. */
|
|
21
|
-
async function flushBuffer(context, buffer) {
|
|
22
|
-
if (buffer.keys.length === 0)
|
|
23
|
-
return;
|
|
24
|
-
await (0, batch_write_1.batchWriteAll)(context.client, context.tableName, buffer.keys.map((Key) => ({ DeleteRequest: { Key } })));
|
|
25
|
-
if (context.offloader) {
|
|
26
|
-
await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)(buffer.descriptors), 'deleteThread', context.logger);
|
|
27
|
-
}
|
|
28
|
-
buffer.keys = [];
|
|
29
|
-
buffer.descriptors = [];
|
|
7
|
+
const validation_1 = require("../internal/validation");
|
|
8
|
+
/** The offloaded payloads a checkpointer row can reference. */
|
|
9
|
+
function descriptorsOf(row) {
|
|
10
|
+
return [
|
|
11
|
+
row.metadata,
|
|
12
|
+
row.checkpoint,
|
|
13
|
+
row.value,
|
|
14
|
+
];
|
|
30
15
|
}
|
|
31
16
|
/**
|
|
32
17
|
* Delete every checkpoint, payload, and write for a thread (all share the
|
|
33
|
-
* thread's partition), best-effort deleting any offloaded S3 objects.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* silently truncated at the in-memory page caps.
|
|
18
|
+
* thread's partition), best-effort deleting any offloaded S3 objects. Rows
|
|
19
|
+
* this adapter does not own are left in place and logged, so a shared-table
|
|
20
|
+
* partition holding a foreign row is never collaterally wiped.
|
|
37
21
|
*/
|
|
38
22
|
async function deleteThread(context, threadId) {
|
|
39
|
-
(0,
|
|
40
|
-
|
|
41
|
-
const buffer = { keys: [], descriptors: [] };
|
|
42
|
-
const pages = (0, paginate_1.paginateQuery)({
|
|
23
|
+
(0, validation_1.validateThreadId)(threadId);
|
|
24
|
+
await (0, partition_delete_1.deletePartitionRows)({
|
|
43
25
|
client: context.client,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
26
|
+
tableName: context.tableName,
|
|
27
|
+
params: (0, query_1.partitionQuery)(context.tableName, (0, keys_1.partitionKey)(threadId), { consistent: true }),
|
|
28
|
+
logger: context.logger,
|
|
29
|
+
offloader: context.offloader,
|
|
30
|
+
operation: 'deleteThread',
|
|
31
|
+
ownsSortKey: keys_1.isCheckpointerSortKey,
|
|
32
|
+
descriptorsOf,
|
|
47
33
|
});
|
|
48
|
-
for await (const raw of pages) {
|
|
49
|
-
bufferItem(buffer, raw);
|
|
50
|
-
if (buffer.keys.length >= constants_1.BATCH_WRITE_MAX)
|
|
51
|
-
await flushBuffer(context, buffer);
|
|
52
|
-
}
|
|
53
|
-
await flushBuffer(context, buffer);
|
|
54
34
|
}
|
|
55
35
|
//# sourceMappingURL=delete-thread.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete-thread.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/delete-thread.ts"],"names":[],"mappings":";;;AACA,
|
|
1
|
+
{"version":3,"file":"delete-thread.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/delete-thread.ts"],"names":[],"mappings":";;;AACA,6EAA6E;AAE7E,2CAAuE;AACvE,6CAAmD;AAEnD,uDAA0D;AAE1D,+DAA+D;AAC/D,SAAS,aAAa,CAAC,GAAY;IACjC,OAAO;QACL,GAAG,CAAC,QAAyC;QAC7C,GAAG,CAAC,UAA2C;QAC/C,GAAG,CAAC,KAAsC;KAC3C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACI,KAAK,uBAAuB,OAA4B,EAAE,QAAgB;IAC/E,IAAA,6BAAgB,EAAC,QAAQ,CAAC,CAAC;IAC3B,MAAM,IAAA,sCAAmB,EAAC;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,MAAM,EAAE,IAAA,sBAAc,EAAC,OAAO,CAAC,SAAS,EAAE,IAAA,mBAAY,EAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QACvF,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,SAAS,EAAE,cAAc;QACzB,WAAW,EAAE,4BAAqB;QAClC,aAAa;KACd,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAS9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAS9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAyC7D;;;;GAIG;AACH,wBAAuB,eAAe,CACpC,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAAC,eAAe,CAAC,CAuCjC"}
|
|
@@ -16,6 +16,16 @@ function readListOptions(options) {
|
|
|
16
16
|
limit: options?.limit,
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* True when `meta` passes the key-level list filters: `before` (strictly older
|
|
21
|
+
* checkpoints) and an explicitly requested `checkpoint_id`. Split out from the
|
|
22
|
+
* loop so each filter stays independently readable.
|
|
23
|
+
*/
|
|
24
|
+
function passesKeyFilters(meta, checkpointId, before) {
|
|
25
|
+
if (checkpointId !== undefined && meta.checkpointId !== checkpointId)
|
|
26
|
+
return false;
|
|
27
|
+
return before === undefined || meta.checkpointId < before;
|
|
28
|
+
}
|
|
19
29
|
/** True when `meta` passes the (optional) metadata-equality filter. */
|
|
20
30
|
async function passesMetadataFilter(context, meta, filter) {
|
|
21
31
|
if (!filter)
|
|
@@ -33,13 +43,30 @@ async function* listCheckpoints(context, config, options) {
|
|
|
33
43
|
const { before, filter, limit } = readListOptions(options);
|
|
34
44
|
const params = (0, query_1.beginsWithQuery)(context.tableName, (0, keys_1.partitionKey)(threadId), (0, keys_1.metaSortKeyPrefix)(checkpointNs));
|
|
35
45
|
let yielded = 0;
|
|
36
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Unbounded: this is a generator that streams and never accumulates, and
|
|
48
|
+
* `limit` already returns early, so the caller's own bound is what stops the
|
|
49
|
+
* read. The shared in-memory cap counts *raw rows pulled*, not
|
|
50
|
+
* filter-matched ones, so leaving it in place turned a caller asking for a
|
|
51
|
+
* handful of rare matches over a large thread into a hard
|
|
52
|
+
* ResultTruncatedError instead of the true (possibly empty) answer.
|
|
53
|
+
*/
|
|
54
|
+
for await (const raw of (0, paginate_1.paginateQuery)({
|
|
55
|
+
client: context.client,
|
|
56
|
+
params,
|
|
57
|
+
maxItems: Number.POSITIVE_INFINITY,
|
|
58
|
+
maxIterations: Number.POSITIVE_INFINITY,
|
|
59
|
+
})) {
|
|
37
60
|
if (limit !== undefined && yielded >= limit)
|
|
38
61
|
return;
|
|
39
|
-
const meta = raw;
|
|
40
|
-
if (
|
|
62
|
+
const meta = (0, item_reader_1.narrowMetaItem)(raw);
|
|
63
|
+
if (!meta) {
|
|
64
|
+
context.logger.warn('list: skipped a row that is not a checkpoint meta item', {
|
|
65
|
+
sortKey: raw.SK,
|
|
66
|
+
});
|
|
41
67
|
continue;
|
|
42
|
-
|
|
68
|
+
}
|
|
69
|
+
if (!passesKeyFilters(meta, checkpointId, before))
|
|
43
70
|
continue;
|
|
44
71
|
if (!(await passesMetadataFilter(context, meta, filter)))
|
|
45
72
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/list.ts"],"names":[],"mappings":";;;AAGA,6DAA+D;AAC/D,mDAAqD;AACrD,2DAA4D;AAC5D,2DAA2E;AAC3E,
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/list.ts"],"names":[],"mappings":";;;AAGA,6DAA+D;AAC/D,mDAAqD;AACrD,2DAA4D;AAC5D,2DAA2E;AAC3E,yDAAuE;AACvE,2CAAmE;AACnE,6CAAoD;AAIpD,kDAAkD;AAClD,SAAS,eAAe,CAAC,OAA+B;IAKtD,OAAO;QACL,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,aAAmC;QAC1E,MAAM,EAAE,OAAO,EAAE,MAAiD;QAClE,KAAK,EAAE,OAAO,EAAE,KAAK;KACtB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CACvB,IAAwB,EACxB,YAAgC,EAChC,MAA0B;IAE1B,IAAI,YAAY,KAAK,SAAS,IAAI,IAAI,CAAC,YAAY,KAAK,YAAY;QAAE,OAAO,KAAK,CAAC;IACnF,OAAO,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;AAC5D,CAAC;AAED,uEAAuE;AACvE,KAAK,UAAU,oBAAoB,CACjC,OAA4B,EAC5B,IAAwB,EACxB,MAA+C;IAE/C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAA,0BAAY,EAAC,OAAO,EAAE,IAAI,CAAC,CAAgC,CAAC;IACpF,OAAO,IAAA,4BAAa,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACI,KAAK,SAAS,CAAC,iBACpB,OAA4B,EAC5B,MAAsB,EACtB,OAA+B;IAE/B,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,IAAA,+BAAgB,EAAC,MAAM,CAAC,CAAC;IAC1E,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAA,uBAAe,EAC5B,OAAO,CAAC,SAAS,EACjB,IAAA,mBAAY,EAAC,QAAQ,CAAC,EACtB,IAAA,wBAAiB,EAAC,YAAY,CAAC,CAChC,CAAC;IACF,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB;;;;;;;OAOG;IACH,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,IAAA,wBAAa,EAAC;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM;QACN,QAAQ,EAAE,MAAM,CAAC,iBAAiB;QAClC,aAAa,EAAE,MAAM,CAAC,iBAAiB;KACxC,CAAC,EAAE,CAAC;QACH,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK;YAAE,OAAO;QACpD,MAAM,IAAI,GAAG,IAAA,4BAAc,EAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,wDAAwD,EAAE;gBAC5E,OAAO,EAAE,GAAG,CAAC,EAAY;aAC1B,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC;YAAE,SAAS;QAC5D,IAAI,CAAC,CAAC,MAAM,oBAAoB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAAE,SAAS;QACnE,MAAM,KAAK,GAAG,MAAM,IAAA,wBAAa,EAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;QACzE,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,CAAC;YACZ,OAAO,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"put-writes.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"put-writes.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAUpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA2E7D;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
|
|
@@ -1,44 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.putWrites = putWrites;
|
|
4
|
-
const node_crypto_1 = require("node:crypto");
|
|
5
4
|
const descriptor_keys_1 = require("../../shared/codec/descriptor-keys");
|
|
6
5
|
const orphans_1 = require("../../shared/codec/s3/orphans");
|
|
7
6
|
const retry_1 = require("../../shared/dynamodb/retry");
|
|
8
7
|
const errors_1 = require("../../shared/errors/errors");
|
|
8
|
+
const ulid_1 = require("../../shared/ulid");
|
|
9
9
|
const ttl_1 = require("../../shared/validation/ttl");
|
|
10
10
|
const configurable_1 = require("../internal/configurable");
|
|
11
11
|
const item_writer_1 = require("../internal/item-writer");
|
|
12
12
|
const special_write_cleanup_1 = require("../internal/special-write-cleanup");
|
|
13
13
|
const validation_1 = require("../internal/validation");
|
|
14
|
+
const write_guard_1 = require("../internal/write-guard");
|
|
14
15
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Stamps each `putWrites` call, serving two purposes at once. It nonces every
|
|
17
|
+
* S3 upload, so a repeated write never shares an object with an earlier
|
|
18
|
+
* attempt. And because ULIDs are lexicographically time-ordered — and this
|
|
19
|
+
* factory is strictly monotonic even within a single millisecond — it lets the
|
|
20
|
+
* read side identify the *earliest* call that wrote a given channel (see
|
|
21
|
+
* `dropSupersededWrites`). A random UUID nonces just as well but carries no
|
|
22
|
+
* ordering, which would leave that choice arbitrary.
|
|
18
23
|
*/
|
|
19
|
-
|
|
20
|
-
return error.name === 'ConditionalCheckFailedException';
|
|
21
|
-
}
|
|
24
|
+
const nextWriteGroup = (0, ulid_1.createUlidFactory)();
|
|
22
25
|
/** Best-effort delete `items`' offloaded S3 objects, if an offloader is configured. */
|
|
23
26
|
async function cleanUpItems(context, items) {
|
|
24
27
|
if (!context.offloader)
|
|
25
28
|
return;
|
|
26
29
|
await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)(items.map((item) => item.value)), 'putWrites', context.logger);
|
|
27
30
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Dedup writes to the same special channel (last-write-wins) before upload,
|
|
30
|
-
* so a discarded duplicate is never uploaded. A regular write's index comes
|
|
31
|
-
* from its position here (post-dedup), not the caller's raw array — stable
|
|
32
|
-
* across retries of an identical array, which is what first-write-wins needs.
|
|
33
|
-
*/
|
|
34
|
-
function dedupeWritesByIndex(writes) {
|
|
35
|
-
const byIndex = new Map();
|
|
36
|
-
writes.forEach((write, positional) => {
|
|
37
|
-
const index = (0, item_writer_1.resolveWriteIndex)(write[0], positional);
|
|
38
|
-
byIndex.set(index, write);
|
|
39
|
-
});
|
|
40
|
-
return [...byIndex.values()];
|
|
41
|
-
}
|
|
42
31
|
/**
|
|
43
32
|
* Write regular items with a first-write-wins guard. Every `PutCommand` fully
|
|
44
33
|
* settles (`Promise.allSettled`) before this resolves and never rejects; a
|
|
@@ -51,13 +40,14 @@ async function writeRegularItems(context, items) {
|
|
|
51
40
|
TableName: context.tableName,
|
|
52
41
|
Item: item,
|
|
53
42
|
ConditionExpression: 'attribute_not_exists(PK)',
|
|
43
|
+
ReturnValuesOnConditionCheckFailure: 'ALL_OLD',
|
|
54
44
|
}))));
|
|
55
45
|
results.forEach((result, index) => {
|
|
56
46
|
if (result.status === 'fulfilled')
|
|
57
47
|
return;
|
|
58
48
|
const reason = result.reason;
|
|
59
|
-
if (isConditionalCheckFailed(reason)) {
|
|
60
|
-
|
|
49
|
+
if ((0, write_guard_1.isConditionalCheckFailed)(reason)) {
|
|
50
|
+
(0, write_guard_1.reportGuardRejection)(context, items[index], reason);
|
|
61
51
|
return;
|
|
62
52
|
}
|
|
63
53
|
failed.push(items[index]);
|
|
@@ -84,7 +74,7 @@ async function putWrites(context, config, writes, taskId) {
|
|
|
84
74
|
if (writes.length === 0)
|
|
85
75
|
return;
|
|
86
76
|
const ttlTimestamp = context.ttl ? (0, ttl_1.calculateTtlTimestamp)(context.ttl) : undefined;
|
|
87
|
-
const items = await (0, item_writer_1.buildWriteItems)(context, threadId, checkpointNs, checkpointId, taskId,
|
|
77
|
+
const items = await (0, item_writer_1.buildWriteItems)(context, threadId, checkpointNs, checkpointId, taskId, writes, nextWriteGroup(), ttlTimestamp);
|
|
88
78
|
const special = items.filter((item) => item.index < 0);
|
|
89
79
|
const regular = items.filter((item) => item.index >= 0);
|
|
90
80
|
const [specialError, regularOutcome] = await Promise.all([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"put-writes.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"put-writes.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
import type { Checkpoint, CheckpointMetadata, CheckpointPendingWrite } from '@langchain/langgraph-checkpoint';
|
|
2
|
+
import type { DocItem } from '../../shared/dynamodb/types';
|
|
2
3
|
import type { CheckpointMetaItem, CheckpointPayloadItem, CheckpointWriteItem } from '../types';
|
|
3
4
|
import type { CheckpointerContext } from './setup';
|
|
5
|
+
/**
|
|
6
|
+
* Narrow a raw row to a {@link CheckpointMetaItem}, or `undefined` for a row
|
|
7
|
+
* that merely shares the `META#` sort-key prefix. Replaces an unchecked cast
|
|
8
|
+
* at the one boundary where a row may not have been written by this adapter.
|
|
9
|
+
*/
|
|
10
|
+
export declare function narrowMetaItem(raw: DocItem): CheckpointMetaItem | undefined;
|
|
4
11
|
/** Decode the checkpoint stored in a PAYLOAD item. */
|
|
5
12
|
export declare function readCheckpoint(context: CheckpointerContext, item: CheckpointPayloadItem): Promise<Checkpoint>;
|
|
6
13
|
/** Decode the metadata stored in a META item. */
|
|
7
14
|
export declare function readMetadata(context: CheckpointerContext, item: CheckpointMetaItem): Promise<CheckpointMetadata>;
|
|
15
|
+
/**
|
|
16
|
+
* Drop rows a later `putWrites` call added for a task and channel an earlier
|
|
17
|
+
* call had already committed.
|
|
18
|
+
*
|
|
19
|
+
* A regular write's index is its position in the caller's array, so a retried
|
|
20
|
+
* task whose write mix changed can place an already-committed channel at a
|
|
21
|
+
* second index — a new row, which would replay that channel's value twice. For
|
|
22
|
+
* an accumulating channel (a `messages` add-reducer, a Topic) that
|
|
23
|
+
* double-counts. Every row carries the `writeGroup` of the call that wrote it,
|
|
24
|
+
* which distinguishes that case from a channel a *single* call legitimately
|
|
25
|
+
* wrote more than once (a task emitting two Sends), where both values must
|
|
26
|
+
* survive.
|
|
27
|
+
*
|
|
28
|
+
* Write groups are time-ordered ULIDs, so the smallest one for a `(task,
|
|
29
|
+
* channel)` pair identifies the earliest committed call — first-write-wins,
|
|
30
|
+
* the same rule the write-side guard applies, extended to the case the guard
|
|
31
|
+
* cannot see. Selecting by *order encountered* would not do: rows arrive
|
|
32
|
+
* sorted by index, and a retry emitting fewer writes moves an
|
|
33
|
+
* already-committed channel to a smaller index, where it sorts ahead of the
|
|
34
|
+
* original row and would hand back the later call's value instead. The
|
|
35
|
+
* superseded row stays in DynamoDB and is removed by `deleteThread` or TTL
|
|
36
|
+
* like any other.
|
|
37
|
+
*/
|
|
38
|
+
export declare function dropSupersededWrites(items: CheckpointWriteItem[]): CheckpointWriteItem[];
|
|
8
39
|
/** Decode WRITE items into `[taskId, channel, value]` pending-write tuples. */
|
|
9
40
|
export declare function toPendingWrites(context: CheckpointerContext, items: CheckpointWriteItem[]): Promise<CheckpointPendingWrite[]>;
|
|
10
41
|
//# sourceMappingURL=item-reader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item-reader.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-reader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,sDAAsD;AACtD,wBAAsB,cAAc,CAClC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,iDAAiD;AACjD,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,kBAAkB,CAAC,CAE7B;AAED,+EAA+E;AAC/E,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,EAC5B,KAAK,EAAE,mBAAmB,EAAE,GAC3B,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAQnC"}
|
|
1
|
+
{"version":3,"file":"item-reader.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-reader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,GAAG,SAAS,CAM3E;AAED,sDAAsD;AACtD,wBAAsB,cAAc,CAClC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,iDAAiD;AACjD,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,kBAAkB,CAAC,CAE7B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,EAAE,GAAG,mBAAmB,EAAE,CASxF;AAED,+EAA+E;AAC/E,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,EAC5B,KAAK,EAAE,mBAAmB,EAAE,GAC3B,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAQnC"}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.narrowMetaItem = narrowMetaItem;
|
|
3
4
|
exports.readCheckpoint = readCheckpoint;
|
|
4
5
|
exports.readMetadata = readMetadata;
|
|
6
|
+
exports.dropSupersededWrites = dropSupersededWrites;
|
|
5
7
|
exports.toPendingWrites = toPendingWrites;
|
|
6
8
|
const codec_1 = require("../../shared/codec/codec");
|
|
7
9
|
const item_writer_1 = require("./item-writer");
|
|
10
|
+
/**
|
|
11
|
+
* Narrow a raw row to a {@link CheckpointMetaItem}, or `undefined` for a row
|
|
12
|
+
* that merely shares the `META#` sort-key prefix. Replaces an unchecked cast
|
|
13
|
+
* at the one boundary where a row may not have been written by this adapter.
|
|
14
|
+
*/
|
|
15
|
+
function narrowMetaItem(raw) {
|
|
16
|
+
return typeof raw.checkpointId === 'string' &&
|
|
17
|
+
typeof raw.checkpointNs === 'string' &&
|
|
18
|
+
raw.metadata !== undefined
|
|
19
|
+
? raw
|
|
20
|
+
: undefined;
|
|
21
|
+
}
|
|
8
22
|
/** Decode the checkpoint stored in a PAYLOAD item. */
|
|
9
23
|
async function readCheckpoint(context, item) {
|
|
10
24
|
return (0, codec_1.decodePayload)(item.checkpoint, (0, item_writer_1.codecDeps)(context));
|
|
@@ -13,11 +27,45 @@ async function readCheckpoint(context, item) {
|
|
|
13
27
|
async function readMetadata(context, item) {
|
|
14
28
|
return (0, codec_1.decodePayload)(item.metadata, (0, item_writer_1.codecDeps)(context));
|
|
15
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Drop rows a later `putWrites` call added for a task and channel an earlier
|
|
32
|
+
* call had already committed.
|
|
33
|
+
*
|
|
34
|
+
* A regular write's index is its position in the caller's array, so a retried
|
|
35
|
+
* task whose write mix changed can place an already-committed channel at a
|
|
36
|
+
* second index — a new row, which would replay that channel's value twice. For
|
|
37
|
+
* an accumulating channel (a `messages` add-reducer, a Topic) that
|
|
38
|
+
* double-counts. Every row carries the `writeGroup` of the call that wrote it,
|
|
39
|
+
* which distinguishes that case from a channel a *single* call legitimately
|
|
40
|
+
* wrote more than once (a task emitting two Sends), where both values must
|
|
41
|
+
* survive.
|
|
42
|
+
*
|
|
43
|
+
* Write groups are time-ordered ULIDs, so the smallest one for a `(task,
|
|
44
|
+
* channel)` pair identifies the earliest committed call — first-write-wins,
|
|
45
|
+
* the same rule the write-side guard applies, extended to the case the guard
|
|
46
|
+
* cannot see. Selecting by *order encountered* would not do: rows arrive
|
|
47
|
+
* sorted by index, and a retry emitting fewer writes moves an
|
|
48
|
+
* already-committed channel to a smaller index, where it sorts ahead of the
|
|
49
|
+
* original row and would hand back the later call's value instead. The
|
|
50
|
+
* superseded row stays in DynamoDB and is removed by `deleteThread` or TTL
|
|
51
|
+
* like any other.
|
|
52
|
+
*/
|
|
53
|
+
function dropSupersededWrites(items) {
|
|
54
|
+
const identity = (item) => `${item.taskId}\u0000${item.channel}`;
|
|
55
|
+
const earliestGroup = new Map();
|
|
56
|
+
for (const item of items) {
|
|
57
|
+
const id = identity(item);
|
|
58
|
+
const seen = earliestGroup.get(id);
|
|
59
|
+
if (seen === undefined || item.writeGroup < seen)
|
|
60
|
+
earliestGroup.set(id, item.writeGroup);
|
|
61
|
+
}
|
|
62
|
+
return items.filter((item) => earliestGroup.get(identity(item)) === item.writeGroup);
|
|
63
|
+
}
|
|
16
64
|
/** Decode WRITE items into `[taskId, channel, value]` pending-write tuples. */
|
|
17
65
|
async function toPendingWrites(context, items) {
|
|
18
66
|
const deps = (0, item_writer_1.codecDeps)(context);
|
|
19
67
|
const pending = [];
|
|
20
|
-
for (const item of items) {
|
|
68
|
+
for (const item of dropSupersededWrites(items)) {
|
|
21
69
|
const value = await (0, codec_1.decodePayload)(item.value, deps);
|
|
22
70
|
pending.push([item.taskId, item.channel, value]);
|
|
23
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item-reader.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-reader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"item-reader.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-reader.ts"],"names":[],"mappings":";;;;;;;AAMA,oDAAyD;AAGzD,+CAA0C;AAG1C;;;;GAIG;AACH,wBAA+B,GAAY;IACzC,OAAO,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ;QACzC,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ;QACpC,GAAG,CAAC,QAAQ,KAAK,SAAS;QAC1B,CAAC,CAAE,GAA0B;QAC7B,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,sDAAsD;AAC/C,KAAK,yBACV,OAA4B,EAC5B,IAA2B;IAE3B,OAAO,IAAA,qBAAa,EAAa,IAAI,CAAC,UAAU,EAAE,IAAA,uBAAS,EAAC,OAAO,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,iDAAiD;AAC1C,KAAK,uBACV,OAA4B,EAC5B,IAAwB;IAExB,OAAO,IAAA,qBAAa,EAAqB,IAAI,CAAC,QAAQ,EAAE,IAAA,uBAAS,EAAC,OAAO,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,8BAAqC,KAA4B;IAC/D,MAAM,QAAQ,GAAG,CAAC,IAAyB,EAAU,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC;IAC9F,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAChD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,UAAU,GAAG,IAAI;YAAE,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC;AACvF,CAAC;AAED,+EAA+E;AACxE,KAAK,0BACV,OAA4B,EAC5B,KAA4B;IAE5B,MAAM,IAAI,GAAG,IAAA,uBAAS,EAAC,OAAO,CAAC,CAAC;IAChC,MAAM,OAAO,GAA6B,EAAE,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,MAAM,IAAA,qBAAa,EAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -4,14 +4,6 @@ import type { CheckpointMetaItem, CheckpointPayloadItem, CheckpointWriteItem } f
|
|
|
4
4
|
import type { CheckpointerContext } from './setup';
|
|
5
5
|
/** Map a context to the codec collaborators. */
|
|
6
6
|
export declare function codecDeps(context: CheckpointerContext): CodecDeps;
|
|
7
|
-
/**
|
|
8
|
-
* Resolve a write's DynamoDB sort-key index: WRITES_IDX_MAP's slot for a
|
|
9
|
-
* known special channel, else its position. `Object.hasOwn` guards against
|
|
10
|
-
* WRITES_IDX_MAP's own `Object.prototype` chain — a channel literally named
|
|
11
|
-
* `constructor`/`toString`/etc. must fall through to `positional`, not
|
|
12
|
-
* resolve to an inherited function reference.
|
|
13
|
-
*/
|
|
14
|
-
export declare function resolveWriteIndex(channel: string, positional: number): number;
|
|
15
7
|
/** Encode a checkpoint + metadata into its META and PAYLOAD items. */
|
|
16
8
|
export declare function buildCheckpointItems(context: CheckpointerContext, threadId: string, checkpointNs: string, checkpoint: Checkpoint, metadata: CheckpointMetadata, parentCheckpointId?: string, ttlTimestamp?: number): Promise<{
|
|
17
9
|
meta: CheckpointMetaItem;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item-writer.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-writer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"item-writer.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-writer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEpG,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,0BAA0B,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAGnD,gDAAgD;AAChD,wBAAgB,SAAS,CAAC,OAAO,EAAE,mBAAmB,GAAG,SAAS,CAEjE;AAOD,sEAAsE;AACtE,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,kBAAkB,EAC5B,kBAAkB,CAAC,EAAE,MAAM,EAC3B,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,qBAAqB,CAAA;CAAE,CAAC,CAwBvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EAAE,EACtB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAgChC"}
|