@farukada/aws-langgraph-dynamodb-ts 0.0.7 → 0.0.9
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 +195 -744
- package/dist/checkpointer/actions/get-tuple.d.ts.map +1 -1
- package/dist/checkpointer/actions/get-tuple.js.map +1 -1
- package/dist/checkpointer/actions/put.d.ts.map +1 -1
- package/dist/checkpointer/actions/put.js.map +1 -1
- package/dist/checkpointer/index.d.ts +3 -3
- package/dist/checkpointer/index.d.ts.map +1 -1
- package/dist/checkpointer/index.js +4 -4
- package/dist/checkpointer/index.js.map +1 -1
- package/dist/checkpointer/utils/validation.d.ts.map +1 -1
- package/dist/checkpointer/utils/validation.js +1 -0
- package/dist/checkpointer/utils/validation.js.map +1 -1
- package/dist/history/actions/add-message.d.ts +13 -0
- package/dist/history/actions/add-message.d.ts.map +1 -0
- package/dist/history/actions/add-message.js +64 -0
- package/dist/history/actions/add-message.js.map +1 -0
- package/dist/history/actions/add-messages.d.ts +14 -0
- package/dist/history/actions/add-messages.d.ts.map +1 -0
- package/dist/history/actions/add-messages.js +65 -0
- package/dist/history/actions/add-messages.js.map +1 -0
- package/dist/history/actions/clear.d.ts +13 -0
- package/dist/history/actions/clear.d.ts.map +1 -0
- package/dist/history/actions/clear.js +32 -0
- package/dist/history/actions/clear.js.map +1 -0
- package/dist/history/actions/get-messages.d.ts +15 -0
- package/dist/history/actions/get-messages.d.ts.map +1 -0
- package/dist/history/actions/get-messages.js +40 -0
- package/dist/history/actions/get-messages.js.map +1 -0
- package/dist/history/actions/index.d.ts +9 -0
- package/dist/history/actions/index.d.ts.map +1 -0
- package/dist/history/actions/index.js +17 -0
- package/dist/history/actions/index.js.map +1 -0
- package/dist/history/actions/list-sessions.d.ts +14 -0
- package/dist/history/actions/list-sessions.d.ts.map +1 -0
- package/dist/history/actions/list-sessions.js +56 -0
- package/dist/history/actions/list-sessions.js.map +1 -0
- package/dist/history/index.d.ts +74 -0
- package/dist/history/index.d.ts.map +1 -0
- package/dist/history/index.js +125 -0
- package/dist/history/index.js.map +1 -0
- package/dist/history/types/index.d.ts +131 -0
- package/dist/history/types/index.d.ts.map +1 -0
- package/dist/history/types/index.js +6 -0
- package/dist/history/types/index.js.map +1 -0
- package/dist/history/utils/index.d.ts +7 -0
- package/dist/history/utils/index.d.ts.map +1 -0
- package/dist/history/utils/index.js +25 -0
- package/dist/history/utils/index.js.map +1 -0
- package/dist/history/utils/title-generator.d.ts +12 -0
- package/dist/history/utils/title-generator.d.ts.map +1 -0
- package/dist/history/utils/title-generator.js +52 -0
- package/dist/history/utils/title-generator.js.map +1 -0
- package/dist/history/utils/validation.d.ts +60 -0
- package/dist/history/utils/validation.d.ts.map +1 -0
- package/dist/history/utils/validation.js +171 -0
- package/dist/history/utils/validation.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/shared/utils/retry.d.ts.map +1 -1
- package/dist/shared/utils/retry.js +2 -0
- package/dist/shared/utils/retry.js.map +1 -1
- package/dist/store/actions/get-operation.d.ts.map +1 -1
- package/dist/store/actions/get-operation.js.map +1 -1
- package/dist/store/actions/list-namespaces-operation.d.ts.map +1 -1
- package/dist/store/actions/list-namespaces-operation.js.map +1 -1
- package/dist/store/actions/put-operation.d.ts.map +1 -1
- package/dist/store/actions/put-operation.js.map +1 -1
- package/dist/store/actions/search-operation.d.ts.map +1 -1
- package/dist/store/actions/search-operation.js.map +1 -1
- package/dist/store/index.d.ts +3 -3
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +4 -4
- package/dist/store/index.js.map +1 -1
- package/dist/store/utils/validation.d.ts +1 -1
- package/dist/store/utils/validation.d.ts.map +1 -1
- package/dist/store/utils/validation.js +2 -1
- package/dist/store/utils/validation.js.map +1 -1
- package/package.json +10 -8
package/README.md
CHANGED
|
@@ -8,18 +8,17 @@
|
|
|
8
8
|
|
|
9
9
|
> ⚠️ **Active Development**: This package is currently in active development. APIs may change between versions.
|
|
10
10
|
|
|
11
|
-
AWS DynamoDB implementation for LangGraph
|
|
11
|
+
AWS DynamoDB implementation for LangGraph persistence in TypeScript. Provides checkpoint storage, memory store with semantic search, and chat message history.
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
15
|
- 🔄 **Checkpoint Saver**: Persistent checkpoint storage for LangGraph state management
|
|
16
|
-
- 💾 **Memory Store**: Long-term memory storage with namespace support
|
|
17
|
-
-
|
|
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
18
|
- ⚡ **Optimized Performance**: Efficient querying with composite keys and batch operations
|
|
19
19
|
- 🔒 **Type-Safe**: Full TypeScript support with comprehensive type definitions
|
|
20
20
|
- ♻️ **TTL Support**: Automatic data expiration with configurable Time-To-Live
|
|
21
21
|
- 🔁 **Retry Logic**: Built-in retry mechanisms for transient DynamoDB errors
|
|
22
|
-
- 🧪 **Well-Tested**: Comprehensive test coverage with Jest
|
|
23
22
|
|
|
24
23
|
## Installation
|
|
25
24
|
|
|
@@ -29,846 +28,313 @@ npm install @farukada/aws-langgraph-dynamodb-ts
|
|
|
29
28
|
|
|
30
29
|
### Peer Dependencies
|
|
31
30
|
|
|
32
|
-
Make sure you have the required peer dependencies installed:
|
|
33
|
-
|
|
34
31
|
```bash
|
|
35
32
|
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb @langchain/core @langchain/langgraph @langchain/langgraph-checkpoint
|
|
36
|
-
```
|
|
37
33
|
|
|
38
|
-
For semantic search
|
|
39
|
-
|
|
40
|
-
```bash
|
|
34
|
+
# Optional: For semantic search in Memory Store
|
|
41
35
|
npm install @langchain/aws
|
|
42
36
|
```
|
|
43
37
|
|
|
44
38
|
## Infrastructure Setup
|
|
45
39
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
### AWS CDK (TypeScript)
|
|
40
|
+
Create the required DynamoDB tables using AWS CDK or Terraform:
|
|
49
41
|
|
|
50
42
|
```typescript
|
|
51
|
-
|
|
43
|
+
// AWS CDK (TypeScript)
|
|
52
44
|
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
|
|
53
|
-
import { Construct } from 'constructs';
|
|
54
|
-
|
|
55
|
-
export class LangGraphDynamoDBStack extends cdk.Stack {
|
|
56
|
-
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
|
|
57
|
-
super(scope, id, props);
|
|
58
|
-
|
|
59
|
-
// Checkpoints Table
|
|
60
|
-
const checkpointsTable = new dynamodb.Table(this, 'LangGraphCheckpoints', {
|
|
61
|
-
tableName: 'langgraph-checkpoints',
|
|
62
|
-
partitionKey: {
|
|
63
|
-
name: 'thread_id',
|
|
64
|
-
type: dynamodb.AttributeType.STRING,
|
|
65
|
-
},
|
|
66
|
-
sortKey: {
|
|
67
|
-
name: 'checkpoint_id',
|
|
68
|
-
type: dynamodb.AttributeType.STRING,
|
|
69
|
-
},
|
|
70
|
-
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
|
|
71
|
-
timeToLiveAttribute: 'ttl',
|
|
72
|
-
removalPolicy: cdk.RemovalPolicy.DESTROY, // Change for production
|
|
73
|
-
pointInTimeRecovery: true,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// Writes Table
|
|
77
|
-
const writesTable = new dynamodb.Table(this, 'LangGraphWrites', {
|
|
78
|
-
tableName: 'langgraph-writes',
|
|
79
|
-
partitionKey: {
|
|
80
|
-
name: 'thread_id_checkpoint_id_checkpoint_ns',
|
|
81
|
-
type: dynamodb.AttributeType.STRING,
|
|
82
|
-
},
|
|
83
|
-
sortKey: {
|
|
84
|
-
name: 'task_id_idx',
|
|
85
|
-
type: dynamodb.AttributeType.STRING,
|
|
86
|
-
},
|
|
87
|
-
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
|
|
88
|
-
timeToLiveAttribute: 'ttl',
|
|
89
|
-
removalPolicy: cdk.RemovalPolicy.DESTROY, // Change for production
|
|
90
|
-
pointInTimeRecovery: true,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// Memory Store Table
|
|
94
|
-
const memoryTable = new dynamodb.Table(this, 'LangGraphMemory', {
|
|
95
|
-
tableName: 'langgraph-memory',
|
|
96
|
-
partitionKey: {
|
|
97
|
-
name: 'user_id',
|
|
98
|
-
type: dynamodb.AttributeType.STRING,
|
|
99
|
-
},
|
|
100
|
-
sortKey: {
|
|
101
|
-
name: 'namespace_key',
|
|
102
|
-
type: dynamodb.AttributeType.STRING,
|
|
103
|
-
},
|
|
104
|
-
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
|
|
105
|
-
timeToLiveAttribute: 'ttl',
|
|
106
|
-
removalPolicy: cdk.RemovalPolicy.DESTROY, // Change for production
|
|
107
|
-
pointInTimeRecovery: true,
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// Output table names
|
|
111
|
-
new cdk.CfnOutput(this, 'CheckpointsTableName', {
|
|
112
|
-
value: checkpointsTable.tableName,
|
|
113
|
-
description: 'Checkpoints table name',
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
new cdk.CfnOutput(this, 'WritesTableName', {
|
|
117
|
-
value: writesTable.tableName,
|
|
118
|
-
description: 'Writes table name',
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
new cdk.CfnOutput(this, 'MemoryTableName', {
|
|
122
|
-
value: memoryTable.tableName,
|
|
123
|
-
description: 'Memory table name',
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
45
|
|
|
129
|
-
|
|
46
|
+
// For DynamoDBSaver - Checkpoints Table
|
|
47
|
+
new dynamodb.Table(this, 'Checkpoints', {
|
|
48
|
+
tableName: 'langgraph-checkpoints',
|
|
49
|
+
partitionKey: { name: 'thread_id', type: dynamodb.AttributeType.STRING },
|
|
50
|
+
sortKey: { name: 'checkpoint_id', type: dynamodb.AttributeType.STRING },
|
|
51
|
+
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
|
|
52
|
+
timeToLiveAttribute: 'ttl',
|
|
53
|
+
});
|
|
130
54
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
name
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
55
|
+
// For DynamoDBSaver - Writes Table
|
|
56
|
+
new dynamodb.Table(this, 'Writes', {
|
|
57
|
+
tableName: 'langgraph-writes',
|
|
58
|
+
partitionKey: { name: 'thread_id_checkpoint_id_checkpoint_ns', type: dynamodb.AttributeType.STRING },
|
|
59
|
+
sortKey: { name: 'task_id_idx', type: dynamodb.AttributeType.STRING },
|
|
60
|
+
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
|
|
61
|
+
timeToLiveAttribute: 'ttl',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// For DynamoDBStore - Memory Table
|
|
65
|
+
new dynamodb.Table(this, 'Memory', {
|
|
66
|
+
tableName: 'langgraph-memory',
|
|
67
|
+
partitionKey: { name: 'user_id', type: dynamodb.AttributeType.STRING },
|
|
68
|
+
sortKey: { name: 'namespace_key', type: dynamodb.AttributeType.STRING },
|
|
69
|
+
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
|
|
70
|
+
timeToLiveAttribute: 'ttl',
|
|
71
|
+
});
|
|
138
72
|
|
|
73
|
+
// For DynamoDBChatMessageHistory - Chat History Table
|
|
74
|
+
new dynamodb.Table(this, 'ChatHistory', {
|
|
75
|
+
tableName: 'langgraph-chat-history',
|
|
76
|
+
partitionKey: { name: 'userId', type: dynamodb.AttributeType.STRING },
|
|
77
|
+
sortKey: { name: 'sessionId', type: dynamodb.AttributeType.STRING },
|
|
78
|
+
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
|
|
79
|
+
timeToLiveAttribute: 'ttl',
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```hcl
|
|
84
|
+
# Terraform
|
|
85
|
+
|
|
86
|
+
# For DynamoDBSaver - Checkpoints Table
|
|
87
|
+
resource "aws_dynamodb_table" "checkpoints" {
|
|
88
|
+
name = "langgraph-checkpoints"
|
|
89
|
+
billing_mode = "PAY_PER_REQUEST"
|
|
90
|
+
hash_key = "thread_id"
|
|
91
|
+
range_key = "checkpoint_id"
|
|
92
|
+
|
|
139
93
|
attribute {
|
|
140
94
|
name = "thread_id"
|
|
141
95
|
type = "S"
|
|
142
96
|
}
|
|
143
|
-
|
|
144
97
|
attribute {
|
|
145
98
|
name = "checkpoint_id"
|
|
146
99
|
type = "S"
|
|
147
100
|
}
|
|
148
|
-
|
|
101
|
+
|
|
149
102
|
ttl {
|
|
150
103
|
attribute_name = "ttl"
|
|
151
104
|
enabled = true
|
|
152
105
|
}
|
|
153
|
-
|
|
154
|
-
point_in_time_recovery {
|
|
155
|
-
enabled = true
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
tags = {
|
|
159
|
-
Name = "LangGraph Checkpoints"
|
|
160
|
-
Environment = "production"
|
|
161
|
-
}
|
|
162
106
|
}
|
|
163
107
|
|
|
164
|
-
# Writes Table
|
|
165
|
-
resource "aws_dynamodb_table" "
|
|
166
|
-
name
|
|
167
|
-
billing_mode
|
|
168
|
-
hash_key
|
|
169
|
-
range_key
|
|
170
|
-
|
|
108
|
+
# For DynamoDBSaver - Writes Table
|
|
109
|
+
resource "aws_dynamodb_table" "writes" {
|
|
110
|
+
name = "langgraph-writes"
|
|
111
|
+
billing_mode = "PAY_PER_REQUEST"
|
|
112
|
+
hash_key = "thread_id_checkpoint_id_checkpoint_ns"
|
|
113
|
+
range_key = "task_id_idx"
|
|
114
|
+
|
|
171
115
|
attribute {
|
|
172
116
|
name = "thread_id_checkpoint_id_checkpoint_ns"
|
|
173
117
|
type = "S"
|
|
174
118
|
}
|
|
175
|
-
|
|
176
119
|
attribute {
|
|
177
120
|
name = "task_id_idx"
|
|
178
121
|
type = "S"
|
|
179
122
|
}
|
|
180
|
-
|
|
123
|
+
|
|
181
124
|
ttl {
|
|
182
125
|
attribute_name = "ttl"
|
|
183
126
|
enabled = true
|
|
184
127
|
}
|
|
185
|
-
|
|
186
|
-
point_in_time_recovery {
|
|
187
|
-
enabled = true
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
tags = {
|
|
191
|
-
Name = "LangGraph Writes"
|
|
192
|
-
Environment = "production"
|
|
193
|
-
}
|
|
194
128
|
}
|
|
195
129
|
|
|
196
|
-
# Memory
|
|
197
|
-
resource "aws_dynamodb_table" "
|
|
198
|
-
name
|
|
199
|
-
billing_mode
|
|
200
|
-
hash_key
|
|
201
|
-
range_key
|
|
202
|
-
|
|
130
|
+
# For DynamoDBStore - Memory Table
|
|
131
|
+
resource "aws_dynamodb_table" "memory" {
|
|
132
|
+
name = "langgraph-memory"
|
|
133
|
+
billing_mode = "PAY_PER_REQUEST"
|
|
134
|
+
hash_key = "user_id"
|
|
135
|
+
range_key = "namespace_key"
|
|
136
|
+
|
|
203
137
|
attribute {
|
|
204
138
|
name = "user_id"
|
|
205
139
|
type = "S"
|
|
206
140
|
}
|
|
207
|
-
|
|
208
141
|
attribute {
|
|
209
142
|
name = "namespace_key"
|
|
210
143
|
type = "S"
|
|
211
144
|
}
|
|
212
|
-
|
|
145
|
+
|
|
213
146
|
ttl {
|
|
214
147
|
attribute_name = "ttl"
|
|
215
148
|
enabled = true
|
|
216
149
|
}
|
|
150
|
+
}
|
|
217
151
|
|
|
218
|
-
|
|
219
|
-
|
|
152
|
+
# For DynamoDBChatMessageHistory - Chat History Table
|
|
153
|
+
resource "aws_dynamodb_table" "chat_history" {
|
|
154
|
+
name = "langgraph-chat-history"
|
|
155
|
+
billing_mode = "PAY_PER_REQUEST"
|
|
156
|
+
hash_key = "userId"
|
|
157
|
+
range_key = "sessionId"
|
|
158
|
+
|
|
159
|
+
attribute {
|
|
160
|
+
name = "userId"
|
|
161
|
+
type = "S"
|
|
220
162
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
163
|
+
attribute {
|
|
164
|
+
name = "sessionId"
|
|
165
|
+
type = "S"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
ttl {
|
|
169
|
+
attribute_name = "ttl"
|
|
170
|
+
enabled = true
|
|
225
171
|
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
# Outputs
|
|
229
|
-
output "checkpoints_table_name" {
|
|
230
|
-
value = aws_dynamodb_table.langgraph_checkpoints.name
|
|
231
|
-
description = "Checkpoints table name"
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
output "writes_table_name" {
|
|
235
|
-
value = aws_dynamodb_table.langgraph_writes.name
|
|
236
|
-
description = "Writes table name"
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
output "memory_table_name" {
|
|
240
|
-
value = aws_dynamodb_table.langgraph_memory.name
|
|
241
|
-
description = "Memory table name"
|
|
242
172
|
}
|
|
243
173
|
```
|
|
244
174
|
|
|
245
|
-
##
|
|
246
|
-
|
|
247
|
-
### DynamoDBSaver (Checkpoint Saver)
|
|
248
|
-
|
|
249
|
-
The checkpoint saver enables persistent state management for LangGraph workflows.
|
|
175
|
+
## Quick Start
|
|
250
176
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
import { DynamoDBSaver } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
255
|
-
|
|
256
|
-
// Initialize the saver
|
|
257
|
-
const checkpointer = new DynamoDBSaver({
|
|
258
|
-
checkpointsTableName: 'langgraph-checkpoints',
|
|
259
|
-
writesTableName: 'langgraph-writes',
|
|
260
|
-
ttlDays: 30, // Optional: Auto-delete after 30 days
|
|
261
|
-
clientConfig: {
|
|
262
|
-
region: 'us-east-1',
|
|
263
|
-
// Add other AWS SDK configurations as needed
|
|
264
|
-
},
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// Save a checkpoint
|
|
268
|
-
await checkpointer.put(
|
|
269
|
-
{
|
|
270
|
-
configurable: {
|
|
271
|
-
thread_id: 'conversation-123',
|
|
272
|
-
checkpoint_ns: 'default',
|
|
273
|
-
},
|
|
274
|
-
},
|
|
275
|
-
checkpoint,
|
|
276
|
-
metadata,
|
|
277
|
-
{}
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
// Retrieve a checkpoint
|
|
281
|
-
const tuple = await checkpointer.getTuple({
|
|
282
|
-
configurable: {
|
|
283
|
-
thread_id: 'conversation-123',
|
|
284
|
-
checkpoint_id: 'checkpoint-456',
|
|
285
|
-
},
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
// List checkpoints for a thread
|
|
289
|
-
for await (const checkpoint of checkpointer.list(
|
|
290
|
-
{ configurable: { thread_id: 'conversation-123' } },
|
|
291
|
-
{ limit: 10 }
|
|
292
|
-
)) {
|
|
293
|
-
console.log(checkpoint);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Delete a thread and all its checkpoints
|
|
297
|
-
await checkpointer.deleteThread('conversation-123');
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
#### With LangGraph
|
|
177
|
+
### DynamoDBSaver (Checkpoint Storage)
|
|
301
178
|
|
|
302
179
|
```typescript
|
|
303
180
|
import { StateGraph } from '@langchain/langgraph';
|
|
304
181
|
import { DynamoDBSaver } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
305
182
|
|
|
306
|
-
// Create the checkpointer
|
|
307
183
|
const checkpointer = new DynamoDBSaver({
|
|
308
184
|
checkpointsTableName: 'langgraph-checkpoints',
|
|
309
185
|
writesTableName: 'langgraph-writes',
|
|
310
|
-
ttlDays: 30,
|
|
186
|
+
ttlDays: 30, // Optional
|
|
187
|
+
clientConfig: { region: 'us-east-1' }, // Optional
|
|
311
188
|
});
|
|
312
189
|
|
|
313
|
-
//
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
190
|
+
// Use with LangGraph
|
|
191
|
+
const workflow = new StateGraph({ /* ... */ })
|
|
192
|
+
.addNode('step1', async (state) => { /* ... */ })
|
|
193
|
+
.addEdge('__start__', 'step1');
|
|
318
194
|
|
|
319
|
-
// Create a graph with checkpoint support
|
|
320
|
-
const workflow = new StateGraph<AgentState>({
|
|
321
|
-
channels: {
|
|
322
|
-
messages: {
|
|
323
|
-
value: (x: string[], y: string[]) => x.concat(y),
|
|
324
|
-
default: () => [],
|
|
325
|
-
},
|
|
326
|
-
step: {
|
|
327
|
-
value: (x?: number, y?: number) => y ?? x ?? 0,
|
|
328
|
-
default: () => 0,
|
|
329
|
-
},
|
|
330
|
-
},
|
|
331
|
-
})
|
|
332
|
-
.addNode('processMessage', async (state: AgentState) => {
|
|
333
|
-
// Your processing logic here
|
|
334
|
-
return {
|
|
335
|
-
messages: [...state.messages, 'Processed message'],
|
|
336
|
-
step: state.step + 1,
|
|
337
|
-
};
|
|
338
|
-
})
|
|
339
|
-
.addEdge('__start__', 'processMessage')
|
|
340
|
-
.addEdge('processMessage', '__end__');
|
|
341
|
-
|
|
342
|
-
// Compile with checkpointer
|
|
343
195
|
const app = workflow.compile({ checkpointer });
|
|
344
196
|
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
configurable: {
|
|
348
|
-
thread_id: 'user-session-123',
|
|
349
|
-
},
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
const result = await app.invoke(
|
|
353
|
-
{ messages: ['Hello'], step: 0 },
|
|
354
|
-
config
|
|
355
|
-
);
|
|
356
|
-
|
|
357
|
-
// Resume from checkpoint later
|
|
358
|
-
const resumed = await app.invoke(
|
|
359
|
-
{ messages: ['Continue'], step: result.step },
|
|
360
|
-
config
|
|
361
|
-
);
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
### DynamoDBStore (Memory Store)
|
|
365
|
-
|
|
366
|
-
The memory store provides long-term storage for agent memories with optional semantic search.
|
|
367
|
-
|
|
368
|
-
#### Standalone Usage
|
|
369
|
-
|
|
370
|
-
```typescript
|
|
371
|
-
import { DynamoDBStore } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
372
|
-
|
|
373
|
-
// Initialize without embeddings (keyword search only)
|
|
374
|
-
const store = new DynamoDBStore({
|
|
375
|
-
memoryTableName: 'langgraph-memory',
|
|
376
|
-
ttlDays: 90,
|
|
377
|
-
clientConfig: {
|
|
378
|
-
region: 'us-east-1',
|
|
379
|
-
},
|
|
197
|
+
// State is automatically persisted and can be resumed
|
|
198
|
+
await app.invoke(input, {
|
|
199
|
+
configurable: { thread_id: 'conversation-123' }
|
|
380
200
|
});
|
|
381
|
-
|
|
382
|
-
// Store a memory
|
|
383
|
-
await store.batch(
|
|
384
|
-
[
|
|
385
|
-
{
|
|
386
|
-
namespace: ['user', 'preferences'],
|
|
387
|
-
key: 'theme',
|
|
388
|
-
value: { color: 'dark', fontSize: 14 },
|
|
389
|
-
},
|
|
390
|
-
],
|
|
391
|
-
{
|
|
392
|
-
configurable: { user_id: 'user-123' },
|
|
393
|
-
}
|
|
394
|
-
);
|
|
395
|
-
|
|
396
|
-
// Retrieve a memory
|
|
397
|
-
const [memory] = await store.batch(
|
|
398
|
-
[
|
|
399
|
-
{
|
|
400
|
-
namespace: ['user', 'preferences'],
|
|
401
|
-
key: 'theme',
|
|
402
|
-
},
|
|
403
|
-
],
|
|
404
|
-
{
|
|
405
|
-
configurable: { user_id: 'user-123' },
|
|
406
|
-
}
|
|
407
|
-
);
|
|
408
|
-
|
|
409
|
-
console.log(memory); // { key: 'theme', namespace: ['user', 'preferences'], value: {...}, ... }
|
|
410
|
-
|
|
411
|
-
// Search memories
|
|
412
|
-
const [results] = await store.batch(
|
|
413
|
-
[
|
|
414
|
-
{
|
|
415
|
-
namespacePrefix: ['user'],
|
|
416
|
-
limit: 10,
|
|
417
|
-
offset: 0,
|
|
418
|
-
filter: {
|
|
419
|
-
'value.color': { $eq: 'dark' },
|
|
420
|
-
},
|
|
421
|
-
},
|
|
422
|
-
],
|
|
423
|
-
{
|
|
424
|
-
configurable: { user_id: 'user-123' },
|
|
425
|
-
}
|
|
426
|
-
);
|
|
427
|
-
|
|
428
|
-
// List namespaces
|
|
429
|
-
const [namespaces] = await store.batch(
|
|
430
|
-
[
|
|
431
|
-
{
|
|
432
|
-
limit: 100,
|
|
433
|
-
offset: 0,
|
|
434
|
-
},
|
|
435
|
-
],
|
|
436
|
-
{
|
|
437
|
-
configurable: { user_id: 'user-123' },
|
|
438
|
-
}
|
|
439
|
-
);
|
|
440
201
|
```
|
|
441
202
|
|
|
442
|
-
|
|
203
|
+
### DynamoDBStore (Memory Storage)
|
|
443
204
|
|
|
444
205
|
```typescript
|
|
445
206
|
import { DynamoDBStore } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
446
207
|
import { BedrockEmbeddings } from '@langchain/aws';
|
|
447
208
|
|
|
448
|
-
//
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
209
|
+
// Without semantic search
|
|
210
|
+
const store = new DynamoDBStore({
|
|
211
|
+
memoryTableName: 'langgraph-memory',
|
|
212
|
+
ttlDays: 90, // Optional
|
|
452
213
|
});
|
|
453
214
|
|
|
454
|
-
|
|
215
|
+
// With semantic search
|
|
216
|
+
const storeWithEmbeddings = new DynamoDBStore({
|
|
455
217
|
memoryTableName: 'langgraph-memory',
|
|
456
|
-
embedding:
|
|
457
|
-
ttlDays: 90,
|
|
458
|
-
clientConfig: {
|
|
218
|
+
embedding: new BedrockEmbeddings({
|
|
459
219
|
region: 'us-east-1',
|
|
460
|
-
|
|
220
|
+
model: 'amazon.titan-embed-text-v1',
|
|
221
|
+
}),
|
|
461
222
|
});
|
|
462
223
|
|
|
463
|
-
// Store
|
|
464
|
-
await store.batch(
|
|
465
|
-
[
|
|
466
|
-
{
|
|
467
|
-
namespace: ['documents'],
|
|
468
|
-
key: 'doc1',
|
|
469
|
-
value: {
|
|
470
|
-
title: 'Introduction to AI',
|
|
471
|
-
content: 'Artificial intelligence is transforming the world...',
|
|
472
|
-
},
|
|
473
|
-
index: ['$.content'], // JSONPath to fields to embed
|
|
474
|
-
},
|
|
475
|
-
],
|
|
224
|
+
// Store and search memories
|
|
225
|
+
await store.batch([
|
|
476
226
|
{
|
|
477
|
-
|
|
227
|
+
namespace: ['user', 'preferences'],
|
|
228
|
+
key: 'theme',
|
|
229
|
+
value: { color: 'dark', fontSize: 14 },
|
|
478
230
|
}
|
|
479
|
-
);
|
|
231
|
+
], { configurable: { user_id: 'user-123' } });
|
|
480
232
|
|
|
481
|
-
//
|
|
482
|
-
const [
|
|
483
|
-
[
|
|
484
|
-
{
|
|
485
|
-
namespacePrefix: ['documents'],
|
|
486
|
-
query: 'machine learning basics', // Semantic query
|
|
487
|
-
limit: 5,
|
|
488
|
-
offset: 0,
|
|
489
|
-
},
|
|
490
|
-
],
|
|
233
|
+
// Search with filters
|
|
234
|
+
const [results] = await store.batch([
|
|
491
235
|
{
|
|
492
|
-
|
|
236
|
+
namespacePrefix: ['user'],
|
|
237
|
+
filter: { 'value.color': { $eq: 'dark' } },
|
|
238
|
+
limit: 10,
|
|
493
239
|
}
|
|
494
|
-
);
|
|
240
|
+
], { configurable: { user_id: 'user-123' } });
|
|
495
241
|
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
console.log(`Score: ${result.score}, Doc: ${result.item.value.title}`);
|
|
499
|
-
});
|
|
242
|
+
// Use with LangGraph
|
|
243
|
+
const app = workflow.compile({ checkpointer, store });
|
|
500
244
|
```
|
|
501
245
|
|
|
502
|
-
|
|
246
|
+
### DynamoDBChatMessageHistory (Chat History)
|
|
503
247
|
|
|
504
248
|
```typescript
|
|
505
|
-
import {
|
|
506
|
-
import {
|
|
507
|
-
import { BedrockEmbeddings } from '@langchain/aws';
|
|
508
|
-
import type { LangGraphRunnableConfig } from '@langchain/langgraph';
|
|
249
|
+
import { DynamoDBChatMessageHistory } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
250
|
+
import { HumanMessage, AIMessage } from '@langchain/core/messages';
|
|
509
251
|
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
252
|
+
const history = new DynamoDBChatMessageHistory({
|
|
253
|
+
tableName: 'langgraph-chat-history',
|
|
254
|
+
ttlDays: 30, // Optional
|
|
255
|
+
clientConfig: { region: 'us-east-1' }, // Optional
|
|
513
256
|
});
|
|
514
257
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
embedding: embeddings,
|
|
518
|
-
});
|
|
258
|
+
// Add message with session title
|
|
259
|
+
await history.addMessage('user-123', 'session-456', new HumanMessage('Hello!'), 'Greeting Session');
|
|
519
260
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
+
]);
|
|
524
266
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
messages: {
|
|
528
|
-
value: (x: string[], y: string[]) => x.concat(y),
|
|
529
|
-
default: () => [],
|
|
530
|
-
},
|
|
531
|
-
relevantMemories: {
|
|
532
|
-
value: (x: any[], y: any[]) => y ?? x ?? [],
|
|
533
|
-
default: () => [],
|
|
534
|
-
},
|
|
535
|
-
},
|
|
536
|
-
})
|
|
537
|
-
.addNode('fetchMemories', async (state: AgentState, config?: LangGraphRunnableConfig) => {
|
|
538
|
-
// Access store from config
|
|
539
|
-
const memoryStore = config?.store;
|
|
540
|
-
if (!memoryStore) {
|
|
541
|
-
throw new Error('Store not available in config');
|
|
542
|
-
}
|
|
267
|
+
// Get all messages for a session
|
|
268
|
+
const messages = await history.getMessages('user-123', 'session-456');
|
|
543
269
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
{
|
|
548
|
-
namespacePrefix: ['conversations'],
|
|
549
|
-
query: state.messages[state.messages.length - 1],
|
|
550
|
-
limit: 5,
|
|
551
|
-
offset: 0,
|
|
552
|
-
},
|
|
553
|
-
],
|
|
554
|
-
config
|
|
555
|
-
);
|
|
556
|
-
|
|
557
|
-
return {
|
|
558
|
-
...state,
|
|
559
|
-
relevantMemories: memories.map(m => m.item),
|
|
560
|
-
};
|
|
561
|
-
})
|
|
562
|
-
.addNode('processWithMemory', async (state: AgentState, config?: LangGraphRunnableConfig) => {
|
|
563
|
-
// Access store from config
|
|
564
|
-
const memoryStore = config?.store;
|
|
565
|
-
if (!memoryStore) {
|
|
566
|
-
throw new Error('Store not available in config');
|
|
567
|
-
}
|
|
270
|
+
// List all sessions for a user
|
|
271
|
+
const sessions = await history.listSessions('user-123');
|
|
272
|
+
// Returns: [{ sessionId, title, createdAt, updatedAt, messageCount }, ...]
|
|
568
273
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
// Store new memory
|
|
573
|
-
await memoryStore.batch(
|
|
574
|
-
[
|
|
575
|
-
{
|
|
576
|
-
namespace: ['conversations'],
|
|
577
|
-
key: `msg-${Date.now()}`,
|
|
578
|
-
value: {
|
|
579
|
-
message: state.messages[state.messages.length - 1],
|
|
580
|
-
timestamp: new Date().toISOString(),
|
|
581
|
-
},
|
|
582
|
-
index: ['$.message'],
|
|
583
|
-
},
|
|
584
|
-
],
|
|
585
|
-
config
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
return {
|
|
589
|
-
...state,
|
|
590
|
-
messages: [...state.messages, 'Processed with memory context'],
|
|
591
|
-
};
|
|
592
|
-
})
|
|
593
|
-
.addEdge('__start__', 'fetchMemories')
|
|
594
|
-
.addEdge('fetchMemories', 'processWithMemory')
|
|
595
|
-
.addEdge('processWithMemory', '__end__');
|
|
596
|
-
|
|
597
|
-
// Compile with store - makes it available in config.store
|
|
598
|
-
const app = workflow.compile({ store });
|
|
274
|
+
// Clear a session
|
|
275
|
+
await history.clear('user-123', 'session-456');
|
|
599
276
|
```
|
|
600
277
|
|
|
601
|
-
|
|
278
|
+
## API Reference
|
|
602
279
|
|
|
603
|
-
|
|
604
|
-
import { StateGraph } from '@langchain/langgraph';
|
|
605
|
-
import { DynamoDBSaver, DynamoDBStore } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
606
|
-
import { BedrockEmbeddings } from '@langchain/aws';
|
|
607
|
-
import type { LangGraphRunnableConfig } from '@langchain/langgraph';
|
|
280
|
+
For detailed API documentation, see the [TypeDoc documentation](./docs/README.md):
|
|
608
281
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
region: 'us-east-1',
|
|
612
|
-
model: 'amazon.titan-embed-text-v1',
|
|
613
|
-
});
|
|
282
|
+
- **Classes**: [DynamoDBSaver](./docs/classes/DynamoDBSaver.md), [DynamoDBStore](./docs/classes/DynamoDBStore.md), [DynamoDBChatMessageHistory](./docs/classes/DynamoDBChatMessageHistory.md)
|
|
283
|
+
- **Interfaces**: [DynamoDBSaverOptions](./docs/interfaces/DynamoDBSaverOptions.md), [DynamoDBStoreOptions](./docs/interfaces/DynamoDBStoreOptions.md), [DynamoDBChatMessageHistoryOptions](./docs/interfaces/DynamoDBChatMessageHistoryOptions.md), [SessionMetadata](./docs/interfaces/SessionMetadata.md)
|
|
614
284
|
|
|
615
|
-
|
|
616
|
-
checkpointsTableName: 'langgraph-checkpoints',
|
|
617
|
-
writesTableName: 'langgraph-writes',
|
|
618
|
-
ttlDays: 30,
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
const store = new DynamoDBStore({
|
|
622
|
-
memoryTableName: 'langgraph-memory',
|
|
623
|
-
embedding: embeddings,
|
|
624
|
-
ttlDays: 90,
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
// Define agent state
|
|
628
|
-
interface ConversationState {
|
|
629
|
-
messages: string[];
|
|
630
|
-
memories: any[];
|
|
631
|
-
userId: string;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// Create workflow
|
|
635
|
-
const workflow = new StateGraph<ConversationState>({
|
|
636
|
-
channels: {
|
|
637
|
-
messages: {
|
|
638
|
-
value: (x: string[], y: string[]) => x.concat(y),
|
|
639
|
-
default: () => [],
|
|
640
|
-
},
|
|
641
|
-
memories: {
|
|
642
|
-
value: (x: any[], y: any[]) => y ?? x ?? [],
|
|
643
|
-
default: () => [],
|
|
644
|
-
},
|
|
645
|
-
userId: {
|
|
646
|
-
value: (x?: string, y?: string) => y ?? x ?? '',
|
|
647
|
-
default: () => '',
|
|
648
|
-
},
|
|
649
|
-
},
|
|
650
|
-
})
|
|
651
|
-
.addNode('loadMemories', async (state: ConversationState, config?: LangGraphRunnableConfig) => {
|
|
652
|
-
// Access store from config
|
|
653
|
-
const memoryStore = config?.store;
|
|
654
|
-
if (!memoryStore) {
|
|
655
|
-
throw new Error('Store not available in config');
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
const [memories] = await memoryStore.batch(
|
|
659
|
-
[
|
|
660
|
-
{
|
|
661
|
-
namespacePrefix: ['user', state.userId],
|
|
662
|
-
query: state.messages[state.messages.length - 1],
|
|
663
|
-
limit: 3,
|
|
664
|
-
offset: 0,
|
|
665
|
-
},
|
|
666
|
-
],
|
|
667
|
-
config
|
|
668
|
-
);
|
|
669
|
-
|
|
670
|
-
return { ...state, memories: memories.map(m => m.item) };
|
|
671
|
-
})
|
|
672
|
-
.addNode('processMessage', async (state: ConversationState, config?: LangGraphRunnableConfig) => {
|
|
673
|
-
// Access store from config
|
|
674
|
-
const memoryStore = config?.store;
|
|
675
|
-
if (!memoryStore) {
|
|
676
|
-
throw new Error('Store not available in config');
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
// Your AI processing logic here
|
|
680
|
-
const response = `Processed: ${state.messages[state.messages.length - 1]}`;
|
|
681
|
-
|
|
682
|
-
// Store interaction as memory
|
|
683
|
-
await memoryStore.batch(
|
|
684
|
-
[
|
|
685
|
-
{
|
|
686
|
-
namespace: ['user', state.userId, 'interactions'],
|
|
687
|
-
key: `interaction-${Date.now()}`,
|
|
688
|
-
value: {
|
|
689
|
-
query: state.messages[state.messages.length - 1],
|
|
690
|
-
response: response,
|
|
691
|
-
timestamp: new Date().toISOString(),
|
|
692
|
-
},
|
|
693
|
-
index: ['$.query', '$.response'],
|
|
694
|
-
},
|
|
695
|
-
],
|
|
696
|
-
config
|
|
697
|
-
);
|
|
698
|
-
|
|
699
|
-
return {
|
|
700
|
-
...state,
|
|
701
|
-
messages: [...state.messages, response],
|
|
702
|
-
};
|
|
703
|
-
})
|
|
704
|
-
.addEdge('__start__', 'loadMemories')
|
|
705
|
-
.addEdge('loadMemories', 'processMessage')
|
|
706
|
-
.addEdge('processMessage', '__end__');
|
|
707
|
-
|
|
708
|
-
// Compile with BOTH checkpointer and store
|
|
709
|
-
const app = workflow.compile({
|
|
710
|
-
checkpointer,
|
|
711
|
-
store,
|
|
712
|
-
});
|
|
285
|
+
## Advanced Features
|
|
713
286
|
|
|
714
|
-
|
|
715
|
-
const userId = 'user-123';
|
|
716
|
-
const threadId = 'conversation-456';
|
|
287
|
+
### Memory Store Filters
|
|
717
288
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
memories: [],
|
|
722
|
-
userId: userId,
|
|
723
|
-
},
|
|
289
|
+
```typescript
|
|
290
|
+
// JSONPath-based filtering with operators: $eq, $ne, $gt, $gte, $lt, $lte
|
|
291
|
+
const [results] = await store.batch([
|
|
724
292
|
{
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
293
|
+
namespacePrefix: ['products'],
|
|
294
|
+
filter: {
|
|
295
|
+
'value.price': { $gte: 10, $lte: 100 },
|
|
296
|
+
'value.category': { $eq: 'electronics' },
|
|
728
297
|
},
|
|
298
|
+
limit: 10,
|
|
729
299
|
}
|
|
730
|
-
);
|
|
300
|
+
], { configurable: { user_id: 'user-123' } });
|
|
301
|
+
```
|
|
731
302
|
|
|
732
|
-
|
|
303
|
+
### Semantic Search
|
|
733
304
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
messages: ['Tell me more about neural networks'],
|
|
738
|
-
memories: [],
|
|
739
|
-
userId: userId,
|
|
740
|
-
},
|
|
305
|
+
```typescript
|
|
306
|
+
// Requires embedding configuration
|
|
307
|
+
const [results] = await store.batch([
|
|
741
308
|
{
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
309
|
+
namespace: ['documents'],
|
|
310
|
+
key: 'doc1',
|
|
311
|
+
value: { content: 'AI is transforming the world...' },
|
|
312
|
+
index: ['$.content'], // Fields to embed
|
|
746
313
|
}
|
|
747
|
-
);
|
|
748
|
-
```
|
|
749
|
-
|
|
750
|
-
## Configuration Options
|
|
751
|
-
|
|
752
|
-
### DynamoDBSaver Options
|
|
753
|
-
|
|
754
|
-
| Option | Type | Required | Description |
|
|
755
|
-
|--------|------|----------|-------------|
|
|
756
|
-
| `checkpointsTableName` | `string` | Yes | Name of the DynamoDB table for checkpoints |
|
|
757
|
-
| `writesTableName` | `string` | Yes | Name of the DynamoDB table for pending writes |
|
|
758
|
-
| `ttlDays` | `number` | No | TTL in days (1-1825), enables automatic expiration |
|
|
759
|
-
| `clientConfig` | `DynamoDBClientConfig` | No | AWS SDK DynamoDB client configuration |
|
|
760
|
-
| `serde` | `SerializerProtocol` | No | Custom serializer for checkpoint data |
|
|
761
|
-
|
|
762
|
-
### DynamoDBStore Options
|
|
763
|
-
|
|
764
|
-
| Option | Type | Required | Description |
|
|
765
|
-
|--------|------|----------|-------------|
|
|
766
|
-
| `memoryTableName` | `string` | Yes | Name of the DynamoDB table for memory storage |
|
|
767
|
-
| `embedding` | `BedrockEmbeddings` | No | Bedrock embeddings for semantic search |
|
|
768
|
-
| `ttlDays` | `number` | No | TTL in days (1-1825), enables automatic expiration |
|
|
769
|
-
| `clientConfig` | `DynamoDBClientConfig` | No | AWS SDK DynamoDB client configuration |
|
|
770
|
-
|
|
771
|
-
## Advanced Features
|
|
772
|
-
|
|
773
|
-
### Filter Operations
|
|
314
|
+
], config);
|
|
774
315
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
offset: 0,
|
|
784
|
-
filter: {
|
|
785
|
-
'value.price': { $gte: 10, $lte: 100 },
|
|
786
|
-
'value.category': { $eq: 'electronics' },
|
|
787
|
-
'value.inStock': { $ne: false },
|
|
788
|
-
},
|
|
789
|
-
},
|
|
790
|
-
],
|
|
791
|
-
{ configurable: { user_id: 'user-123' } }
|
|
792
|
-
);
|
|
316
|
+
// Query semantically
|
|
317
|
+
const [semanticResults] = await store.batch([
|
|
318
|
+
{
|
|
319
|
+
namespacePrefix: ['documents'],
|
|
320
|
+
query: 'machine learning basics', // Semantic query
|
|
321
|
+
limit: 5,
|
|
322
|
+
}
|
|
323
|
+
], config);
|
|
793
324
|
```
|
|
794
325
|
|
|
795
|
-
Supported operators:
|
|
796
|
-
- `$eq`: Equal
|
|
797
|
-
- `$ne`: Not equal
|
|
798
|
-
- `$gt`: Greater than
|
|
799
|
-
- `$gte`: Greater than or equal
|
|
800
|
-
- `$lt`: Less than
|
|
801
|
-
- `$lte`: Less than or equal
|
|
802
|
-
|
|
803
326
|
### Namespace Organization
|
|
804
327
|
|
|
805
|
-
Organize memories hierarchically:
|
|
806
|
-
|
|
807
328
|
```typescript
|
|
808
|
-
//
|
|
809
|
-
['user',
|
|
810
|
-
|
|
811
|
-
// Conversation history
|
|
812
|
-
['user', 'userId', 'conversations', 'threadId']
|
|
813
|
-
|
|
814
|
-
// Documents
|
|
329
|
+
// Hierarchical organization
|
|
330
|
+
['user', userId, 'preferences']
|
|
331
|
+
['user', userId, 'conversations', threadId]
|
|
815
332
|
['documents', 'category', 'subcategory']
|
|
816
|
-
|
|
817
|
-
// Facts about topics
|
|
818
|
-
['knowledge', 'topic', 'subtopic']
|
|
819
|
-
```
|
|
820
|
-
|
|
821
|
-
### Batch Operations
|
|
822
|
-
|
|
823
|
-
Execute multiple operations efficiently:
|
|
824
|
-
|
|
825
|
-
```typescript
|
|
826
|
-
const results = await store.batch(
|
|
827
|
-
[
|
|
828
|
-
// Get operation
|
|
829
|
-
{ namespace: ['docs'], key: 'doc1' },
|
|
830
|
-
|
|
831
|
-
// Put operation
|
|
832
|
-
{ namespace: ['docs'], key: 'doc2', value: { content: 'text' } },
|
|
833
|
-
|
|
834
|
-
// Search operation
|
|
835
|
-
{ namespacePrefix: ['docs'], limit: 10, offset: 0 },
|
|
836
|
-
|
|
837
|
-
// List namespaces
|
|
838
|
-
{ limit: 100, offset: 0 },
|
|
839
|
-
],
|
|
840
|
-
{ configurable: { user_id: 'user-123' } }
|
|
841
|
-
);
|
|
842
|
-
```
|
|
843
|
-
|
|
844
|
-
## Error Handling
|
|
845
|
-
|
|
846
|
-
The package includes built-in retry logic for transient DynamoDB errors:
|
|
847
|
-
|
|
848
|
-
```typescript
|
|
849
|
-
import { DynamoDBSaver } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
850
|
-
|
|
851
|
-
try {
|
|
852
|
-
const checkpointer = new DynamoDBSaver({
|
|
853
|
-
checkpointsTableName: 'langgraph-checkpoints',
|
|
854
|
-
writesTableName: 'langgraph-writes',
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
await checkpointer.put(config, checkpoint, metadata, {});
|
|
858
|
-
} catch (error) {
|
|
859
|
-
if (error.name === 'ValidationError') {
|
|
860
|
-
console.error('Invalid input:', error.message);
|
|
861
|
-
} else if (error.name === 'ResourceNotFoundException') {
|
|
862
|
-
console.error('Table not found:', error.message);
|
|
863
|
-
} else {
|
|
864
|
-
console.error('Unexpected error:', error);
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
333
|
```
|
|
868
334
|
|
|
869
335
|
## IAM Permissions
|
|
870
336
|
|
|
871
|
-
|
|
337
|
+
Required permissions for your AWS IAM role:
|
|
872
338
|
|
|
873
339
|
```json
|
|
874
340
|
{
|
|
@@ -882,56 +348,48 @@ Your application needs the following IAM permissions:
|
|
|
882
348
|
"dynamodb:UpdateItem",
|
|
883
349
|
"dynamodb:DeleteItem",
|
|
884
350
|
"dynamodb:Query",
|
|
885
|
-
"dynamodb:Scan",
|
|
886
351
|
"dynamodb:BatchWriteItem"
|
|
887
352
|
],
|
|
888
353
|
"Resource": [
|
|
889
354
|
"arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-checkpoints",
|
|
890
355
|
"arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-writes",
|
|
891
|
-
"arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-memory"
|
|
356
|
+
"arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-memory",
|
|
357
|
+
"arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-chat-history"
|
|
892
358
|
]
|
|
893
359
|
},
|
|
894
360
|
{
|
|
895
361
|
"Effect": "Allow",
|
|
896
|
-
"Action": [
|
|
897
|
-
"bedrock:InvokeModel"
|
|
898
|
-
],
|
|
362
|
+
"Action": ["bedrock:InvokeModel"],
|
|
899
363
|
"Resource": "arn:aws:bedrock:REGION::foundation-model/amazon.titan-embed-text-v1"
|
|
900
364
|
}
|
|
901
365
|
]
|
|
902
366
|
}
|
|
903
367
|
```
|
|
904
368
|
|
|
905
|
-
## Performance Considerations
|
|
906
|
-
|
|
907
|
-
- **Batch Operations**: Use batch operations to reduce API calls
|
|
908
|
-
- **TTL**: Configure TTL to automatically clean up old data
|
|
909
|
-
- **Billing Mode**: Use PAY_PER_REQUEST for variable workloads, PROVISIONED for predictable traffic
|
|
910
|
-
- **Point-in-Time Recovery**: Enable for production workloads
|
|
911
|
-
- **Semantic Search**: Embedding generation adds latency; cache when possible
|
|
912
|
-
|
|
913
369
|
## Testing
|
|
914
370
|
|
|
915
|
-
Run the test suite:
|
|
916
|
-
|
|
917
371
|
```bash
|
|
372
|
+
# Run tests
|
|
918
373
|
npm test
|
|
919
|
-
```
|
|
920
|
-
|
|
921
|
-
Run tests with coverage:
|
|
922
374
|
|
|
923
|
-
|
|
375
|
+
# Run tests with coverage
|
|
924
376
|
npm test -- --coverage
|
|
377
|
+
|
|
378
|
+
# Type checking
|
|
379
|
+
npm run typecheck
|
|
380
|
+
|
|
381
|
+
# Lint
|
|
382
|
+
npm run lint
|
|
925
383
|
```
|
|
926
384
|
|
|
927
385
|
## Contributing
|
|
928
386
|
|
|
929
|
-
Contributions are welcome!
|
|
387
|
+
Contributions are welcome! Please:
|
|
930
388
|
|
|
931
389
|
1. Check existing issues or create a new one
|
|
932
390
|
2. Fork the repository
|
|
933
391
|
3. Create a feature branch
|
|
934
|
-
4.
|
|
392
|
+
4. Add tests for your changes
|
|
935
393
|
5. Submit a pull request
|
|
936
394
|
|
|
937
395
|
## License
|
|
@@ -945,16 +403,9 @@ MIT © [FarukAda](https://github.com/farukada)
|
|
|
945
403
|
- [Issue Tracker](https://github.com/farukada/aws-langgraph-dynamodb-ts/issues)
|
|
946
404
|
- [LangGraph Documentation](https://langchain-ai.github.io/langgraphjs/)
|
|
947
405
|
- [AWS DynamoDB Documentation](https://docs.aws.amazon.com/dynamodb/)
|
|
948
|
-
- [AWS Bedrock Documentation](https://docs.aws.amazon.com/bedrock/)
|
|
949
|
-
|
|
950
|
-
## Acknowledgments
|
|
951
|
-
|
|
952
|
-
Built with:
|
|
953
|
-
- [LangGraph](https://github.com/langchain-ai/langgraphjs) - Framework for building stateful AI agents
|
|
954
|
-
- [AWS SDK for JavaScript](https://aws.amazon.com/sdk-for-javascript/) - AWS service integration
|
|
955
|
-
- [LangChain](https://github.com/langchain-ai/langchainjs) - LLM application framework
|
|
956
406
|
|
|
957
407
|
---
|
|
958
408
|
|
|
959
|
-
|
|
409
|
+
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)
|
|
410
|
+
|
|
960
411
|
|