@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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +960 -0
  3. package/dist/checkpointer/actions/delete-thread.d.ts +9 -0
  4. package/dist/checkpointer/actions/delete-thread.d.ts.map +1 -0
  5. package/dist/checkpointer/actions/delete-thread.js +109 -0
  6. package/dist/checkpointer/actions/delete-thread.js.map +1 -0
  7. package/dist/checkpointer/actions/get-tuple.d.ts +11 -0
  8. package/dist/checkpointer/actions/get-tuple.d.ts.map +1 -0
  9. package/dist/checkpointer/actions/get-tuple.js +96 -0
  10. package/dist/checkpointer/actions/get-tuple.js.map +1 -0
  11. package/dist/checkpointer/actions/index.d.ts +5 -0
  12. package/dist/checkpointer/actions/index.d.ts.map +1 -0
  13. package/dist/checkpointer/actions/index.js +21 -0
  14. package/dist/checkpointer/actions/index.js.map +1 -0
  15. package/dist/checkpointer/actions/put-writes.d.ts +9 -0
  16. package/dist/checkpointer/actions/put-writes.d.ts.map +1 -0
  17. package/dist/checkpointer/actions/put-writes.js +63 -0
  18. package/dist/checkpointer/actions/put-writes.js.map +1 -0
  19. package/dist/checkpointer/actions/put.d.ts +11 -0
  20. package/dist/checkpointer/actions/put.d.ts.map +1 -0
  21. package/dist/checkpointer/actions/put.js +55 -0
  22. package/dist/checkpointer/actions/put.js.map +1 -0
  23. package/dist/checkpointer/actions/validate-configurable.d.ts +10 -0
  24. package/dist/checkpointer/actions/validate-configurable.d.ts.map +1 -0
  25. package/dist/checkpointer/actions/validate-configurable.js +39 -0
  26. package/dist/checkpointer/actions/validate-configurable.js.map +1 -0
  27. package/dist/checkpointer/actions/writer.d.ts +50 -0
  28. package/dist/checkpointer/actions/writer.d.ts.map +1 -0
  29. package/dist/checkpointer/actions/writer.js +109 -0
  30. package/dist/checkpointer/actions/writer.js.map +1 -0
  31. package/dist/checkpointer/index.d.ts +70 -0
  32. package/dist/checkpointer/index.d.ts.map +1 -0
  33. package/dist/checkpointer/index.js +172 -0
  34. package/dist/checkpointer/index.js.map +1 -0
  35. package/dist/checkpointer/types/index.d.ts +162 -0
  36. package/dist/checkpointer/types/index.d.ts.map +1 -0
  37. package/dist/checkpointer/types/index.js +3 -0
  38. package/dist/checkpointer/types/index.js.map +1 -0
  39. package/dist/checkpointer/utils/index.d.ts +6 -0
  40. package/dist/checkpointer/utils/index.d.ts.map +1 -0
  41. package/dist/checkpointer/utils/index.js +26 -0
  42. package/dist/checkpointer/utils/index.js.map +1 -0
  43. package/dist/checkpointer/utils/validation.d.ts +48 -0
  44. package/dist/checkpointer/utils/validation.d.ts.map +1 -0
  45. package/dist/checkpointer/utils/validation.js +190 -0
  46. package/dist/checkpointer/utils/validation.js.map +1 -0
  47. package/dist/index.d.ts +5 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +8 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/shared/index.d.ts +5 -0
  52. package/dist/shared/index.d.ts.map +1 -0
  53. package/dist/shared/index.js +21 -0
  54. package/dist/shared/index.js.map +1 -0
  55. package/dist/shared/utils/constants.d.ts +11 -0
  56. package/dist/shared/utils/constants.d.ts.map +1 -0
  57. package/dist/shared/utils/constants.js +35 -0
  58. package/dist/shared/utils/constants.js.map +1 -0
  59. package/dist/shared/utils/index.d.ts +6 -0
  60. package/dist/shared/utils/index.d.ts.map +1 -0
  61. package/dist/shared/utils/index.js +22 -0
  62. package/dist/shared/utils/index.js.map +1 -0
  63. package/dist/shared/utils/retry.d.ts +19 -0
  64. package/dist/shared/utils/retry.d.ts.map +1 -0
  65. package/dist/shared/utils/retry.js +102 -0
  66. package/dist/shared/utils/retry.js.map +1 -0
  67. package/dist/store/actions/get-operation.d.ts +11 -0
  68. package/dist/store/actions/get-operation.d.ts.map +1 -0
  69. package/dist/store/actions/get-operation.js +42 -0
  70. package/dist/store/actions/get-operation.js.map +1 -0
  71. package/dist/store/actions/index.d.ts +5 -0
  72. package/dist/store/actions/index.d.ts.map +1 -0
  73. package/dist/store/actions/index.js +21 -0
  74. package/dist/store/actions/index.js.map +1 -0
  75. package/dist/store/actions/list-namespaces-operation.d.ts +10 -0
  76. package/dist/store/actions/list-namespaces-operation.d.ts.map +1 -0
  77. package/dist/store/actions/list-namespaces-operation.js +212 -0
  78. package/dist/store/actions/list-namespaces-operation.js.map +1 -0
  79. package/dist/store/actions/put-operation.d.ts +9 -0
  80. package/dist/store/actions/put-operation.d.ts.map +1 -0
  81. package/dist/store/actions/put-operation.js +94 -0
  82. package/dist/store/actions/put-operation.js.map +1 -0
  83. package/dist/store/actions/search-operation.d.ts +11 -0
  84. package/dist/store/actions/search-operation.d.ts.map +1 -0
  85. package/dist/store/actions/search-operation.js +150 -0
  86. package/dist/store/actions/search-operation.js.map +1 -0
  87. package/dist/store/index.d.ts +73 -0
  88. package/dist/store/index.d.ts.map +1 -0
  89. package/dist/store/index.js +145 -0
  90. package/dist/store/index.js.map +1 -0
  91. package/dist/store/types/index.d.ts +97 -0
  92. package/dist/store/types/index.d.ts.map +1 -0
  93. package/dist/store/types/index.js +3 -0
  94. package/dist/store/types/index.js.map +1 -0
  95. package/dist/store/utils/filter.d.ts +16 -0
  96. package/dist/store/utils/filter.d.ts.map +1 -0
  97. package/dist/store/utils/filter.js +71 -0
  98. package/dist/store/utils/filter.js.map +1 -0
  99. package/dist/store/utils/index.d.ts +8 -0
  100. package/dist/store/utils/index.d.ts.map +1 -0
  101. package/dist/store/utils/index.js +28 -0
  102. package/dist/store/utils/index.js.map +1 -0
  103. package/dist/store/utils/result.d.ts +42 -0
  104. package/dist/store/utils/result.d.ts.map +1 -0
  105. package/dist/store/utils/result.js +51 -0
  106. package/dist/store/utils/result.js.map +1 -0
  107. package/dist/store/utils/validation.d.ts +57 -0
  108. package/dist/store/utils/validation.d.ts.map +1 -0
  109. package/dist/store/utils/validation.js +243 -0
  110. package/dist/store/utils/validation.js.map +1 -0
  111. package/package.json +85 -0
package/README.md ADDED
@@ -0,0 +1,960 @@
1
+ # @farukada/aws-langgraph-dynamodb-ts
2
+
3
+ [![npm version](https://badge.fury.io/js/@farukada%2Faws-langgraph-dynamodb-ts.svg)](https://badge.fury.io/js/@farukada%2Faws-langgraph-dynamodb-ts)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Node.js Version](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen)](https://nodejs.org)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.5.3-blue)](https://www.typescriptlang.org/)
7
+ [![AWS SDK](https://img.shields.io/badge/AWS%20SDK-v3-orange)](https://aws.amazon.com/sdk-for-javascript/)
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
+