@farukada/aws-langgraph-dynamodb-ts 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +960 -0
- package/dist/checkpointer/actions/delete-thread.d.ts +9 -0
- package/dist/checkpointer/actions/delete-thread.d.ts.map +1 -0
- package/dist/checkpointer/actions/delete-thread.js +109 -0
- package/dist/checkpointer/actions/delete-thread.js.map +1 -0
- package/dist/checkpointer/actions/get-tuple.d.ts +11 -0
- package/dist/checkpointer/actions/get-tuple.d.ts.map +1 -0
- package/dist/checkpointer/actions/get-tuple.js +96 -0
- package/dist/checkpointer/actions/get-tuple.js.map +1 -0
- package/dist/checkpointer/actions/index.d.ts +5 -0
- package/dist/checkpointer/actions/index.d.ts.map +1 -0
- package/dist/checkpointer/actions/index.js +21 -0
- package/dist/checkpointer/actions/index.js.map +1 -0
- package/dist/checkpointer/actions/put-writes.d.ts +9 -0
- package/dist/checkpointer/actions/put-writes.d.ts.map +1 -0
- package/dist/checkpointer/actions/put-writes.js +63 -0
- package/dist/checkpointer/actions/put-writes.js.map +1 -0
- package/dist/checkpointer/actions/put.d.ts +11 -0
- package/dist/checkpointer/actions/put.d.ts.map +1 -0
- package/dist/checkpointer/actions/put.js +55 -0
- package/dist/checkpointer/actions/put.js.map +1 -0
- package/dist/checkpointer/actions/validate-configurable.d.ts +10 -0
- package/dist/checkpointer/actions/validate-configurable.d.ts.map +1 -0
- package/dist/checkpointer/actions/validate-configurable.js +39 -0
- package/dist/checkpointer/actions/validate-configurable.js.map +1 -0
- package/dist/checkpointer/actions/writer.d.ts +50 -0
- package/dist/checkpointer/actions/writer.d.ts.map +1 -0
- package/dist/checkpointer/actions/writer.js +109 -0
- package/dist/checkpointer/actions/writer.js.map +1 -0
- package/dist/checkpointer/index.d.ts +70 -0
- package/dist/checkpointer/index.d.ts.map +1 -0
- package/dist/checkpointer/index.js +172 -0
- package/dist/checkpointer/index.js.map +1 -0
- package/dist/checkpointer/types/index.d.ts +162 -0
- package/dist/checkpointer/types/index.d.ts.map +1 -0
- package/dist/checkpointer/types/index.js +3 -0
- package/dist/checkpointer/types/index.js.map +1 -0
- package/dist/checkpointer/utils/index.d.ts +6 -0
- package/dist/checkpointer/utils/index.d.ts.map +1 -0
- package/dist/checkpointer/utils/index.js +26 -0
- package/dist/checkpointer/utils/index.js.map +1 -0
- package/dist/checkpointer/utils/validation.d.ts +48 -0
- package/dist/checkpointer/utils/validation.d.ts.map +1 -0
- package/dist/checkpointer/utils/validation.js +190 -0
- package/dist/checkpointer/utils/validation.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/shared/index.d.ts +5 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +21 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/utils/constants.d.ts +11 -0
- package/dist/shared/utils/constants.d.ts.map +1 -0
- package/dist/shared/utils/constants.js +35 -0
- package/dist/shared/utils/constants.js.map +1 -0
- package/dist/shared/utils/index.d.ts +6 -0
- package/dist/shared/utils/index.d.ts.map +1 -0
- package/dist/shared/utils/index.js +22 -0
- package/dist/shared/utils/index.js.map +1 -0
- package/dist/shared/utils/retry.d.ts +19 -0
- package/dist/shared/utils/retry.d.ts.map +1 -0
- package/dist/shared/utils/retry.js +102 -0
- package/dist/shared/utils/retry.js.map +1 -0
- package/dist/store/actions/get-operation.d.ts +11 -0
- package/dist/store/actions/get-operation.d.ts.map +1 -0
- package/dist/store/actions/get-operation.js +42 -0
- package/dist/store/actions/get-operation.js.map +1 -0
- package/dist/store/actions/index.d.ts +5 -0
- package/dist/store/actions/index.d.ts.map +1 -0
- package/dist/store/actions/index.js +21 -0
- package/dist/store/actions/index.js.map +1 -0
- package/dist/store/actions/list-namespaces-operation.d.ts +10 -0
- package/dist/store/actions/list-namespaces-operation.d.ts.map +1 -0
- package/dist/store/actions/list-namespaces-operation.js +212 -0
- package/dist/store/actions/list-namespaces-operation.js.map +1 -0
- package/dist/store/actions/put-operation.d.ts +9 -0
- package/dist/store/actions/put-operation.d.ts.map +1 -0
- package/dist/store/actions/put-operation.js +94 -0
- package/dist/store/actions/put-operation.js.map +1 -0
- package/dist/store/actions/search-operation.d.ts +11 -0
- package/dist/store/actions/search-operation.d.ts.map +1 -0
- package/dist/store/actions/search-operation.js +150 -0
- package/dist/store/actions/search-operation.js.map +1 -0
- package/dist/store/index.d.ts +73 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +145 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/types/index.d.ts +97 -0
- package/dist/store/types/index.d.ts.map +1 -0
- package/dist/store/types/index.js +3 -0
- package/dist/store/types/index.js.map +1 -0
- package/dist/store/utils/filter.d.ts +16 -0
- package/dist/store/utils/filter.d.ts.map +1 -0
- package/dist/store/utils/filter.js +71 -0
- package/dist/store/utils/filter.js.map +1 -0
- package/dist/store/utils/index.d.ts +8 -0
- package/dist/store/utils/index.d.ts.map +1 -0
- package/dist/store/utils/index.js +28 -0
- package/dist/store/utils/index.js.map +1 -0
- package/dist/store/utils/result.d.ts +42 -0
- package/dist/store/utils/result.d.ts.map +1 -0
- package/dist/store/utils/result.js +51 -0
- package/dist/store/utils/result.js.map +1 -0
- package/dist/store/utils/validation.d.ts +57 -0
- package/dist/store/utils/validation.d.ts.map +1 -0
- package/dist/store/utils/validation.js +243 -0
- package/dist/store/utils/validation.js.map +1 -0
- package/package.json +85 -0
package/README.md
ADDED
|
@@ -0,0 +1,960 @@
|
|
|
1
|
+
# @farukada/aws-langgraph-dynamodb-ts
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/@farukada%2Faws-langgraph-dynamodb-ts)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://aws.amazon.com/sdk-for-javascript/)
|
|
8
|
+
|
|
9
|
+
> ⚠️ **Active Development**: This package is currently in active development. APIs may change between versions.
|
|
10
|
+
|
|
11
|
+
AWS DynamoDB implementation for LangGraph Memory Store and Checkpoint Saver in TypeScript. This package provides persistent storage solutions for LangGraph applications using AWS DynamoDB, enabling state management and memory persistence for AI agents and conversational workflows.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- 🔄 **Checkpoint Saver**: Persistent checkpoint storage for LangGraph state management
|
|
16
|
+
- 💾 **Memory Store**: Long-term memory storage with namespace support
|
|
17
|
+
- 🔍 **Semantic Search**: Optional vector embeddings via AWS Bedrock for semantic search capabilities
|
|
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
|
|
22
|
+
- 🧪 **Well-Tested**: Comprehensive test coverage with Jest
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @farukada/aws-langgraph-dynamodb-ts
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Peer Dependencies
|
|
31
|
+
|
|
32
|
+
Make sure you have the required peer dependencies installed:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb @langchain/core @langchain/langgraph @langchain/langgraph-checkpoint
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For semantic search capabilities, also install:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install @langchain/aws
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Infrastructure Setup
|
|
45
|
+
|
|
46
|
+
Before using this package, you need to create the required DynamoDB tables. Below are examples using both AWS CDK and Terraform.
|
|
47
|
+
|
|
48
|
+
### AWS CDK (TypeScript)
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import * as cdk from 'aws-cdk-lib';
|
|
52
|
+
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
|
+
|
|
129
|
+
### Terraform
|
|
130
|
+
|
|
131
|
+
```hcl
|
|
132
|
+
# Checkpoints Table
|
|
133
|
+
resource "aws_dynamodb_table" "langgraph_checkpoints" {
|
|
134
|
+
name = "langgraph-checkpoints"
|
|
135
|
+
billing_mode = "PAY_PER_REQUEST"
|
|
136
|
+
hash_key = "thread_id"
|
|
137
|
+
range_key = "checkpoint_id"
|
|
138
|
+
|
|
139
|
+
attribute {
|
|
140
|
+
name = "thread_id"
|
|
141
|
+
type = "S"
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
attribute {
|
|
145
|
+
name = "checkpoint_id"
|
|
146
|
+
type = "S"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
ttl {
|
|
150
|
+
attribute_name = "ttl"
|
|
151
|
+
enabled = true
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
point_in_time_recovery {
|
|
155
|
+
enabled = true
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
tags = {
|
|
159
|
+
Name = "LangGraph Checkpoints"
|
|
160
|
+
Environment = "production"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# Writes Table
|
|
165
|
+
resource "aws_dynamodb_table" "langgraph_writes" {
|
|
166
|
+
name = "langgraph-writes"
|
|
167
|
+
billing_mode = "PAY_PER_REQUEST"
|
|
168
|
+
hash_key = "thread_id_checkpoint_id_checkpoint_ns"
|
|
169
|
+
range_key = "task_id_idx"
|
|
170
|
+
|
|
171
|
+
attribute {
|
|
172
|
+
name = "thread_id_checkpoint_id_checkpoint_ns"
|
|
173
|
+
type = "S"
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
attribute {
|
|
177
|
+
name = "task_id_idx"
|
|
178
|
+
type = "S"
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
ttl {
|
|
182
|
+
attribute_name = "ttl"
|
|
183
|
+
enabled = true
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
point_in_time_recovery {
|
|
187
|
+
enabled = true
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
tags = {
|
|
191
|
+
Name = "LangGraph Writes"
|
|
192
|
+
Environment = "production"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# Memory Store Table
|
|
197
|
+
resource "aws_dynamodb_table" "langgraph_memory" {
|
|
198
|
+
name = "langgraph-memory"
|
|
199
|
+
billing_mode = "PAY_PER_REQUEST"
|
|
200
|
+
hash_key = "user_id"
|
|
201
|
+
range_key = "namespace_key"
|
|
202
|
+
|
|
203
|
+
attribute {
|
|
204
|
+
name = "user_id"
|
|
205
|
+
type = "S"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
attribute {
|
|
209
|
+
name = "namespace_key"
|
|
210
|
+
type = "S"
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
ttl {
|
|
214
|
+
attribute_name = "ttl"
|
|
215
|
+
enabled = true
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
point_in_time_recovery {
|
|
219
|
+
enabled = true
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
tags = {
|
|
223
|
+
Name = "LangGraph Memory"
|
|
224
|
+
Environment = "production"
|
|
225
|
+
}
|
|
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
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Usage
|
|
246
|
+
|
|
247
|
+
### DynamoDBSaver (Checkpoint Saver)
|
|
248
|
+
|
|
249
|
+
The checkpoint saver enables persistent state management for LangGraph workflows.
|
|
250
|
+
|
|
251
|
+
#### Standalone Usage
|
|
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
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { StateGraph } from '@langchain/langgraph';
|
|
304
|
+
import { DynamoDBSaver } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
305
|
+
|
|
306
|
+
// Create the checkpointer
|
|
307
|
+
const checkpointer = new DynamoDBSaver({
|
|
308
|
+
checkpointsTableName: 'langgraph-checkpoints',
|
|
309
|
+
writesTableName: 'langgraph-writes',
|
|
310
|
+
ttlDays: 30,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Define your state interface
|
|
314
|
+
interface AgentState {
|
|
315
|
+
messages: string[];
|
|
316
|
+
step: number;
|
|
317
|
+
}
|
|
318
|
+
|
|
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
|
+
const app = workflow.compile({ checkpointer });
|
|
344
|
+
|
|
345
|
+
// Run with a specific thread
|
|
346
|
+
const config = {
|
|
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
|
+
},
|
|
380
|
+
});
|
|
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
|
+
```
|
|
441
|
+
|
|
442
|
+
#### With Semantic Search (Bedrock Embeddings)
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { DynamoDBStore } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
446
|
+
import { BedrockEmbeddings } from '@langchain/aws';
|
|
447
|
+
|
|
448
|
+
// Initialize with embeddings for semantic search
|
|
449
|
+
const embeddings = new BedrockEmbeddings({
|
|
450
|
+
region: 'us-east-1',
|
|
451
|
+
model: 'amazon.titan-embed-text-v1',
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
const store = new DynamoDBStore({
|
|
455
|
+
memoryTableName: 'langgraph-memory',
|
|
456
|
+
embedding: embeddings,
|
|
457
|
+
ttlDays: 90,
|
|
458
|
+
clientConfig: {
|
|
459
|
+
region: 'us-east-1',
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Store memories with semantic indexing
|
|
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
|
+
],
|
|
476
|
+
{
|
|
477
|
+
configurable: { user_id: 'user-123' },
|
|
478
|
+
}
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
// Semantic search with query
|
|
482
|
+
const [semanticResults] = await store.batch(
|
|
483
|
+
[
|
|
484
|
+
{
|
|
485
|
+
namespacePrefix: ['documents'],
|
|
486
|
+
query: 'machine learning basics', // Semantic query
|
|
487
|
+
limit: 5,
|
|
488
|
+
offset: 0,
|
|
489
|
+
},
|
|
490
|
+
],
|
|
491
|
+
{
|
|
492
|
+
configurable: { user_id: 'user-123' },
|
|
493
|
+
}
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
// Results include similarity scores
|
|
497
|
+
semanticResults.forEach(result => {
|
|
498
|
+
console.log(`Score: ${result.score}, Doc: ${result.item.value.title}`);
|
|
499
|
+
});
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
#### With LangGraph Agent
|
|
503
|
+
|
|
504
|
+
```typescript
|
|
505
|
+
import { StateGraph } from '@langchain/langgraph';
|
|
506
|
+
import { DynamoDBStore } from '@farukada/aws-langgraph-dynamodb-ts';
|
|
507
|
+
import { BedrockEmbeddings } from '@langchain/aws';
|
|
508
|
+
import type { LangGraphRunnableConfig } from '@langchain/langgraph';
|
|
509
|
+
|
|
510
|
+
const embeddings = new BedrockEmbeddings({
|
|
511
|
+
region: 'us-east-1',
|
|
512
|
+
model: 'amazon.titan-embed-text-v1',
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
const store = new DynamoDBStore({
|
|
516
|
+
memoryTableName: 'langgraph-memory',
|
|
517
|
+
embedding: embeddings,
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
interface AgentState {
|
|
521
|
+
messages: string[];
|
|
522
|
+
relevantMemories: any[];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const workflow = new StateGraph<AgentState>({
|
|
526
|
+
channels: {
|
|
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
|
+
}
|
|
543
|
+
|
|
544
|
+
// Semantic search for relevant memories
|
|
545
|
+
const [memories] = await memoryStore.batch(
|
|
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
|
+
}
|
|
568
|
+
|
|
569
|
+
// Use memories in your agent logic
|
|
570
|
+
console.log('Relevant memories:', state.relevantMemories);
|
|
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 });
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### Complete Example: Agent with Memory and Checkpointing
|
|
602
|
+
|
|
603
|
+
```typescript
|
|
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';
|
|
608
|
+
|
|
609
|
+
// Setup infrastructure components
|
|
610
|
+
const embeddings = new BedrockEmbeddings({
|
|
611
|
+
region: 'us-east-1',
|
|
612
|
+
model: 'amazon.titan-embed-text-v1',
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
const checkpointer = new DynamoDBSaver({
|
|
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
|
+
});
|
|
713
|
+
|
|
714
|
+
// Run conversation with persistence
|
|
715
|
+
const userId = 'user-123';
|
|
716
|
+
const threadId = 'conversation-456';
|
|
717
|
+
|
|
718
|
+
const result = await app.invoke(
|
|
719
|
+
{
|
|
720
|
+
messages: ['What is machine learning?'],
|
|
721
|
+
memories: [],
|
|
722
|
+
userId: userId,
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
configurable: {
|
|
726
|
+
thread_id: threadId,
|
|
727
|
+
user_id: userId,
|
|
728
|
+
},
|
|
729
|
+
}
|
|
730
|
+
);
|
|
731
|
+
|
|
732
|
+
console.log('Agent Response:', result);
|
|
733
|
+
|
|
734
|
+
// Later, resume the conversation
|
|
735
|
+
const continued = await app.invoke(
|
|
736
|
+
{
|
|
737
|
+
messages: ['Tell me more about neural networks'],
|
|
738
|
+
memories: [],
|
|
739
|
+
userId: userId,
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
configurable: {
|
|
743
|
+
thread_id: threadId,
|
|
744
|
+
user_id: userId,
|
|
745
|
+
},
|
|
746
|
+
}
|
|
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
|
|
774
|
+
|
|
775
|
+
The store supports JSONPath-based filtering:
|
|
776
|
+
|
|
777
|
+
```typescript
|
|
778
|
+
const [results] = await store.batch(
|
|
779
|
+
[
|
|
780
|
+
{
|
|
781
|
+
namespacePrefix: ['products'],
|
|
782
|
+
limit: 10,
|
|
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
|
+
);
|
|
793
|
+
```
|
|
794
|
+
|
|
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
|
+
### Namespace Organization
|
|
804
|
+
|
|
805
|
+
Organize memories hierarchically:
|
|
806
|
+
|
|
807
|
+
```typescript
|
|
808
|
+
// User preferences
|
|
809
|
+
['user', 'userId', 'preferences']
|
|
810
|
+
|
|
811
|
+
// Conversation history
|
|
812
|
+
['user', 'userId', 'conversations', 'threadId']
|
|
813
|
+
|
|
814
|
+
// Documents
|
|
815
|
+
['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
|
+
```
|
|
868
|
+
|
|
869
|
+
## IAM Permissions
|
|
870
|
+
|
|
871
|
+
Your application needs the following IAM permissions:
|
|
872
|
+
|
|
873
|
+
```json
|
|
874
|
+
{
|
|
875
|
+
"Version": "2012-10-17",
|
|
876
|
+
"Statement": [
|
|
877
|
+
{
|
|
878
|
+
"Effect": "Allow",
|
|
879
|
+
"Action": [
|
|
880
|
+
"dynamodb:GetItem",
|
|
881
|
+
"dynamodb:PutItem",
|
|
882
|
+
"dynamodb:UpdateItem",
|
|
883
|
+
"dynamodb:DeleteItem",
|
|
884
|
+
"dynamodb:Query",
|
|
885
|
+
"dynamodb:Scan",
|
|
886
|
+
"dynamodb:BatchWriteItem"
|
|
887
|
+
],
|
|
888
|
+
"Resource": [
|
|
889
|
+
"arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-checkpoints",
|
|
890
|
+
"arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-writes",
|
|
891
|
+
"arn:aws:dynamodb:REGION:ACCOUNT:table/langgraph-memory"
|
|
892
|
+
]
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
"Effect": "Allow",
|
|
896
|
+
"Action": [
|
|
897
|
+
"bedrock:InvokeModel"
|
|
898
|
+
],
|
|
899
|
+
"Resource": "arn:aws:bedrock:REGION::foundation-model/amazon.titan-embed-text-v1"
|
|
900
|
+
}
|
|
901
|
+
]
|
|
902
|
+
}
|
|
903
|
+
```
|
|
904
|
+
|
|
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
|
+
## Testing
|
|
914
|
+
|
|
915
|
+
Run the test suite:
|
|
916
|
+
|
|
917
|
+
```bash
|
|
918
|
+
npm test
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
Run tests with coverage:
|
|
922
|
+
|
|
923
|
+
```bash
|
|
924
|
+
npm test -- --coverage
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
## Contributing
|
|
928
|
+
|
|
929
|
+
Contributions are welcome! Since this package is in active development, please:
|
|
930
|
+
|
|
931
|
+
1. Check existing issues or create a new one
|
|
932
|
+
2. Fork the repository
|
|
933
|
+
3. Create a feature branch
|
|
934
|
+
4. Make your changes with tests
|
|
935
|
+
5. Submit a pull request
|
|
936
|
+
|
|
937
|
+
## License
|
|
938
|
+
|
|
939
|
+
MIT © [FarukAda](https://github.com/farukada)
|
|
940
|
+
|
|
941
|
+
## Links
|
|
942
|
+
|
|
943
|
+
- [GitHub Repository](https://github.com/farukada/aws-langgraph-dynamodb-ts)
|
|
944
|
+
- [npm Package](https://www.npmjs.com/package/@farukada/aws-langgraph-dynamodb-ts)
|
|
945
|
+
- [Issue Tracker](https://github.com/farukada/aws-langgraph-dynamodb-ts/issues)
|
|
946
|
+
- [LangGraph Documentation](https://langchain-ai.github.io/langgraphjs/)
|
|
947
|
+
- [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
|
+
|
|
957
|
+
---
|
|
958
|
+
|
|
959
|
+
**Note**: This package is under active development. Star the repository on GitHub to stay updated with new features and improvements!
|
|
960
|
+
|