0
# Prebuilt Components
1
2
Ready-to-use components for common patterns including message handling, specialized state management, and workflow templates.
3
4
## Capabilities
5
6
### MessagesState
7
8
Specialized agent state for handling message-based workflows, commonly used for chat applications and conversational AI systems.
9
10
```java { .api }
11
/**
12
* Agent state specialized for message collections
13
* @param <T> Type of messages stored in the state
14
*/
15
class MessagesState<T> extends AgentState {
16
/**
17
* Default schema with appender channel for messages
18
*/
19
static final Map<String, Channel<?>> SCHEMA;
20
21
/**
22
* Creates MessagesState with initial data
23
* @param initData Initial state data including messages
24
*/
25
MessagesState(Map<String, Object> initData);
26
27
/**
28
* Retrieves the list of messages
29
* @return List of messages in the state
30
* @throws RuntimeException if messages key not found
31
*/
32
List<T> messages();
33
34
/**
35
* Gets the most recent message
36
* @return Optional containing last message if present
37
*/
38
Optional<T> lastMessage();
39
40
/**
41
* Gets message at position N from the end
42
* @param n Position from end (0 = last, 1 = second to last, etc.)
43
* @return Optional containing message at position if exists
44
*/
45
Optional<T> lastMinus(int n);
46
}
47
```
48
49
**Usage Examples:**
50
51
```java
52
// String-based messages
53
class StringMessagesState extends MessagesState<String> {
54
public StringMessagesState(Map<String, Object> initData) {
55
super(initData);
56
}
57
}
58
59
// Create initial state with messages
60
List<String> initialMessages = List.of("Hello", "How can I help?");
61
Map<String, Object> initData = Map.of("messages", new ArrayList<>(initialMessages));
62
StringMessagesState state = new StringMessagesState(initData);
63
64
// Access messages
65
List<String> allMessages = state.messages();
66
Optional<String> lastMsg = state.lastMessage();
67
Optional<String> secondToLast = state.lastMinus(1);
68
69
System.out.println("All messages: " + allMessages);
70
System.out.println("Last message: " + lastMsg.orElse("None"));
71
System.out.println("Previous message: " + secondToLast.orElse("None"));
72
73
// Custom message type
74
class ChatMessage {
75
private final String content;
76
private final String sender;
77
private final long timestamp;
78
79
public ChatMessage(String content, String sender) {
80
this.content = content;
81
this.sender = sender;
82
this.timestamp = System.currentTimeMillis();
83
}
84
85
// getters...
86
public String getContent() { return content; }
87
public String getSender() { return sender; }
88
public long getTimestamp() { return timestamp; }
89
}
90
91
class ChatMessagesState extends MessagesState<ChatMessage> {
92
public ChatMessagesState(Map<String, Object> initData) {
93
super(initData);
94
}
95
}
96
97
// Use with custom message type
98
List<ChatMessage> chatMessages = List.of(
99
new ChatMessage("Hello there!", "user"),
100
new ChatMessage("Hi! How can I assist you?", "assistant")
101
);
102
103
ChatMessagesState chatState = new ChatMessagesState(
104
Map.of("messages", new ArrayList<>(chatMessages))
105
);
106
107
Optional<ChatMessage> lastChatMsg = chatState.lastMessage();
108
if (lastChatMsg.isPresent()) {
109
System.out.println("Last from: " + lastChatMsg.get().getSender());
110
System.out.println("Content: " + lastChatMsg.get().getContent());
111
}
112
```
113
114
### MessagesStateGraph
115
116
Specialized StateGraph for MessagesState that automatically configures message appending behavior.
117
118
```java { .api }
119
/**
120
* StateGraph specialized for MessagesState
121
* @param <T> Type of messages in the state
122
*/
123
class MessagesStateGraph<T> extends StateGraph<MessagesState<T>> {
124
/**
125
* Creates MessagesStateGraph with default constructor factory
126
*/
127
MessagesStateGraph();
128
129
/**
130
* Creates MessagesStateGraph with custom state serializer
131
* @param stateSerializer Serializer for MessagesState instances
132
*/
133
MessagesStateGraph(StateSerializer<MessagesState<T>> stateSerializer);
134
}
135
```
136
137
**Usage Examples:**
138
139
```java
140
// Simple messages graph with default serialization
141
MessagesStateGraph<String> simpleMessagesGraph = new MessagesStateGraph<>();
142
143
// Add nodes that work with messages
144
simpleMessagesGraph.addNode("receive_input", (state) -> {
145
String userInput = state.<String>value("user_input").orElse("");
146
// Messages are automatically appended due to MessagesState.SCHEMA
147
return CompletableFuture.completedFuture(Map.of("messages", List.of(userInput)));
148
});
149
150
simpleMessagesGraph.addNode("generate_response", (state) -> {
151
List<String> messages = state.messages();
152
String lastMessage = state.lastMessage().orElse("");
153
154
// Simple echo response
155
String response = "Echo: " + lastMessage;
156
return CompletableFuture.completedFuture(Map.of("messages", List.of(response)));
157
});
158
159
simpleMessagesGraph.addNode("display_conversation", (state) -> {
160
List<String> messages = state.messages();
161
System.out.println("Conversation history:");
162
for (int i = 0; i < messages.size(); i++) {
163
System.out.println((i + 1) + ". " + messages.get(i));
164
}
165
return CompletableFuture.completedFuture(Map.of("displayed", true));
166
});
167
168
// Set up flow
169
simpleMessagesGraph.addEdge(StateGraph.START, "receive_input");
170
simpleMessagesGraph.addEdge("receive_input", "generate_response");
171
simpleMessagesGraph.addEdge("generate_response", "display_conversation");
172
simpleMessagesGraph.addEdge("display_conversation", StateGraph.END);
173
174
// Compile and run
175
CompiledGraph<MessagesState<String>> app = simpleMessagesGraph.compile();
176
Optional<MessagesState<String>> result = app.invoke(Map.of("user_input", "Hello AI!"));
177
178
if (result.isPresent()) {
179
List<String> finalMessages = result.get().messages();
180
System.out.println("Final conversation: " + finalMessages);
181
}
182
183
// Advanced messages graph with custom serialization
184
StateSerializer<MessagesState<ChatMessage>> chatSerializer =
185
new JacksonStateSerializer<>(ChatMessagesState::new);
186
187
MessagesStateGraph<ChatMessage> chatGraph = new MessagesStateGraph<>(chatSerializer);
188
189
// Add sophisticated chat nodes
190
chatGraph.addNode("process_user_message", (state) -> {
191
String userInput = state.<String>value("user_input").orElse("");
192
ChatMessage userMsg = new ChatMessage(userInput, "user");
193
194
return CompletableFuture.completedFuture(Map.of(
195
"messages", List.of(userMsg),
196
"last_user_input", userInput
197
));
198
});
199
200
chatGraph.addNode("generate_ai_response", (state) -> {
201
String lastUserInput = state.<String>value("last_user_input").orElse("");
202
203
// Simulate AI processing based on conversation history
204
List<ChatMessage> history = state.messages();
205
String context = "Previous messages: " + history.size();
206
String response = generateAIResponse(lastUserInput, context);
207
208
ChatMessage aiMsg = new ChatMessage(response, "assistant");
209
210
return CompletableFuture.completedFuture(Map.of(
211
"messages", List.of(aiMsg),
212
"response_generated", true
213
));
214
});
215
216
// Chat routing based on message analysis
217
chatGraph.addNode("analyze_intent", (state, config) -> {
218
Optional<ChatMessage> lastMsg = state.lastMessage();
219
220
return CompletableFuture.supplyAsync(() -> {
221
if (lastMsg.isPresent() && "user".equals(lastMsg.get().getSender())) {
222
String content = lastMsg.get().getContent().toLowerCase();
223
224
String intent;
225
if (content.contains("goodbye") || content.contains("bye")) {
226
intent = "farewell";
227
} else if (content.contains("help") || content.contains("?")) {
228
intent = "help_request";
229
} else {
230
intent = "general_chat";
231
}
232
233
return new Command(intent, Map.of("detected_intent", intent));
234
}
235
236
return new Command("general_chat", Map.of());
237
});
238
}, Map.of(
239
"farewell", "farewell_handler",
240
"help_request", "help_handler",
241
"general_chat", "general_chat_handler"
242
));
243
244
private String generateAIResponse(String input, String context) {
245
// Simulate AI response generation
246
return "AI response to: " + input + " (Context: " + context + ")";
247
}
248
```
249
250
### Message Processing Patterns
251
252
Common patterns for working with message-based workflows.
253
254
```java { .api }
255
// Message filtering and transformation
256
AsyncNodeAction<MessagesState<ChatMessage>> messageFilter = (state) -> {
257
List<ChatMessage> messages = state.messages();
258
259
// Filter out system messages older than 1 hour
260
long oneHourAgo = System.currentTimeMillis() - 3600000;
261
List<ChatMessage> filtered = messages.stream()
262
.filter(msg -> !"system".equals(msg.getSender()) || msg.getTimestamp() > oneHourAgo)
263
.collect(Collectors.toList());
264
265
return CompletableFuture.completedFuture(Map.of(
266
"messages", filtered,
267
"filtered_count", messages.size() - filtered.size()
268
));
269
};
270
271
// Message summarization
272
AsyncNodeAction<MessagesState<String>> messageSummarizer = (state) -> {
273
List<String> messages = state.messages();
274
275
return CompletableFuture.supplyAsync(() -> {
276
if (messages.size() > 10) {
277
// Summarize older messages, keep recent ones
278
List<String> recent = messages.subList(messages.size() - 5, messages.size());
279
String summary = "Summary of " + (messages.size() - 5) + " earlier messages: " +
280
messages.subList(0, messages.size() - 5).stream()
281
.collect(Collectors.joining(", "));
282
283
List<String> condensed = new ArrayList<>();
284
condensed.add(summary);
285
condensed.addAll(recent);
286
287
return Map.of("messages", condensed, "summarized", true);
288
}
289
290
return Map.of("summarized", false);
291
});
292
};
293
294
// Message validation
295
AsyncNodeAction<MessagesState<String>> messageValidator = (state) -> {
296
Optional<String> lastMsg = state.lastMessage();
297
298
return CompletableFuture.completedFuture(
299
lastMsg.map(msg -> {
300
boolean isValid = msg.length() > 0 && msg.length() <= 1000 &&
301
!msg.trim().isEmpty();
302
303
Map<String, Object> updates = new HashMap<>();
304
updates.put("last_message_valid", isValid);
305
306
if (!isValid) {
307
updates.put("messages", List.of("Please provide a valid message (1-1000 characters)"));
308
}
309
310
return updates;
311
}).orElse(Map.of("last_message_valid", false))
312
);
313
};
314
```
315
316
### Conversation Flow Management
317
318
Advanced patterns for managing conversational workflows.
319
320
```java { .api }
321
// Conversation state tracking
322
class ConversationState extends MessagesState<ChatMessage> {
323
public ConversationState(Map<String, Object> initData) {
324
super(initData);
325
}
326
327
public Optional<String> getCurrentTopic() {
328
return this.value("current_topic");
329
}
330
331
public Optional<Integer> getMessageCount() {
332
return Optional.of(messages().size());
333
}
334
335
public boolean hasRecentActivity() {
336
return lastMessage()
337
.map(msg -> System.currentTimeMillis() - msg.getTimestamp() < 300000) // 5 min
338
.orElse(false);
339
}
340
}
341
342
// Conversation management graph
343
MessagesStateGraph<ChatMessage> conversationGraph = new MessagesStateGraph<ChatMessage>() {
344
@Override
345
protected ConversationState getStateFactory() {
346
return ConversationState::new;
347
}
348
};
349
350
// Topic tracking node
351
conversationGraph.addNode("track_topic", (state) -> {
352
Optional<ChatMessage> lastMsg = state.lastMessage();
353
354
return CompletableFuture.supplyAsync(() -> {
355
String topic = "general";
356
357
if (lastMsg.isPresent()) {
358
String content = lastMsg.get().getContent().toLowerCase();
359
if (content.contains("weather")) topic = "weather";
360
else if (content.contains("food") || content.contains("recipe")) topic = "cooking";
361
else if (content.contains("help") || content.contains("support")) topic = "support";
362
}
363
364
return Map.of(
365
"current_topic", topic,
366
"topic_changed", !topic.equals(state.<String>value("current_topic").orElse("general"))
367
);
368
});
369
});
370
371
// Context-aware response generation
372
conversationGraph.addNode("contextual_response", (state) -> {
373
List<ChatMessage> messages = state.messages();
374
String currentTopic = state.<String>value("current_topic").orElse("general");
375
376
return CompletableFuture.supplyAsync(() -> {
377
// Generate response based on conversation context
378
String response = generateContextualResponse(messages, currentTopic);
379
ChatMessage aiResponse = new ChatMessage(response, "assistant");
380
381
return Map.of(
382
"messages", List.of(aiResponse),
383
"response_context", currentTopic
384
);
385
});
386
});
387
388
// Conversation cleanup
389
conversationGraph.addNode("cleanup_old_messages", (state) -> {
390
List<ChatMessage> messages = state.messages();
391
392
return CompletableFuture.completedFuture(
393
messages.size() > 50 ?
394
Map.of("messages", messages.subList(messages.size() - 30, messages.size())) :
395
Map.of("cleanup_performed", false)
396
);
397
});
398
399
private String generateContextualResponse(List<ChatMessage> messages, String topic) {
400
// Context-aware response generation logic
401
return "Response for topic '" + topic + "' based on " + messages.size() + " messages";
402
}
403
```
404
405
### Integration with Standard StateGraph
406
407
Combining MessagesState with regular StateGraph features.
408
409
```java { .api }
410
// Hybrid state combining messages with other data
411
class HybridState extends MessagesState<String> {
412
public HybridState(Map<String, Object> initData) {
413
super(initData);
414
}
415
416
public Optional<Integer> getScore() {
417
return this.value("score");
418
}
419
420
public Optional<String> getUserId() {
421
return this.value("user_id");
422
}
423
}
424
425
// Custom schema combining messages with other channels
426
Map<String, Channel<?>> hybridSchema = new HashMap<>(MessagesState.SCHEMA);
427
hybridSchema.put("score", (key, current, newVal) ->
428
Math.max((Integer) (current != null ? current : 0), (Integer) newVal));
429
hybridSchema.put("analytics", Channels.appender(ArrayList::new));
430
431
// Create graph with hybrid schema
432
StateGraph<HybridState> hybridGraph = new StateGraph<>(hybridSchema, HybridState::new);
433
434
hybridGraph.addNode("process_message_with_scoring", (state) -> {
435
Optional<String> lastMsg = state.lastMessage();
436
int currentScore = state.getScore().orElse(0);
437
438
return CompletableFuture.supplyAsync(() -> {
439
Map<String, Object> updates = new HashMap<>();
440
441
if (lastMsg.isPresent()) {
442
String msg = lastMsg.get();
443
updates.put("messages", List.of("Processed: " + msg));
444
445
// Calculate score based on message
446
int msgScore = msg.length() > 10 ? 10 : 5;
447
updates.put("score", msgScore);
448
449
// Add analytics event
450
updates.put("analytics", List.of(Map.of(
451
"event", "message_processed",
452
"score_delta", msgScore,
453
"timestamp", System.currentTimeMillis()
454
)));
455
}
456
457
return updates;
458
});
459
});
460
461
// Compile and use hybrid graph
462
CompiledGraph<HybridState> hybridApp = hybridGraph.compile();
463
Optional<HybridState> hybridResult = hybridApp.invoke(Map.of(
464
"messages", List.of("Hello hybrid world!"),
465
"user_id", "user123",
466
"score", 0
467
));
468
469
if (hybridResult.isPresent()) {
470
HybridState finalState = hybridResult.get();
471
System.out.println("Messages: " + finalState.messages());
472
System.out.println("Final score: " + finalState.getScore().orElse(0));
473
System.out.println("User: " + finalState.getUserId().orElse("unknown"));
474
}
475
```