@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.
- package/README.md +353 -242
- package/dist/checkpointer/actions/delete-thread.d.ts +3 -3
- package/dist/checkpointer/actions/delete-thread.d.ts.map +1 -1
- package/dist/checkpointer/actions/delete-thread.js +95 -52
- package/dist/checkpointer/actions/delete-thread.js.map +1 -1
- package/dist/checkpointer/actions/get-tuple.d.ts +8 -1
- package/dist/checkpointer/actions/get-tuple.d.ts.map +1 -1
- package/dist/checkpointer/actions/get-tuple.js +72 -20
- package/dist/checkpointer/actions/get-tuple.js.map +1 -1
- package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
- package/dist/checkpointer/actions/put-writes.js +27 -22
- package/dist/checkpointer/actions/put-writes.js.map +1 -1
- package/dist/checkpointer/actions/put.d.ts +6 -2
- package/dist/checkpointer/actions/put.d.ts.map +1 -1
- package/dist/checkpointer/actions/put.js +80 -19
- package/dist/checkpointer/actions/put.js.map +1 -1
- package/dist/checkpointer/index.d.ts +43 -2
- package/dist/checkpointer/index.d.ts.map +1 -1
- package/dist/checkpointer/index.js +226 -11
- package/dist/checkpointer/index.js.map +1 -1
- package/dist/checkpointer/types/index.d.ts +66 -4
- package/dist/checkpointer/types/index.d.ts.map +1 -1
- package/dist/checkpointer/types/index.js +10 -0
- package/dist/checkpointer/types/index.js.map +1 -1
- package/dist/checkpointer/utils/deserialization.d.ts +8 -3
- package/dist/checkpointer/utils/deserialization.d.ts.map +1 -1
- package/dist/checkpointer/utils/deserialization.js +23 -5
- package/dist/checkpointer/utils/deserialization.js.map +1 -1
- package/dist/checkpointer/utils/validation.d.ts +1 -1
- package/dist/checkpointer/utils/validation.js +1 -1
- package/dist/checkpointer/utils/validation.js.map +1 -1
- package/dist/factory.d.ts +8 -6
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +16 -4
- package/dist/factory.js.map +1 -1
- package/dist/history/actions/add-message.d.ts +2 -1
- package/dist/history/actions/add-message.d.ts.map +1 -1
- package/dist/history/actions/add-message.js +22 -18
- package/dist/history/actions/add-message.js.map +1 -1
- package/dist/history/actions/add-messages.d.ts +3 -2
- package/dist/history/actions/add-messages.d.ts.map +1 -1
- package/dist/history/actions/add-messages.js +30 -20
- package/dist/history/actions/add-messages.js.map +1 -1
- package/dist/history/actions/clear.d.ts +4 -3
- package/dist/history/actions/clear.d.ts.map +1 -1
- package/dist/history/actions/clear.js +47 -12
- 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 +41 -15
- package/dist/history/actions/get-messages.js.map +1 -1
- package/dist/history/actions/list-sessions.d.ts +11 -0
- package/dist/history/actions/list-sessions.d.ts.map +1 -1
- package/dist/history/actions/list-sessions.js +23 -3
- package/dist/history/actions/list-sessions.js.map +1 -1
- package/dist/history/index.d.ts +8 -0
- package/dist/history/index.d.ts.map +1 -1
- package/dist/history/index.js +22 -2
- package/dist/history/index.js.map +1 -1
- package/dist/history/types/index.d.ts +27 -5
- package/dist/history/types/index.d.ts.map +1 -1
- package/dist/history/types/index.js +1 -0
- package/dist/history/types/index.js.map +1 -1
- package/dist/history/utils/title-generator.js.map +1 -1
- package/dist/history/utils/update-expression-builder.d.ts +45 -21
- package/dist/history/utils/update-expression-builder.d.ts.map +1 -1
- package/dist/history/utils/update-expression-builder.js +105 -38
- package/dist/history/utils/update-expression-builder.js.map +1 -1
- package/dist/history/utils/validation.d.ts +8 -0
- package/dist/history/utils/validation.d.ts.map +1 -1
- package/dist/history/utils/validation.js +22 -0
- package/dist/history/utils/validation.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/shared/utils/batch-write.d.ts +36 -0
- package/dist/shared/utils/batch-write.d.ts.map +1 -0
- package/dist/shared/utils/batch-write.js +62 -0
- package/dist/shared/utils/batch-write.js.map +1 -0
- package/dist/shared/utils/compressor.d.ts +48 -0
- package/dist/shared/utils/compressor.d.ts.map +1 -0
- package/dist/shared/utils/compressor.js +68 -0
- package/dist/shared/utils/compressor.js.map +1 -0
- package/dist/shared/utils/constants.d.ts +2 -0
- package/dist/shared/utils/constants.d.ts.map +1 -1
- package/dist/shared/utils/constants.js +3 -7
- package/dist/shared/utils/constants.js.map +1 -1
- package/dist/shared/utils/index.d.ts +4 -0
- package/dist/shared/utils/index.d.ts.map +1 -1
- package/dist/shared/utils/index.js +4 -0
- package/dist/shared/utils/index.js.map +1 -1
- package/dist/shared/utils/logger.d.ts +48 -0
- package/dist/shared/utils/logger.d.ts.map +1 -0
- package/dist/shared/utils/logger.js +62 -0
- package/dist/shared/utils/logger.js.map +1 -0
- package/dist/shared/utils/retry.d.ts +1 -1
- package/dist/shared/utils/retry.d.ts.map +1 -1
- package/dist/shared/utils/retry.js +13 -6
- package/dist/shared/utils/retry.js.map +1 -1
- package/dist/shared/utils/s3-offloader.d.ts +146 -0
- package/dist/shared/utils/s3-offloader.d.ts.map +1 -0
- package/dist/shared/utils/s3-offloader.js +271 -0
- package/dist/shared/utils/s3-offloader.js.map +1 -0
- package/dist/shared/utils/ttl.d.ts +8 -1
- package/dist/shared/utils/ttl.d.ts.map +1 -1
- package/dist/shared/utils/ttl.js +12 -8
- package/dist/shared/utils/ttl.js.map +1 -1
- package/dist/store/actions/get-operation.js +2 -2
- package/dist/store/actions/get-operation.js.map +1 -1
- package/dist/store/actions/put-operation.d.ts.map +1 -1
- package/dist/store/actions/put-operation.js +2 -1
- package/dist/store/actions/put-operation.js.map +1 -1
- package/dist/store/actions/search-operation.d.ts +6 -0
- package/dist/store/actions/search-operation.d.ts.map +1 -1
- package/dist/store/actions/search-operation.js +41 -21
- package/dist/store/actions/search-operation.js.map +1 -1
- package/dist/store/index.d.ts +10 -2
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +28 -5
- package/dist/store/index.js.map +1 -1
- package/dist/store/types/index.d.ts +7 -5
- package/dist/store/types/index.d.ts.map +1 -1
- package/dist/store/utils/index.d.ts +0 -1
- package/dist/store/utils/index.d.ts.map +1 -1
- package/dist/store/utils/index.js +0 -1
- package/dist/store/utils/index.js.map +1 -1
- package/dist/store/utils/validation.js +1 -1
- package/dist/store/utils/validation.js.map +1 -1
- package/package.json +41 -24
- package/dist/store/utils/result.d.ts +0 -42
- package/dist/store/utils/result.d.ts.map +0 -1
- package/dist/store/utils/result.js +0 -51
- 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
|
-
[](https://www.npmjs.com/package/@farukada/aws-langgraph-dynamodb-ts)
|
|
4
|
+
[](https://github.com/sponsors/FarukAda)
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
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
|
-
|
|
27
|
+
---
|
|
10
28
|
|
|
11
|
-
|
|
29
|
+
<a id="features"></a>
|
|
12
30
|
|
|
13
31
|
## Features
|
|
14
32
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
91
|
+
#### Peer Dependencies
|
|
30
92
|
|
|
31
93
|
```bash
|
|
32
|
-
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
|
|
94
|
+
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb \
|
|
95
|
+
@langchain/core @langchain/langgraph @langchain/langgraph-checkpoint
|
|
33
96
|
|
|
34
|
-
# Optional
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
84
|
-
|
|
238
|
+
</details>
|
|
239
|
+
|
|
240
|
+
<details>
|
|
241
|
+
<summary><strong>Terraform</strong></summary>
|
|
85
242
|
|
|
86
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
285
|
+
</details>
|
|
176
286
|
|
|
177
|
-
|
|
287
|
+
<a id="advanced-features"></a>
|
|
178
288
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
187
|
-
|
|
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
|
-
###
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
import { DynamoDBStore } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
207
|
-
import { BedrockEmbeddings } from '@langchain/aws';
|
|
306
|
+
### S3 Offloading
|
|
208
307
|
|
|
209
|
-
|
|
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
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
### Memory Store Filters
|
|
327
|
+
### Store Filters
|
|
296
328
|
|
|
297
329
|
```typescript
|
|
298
|
-
// JSONPath-based filtering
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
461
|
+
</details>
|
|
378
462
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
|
|
384
|
-
npm test -- --coverage
|
|
474
|
+
## Testing
|
|
385
475
|
|
|
386
|
-
|
|
387
|
-
npm
|
|
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
|
-
|
|
390
|
-
|
|
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
|
|
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
|
-
|
|
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>
|