Skip to content

Commit 6b795e7

Browse files
author
Yuriy Bezsonov
committed
improve app, refactor, add comment
1 parent 6bf07a1 commit 6b795e7

File tree

10 files changed

+147
-171
lines changed

10 files changed

+147
-171
lines changed

samples/spring-ai-te-agent/ai-agent/src/main/java/com/example/ai/agent/controller/ChatController.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
import com.example.ai.agent.service.ConversationSummaryService;
66
import com.example.ai.agent.service.DocumentChatService;
77
import org.springframework.http.MediaType;
8-
import org.springframework.web.bind.annotation.PostMapping;
9-
import org.springframework.web.bind.annotation.RequestBody;
10-
import org.springframework.web.bind.annotation.RequestMapping;
11-
import org.springframework.web.bind.annotation.RestController;
8+
import org.springframework.web.bind.annotation.*;
129
import org.slf4j.Logger;
1310
import org.slf4j.LoggerFactory;
1411
import reactor.core.publisher.Flux;
@@ -39,11 +36,10 @@ public Flux<String> chat(@RequestBody ChatRequest request, Principal principal)
3936
String userId = getUserId(request.userId(), principal);
4037
chatMemoryService.setCurrentUserId(userId);
4138

42-
if (hasFile(request)) {
43-
return documentChatService.processDocument(request.prompt(), request.fileBase64(), request.fileName());
44-
} else {
45-
return chatService.processChat(request.prompt());
46-
}
39+
// Route to document analysis or regular chat
40+
return hasFile(request)
41+
? documentChatService.processDocument(request.prompt(), request.fileBase64(), request.fileName())
42+
: chatService.processChat(request.prompt());
4743
}
4844

4945
@PostMapping("summarize")
@@ -58,9 +54,11 @@ public String summarize(@RequestBody(required = false) SummarizeRequest request,
5854
}
5955

6056
private String getUserId(String requestUserId, Principal principal) {
57+
// Production: use authenticated user from Spring Security
6158
if (principal != null) {
6259
return principal.getName();
6360
}
61+
// Development: use provided userId or default
6462
return requestUserId != null ? requestUserId : "user1";
6563
}
6664

