@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.
Files changed (80) hide show
  1. package/README.md +195 -744
  2. package/dist/checkpointer/actions/get-tuple.d.ts.map +1 -1
  3. package/dist/checkpointer/actions/get-tuple.js.map +1 -1
  4. package/dist/checkpointer/actions/put.d.ts.map +1 -1
  5. package/dist/checkpointer/actions/put.js.map +1 -1
  6. package/dist/checkpointer/index.d.ts +3 -3
  7. package/dist/checkpointer/index.d.ts.map +1 -1
  8. package/dist/checkpointer/index.js +4 -4
  9. package/dist/checkpointer/index.js.map +1 -1
  10. package/dist/checkpointer/utils/validation.d.ts.map +1 -1
  11. package/dist/checkpointer/utils/validation.js +1 -0
  12. package/dist/checkpointer/utils/validation.js.map +1 -1
  13. package/dist/history/actions/add-message.d.ts +13 -0
  14. package/dist/history/actions/add-message.d.ts.map +1 -0
  15. package/dist/history/actions/add-message.js +64 -0
  16. package/dist/history/actions/add-message.js.map +1 -0
  17. package/dist/history/actions/add-messages.d.ts +14 -0
  18. package/dist/history/actions/add-messages.d.ts.map +1 -0
  19. package/dist/history/actions/add-messages.js +65 -0
  20. package/dist/history/actions/add-messages.js.map +1 -0
  21. package/dist/history/actions/clear.d.ts +13 -0
  22. package/dist/history/actions/clear.d.ts.map +1 -0
  23. package/dist/history/actions/clear.js +32 -0
  24. package/dist/history/actions/clear.js.map +1 -0
  25. package/dist/history/actions/get-messages.d.ts +15 -0
  26. package/dist/history/actions/get-messages.d.ts.map +1 -0
  27. package/dist/history/actions/get-messages.js +40 -0
  28. package/dist/history/actions/get-messages.js.map +1 -0
  29. package/dist/history/actions/index.d.ts +9 -0
  30. package/dist/history/actions/index.d.ts.map +1 -0
  31. package/dist/history/actions/index.js +17 -0
  32. package/dist/history/actions/index.js.map +1 -0
  33. package/dist/history/actions/list-sessions.d.ts +14 -0
  34. package/dist/history/actions/list-sessions.d.ts.map +1 -0
  35. package/dist/history/actions/list-sessions.js +56 -0
  36. package/dist/history/actions/list-sessions.js.map +1 -0
  37. package/dist/history/index.d.ts +74 -0
  38. package/dist/history/index.d.ts.map +1 -0
  39. package/dist/history/index.js +125 -0
  40. package/dist/history/index.js.map +1 -0
  41. package/dist/history/types/index.d.ts +131 -0
  42. package/dist/history/types/index.d.ts.map +1 -0
  43. package/dist/history/types/index.js +6 -0
  44. package/dist/history/types/index.js.map +1 -0
  45. package/dist/history/utils/index.d.ts +7 -0
  46. package/dist/history/utils/index.d.ts.map +1 -0
  47. package/dist/history/utils/index.js +25 -0
  48. package/dist/history/utils/index.js.map +1 -0
  49. package/dist/history/utils/title-generator.d.ts +12 -0
  50. package/dist/history/utils/title-generator.d.ts.map +1 -0
  51. package/dist/history/utils/title-generator.js +52 -0
  52. package/dist/history/utils/title-generator.js.map +1 -0
  53. package/dist/history/utils/validation.d.ts +60 -0
  54. package/dist/history/utils/validation.d.ts.map +1 -0
  55. package/dist/history/utils/validation.js +171 -0
  56. package/dist/history/utils/validation.js.map +1 -0
  57. package/dist/index.d.ts +2 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +3 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/shared/utils/retry.d.ts.map +1 -1
  62. package/dist/shared/utils/retry.js +2 -0
  63. package/dist/shared/utils/retry.js.map +1 -1
  64. package/dist/store/actions/get-operation.d.ts.map +1 -1
  65. package/dist/store/actions/get-operation.js.map +1 -1
  66. package/dist/store/actions/list-namespaces-operation.d.ts.map +1 -1
  67. package/dist/store/actions/list-namespaces-operation.js.map +1 -1
  68. package/dist/store/actions/put-operation.d.ts.map +1 -1
  69. package/dist/store/actions/put-operation.js.map +1 -1
  70. package/dist/store/actions/search-operation.d.ts.map +1 -1
  71. package/dist/store/actions/search-operation.js.map +1 -1
  72. package/dist/store/index.d.ts +3 -3
  73. package/dist/store/index.d.ts.map +1 -1
  74. package/dist/store/index.js +4 -4
  75. package/dist/store/index.js.map +1 -1
  76. package/dist/store/utils/validation.d.ts +1 -1
  77. package/dist/store/utils/validation.d.ts.map +1 -1
  78. package/dist/store/utils/validation.js +2 -1
  79. package/dist/store/utils/validation.js.map +1 -1
  80. 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 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.
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
- - 🔍 **Semantic Search**: Optional vector embeddings via AWS Bedrock for semantic search capabilities
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 capabilities, also install:
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
- 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)
40
+ Create the required DynamoDB tables using AWS CDK or Terraform:
49
41
 
