@farukada/aws-langgraph-dynamodb-ts 0.0.10 → 0.1.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.
Files changed (134) hide show
  1. package/README.md +353 -242
  2. package/dist/checkpointer/actions/delete-thread.d.ts +3 -3
  3. package/dist/checkpointer/actions/delete-thread.d.ts.map +1 -1
  4. package/dist/checkpointer/actions/delete-thread.js +95 -52
  5. package/dist/checkpointer/actions/delete-thread.js.map +1 -1
  6. package/dist/checkpointer/actions/get-tuple.d.ts +8 -1
  7. package/dist/checkpointer/actions/get-tuple.d.ts.map +1 -1
  8. package/dist/checkpointer/actions/get-tuple.js +72 -20
  9. package/dist/checkpointer/actions/get-tuple.js.map +1 -1
  10. package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
  11. package/dist/checkpointer/actions/put-writes.js +27 -22
  12. package/dist/checkpointer/actions/put-writes.js.map +1 -1
  13. package/dist/checkpointer/actions/put.d.ts +6 -2
  14. package/dist/checkpointer/actions/put.d.ts.map +1 -1
  15. package/dist/checkpointer/actions/put.js +80 -19
  16. package/dist/checkpointer/actions/put.js.map +1 -1
  17. package/dist/checkpointer/index.d.ts +43 -2
  18. package/dist/checkpointer/index.d.ts.map +1 -1
  19. package/dist/checkpointer/index.js +226 -11
  20. package/dist/checkpointer/index.js.map +1 -1
  21. package/dist/checkpointer/types/index.d.ts +66 -4
  22. package/dist/checkpointer/types/index.d.ts.map +1 -1
  23. package/dist/checkpointer/types/index.js +10 -0
  24. package/dist/checkpointer/types/index.js.map +1 -1
  25. package/dist/checkpointer/utils/deserialization.d.ts +8 -3
  26. package/dist/checkpointer/utils/deserialization.d.ts.map +1 -1
  27. package/dist/checkpointer/utils/deserialization.js +23 -5
  28. package/dist/checkpointer/utils/deserialization.js.map +1 -1
  29. package/dist/checkpointer/utils/validation.d.ts +1 -1
  30. package/dist/checkpointer/utils/validation.js +1 -1
  31. package/dist/checkpointer/utils/validation.js.map +1 -1
  32. package/dist/factory.d.ts +8 -6
  33. package/dist/factory.d.ts.map +1 -1
  34. package/dist/factory.js +16 -4
  35. package/dist/factory.js.map +1 -1
  36. package/dist/history/actions/add-message.d.ts +2 -1
  37. package/dist/history/actions/add-message.d.ts.map +1 -1
  38. package/dist/history/actions/add-message.js +22 -18
  39. package/dist/history/actions/add-message.js.map +1 -1
  40. package/dist/history/actions/add-messages.d.ts +3 -2
  41. package/dist/history/actions/add-messages.d.ts.map +1 -1
  42. package/dist/history/actions/add-messages.js +30 -20
  43. package/dist/history/actions/add-messages.js.map +1 -1
  44. package/dist/history/actions/clear.d.ts +4 -3
  45. package/dist/history/actions/clear.d.ts.map +1 -1
  46. package/dist/history/actions/clear.js +47 -12
  47. package/dist/history/actions/clear.js.map +1 -1
  48. package/dist/history/actions/get-messages.d.ts +2 -1
  49. package/dist/history/actions/get-messages.d.ts.map +1 -1
  50. package/dist/history/actions/get-messages.js +41 -15
  51. package/dist/history/actions/get-messages.js.map +1 -1
  52. package/dist/history/actions/list-sessions.d.ts +11 -0
  53. package/dist/history/actions/list-sessions.d.ts.map +1 -1
  54. package/dist/history/actions/list-sessions.js +23 -3
  55. package/dist/history/actions/list-sessions.js.map +1 -1
  56. package/dist/history/index.d.ts +8 -0
  57. package/dist/history/index.d.ts.map +1 -1
  58. package/dist/history/index.js +22 -2
  59. package/dist/history/index.js.map +1 -1
  60. package/dist/history/types/index.d.ts +27 -5
  61. package/dist/history/types/index.d.ts.map +1 -1
  62. package/dist/history/types/index.js +1 -0
  63. package/dist/history/types/index.js.map +1 -1
  64. package/dist/history/utils/title-generator.js.map +1 -1
  65. package/dist/history/utils/update-expression-builder.d.ts +45 -21
  66. package/dist/history/utils/update-expression-builder.d.ts.map +1 -1
  67. package/dist/history/utils/update-expression-builder.js +105 -38
  68. package/dist/history/utils/update-expression-builder.js.map +1 -1
  69. package/dist/history/utils/validation.d.ts +8 -0
  70. package/dist/history/utils/validation.d.ts.map +1 -1
  71. package/dist/history/utils/validation.js +22 -0
  72. package/dist/history/utils/validation.js.map +1 -1
  73. package/dist/index.d.ts +2 -0
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +5 -1
  76. package/dist/index.js.map +1 -1
  77. package/dist/shared/utils/batch-write.d.ts +36 -0
  78. package/dist/shared/utils/batch-write.d.ts.map +1 -0
  79. package/dist/shared/utils/batch-write.js +62 -0
  80. package/dist/shared/utils/batch-write.js.map +1 -0
  81. package/dist/shared/utils/compressor.d.ts +48 -0
  82. package/dist/shared/utils/compressor.d.ts.map +1 -0
  83. package/dist/shared/utils/compressor.js +68 -0
  84. package/dist/shared/utils/compressor.js.map +1 -0
  85. package/dist/shared/utils/constants.d.ts +2 -0
  86. package/dist/shared/utils/constants.d.ts.map +1 -1
  87. package/dist/shared/utils/constants.js +3 -7
  88. package/dist/shared/utils/constants.js.map +1 -1
  89. package/dist/shared/utils/index.d.ts +4 -0
  90. package/dist/shared/utils/index.d.ts.map +1 -1
  91. package/dist/shared/utils/index.js +4 -0
  92. package/dist/shared/utils/index.js.map +1 -1
  93. package/dist/shared/utils/logger.d.ts +48 -0
  94. package/dist/shared/utils/logger.d.ts.map +1 -0
  95. package/dist/shared/utils/logger.js +62 -0
  96. package/dist/shared/utils/logger.js.map +1 -0
  97. package/dist/shared/utils/retry.d.ts +1 -1
  98. package/dist/shared/utils/retry.d.ts.map +1 -1
  99. package/dist/shared/utils/retry.js +13 -6
  100. package/dist/shared/utils/retry.js.map +1 -1
  101. package/dist/shared/utils/s3-offloader.d.ts +146 -0
  102. package/dist/shared/utils/s3-offloader.d.ts.map +1 -0
  103. package/dist/shared/utils/s3-offloader.js +271 -0
  104. package/dist/shared/utils/s3-offloader.js.map +1 -0
  105. package/dist/shared/utils/ttl.d.ts +8 -1
  106. package/dist/shared/utils/ttl.d.ts.map +1 -1
  107. package/dist/shared/utils/ttl.js +12 -8
  108. package/dist/shared/utils/ttl.js.map +1 -1
  109. package/dist/store/actions/get-operation.js +2 -2
  110. package/dist/store/actions/get-operation.js.map +1 -1
  111. package/dist/store/actions/put-operation.d.ts.map +1 -1
  112. package/dist/store/actions/put-operation.js +2 -1
  113. package/dist/store/actions/put-operation.js.map +1 -1
  114. package/dist/store/actions/search-operation.d.ts +6 -0
  115. package/dist/store/actions/search-operation.d.ts.map +1 -1
  116. package/dist/store/actions/search-operation.js +41 -21
  117. package/dist/store/actions/search-operation.js.map +1 -1
  118. package/dist/store/index.d.ts +10 -2
  119. package/dist/store/index.d.ts.map +1 -1
  120. package/dist/store/index.js +28 -5
  121. package/dist/store/index.js.map +1 -1
  122. package/dist/store/types/index.d.ts +7 -5
  123. package/dist/store/types/index.d.ts.map +1 -1
  124. package/dist/store/utils/index.d.ts +0 -1
  125. package/dist/store/utils/index.d.ts.map +1 -1
  126. package/dist/store/utils/index.js +0 -1
  127. package/dist/store/utils/index.js.map +1 -1
  128. package/dist/store/utils/validation.js +1 -1
  129. package/dist/store/utils/validation.js.map +1 -1
  130. package/package.json +41 -24
  131. package/dist/store/utils/result.d.ts +0 -42
  132. package/dist/store/utils/result.d.ts.map +0 -1
  133. package/dist/store/utils/result.js +0 -51
  134. package/dist/store/utils/result.js.map +0 -1