samples/spring-ai-te-agent/ai-agent/src/main/java/com/example/ai/agent/controller/WebViewController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class WebViewController {
1616

1717
@GetMapping("/")
1818
public String index(Model model) {
19+
// Pass feature flags to UI for conditional rendering
1920
model.addAttribute("multiUserEnabled", multiUserEnabled);
2021
model.addAttribute("multiModalEnabled", multiModalEnabled);
2122
return "chat";

samples/spring-ai-te-agent/ai-agent/src/main/java/com/example/ai/agent/service/ChatMemoryService.java

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,10 @@
44
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
55
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
66
import org.springframework.ai.chat.memory.repository.jdbc.PostgresChatMemoryRepositoryDialect;
7-
import org.springframework.ai.chat.messages.UserMessage;
8-
import org.springframework.ai.chat.messages.AssistantMessage;
9-
import org.springframework.ai.chat.messages.SystemMessage;
10-
import org.springframework.ai.chat.messages.Message;
7+
import org.springframework.ai.chat.messages.*;
118
import org.springframework.ai.chat.prompt.Prompt;
129
import reactor.core.publisher.Flux;
13-
1410
import org.springframework.stereotype.Service;
15-
1611
import org.slf4j.Logger;
1712
import org.slf4j.LoggerFactory;
1813
import javax.sql.DataSource;
@@ -25,15 +20,14 @@ public class ChatMemoryService {
2520
private static final int MAX_CONTEXT_SUMMARIES = 10;
2621
private static final int MAX_PREFERENCES = 1;
2722

23+
// Three-tier memory: Session (recent), Context (summaries), Preferences (profile)
2824
private final MessageWindowChatMemory sessionMemory;
2925
private final MessageWindowChatMemory contextMemory;
3026
private final MessageWindowChatMemory preferencesMemory;
31-
32-
// Thread-local to store current userId per request
3327
private final ThreadLocal<String> currentUserId = ThreadLocal.withInitial(() -> "user1");
3428

3529
public ChatMemoryService(DataSource dataSource) {
36-
30+
// Single JDBC repository shared by all three memory tiers
3731
var jdbcRepository = JdbcChatMemoryRepository.builder()
3832
.dataSource(dataSource)
3933
.dialect(new PostgresChatMemoryRepositoryDialect())
@@ -55,79 +49,50 @@ public ChatMemoryService(DataSource dataSource) {
5549
.build();
5650
}
5751

58-
public MessageWindowChatMemory getSessionMemory() {
59-
return sessionMemory;
60-
}
61-
62-
public MessageWindowChatMemory getContextMemory() {
63-
return contextMemory;
64-
}
65-
66-
public MessageWindowChatMemory getPreferencesMemory() {
67-
return preferencesMemory;
68-
}
69-
70-
public void setCurrentUserId(String userId) {
71-
this.currentUserId.set(userId);
72-
}
73-
74-
public String getCurrentConversationId() {
75-
return currentUserId.get();
76-
}
77-
7852
public Flux<String> callWithMemory(ChatClient chatClient, String prompt) {
7953
String conversationId = getCurrentConversationId();
8054

81-
// Check if first message - load previous context from JDBC
55+
// 1. Auto-load previous context on first message
8256
if (sessionMemory.get(conversationId).isEmpty()) {
8357
loadPreviousContext(conversationId, chatClient);
8458
}
8559

86-
// Add user message to session memory
87-
UserMessage userMessage = new UserMessage(prompt);
88-
sessionMemory.add(conversationId, userMessage);
60+
// 2. Add user message to session memory
61+
sessionMemory.add(conversationId, new UserMessage(prompt));
8962

90-
// Stream response with conversation history
63+
// 3. Stream AI response with full conversation history
9164
StringBuilder fullResponse = new StringBuilder();
92-
9365
return chatClient
9466
.prompt(new Prompt(sessionMemory.get(conversationId)))
9567
.stream()
9668
.content()
97-
.doOnNext(chunk -> {
98-
fullResponse.append(chunk);
99-
logger.debug("Streaming chunk: {} chars", chunk.length());
100-
})
69+
.doOnNext(fullResponse::append)
10170
.doOnComplete(() -> {
102-
// Add complete assistant response to session memory after streaming completes
71+
// 4. Save complete response to session memory
10372
String responseText = fullResponse.toString();
104-
if (responseText != null && !responseText.isEmpty()) {
105-
AssistantMessage assistantMessage = new AssistantMessage(responseText);
106-
sessionMemory.add(conversationId, assistantMessage);
107-
logger.info("Added assistant response to memory: {} chars", responseText.length());
73+
if (!responseText.isEmpty()) {
74+
sessionMemory.add(conversationId, new AssistantMessage(responseText));
75+
logger.info("Saved response to memory: {} chars", responseText.length());
10876
}
109-
logger.info("Completed streaming response.");
11077
});
11178
}
11279

11380
private void loadPreviousContext(String conversationId, ChatClient chatClient) {
114-
logger.info("Loading previous context for conversation: {}", conversationId);
81+
logger.info("Loading previous context for: {}", conversationId);
11582

116-
// Load user preferences (userId_preferences)
83+
// 1. Load preferences (userId_preferences) and context (userId_context)
11784
List<Message> preferences = preferencesMemory.get(conversationId + "_preferences");
11885
String preferencesText = preferences.isEmpty() ? "" : preferences.get(0).getText();
119-
120-
// Load context summaries (userId_context)
12186
List<Message> summaries = contextMemory.get(conversationId + "_context");
12287

12388
if (summaries.isEmpty() && preferencesText.isEmpty()) {
12489
logger.info("No previous context found");
12590
return;
12691
}
12792

128-
logger.info("Found {} context summaries and {} preferences", summaries.size(), preferences.isEmpty() ? 0 : 1);
93+
logger.info("Found {} summaries, {} preferences", summaries.size(), preferences.isEmpty() ? 0 : 1);
12994

130-
// Combine preferences and summaries
95+
// 2. Combine preferences and summaries
13196
StringBuilder contextBuilder = new StringBuilder();
13297
if (!preferencesText.isEmpty()) {
13398
contextBuilder.append("User Preferences:\n").append(preferencesText).append("\n\n");
@@ -137,13 +102,15 @@ private void loadPreviousContext(String conversationId, ChatClient chatClient) {
137102
summaries.forEach(msg -> contextBuilder.append(msg.getText()).append("\n\n"));
138103
}
139104

140-
String combinedContext = contextBuilder.toString();
105+
// 3. Summarize combined context with AI
141106
var chatResponse = chatClient.prompt()
142-
.user("Summarize this user information concisely:\n\n" + combinedContext)
107+
.user("Summarize this user information concisely:\n\n" + contextBuilder)
143108
.call()
144109
.chatResponse();
145110

146-
String contextSummary = (chatResponse != null && chatResponse.getResult() != null && chatResponse.getResult().getOutput() != null)
111+
String contextSummary = (chatResponse != null &&
112+
chatResponse.getResult() != null &&
113+
chatResponse.getResult().getOutput() != null)
147114
? chatResponse.getResult().getOutput().getText()
148115
: null;
149116

@@ -152,13 +119,34 @@ private void loadPreviousContext(String conversationId, ChatClient chatClient) {
152119
return;
153120
}
154121

155-
logger.info("Loaded context summary: {}", contextSummary);
156-
122+
// 4. Add context as system message to session memory
157123
String contextMessage = String.format(
158124
"You are continuing a conversation with this user. Here is what you know:\n\n%s\n\n" +
159125
"Use this information to provide personalized responses.",
160126
contextSummary
161127
);
162128
sessionMemory.add(conversationId, new SystemMessage(contextMessage));
129+
logger.info("Loaded context summary");
130+
}
131+
132+
// Getters and setters
133+
public MessageWindowChatMemory getSessionMemory() {
134+
return sessionMemory;
135+
}
136+
137+
public MessageWindowChatMemory getContextMemory() {
138+
return contextMemory;
139+
}
140+
141+
public MessageWindowChatMemory getPreferencesMemory() {
142+
return preferencesMemory;
143+
}
144+
145+
public void setCurrentUserId(String userId) {
146+
this.currentUserId.set(userId);
147+
}
148+
149+
public String getCurrentConversationId() {
150+
return currentUserId.get();
163151
}
164152
}

samples/spring-ai-te-agent/ai-agent/src/main/java/com/example/ai/agent/service/ChatService.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import org.slf4j.LoggerFactory;
55
import org.springframework.stereotype.Service;
66
import reactor.core.publisher.Flux;
7-
87
import org.springframework.ai.chat.client.ChatClient;
98
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
109
import org.springframework.ai.vectorstore.VectorStore;
@@ -36,20 +35,19 @@ public ChatService(ChatMemoryService chatMemoryService,
3635
ToolCallbackProvider tools,
3736
ChatClient.Builder chatClientBuilder) {
3837

38+
// Build ChatClient with RAG, tools, and memory
3939
this.chatClient = chatClientBuilder
4040
.defaultSystem(SYSTEM_PROMPT)
41-
.defaultAdvisors(
42-
QuestionAnswerAdvisor.builder(vectorStore).build()
43-
)
44-
.defaultTools(dateTimeService, weatherService)
45-
.defaultToolCallbacks(tools)
41+
.defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore).build()) // RAG for policies
42+
.defaultTools(dateTimeService, weatherService) // Custom tools
43+
.defaultToolCallbacks(tools) // Auto-registered tools from MCP
4644
.build();
4745

4846
this.chatMemoryService = chatMemoryService;
4947
}
5048

5149
public Flux<String> processChat(String prompt) {
52-
logger.info("Processing streaming chat request - prompt: '{}'", prompt);
50+
logger.info("Processing chat: '{}'", prompt);
5351
try {
5452
// Simple streaming without memory:
5553
// return chatClient
@@ -58,7 +56,7 @@ public Flux<String> processChat(String prompt) {
5856
// .content();
5957
return chatMemoryService.callWithMemory(chatClient, prompt);
6058
} catch (Exception e) {
61-
logger.error("Error processing streaming chat request", e);
59+
logger.error("Error processing chat", e);
6260
return Flux.just("I don't know - there was an error processing your request.");
6361
}
6462
}

0 commit comments

Comments
 (0)