50
42
  ```typescript
51
- import * as cdk from 'aws-cdk-lib';
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
- ### Terraform
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
- ```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"
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" "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
-
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 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
-
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
- point_in_time_recovery {
219
- enabled = true
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
- tags = {
223
- Name = "LangGraph Memory"
224
- Environment = "production"
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
- ## Usage
246
-
247
- ### DynamoDBSaver (Checkpoint Saver)
248
-
249
- The checkpoint saver enables persistent state management for LangGraph workflows.
175
+ ## Quick Start
250
176
 
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
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
- // Define your state interface
314
- interface AgentState {
315
- messages: string[];
316
- step: number;
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
- // 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
- },
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
- #### With Semantic Search (Bedrock Embeddings)
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
- // Initialize with embeddings for semantic search
449
- const embeddings = new BedrockEmbeddings({
450
- region: 'us-east-1',
451
- model: 'amazon.titan-embed-text-v1',
209
+ // Without semantic search
210
+ const store = new DynamoDBStore({
211
+ memoryTableName: 'langgraph-memory',
212
+ ttlDays: 90, // Optional
452
213
  });
453
214
 
454
- const store = new DynamoDBStore({
215
+ // With semantic search
216
+ const storeWithEmbeddings = new DynamoDBStore({
455
217
  memoryTableName: 'langgraph-memory',
456
- embedding: embeddings,
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 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
- ],
224
+ // Store and search memories
225
+ await store.batch([
476
226
  {
477
- configurable: { user_id: 'user-123' },
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
- // 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
- ],
233
+ // Search with filters
234
+ const [results] = await store.batch([
491
235
  {
492
- configurable: { user_id: 'user-123' },
236
+ namespacePrefix: ['user'],
237
+ filter: { 'value.color': { $eq: 'dark' } },
238
+ limit: 10,
493
239
  }
494
- );
240
+ ], { configurable: { user_id: 'user-123' } });
495
241
 
496
- // Results include similarity scores
497
- semanticResults.forEach(result => {
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
- #### With LangGraph Agent
246
+ ### DynamoDBChatMessageHistory (Chat History)
503
247
 
504
248
  ```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';
249
+ import { DynamoDBChatMessageHistory } from '@farukada/aws-langgraph-dynamodb-ts';
250
+ import { HumanMessage, AIMessage } from '@langchain/core/messages';
509
251
 
510
- const embeddings = new BedrockEmbeddings({
511
- region: 'us-east-1',
512
- model: 'amazon.titan-embed-text-v1',
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
- const store = new DynamoDBStore({
516
- memoryTableName: 'langgraph-memory',
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
- interface AgentState {
521
- messages: string[];
522
- relevantMemories: any[];
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
- 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
- }
267
+ // Get all messages for a session
268
+ const messages = await history.getMessages('user-123', 'session-456');
543
269
 
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
- }
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
- // 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 });
274
+ // Clear a session
275
+ await history.clear('user-123', 'session-456');
599
276
  ```
600
277
 
601
- ### Complete Example: Agent with Memory and Checkpointing
278
+ ## API Reference
602
279
 
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';
280
+ For detailed API documentation, see the [TypeDoc documentation](./docs/README.md):
608
281
 
609
- // Setup infrastructure components
610
- const embeddings = new BedrockEmbeddings({
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
- 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
- });
285
+ ## Advanced Features
713
286
 
714
- // Run conversation with persistence
715
- const userId = 'user-123';
716
- const threadId = 'conversation-456';
287
+ ### Memory Store Filters
717
288
 
718
- const result = await app.invoke(
719
- {
720
- messages: ['What is machine learning?'],
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
- configurable: {
726
- thread_id: threadId,
727
- user_id: userId,
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
- console.log('Agent Response:', result);
303
+ ### Semantic Search
733
304
 
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
- },
305
+ ```typescript
306
+ // Requires embedding configuration
307
+ const [results] = await store.batch([
741
308
  {
742
- configurable: {
743
- thread_id: threadId,
744
- user_id: userId,
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
- 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
- );
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
- // User preferences
809
- ['user', 'userId', 'preferences']
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
- Your application needs the following IAM permissions:
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
- ```bash
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! Since this package is in active development, please:
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. Make your changes with tests
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
- **Note**: This package is under active development. Star the repository on GitHub to stay updated with new features and improvements!
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