Skip to content

Commit 7f943d3

Browse files
committed
Adapt chat memory to Spring AI 1.1.6 mandatory conversationId
Spring AI 1.1.6 removed default conversationId from memory advisors. Move memory advisor from default ChatClient advisors to per-request, activated only when CamelSpringAiChatConversationId header is set.
1 parent a6c4721 commit 7f943d3

3 files changed

Lines changed: 29 additions & 27 deletions

File tree

components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/docs/spring-ai-chat-component.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ The component provides automatic conversation memory management via Spring AI's
293293

294294
IMPORTANT: Do not configure both `chatMemory` and `chatMemoryVectorStore` on the same endpoint. If both are provided, `chatMemory` (MessageChatMemoryAdvisor) will take precedence.
295295

296+
NOTE: Chat memory is only activated when the `CamelSpringAiChatConversationId` header is set on the exchange. Without this header, the memory advisor is not applied, even if `chatMemory` or `chatMemoryVectorStore` is configured. This allows you to selectively enable memory on a per-request basis.
297+
296298
==== Message-based Memory (ChatMemory)
297299

298300
Configure a `ChatMemory` on the endpoint for automatic conversation tracking using traditional message window approach. This strategy keeps a configurable number of recent messages in memory.

components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatProducer.java

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public class SpringAiChatProducer extends DefaultProducer {
7676

7777
private ChatClient chatClient;
7878
private SpringAiChatMcpManager mcpManager;
79+
private Advisor chatMemoryAdvisor;
7980

8081
public SpringAiChatProducer(SpringAiChatEndpoint endpoint) {
8182
super(endpoint);
@@ -120,6 +121,23 @@ protected void doStart() throws Exception {
120121
this.chatClient = builder.build();
121122
}
122123

124+
// Build chat memory advisor (added per-request only when conversationId is set)
125+
ChatMemory chatMemory = getEndpoint().getConfiguration().getChatMemory();
126+
VectorStore chatMemoryVectorStore = getEndpoint().getConfiguration().getChatMemoryVectorStore();
127+
if (chatMemory != null && chatMemoryVectorStore != null) {
128+
LOG.warn("Both chatMemory and chatMemoryVectorStore are configured. Using MessageChatMemoryAdvisor (chatMemory). "
129+
+ "Configure only one memory type.");
130+
}
131+
if (chatMemory != null) {
132+
this.chatMemoryAdvisor = MessageChatMemoryAdvisor.builder(chatMemory).build();
133+
LOG.debug("MessageChatMemoryAdvisor available (activated per-request via conversationId header)");
134+
} else if (chatMemoryVectorStore != null) {
135+
this.chatMemoryAdvisor = VectorStoreChatMemoryAdvisor.builder(chatMemoryVectorStore)
136+
.defaultTopK(getEndpoint().getConfiguration().getTopK())
137+
.build();
138+
LOG.debug("VectorStoreChatMemoryAdvisor available with topK={}", getEndpoint().getConfiguration().getTopK());
139+
}
140+
123141
// Initialize MCP clients if configured
124142
Map<String, Object> mcpConfig = getEndpoint().getConfiguration().getMcpServer();
125143
if (mcpConfig != null && !mcpConfig.isEmpty()) {
@@ -613,10 +631,12 @@ private void applyRequestOptions(ChatClient.ChatClientRequestSpec request, Excha
613631
LOG.debug("Added tool context with {} entries", toolContext.size());
614632
}
615633

616-
// Apply conversation ID for chat memory if provided
634+
// Add chat memory advisor per-request only when conversationId header is set
617635
String conversationId = exchange.getIn().getHeader(SpringAiChatConstants.CONVERSATION_ID, String.class);
618-
if (conversationId != null) {
619-
request.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId));
636+
if (conversationId != null && chatMemoryAdvisor != null) {
637+
final String convId = conversationId;
638+
request.advisors(chatMemoryAdvisor);
639+
request.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, convId));
620640
}
621641

622642
// Apply SafeGuard advisor overrides if provided via headers
@@ -1179,28 +1199,8 @@ private List<Advisor> buildDefaultAdvisors() {
11791199
advisors.add(safeguardAdvisor);
11801200
}
11811201

1182-
// Add ChatMemory advisor if configured
1183-
ChatMemory chatMemory = getEndpoint().getConfiguration().getChatMemory();
1184-
VectorStore chatMemoryVectorStore = getEndpoint().getConfiguration().getChatMemoryVectorStore();
1185-
1186-
if (chatMemory != null && chatMemoryVectorStore != null) {
1187-
LOG.warn("Both chatMemory and chatMemoryVectorStore are configured. Using MessageChatMemoryAdvisor (chatMemory). " +
1188-
"Configure only one memory type.");
1189-
}
1190-
1191-
if (chatMemory != null) {
1192-
advisors.add(MessageChatMemoryAdvisor.builder(chatMemory).build());
1193-
LOG.debug("MessageChatMemoryAdvisor enabled");
1194-
} else if (chatMemoryVectorStore != null) {
1195-
// Configure VectorStoreChatMemoryAdvisor with conversation isolation
1196-
// The conversationId parameter enables automatic filtering by conversation ID
1197-
advisors.add(VectorStoreChatMemoryAdvisor.builder(chatMemoryVectorStore)
1198-
.conversationId(ChatMemory.CONVERSATION_ID)
1199-
.defaultTopK(getEndpoint().getConfiguration().getTopK())
1200-
.build());
1201-
LOG.debug("VectorStoreChatMemoryAdvisor enabled with conversation isolation and topK={}",
1202-
getEndpoint().getConfiguration().getTopK());
1203-
}
1202+
// Chat memory advisors are NOT added to defaults — they are added per-request
1203+
// only when a conversationId header is present (see applyRequestOptions)
12041204

12051205
// Add QuestionAnswerAdvisor if VectorStore is configured
12061206
VectorStore vectorStore = getEndpoint().getConfiguration().getVectorStore();

components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMemoryIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public void testAutomaticChatMemoryWithChatClient() {
165165
// First message
166166
var exchange1 = template().request("direct:chat-with-auto-memory", e -> {
167167
e.getIn().setBody("My favorite number is 42. Please remember this.");
168-
e.getIn().setHeader("conversationId", conversationId);
168+
e.getIn().setHeader(SpringAiChatConstants.CONVERSATION_ID, conversationId);
169169
});
170170

171171
String response1 = exchange1.getMessage().getBody(String.class);
@@ -174,7 +174,7 @@ public void testAutomaticChatMemoryWithChatClient() {
174174
// Second message - the advisor should remember the context
175175
var exchange2 = template().request("direct:chat-with-auto-memory", e -> {
176176
e.getIn().setBody("What is my favorite number? Answer with just the number.");
177-
e.getIn().setHeader("conversationId", conversationId);
177+
e.getIn().setHeader(SpringAiChatConstants.CONVERSATION_ID, conversationId);
178178
});
179179

180180
String response2 = exchange2.getMessage().getBody(String.class);

0 commit comments

Comments
 (0)