This is an automated email from the ASF dual-hosted git repository.

Croway pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 7f943d3bbbf6925e198fc4cd8612ea16954111ee
Author: Croway <[email protected]>
AuthorDate: Mon May 11 11:59:43 2026 +0200

    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.
---
 .../src/main/docs/spring-ai-chat-component.adoc    |  2 +
 .../springai/chat/SpringAiChatProducer.java        | 50 +++++++++++-----------
 .../springai/chat/SpringAiChatMemoryIT.java        |  4 +-
 3 files changed, 29 insertions(+), 27 deletions(-)

diff --git 
a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/docs/spring-ai-chat-component.adoc
 
b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/docs/spring-ai-chat-component.adoc
index 6b3054cc229e..80af04348f10 100644
--- 
a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/docs/spring-ai-chat-component.adoc
+++ 
b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/docs/spring-ai-chat-component.adoc
@@ -293,6 +293,8 @@ The component provides automatic conversation memory 
management via Spring AI's
 
 IMPORTANT: Do not configure both `chatMemory` and `chatMemoryVectorStore` on 
the same endpoint. If both are provided, `chatMemory` 
(MessageChatMemoryAdvisor) will take precedence.
 
+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.
+
 ==== Message-based Memory (ChatMemory)
 
 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.
diff --git 
a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatProducer.java
 
b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatProducer.java
index 1503c594ef19..34b01bfedb9e 100644
--- 
a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatProducer.java
+++ 
b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatProducer.java
@@ -76,6 +76,7 @@ public class SpringAiChatProducer extends DefaultProducer {
 
     private ChatClient chatClient;
     private SpringAiChatMcpManager mcpManager;
+    private Advisor chatMemoryAdvisor;
 
     public SpringAiChatProducer(SpringAiChatEndpoint endpoint) {
         super(endpoint);
@@ -120,6 +121,23 @@ public class SpringAiChatProducer extends DefaultProducer {
             this.chatClient = builder.build();
         }
 
+        // Build chat memory advisor (added per-request only when 
conversationId is set)
+        ChatMemory chatMemory = 
getEndpoint().getConfiguration().getChatMemory();
+        VectorStore chatMemoryVectorStore = 
getEndpoint().getConfiguration().getChatMemoryVectorStore();
+        if (chatMemory != null && chatMemoryVectorStore != null) {
+            LOG.warn("Both chatMemory and chatMemoryVectorStore are 
configured. Using MessageChatMemoryAdvisor (chatMemory). "
+                     + "Configure only one memory type.");
+        }
+        if (chatMemory != null) {
+            this.chatMemoryAdvisor = 
MessageChatMemoryAdvisor.builder(chatMemory).build();
+            LOG.debug("MessageChatMemoryAdvisor available (activated 
per-request via conversationId header)");
+        } else if (chatMemoryVectorStore != null) {
+            this.chatMemoryAdvisor = 
VectorStoreChatMemoryAdvisor.builder(chatMemoryVectorStore)
+                    .defaultTopK(getEndpoint().getConfiguration().getTopK())
+                    .build();
+            LOG.debug("VectorStoreChatMemoryAdvisor available with topK={}", 
getEndpoint().getConfiguration().getTopK());
+        }
+
         // Initialize MCP clients if configured
         Map<String, Object> mcpConfig = 
getEndpoint().getConfiguration().getMcpServer();
         if (mcpConfig != null && !mcpConfig.isEmpty()) {
@@ -613,10 +631,12 @@ public class SpringAiChatProducer extends DefaultProducer 
{
             LOG.debug("Added tool context with {} entries", 
toolContext.size());
         }
 
-        // Apply conversation ID for chat memory if provided
+        // Add chat memory advisor per-request only when conversationId header 
is set
         String conversationId = 
exchange.getIn().getHeader(SpringAiChatConstants.CONVERSATION_ID, String.class);
-        if (conversationId != null) {
-            request.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, 
conversationId));
+        if (conversationId != null && chatMemoryAdvisor != null) {
+            final String convId = conversationId;
+            request.advisors(chatMemoryAdvisor);
+            request.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, convId));
         }
 
         // Apply SafeGuard advisor overrides if provided via headers
@@ -1179,28 +1199,8 @@ public class SpringAiChatProducer extends 
DefaultProducer {
             advisors.add(safeguardAdvisor);
         }
 
-        // Add ChatMemory advisor if configured
-        ChatMemory chatMemory = 
getEndpoint().getConfiguration().getChatMemory();
-        VectorStore chatMemoryVectorStore = 
getEndpoint().getConfiguration().getChatMemoryVectorStore();
-
-        if (chatMemory != null && chatMemoryVectorStore != null) {
-            LOG.warn("Both chatMemory and chatMemoryVectorStore are 
configured. Using MessageChatMemoryAdvisor (chatMemory). " +
-                     "Configure only one memory type.");
-        }
-
-        if (chatMemory != null) {
-            advisors.add(MessageChatMemoryAdvisor.builder(chatMemory).build());
-            LOG.debug("MessageChatMemoryAdvisor enabled");
-        } else if (chatMemoryVectorStore != null) {
-            // Configure VectorStoreChatMemoryAdvisor with conversation 
isolation
-            // The conversationId parameter enables automatic filtering by 
conversation ID
-            
advisors.add(VectorStoreChatMemoryAdvisor.builder(chatMemoryVectorStore)
-                    .conversationId(ChatMemory.CONVERSATION_ID)
-                    .defaultTopK(getEndpoint().getConfiguration().getTopK())
-                    .build());
-            LOG.debug("VectorStoreChatMemoryAdvisor enabled with conversation 
isolation and topK={}",
-                    getEndpoint().getConfiguration().getTopK());
-        }
+        // Chat memory advisors are NOT added to defaults — they are added 
per-request
+        // only when a conversationId header is present (see 
applyRequestOptions)
 
         // Add QuestionAnswerAdvisor if VectorStore is configured
         VectorStore vectorStore = 
getEndpoint().getConfiguration().getVectorStore();
diff --git 
a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMemoryIT.java
 
b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMemoryIT.java
index 154a0d25b6da..c666df6a6379 100644
--- 
a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMemoryIT.java
+++ 
b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMemoryIT.java
@@ -165,7 +165,7 @@ public class SpringAiChatMemoryIT extends OllamaTestSupport 
{
         // First message
         var exchange1 = template().request("direct:chat-with-auto-memory", e 
-> {
             e.getIn().setBody("My favorite number is 42. Please remember 
this.");
-            e.getIn().setHeader("conversationId", conversationId);
+            e.getIn().setHeader(SpringAiChatConstants.CONVERSATION_ID, 
conversationId);
         });
 
         String response1 = exchange1.getMessage().getBody(String.class);
@@ -174,7 +174,7 @@ public class SpringAiChatMemoryIT extends OllamaTestSupport 
{
         // Second message - the advisor should remember the context
         var exchange2 = template().request("direct:chat-with-auto-memory", e 
-> {
             e.getIn().setBody("What is my favorite number? Answer with just 
the number.");
-            e.getIn().setHeader("conversationId", conversationId);
+            e.getIn().setHeader(SpringAiChatConstants.CONVERSATION_ID, 
conversationId);
         });
 
         String response2 = exchange2.getMessage().getBody(String.class);

Reply via email to