package/README.md CHANGED
@@ -1,49 +1,204 @@
1
1
  # @farukada/aws-langgraph-dynamodb-ts
2
2
 
3
- [![npm version](https://badge.fury.io/js/@farukada%2Faws-langgraph-dynamodb-ts.svg)](https://badge.fury.io/js/@farukada%2Faws-langgraph-dynamodb-ts)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Node.js Version](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen)](https://nodejs.org)
6
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.5.3-blue)](https://www.typescriptlang.org/)
7
- [![AWS SDK](https://img.shields.io/badge/AWS%20SDK-v3-orange)](https://aws.amazon.com/sdk-for-javascript/)
3
+ [![npm version](https://img.shields.io/npm/v/%40farukada%2Faws-langgraph-dynamodb-ts)](https://www.npmjs.com/package/@farukada/aws-langgraph-dynamodb-ts)
4
+ [![Sponsor](https://img.shields.io/badge/Sponsor-FarukAda-ea4aaa?logo=githubsponsors)](https://github.com/sponsors/FarukAda)
5
+ ![Node >=22](https://img.shields.io/badge/node-%3E%3D22-339933)
6
+ ![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178C6)
7
+ ![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)
8
+ ![AWS SDK v3](https://img.shields.io/badge/AWS%20SDK-v3-FF9900)
9
+
10
+ AWS DynamoDB persistence layer for [LangGraph](https://langchain-ai.github.io/langgraphjs/) in TypeScript. Drop-in checkpoint storage, long-term memory with semantic search, and chat message history — all backed by DynamoDB with optional S3 offloading for large payloads.
11
+
12
+ ## Table of Contents
13
+
14
+ - [Features](#features)
15
+ - [Architecture](#architecture)
16
+ - [Quick Start](#quick-start)
17
+ - [Infrastructure Setup](#infrastructure-setup)
18
+ - [Advanced Features](#advanced-features)
19
+ - [Configuration Reference](#configuration-reference)
20
+ - [IAM Permissions](#iam-permissions)
21
+ - [Documentation](#documentation)
22
+ - [Testing](#testing)
23
+ - [Project Structure](#project-structure)
24
+ - [Contributing](#contributing)
25
+ - [License](#license)
8
26
 
9
- > ⚠️ **Active Development**: This package is currently in active development. APIs may change between versions.
27
+ ---
10
28
 
11
- AWS DynamoDB implementation for LangGraph persistence in TypeScript. Provides checkpoint storage, memory store with semantic search, and chat message history.
29
+ <a id="features"></a>
12
30
 
13
31
  ## Features
14
32
 
15
- - 🔄 **Checkpoint Saver**: Persistent checkpoint storage for LangGraph state management
16
- - 💾 **Memory Store**: Long-term memory storage with namespace support and optional semantic search
17
- - 💬 **Chat Message History**: Persistent chat message storage with automatic title generation
18
- - ⚡ **Optimized Performance**: Efficient querying with composite keys and batch operations
19
- - 🔒 **Type-Safe**: Full TypeScript support with comprehensive type definitions
20
- - ♻️ **TTL Support**: Automatic data expiration with configurable Time-To-Live
21
- - 🔁 **Retry Logic**: Built-in retry mechanisms for transient DynamoDB errors
33
+ | Capability | Description |
34
+ |---|---|
35
+ | 🔄 **Checkpoint Saver** | Persistent checkpoint storage for LangGraph state management |
36
+ | 💾 **Memory Store** | Long-term memory with namespace support and optional semantic search |
37
+ | 💬 **Chat History** | Persistent chat message storage with auto-generated session titles |
38
+ | 🗜️ **Compression** | Optional gzip compression with smart thresholds (auto-detect on read) |
39
+ | ☁️ **S3 Offloading** | Transparent S3 offloading for payloads exceeding DynamoDB's 400 KB limit |
40
+ | ⚡ **Performance** | Composite keys, batch operations, exponential-backoff retry |
41
+ | ♻️ **TTL Support** | Automatic data expiration (days or seconds) |
42
+ | 🔒 **Type-Safe** | Full TypeScript with comprehensive type definitions |
43
+ | 🏭 **Factory** | One-line setup via `DynamoDBFactory.createAll()` |
44
+
45
+ <a id="architecture"></a>
46
+
47
+ ## Architecture
48
+
49
+ ```mermaid
50
+ flowchart LR
51
+ subgraph yourApp ["Your Application"]
52
+ graph_node["LangGraph Node"]
53
+ end
54
+
55
+ subgraph library ["@farukada/aws-langgraph-dynamodb-ts"]
56
+ saver["DynamoDBSaver"]
57
+ store["DynamoDBStore"]
58
+ history["DynamoDBChatMessageHistory"]
59
+ compressor["Compressor"]
60
+ offloader["S3Offloader"]
61
+ end
62
+
63
+ subgraph aws ["AWS"]
64
+ ddb[(DynamoDB)]
65
+ s3[(S3)]
66
+ bedrock["Bedrock Embeddings"]
67
+ end
68
+
69
+ graph_node --> saver
70
+ graph_node --> store
71
+ graph_node --> history
72
+ saver --> compressor
73
+ compressor --> offloader
74
+ offloader --> s3
75
+ saver --> ddb
76
+ store --> ddb
77
+ store -.-> bedrock
78
+ history --> ddb
79
+ ```
80
+
81
+ <a id="quick-start"></a>
22
82
 
23
- ## Installation
83
+ ## Quick Start
84
+
85
+ ### Install
24
86
 
25
87
  ```bash
26
88
  npm install @farukada/aws-langgraph-dynamodb-ts
27
89
  ```
28
90
 
29
- ### Peer Dependencies
91
+ #### Peer Dependencies
30
92
 
31
93
  ```bash
32
- npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb @langchain/core @langchain/langgraph @langchain/langgraph-checkpoint
94
+ npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb \
95
+ @langchain/core @langchain/langgraph @langchain/langgraph-checkpoint
33
96
 
34
- # Optional: For semantic search in Memory Store
97
+ # Optional — semantic search in Memory Store
35
98
  npm install @langchain/aws
99
+
100
+ # Optional — S3 offloading for large payloads
101
+ npm install @aws-sdk/client-s3
102
+ ```
103
+
104
+ ### Checkpoint Storage
105
+
106
+ ```typescript
107
+ import { StateGraph } from '@langchain/langgraph';
108
+ import { DynamoDBSaver } from '@farukada/aws-langgraph-dynamodb-ts';
109
+
110
+ const checkpointer = new DynamoDBSaver({
111
+ checkpointsTableName: 'langgraph-checkpoints',
112
+ writesTableName: 'langgraph-writes',
113
+ ttlDays: 30,
114
+ clientConfig: { region: 'us-east-1' },
115
+ });
116
+
117
+ const app = workflow.compile({ checkpointer });
118
+
119
+ // State is automatically persisted and can be resumed
120
+ await app.invoke(input, {
121
+ configurable: { thread_id: 'conversation-123' },
122
+ });
123
+ ```
124
+
125
+ ### Memory Store
126
+
127
+ ```typescript
128
+ import { DynamoDBStore } from '@farukada/aws-langgraph-dynamodb-ts';
129
+ import { BedrockEmbeddings } from '@langchain/aws';
130
+
131
+ const store = new DynamoDBStore({
132
+ memoryTableName: 'langgraph-memory',
133
+ embedding: new BedrockEmbeddings({
134
+ region: 'us-east-1',
135
+ model: 'amazon.titan-embed-text-v1',
136
+ }),
137
+ ttlDays: 90,
138
+ });
139
+
140
+ // Put memories
141
+ await store.batch([
142
+ {
143
+ namespace: ['user', 'preferences'],
144
+ key: 'theme',
145
+ value: { color: 'dark', fontSize: 14 },
146
+ },
147
+ ], { configurable: { user_id: 'user-123' } });
148
+
149
+ // Semantic search
150
+ const [results] = await store.batch([
151
+ { namespacePrefix: ['user'], query: 'color preferences', limit: 5 },
152
+ ], { configurable: { user_id: 'user-123' } });
36
153
  ```
37
154
 
155
+ ### Chat History
156
+
157
+ ```typescript
158
+ import { DynamoDBChatMessageHistory } from '@farukada/aws-langgraph-dynamodb-ts';
159
+ import { HumanMessage, AIMessage } from '@langchain/core/messages';
160
+
161
+ const history = new DynamoDBChatMessageHistory({
162
+ tableName: 'langgraph-chat-history',
163
+ ttlDays: 30,
164
+ });
165
+
166
+ await history.addMessages('user-123', 'session-456', [
167
+ new HumanMessage('Hello!'),
168
+ new AIMessage('Hi there!'),
169
+ ]);
170
+
171
+ const sessions = await history.listSessions('user-123');
172
+ ```
173
+
174
+ ### Factory (One-Liner)
175
+
176
+ ```typescript
177
+ import { DynamoDBFactory } from '@farukada/aws-langgraph-dynamodb-ts';
178
+
179
+ const { checkpointer, store, chatHistory, destroy } = DynamoDBFactory.createAll({
180
+ tablePrefix: 'my-app',
181
+ ttlDays: 30,
182
+ clientConfig: { region: 'us-east-1' },
183
+ });
184
+
185
+ // When done, release the shared DynamoDB client
186
+ destroy();
187
+ ```
188
+
189
+ <a id="infrastructure-setup"></a>
190
+
38
191
  ## Infrastructure Setup
39
192
 
40
- Create the required DynamoDB tables using AWS CDK or Terraform:
193
+ Create the required DynamoDB tables using **AWS CDK** or **Terraform**.
194
+
195
+ <details>
196
+ <summary><strong>AWS CDK (TypeScript)</strong></summary>
41
197
 
42
198
  ```typescript
43
- // AWS CDK (TypeScript)
44
199
  import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
45
200
 
46
- // For DynamoDBSaver - Checkpoints Table
201
+ // Checkpoints
47
202
  new dynamodb.Table(this, 'Checkpoints', {
48
203
  tableName: 'langgraph-checkpoints',
49
204
  partitionKey: { name: 'thread_id', type: dynamodb.AttributeType.STRING },
@@ -52,7 +207,7 @@ new dynamodb.Table(this, 'Checkpoints', {
52
207
  timeToLiveAttribute: 'ttl',
53
208
  });
54
209
 
55
- // For DynamoDBSaver - Writes Table
210
+ // Writes
56
211
  new dynamodb.Table(this, 'Writes', {
57
212
  tableName: 'langgraph-writes',
58
213
  partitionKey: { name: 'thread_id_checkpoint_id_checkpoint_ns', type: dynamodb.AttributeType.STRING },
@@ -61,7 +216,7 @@ new dynamodb.Table(this, 'Writes', {
61
216
  timeToLiveAttribute: 'ttl',
62
217
  });
63
218
 
64
- // For DynamoDBStore - Memory Table
219
+ // Memory
65
220
  new dynamodb.Table(this, 'Memory', {
66
221
  tableName: 'langgraph-memory',
67
222
  partitionKey: { name: 'user_id', type: dynamodb.AttributeType.STRING },
@@ -70,7 +225,7 @@ new dynamodb.Table(this, 'Memory', {
70
225
  timeToLiveAttribute: 'ttl',
71
226
  });
72
227
 
73
- // For DynamoDBChatMessageHistory - Chat History Table
228
+ // Chat History
74
229
  new dynamodb.Table(this, 'ChatHistory', {
75
230
  tableName: 'langgraph-chat-history',
76
231
  partitionKey: { name: 'userId', type: dynamodb.AttributeType.STRING },
@@ -80,222 +235,99 @@ new dynamodb.Table(this, 'ChatHistory', {
80
235
  });
81
236
  ```
82
237
 
83
- ```hcl
84
- # Terraform
238
+ </details>
239
+
240
+ <details>
241
+ <summary><strong>Terraform</strong></summary>
85
242
 
86
- # For DynamoDBSaver - Checkpoints Table
243
+ ```hcl
87
244
  resource "aws_dynamodb_table" "checkpoints" {
88
245
  name = "langgraph-checkpoints"
89
246
  billing_mode = "PAY_PER_REQUEST"
90
247
  hash_key = "thread_id"
91
248
  range_key = "checkpoint_id"
92
-
93
- attribute {
94
- name = "thread_id"
95
- type = "S"
96
- }
97
- attribute {
98
- name = "checkpoint_id"
99
- type = "S"
100
- }
101
-
102
- ttl {
103
- attribute_name = "ttl"
104
- enabled = true
105
- }
249
+ attribute { name = "thread_id" type = "S" }
250
+ attribute { name = "checkpoint_id" type = "S" }
251
+ ttl { attribute_name = "ttl" enabled = true }
106
252
  }
107
253
 
108
- # For DynamoDBSaver - Writes Table
109
254
  resource "aws_dynamodb_table" "writes" {
110
255
  name = "langgraph-writes"
111
256
  billing_mode = "PAY_PER_REQUEST"
112
257
  hash_key = "thread_id_checkpoint_id_checkpoint_ns"
113
258
  range_key = "task_id_idx"
114
-
115
- attribute {
116
- name = "thread_id_checkpoint_id_checkpoint_ns"
117
- type = "S"
118
- }
119
- attribute {
120
- name = "task_id_idx"
121
- type = "S"
122
- }
123
-
124
- ttl {
125
- attribute_name = "ttl"
126
- enabled = true
127
- }
259
+ attribute { name = "thread_id_checkpoint_id_checkpoint_ns" type = "S" }
260
+ attribute { name = "task_id_idx" type = "S" }
261
+ ttl { attribute_name = "ttl" enabled = true }
128
262
  }
129
263
 
130
- # For DynamoDBStore - Memory Table
131
264
  resource "aws_dynamodb_table" "memory" {
132
265
  name = "langgraph-memory"
133
266
  billing_mode = "PAY_PER_REQUEST"
134
267
  hash_key = "user_id"
135
268
  range_key = "namespace_key"
136
-
137
- attribute {
138
- name = "user_id"
139
- type = "S"
140
- }
141
- attribute {
142
- name = "namespace_key"
143
- type = "S"
144
- }
145
-
146
- ttl {
147
- attribute_name = "ttl"
148
- enabled = true
149
- }
269
+ attribute { name = "user_id" type = "S" }
270
+ attribute { name = "namespace_key" type = "S" }
271
+ ttl { attribute_name = "ttl" enabled = true }
150
272
  }
151
273
 
152
- # For DynamoDBChatMessageHistory - Chat History Table
153
274
  resource "aws_dynamodb_table" "chat_history" {
154
275
  name = "langgraph-chat-history"
155
276
  billing_mode = "PAY_PER_REQUEST"
156
277
  hash_key = "userId"
157
278
  range_key = "sessionId"
158
-
159
- attribute {
160
- name = "userId"
161
- type = "S"
162
- }
163
- attribute {
164
- name = "sessionId"
165
- type = "S"
166
- }
167
-
168
- ttl {
169
- attribute_name = "ttl"
170
- enabled = true
171
- }
279
+ attribute { name = "userId" type = "S" }
280
+ attribute { name = "sessionId" type = "S" }
281
+ ttl { attribute_name = "ttl" enabled = true }
172
282
  }
173
283
  ```
174
284
 
175
- ## Quick Start
285
+ </details>
176
286
 
177
- ### DynamoDBSaver (Checkpoint Storage)
287
+ <a id="advanced-features"></a>
178
288
 
179
- ```typescript
180
- import { StateGraph } from '@langchain/langgraph';
181
- import { DynamoDBSaver } from '@farukada/aws-langgraph-dynamodb-ts';
289
+ ## Advanced Features
290
+
291
+ ### Gzip Compression
182
292
 
293
+ Reduce DynamoDB item sizes and costs by enabling transparent gzip compression:
294
+
295
+ ```typescript
183
296
  const checkpointer = new DynamoDBSaver({
184
297
  checkpointsTableName: 'langgraph-checkpoints',
185
298
  writesTableName: 'langgraph-writes',
186
- ttlDays: 30, // Optional
187
- clientConfig: { region: 'us-east-1' }, // Optional
188
- });
189
-
190
- // Use with LangGraph
191
- const workflow = new StateGraph({ /* ... */ })
192
- .addNode('step1', async (state) => { /* ... */ })
193
- .addEdge('__start__', 'step1');
194
-
195
- const app = workflow.compile({ checkpointer });
196
-
197
- // State is automatically persisted and can be resumed
198
- await app.invoke(input, {
199
- configurable: { thread_id: 'conversation-123' }
299
+ compression: {
300
+ enabled: true,
301
+ minSizeBytes: 1024, // Only compress payloads ≥ 1 KB
302
+ },
200
303
  });
201
304
  ```
202
305
 
203
- ### DynamoDBStore (Memory Storage)
204
-
205
- ```typescript
206
- import { DynamoDBStore } from '@farukada/aws-langgraph-dynamodb-ts';
207
- import { BedrockEmbeddings } from '@langchain/aws';
306
+ ### S3 Offloading
208
307
 
209
- // Without semantic search
210
- const store = new DynamoDBStore({
211
- memoryTableName: 'langgraph-memory',
212
- ttlDays: 90, // Optional
213
- });
214
-
215
- // With semantic search
216
- const storeWithEmbeddings = new DynamoDBStore({
217
- memoryTableName: 'langgraph-memory',
218
- embedding: new BedrockEmbeddings({
219
- region: 'us-east-1',
220
- model: 'amazon.titan-embed-text-v1',
221
- }),
222
- });
223
-
224
- // Store and search memories
225
- await store.batch([
226
- {
227
- namespace: ['user', 'preferences'],
228
- key: 'theme',
229
- value: { color: 'dark', fontSize: 14 },
230
- }
231
- ], { configurable: { user_id: 'user-123' } });
232
-
233
- // Search with filters
234
- const [results] = await store.batch([
235
- {
236
- namespacePrefix: ['user'],
237
- filter: { 'value.color': { $eq: 'dark' } },
238
- limit: 10,
239
- }
240
- ], { configurable: { user_id: 'user-123' } });
241
-
242
- // Use with LangGraph
243
- const app = workflow.compile({ checkpointer, store });
244
- ```
245
-
246
- ### DynamoDBChatMessageHistory (Chat History)
308
+ Automatically offload payloads exceeding DynamoDB's 400 KB item limit to S3:
247
309
 
248
310
  ```typescript
249
- import { DynamoDBChatMessageHistory } from '@farukada/aws-langgraph-dynamodb-ts';
250
- import { HumanMessage, AIMessage } from '@langchain/core/messages';
251
-
252
- const history = new DynamoDBChatMessageHistory({
253
- tableName: 'langgraph-chat-history',
254
- ttlDays: 30, // Optional
255
- clientConfig: { region: 'us-east-1' }, // Optional
311
+ const checkpointer = new DynamoDBSaver({
312
+ checkpointsTableName: 'langgraph-checkpoints',
313
+ writesTableName: 'langgraph-writes',
314
+ s3OffloadConfig: {
315
+ bucketName: 'my-checkpoints-bucket',
316
+ keyPrefix: 'langgraph/', // default: 'langgraph-checkpoints/'
317
+ thresholdBytes: 350 * 1024, // default: 350 KB
318
+ serverSideEncryption: 'aws:kms', // optional: 'AES256' or 'aws:kms'
319
+ sseKmsKeyId: 'alias/my-key', // optional: KMS key ID/ARN
320
+ clientConfig: { region: 'us-east-1' },
321
+ },
256
322
  });
257
-
258
- // Add message with session title
259
- await history.addMessage('user-123', 'session-456', new HumanMessage('Hello!'), 'Greeting Session');
260
-
261
- // Add messages (title auto-generated from first message)
262
- await history.addMessages('user-123', 'session-457', [
263
- new HumanMessage('Hello!'),
264
- new AIMessage('Hi there!'),
265
- ]);
266
-
267
- // Get all messages for a session
268
- const messages = await history.getMessages('user-123', 'session-456');
269
-
270
- // List all sessions for a user
271
- const sessions = await history.listSessions('user-123');
272
- // Returns: [{ sessionId, title, createdAt, updatedAt, messageCount }, ...]
273
-
274
- // Clear a session
275
- await history.clear('user-123', 'session-456');
276
323
  ```
277
324
 
278
- ## Documentation
279
-
280
- Comprehensive guides for each module:
281
-
282
- - **[Checkpointer Guide](./docs/checkpointer.md)** - Complete guide to DynamoDBSaver with checkpoint management, workflow persistence, and recovery strategies
283
- - **[Store Guide](./docs/store.md)** - Detailed documentation for DynamoDBStore covering memory storage, semantic search, filtering, and namespace organization
284
- - **[History Guide](./docs/history.md)** - Full guide to DynamoDBChatMessageHistory for chat message storage, session management, and conversation tracking
285
-
286
- ## API Reference
287
-
288
- For detailed API documentation, see the [TypeDoc documentation](./docs/README.md):
289
-
290
- - **Classes**: [DynamoDBSaver](./docs/classes/DynamoDBSaver.md), [DynamoDBStore](./docs/classes/DynamoDBStore.md), [DynamoDBChatMessageHistory](./docs/classes/DynamoDBChatMessageHistory.md)
291
- - **Interfaces**: [DynamoDBSaverOptions](./docs/interfaces/DynamoDBSaverOptions.md), [DynamoDBStoreOptions](./docs/interfaces/DynamoDBStoreOptions.md), [DynamoDBChatMessageHistoryOptions](./docs/interfaces/DynamoDBChatMessageHistoryOptions.md), [SessionMetadata](./docs/interfaces/SessionMetadata.md)
325
+ When TTL and S3 offloading are both enabled, the library automatically configures an S3 lifecycle expiration rule on the bucket (scoped to the key prefix). This requires `s3:GetBucketLifecycleConfiguration` and `s3:PutBucketLifecycleConfiguration` permissions on the bucket. If these permissions are unavailable, a warning is logged but the saver continues to function normally.
292
326
 
293
- ## Advanced Features
294
-
295
- ### Memory Store Filters
327
+ ### Store Filters
296
328
 
297
329
  ```typescript
298
- // JSONPath-based filtering with operators: $eq, $ne, $gt, $gte, $lt, $lte
330
+ // JSONPath-based filtering: $eq, $ne, $gt, $gte, $lt, $lte
299
331
  const [results] = await store.batch([
300
332
  {
301
333
  namespacePrefix: ['products'],
@@ -304,51 +336,84 @@ const [results] = await store.batch([
304
336
  'value.category': { $eq: 'electronics' },
305
337
  },
306
338
  limit: 10,
307
- }
339
+ },
308
340
  ], { configurable: { user_id: 'user-123' } });
309
341
  ```
310
342
 
311
- ### Semantic Search
312
-
313
- ```typescript
314
- // Requires embedding configuration
315
- const [results] = await store.batch([
316
- {
317
- namespace: ['documents'],
318
- key: 'doc1',
319
- value: { content: 'AI is transforming the world...' },
320
- index: ['$.content'], // Fields to embed
321
- }
322
- ], config);
323
-
324
- // Query semantically
325
- const [semanticResults] = await store.batch([
326
- {
327
- namespacePrefix: ['documents'],
328
- query: 'machine learning basics', // Semantic query
329
- limit: 5,
330
- }
331
- ], config);
332
- ```
333
-
334
343
  ### Namespace Organization
335
344
 
336
345
  ```typescript
337
- // Hierarchical organization
346
+ // Hierarchical namespace patterns
338
347
  ['user', userId, 'preferences']
339
348
  ['user', userId, 'conversations', threadId]
340
349
  ['documents', 'category', 'subcategory']
341
350
  ```
342
351
 
352
+ <a id="configuration-reference"></a>
353
+
354
+ ## Configuration Reference
355
+
356
+ ### DynamoDBSaver
357
+
358
+ | Option | Type | Default | Description |
359
+ |---|---|---|---|
360
+ | `checkpointsTableName` | `string` | — | Checkpoints table name (**required**) |
361
+ | `writesTableName` | `string` | — | Writes table name (**required**) |
362
+ | `ttlDays` | `number` | — | TTL in days |
363
+ | `ttlSeconds` | `number` | — | TTL in seconds (overrides `ttlDays`) |
364
+ | `compression` | `object` | — | `{ enabled, minSizeBytes?, level? }` |
365
+ | `s3OffloadConfig` | `object` | — | `{ bucketName, keyPrefix?, thresholdBytes?, serverSideEncryption?, sseKmsKeyId?, clientConfig? }` |
366
+ | `clientConfig` | `object` | — | AWS SDK `DynamoDBClientConfig` |
367
+ | `client` | `DynamoDBDocument` | — | Pre-built client (takes precedence over `clientConfig`) |
368
+
369
+ ### DynamoDBStore
370
+
371
+ | Option | Type | Default | Description |
372
+ |---|---|---|---|
373
+ | `memoryTableName` | `string` | — | Memory table name (**required**) |
374
+ | `embedding` | `EmbeddingsInterface` | — | Any LangChain embeddings provider for semantic search |
375
+ | `ttlDays` | `number` | — | TTL in days |
376
+ | `clientConfig` | `object` | — | AWS SDK `DynamoDBClientConfig` |
377
+ | `client` | `DynamoDBDocument` | — | Pre-built client (takes precedence over `clientConfig`) |
378
+
379
+ ### DynamoDBChatMessageHistory
380
+
381
+ | Option | Type | Default | Description |
382
+ |---|---|---|---|
383
+ | `tableName` | `string` | — | Chat history table name (**required**) |
384
+ | `ttlDays` | `number` | — | TTL in days |
385
+ | `clientConfig` | `object` | — | AWS SDK `DynamoDBClientConfig` |
386
+ | `client` | `DynamoDBDocument` | — | Pre-built client (takes precedence over `clientConfig`) |
387
+
388
+ <a id="iam-permissions"></a>
389
+
343
390
  ## IAM Permissions
344
391
 
345
- Required permissions for your AWS IAM role:
392
+ <details>
393
+ <summary><strong>Minimum IAM Policy</strong></summary>
346
394
 
347
395
  ```json
348
396
  {
349
397
  "Version": "2012-10-17",
350
398
  "Statement": [
351
399
  {
400
+ "Sid": "CheckpointerAccess",
401
+ "Effect": "Allow",
402
+ "Action": [
403
+ "dynamodb:GetItem",
404
+ "dynamodb:PutItem",
405
+ "dynamodb:Query",
406
+ "dynamodb:BatchGetItem",
407
+ "dynamodb:BatchWriteItem",
408
+ "dynamodb:TransactWriteItems"
409
+ ],
410
+ "Resource": [
411
+ "arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-checkpoints",
412
+ "arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-writes"
413
+ ]
414
+ },
415
+ {
416
+ "Sid": "StoreAndHistoryAccess",
352
417
  "Effect": "Allow",
353
418
  "Action": [
354
419
  "dynamodb:GetItem",
@@ -359,40 +424,90 @@ Required permissions for your AWS IAM role:
359
424
  "dynamodb:BatchWriteItem"
360
425
  ],
361
426
  "Resource": [
362
- "arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-checkpoints",
363
- "arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-writes",
364
427
  "arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-memory",
365
428
  "arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-chat-history"
366
429
  ]
367
430
  },
368
431
  {
432
+ "Sid": "OptionalSemanticSearch",
369
433
  "Effect": "Allow",
370
434
  "Action": ["bedrock:InvokeModel"],
371
435
  "Resource": "arn:aws:bedrock:REGION::foundation-model/amazon.titan-embed-text-v1"
436
+ },
437
+ {
438
+ "Sid": "OptionalS3Offloading",
439
+ "Effect": "Allow",
440
+ "Action": [
441
+ "s3:PutObject",
442
+ "s3:GetObject",
443
+ "s3:DeleteObject",
444
+ "s3:DeleteObjects"
445
+ ],
446
+ "Resource": "arn:aws:s3:::YOUR-BUCKET/langgraph-checkpoints/*"
447
+ },
448
+ {
449
+ "Sid": "OptionalS3LifecycleManagement",
450
+ "Effect": "Allow",
451
+ "Action": [
452
+ "s3:GetBucketLifecycleConfiguration",
453
+ "s3:PutBucketLifecycleConfiguration"
454
+ ],
455
+ "Resource": "arn:aws:s3:::YOUR-BUCKET"
372
456
  }
373
457
  ]
374
458
  }
375
459
  ```
376
460
 
377
- ## Testing
461
+ </details>
378
462
 
379
- ```bash
380
- # Run tests
381
- npm test
463
+ <a id="documentation"></a>
464
+
465
+ ## Documentation
466
+
467
+ - **[Checkpointer Guide](./src/checkpointer/checkpointer.md)** — Checkpoint management, workflow persistence, recovery
468
+ - **[Store Guide](./src/store/store.md)** — Memory storage, semantic search, filtering, namespaces
469
+ - **[History Guide](./src/history/history.md)** — Chat message storage, session management
470
+ - **[API Reference (TypeDoc)](./docs/README.md)** — Full class & interface documentation
471
+
472
+ <a id="testing"></a>
382
473
 
383
- # Run tests with coverage
384
- npm test -- --coverage
474
+ ## Testing
385
475
 
386
- # Type checking
387
- npm run typecheck
476
+ ```bash
477
+ npm test # Run all tests
478
+ npm test -- --coverage # With coverage
479
+ npm run build # Type-check + compile
480
+ npm run lint # ESLint
481
+ ```
388
482
 
389
- # Lint
390
- npm run lint
483
+ <a id="project-structure"></a>
484
+
485
+ ## Project Structure
486
+
487
+ ```text
488
+ src/
489
+ ├── checkpointer/ # DynamoDBSaver — checkpoint persistence
490
+ │ ├── actions/ # put, putWrites, getTuple, deleteThread, writer
491
+ │ ├── types/ # TypeScript interfaces & constants
492
+ │ └── utils/ # Deserialization, validation
493
+ ├── store/ # DynamoDBStore — long-term memory
494
+ │ ├── actions/ # get, put, search, listNamespaces
495
+ │ ├── types/ # TypeScript interfaces
496
+ │ └── utils/ # Validation, filtering
497
+ ├── history/ # DynamoDBChatMessageHistory — chat sessions
498
+ │ ├── actions/ # getMessages, addMessage(s), clear, listSessions
499
+ │ ├── types/ # TypeScript interfaces
500
+ │ └── utils/ # Validation, title generation
501
+ ├── shared/ # Cross-cutting utilities
502
+ │ └── utils/ # Compressor, S3Offloader, retry, TTL, batch-write, logger
503
+ └── factory.ts # DynamoDBFactory one-liner setup
391
504
  ```
392
505
 
506
+ <a id="contributing"></a>
507
+
393
508
  ## Contributing
394
509
 
395
- Contributions are welcome! Please:
510
+ Contributions welcome! Please:
396
511
 
397
512
  1. Check existing issues or create a new one
398
513
  2. Fork the repository
@@ -400,20 +515,16 @@ Contributions are welcome! Please:
400
515
  4. Add tests for your changes
401
516
  5. Submit a pull request
402
517
 
518
+ <a id="license"></a>
519
+
403
520
  ## License
404
521
 
405
522
  MIT © [FarukAda](https://github.com/farukada)
406
523
 
407
- ## Links
408
-
409
- - [GitHub Repository](https://github.com/farukada/aws-langgraph-dynamodb-ts)
410
- - [npm Package](https://www.npmjs.com/package/@farukada/aws-langgraph-dynamodb-ts)
411
- - [Issue Tracker](https://github.com/farukada/aws-langgraph-dynamodb-ts/issues)
412
- - [LangGraph Documentation](https://langchain-ai.github.io/langgraphjs/)
413
- - [AWS DynamoDB Documentation](https://docs.aws.amazon.com/dynamodb/)
414
-
415
524
  ---
416
525
 
417
- Built with [LangGraph](https://github.com/langchain-ai/langgraphjs), [AWS SDK](https://aws.amazon.com/sdk-for-javascript/), and [LangChain](https://github.com/langchain-ai/langchainjs)
418
-
419
-
526
+ <p align="center">
527
+ Built with <a href="https://langchain-ai.github.io/langgraphjs/">LangGraph</a> · <a href="https://aws.amazon.com/sdk-for-javascript/">AWS SDK v3</a> · <a href="https://github.com/langchain-ai/langchainjs">LangChain</a>
528
+ <br/>
529
+ <a href="https://www.npmjs.com/package/@farukada/aws-langgraph-dynamodb-ts">npm</a> · <a href="https://github.com/farukada/aws-langgraph-dynamodb-ts">GitHub</a> · <a href="https://github.com/farukada/aws-langgraph-dynamodb-ts/issues">Issues</a>
530
+ </p>