From d3d8408f480cabbb347b732a1d1a2e047abd3267 Mon Sep 17 00:00:00 2001 From: Ivica Cardic Date: Mon, 27 Apr 2026 23:42:53 +0200 Subject: [PATCH 1/2] 1652 Order advisors and wire ToolCallAdvisor in AI agent --- .../memory/builtin/cluster/ChatMemory.java | 2 ++ .../cluster/CassandraChatMemory.java | 2 ++ .../cosmosdb/cluster/CosmosDbChatMemory.java | 2 ++ .../memory/cluster/InMemoryChatMemory.java | 2 ++ .../memory/jdbc/cluster/JdbcChatMemory.java | 2 ++ .../mongodb/cluster/MongoDbChatMemory.java | 2 ++ .../memory/neo4j/cluster/Neo4jChatMemory.java | 2 ++ .../memory/redis/cluster/RedisChatMemory.java | 2 ++ .../memory/cluster/VectorStoreChatMemory.java | 4 ++- .../ai/agent/AiAgentComponentHandler.java | 8 ++++-- .../action/AbstractAiAgentChatAction.java | 28 ++++++++++++++++--- .../ai/agent/action/AiAgentChatAction.java | 14 ++++++---- .../agent/action/AiAgentStreamChatAction.java | 12 +++++--- .../action/AbstractAiAgentChatActionTest.java | 17 +++++++---- .../chat/AiAgentComponentHandlerTest.java | 2 +- 15 files changed, 78 insertions(+), 23 deletions(-) diff --git a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-builtin/src/main/java/com/bytechef/component/ai/agent/chat/memory/builtin/cluster/ChatMemory.java b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-builtin/src/main/java/com/bytechef/component/ai/agent/chat/memory/builtin/cluster/ChatMemory.java index 47a2225d3a6..c6504480449 100644 --- a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-builtin/src/main/java/com/bytechef/component/ai/agent/chat/memory/builtin/cluster/ChatMemory.java +++ b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-builtin/src/main/java/com/bytechef/component/ai/agent/chat/memory/builtin/cluster/ChatMemory.java @@ -22,6 +22,7 @@ import com.bytechef.component.definition.ComponentDsl; import com.bytechef.platform.component.definition.ai.agent.ChatMemoryFunction; import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; +import org.springframework.ai.chat.client.advisor.api.BaseAdvisor; import org.springframework.ai.chat.memory.ChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; @@ -48,6 +49,7 @@ protected static PromptChatMemoryAdvisor apply(ChatMemoryRepository chatMemoryRe .build(); return PromptChatMemoryAdvisor.builder(chatMemory) + .order(BaseAdvisor.HIGHEST_PRECEDENCE + 200) .build(); } } diff --git a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-cassandra/src/main/java/com/bytechef/component/ai/agent/chat/memory/cassandra/cluster/CassandraChatMemory.java b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-cassandra/src/main/java/com/bytechef/component/ai/agent/chat/memory/cassandra/cluster/CassandraChatMemory.java index e91c0adeddd..10cf4b74c73 100644 --- a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-cassandra/src/main/java/com/bytechef/component/ai/agent/chat/memory/cassandra/cluster/CassandraChatMemory.java +++ b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-cassandra/src/main/java/com/bytechef/component/ai/agent/chat/memory/cassandra/cluster/CassandraChatMemory.java @@ -26,6 +26,7 @@ import com.bytechef.platform.component.definition.ai.agent.ChatMemoryFunction; import java.util.Map; import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; +import org.springframework.ai.chat.client.advisor.api.BaseAdvisor; import org.springframework.ai.chat.memory.ChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; @@ -55,6 +56,7 @@ protected static PromptChatMemoryAdvisor apply( .build(); return PromptChatMemoryAdvisor.builder(chatMemory) + .order(BaseAdvisor.HIGHEST_PRECEDENCE + 200) .build(); } } diff --git a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-cosmosdb/src/main/java/com/bytechef/component/ai/agent/chat/memory/cosmosdb/cluster/CosmosDbChatMemory.java b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-cosmosdb/src/main/java/com/bytechef/component/ai/agent/chat/memory/cosmosdb/cluster/CosmosDbChatMemory.java index 93802309f5a..329abf43bcd 100644 --- a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-cosmosdb/src/main/java/com/bytechef/component/ai/agent/chat/memory/cosmosdb/cluster/CosmosDbChatMemory.java +++ b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-cosmosdb/src/main/java/com/bytechef/component/ai/agent/chat/memory/cosmosdb/cluster/CosmosDbChatMemory.java @@ -26,6 +26,7 @@ import com.bytechef.platform.component.definition.ai.agent.ChatMemoryFunction; import java.util.Map; import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; +import org.springframework.ai.chat.client.advisor.api.BaseAdvisor; import org.springframework.ai.chat.memory.ChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; @@ -55,6 +56,7 @@ protected static PromptChatMemoryAdvisor apply( .build(); return PromptChatMemoryAdvisor.builder(chatMemory) + .order(BaseAdvisor.HIGHEST_PRECEDENCE + 200) .build(); } } diff --git a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-in-memory/src/main/java/com/bytechef/component/ai/agent/chat/memory/memory/cluster/InMemoryChatMemory.java b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-in-memory/src/main/java/com/bytechef/component/ai/agent/chat/memory/memory/cluster/InMemoryChatMemory.java index f35cafd4ce1..4af40d3b7a8 100644 --- a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-in-memory/src/main/java/com/bytechef/component/ai/agent/chat/memory/memory/cluster/InMemoryChatMemory.java +++ b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-in-memory/src/main/java/com/bytechef/component/ai/agent/chat/memory/memory/cluster/InMemoryChatMemory.java @@ -26,6 +26,7 @@ import com.bytechef.platform.component.definition.ai.agent.ChatMemoryFunction; import java.util.Map; import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; +import org.springframework.ai.chat.client.advisor.api.BaseAdvisor; import org.springframework.ai.chat.memory.MessageWindowChatMemory; /** @@ -50,6 +51,7 @@ protected static PromptChatMemoryAdvisor apply( Map componentConnections) { return PromptChatMemoryAdvisor.builder(inMemoryChatMemory) + .order(BaseAdvisor.HIGHEST_PRECEDENCE + 200) .build(); } } diff --git a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-jdbc/src/main/java/com/bytechef/component/ai/agent/chat/memory/jdbc/cluster/JdbcChatMemory.java b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-jdbc/src/main/java/com/bytechef/component/ai/agent/chat/memory/jdbc/cluster/JdbcChatMemory.java index 9f96959ebba..ac6bf10bbcf 100644 --- a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-jdbc/src/main/java/com/bytechef/component/ai/agent/chat/memory/jdbc/cluster/JdbcChatMemory.java +++ b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-jdbc/src/main/java/com/bytechef/component/ai/agent/chat/memory/jdbc/cluster/JdbcChatMemory.java @@ -27,6 +27,7 @@ import com.bytechef.platform.component.service.ClusterElementDefinitionService; import java.util.Map; import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; +import org.springframework.ai.chat.client.advisor.api.BaseAdvisor; import org.springframework.ai.chat.memory.ChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; @@ -67,6 +68,7 @@ protected PromptChatMemoryAdvisor apply( .build(); return PromptChatMemoryAdvisor.builder(chatMemory) + .order(BaseAdvisor.HIGHEST_PRECEDENCE + 200) .build(); } } diff --git a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-mongodb/src/main/java/com/bytechef/component/ai/agent/chat/memory/mongodb/cluster/MongoDbChatMemory.java b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-mongodb/src/main/java/com/bytechef/component/ai/agent/chat/memory/mongodb/cluster/MongoDbChatMemory.java index e108ca4fe57..9f7221a513a 100644 --- a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-mongodb/src/main/java/com/bytechef/component/ai/agent/chat/memory/mongodb/cluster/MongoDbChatMemory.java +++ b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-mongodb/src/main/java/com/bytechef/component/ai/agent/chat/memory/mongodb/cluster/MongoDbChatMemory.java @@ -26,6 +26,7 @@ import com.bytechef.platform.component.definition.ai.agent.ChatMemoryFunction; import java.util.Map; import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; +import org.springframework.ai.chat.client.advisor.api.BaseAdvisor; import org.springframework.ai.chat.memory.ChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; @@ -52,6 +53,7 @@ protected static PromptChatMemoryAdvisor apply( .build(); return PromptChatMemoryAdvisor.builder(chatMemory) + .order(BaseAdvisor.HIGHEST_PRECEDENCE + 200) .build(); } diff --git a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-neo4j/src/main/java/com/bytechef/component/ai/agent/chat/memory/neo4j/cluster/Neo4jChatMemory.java b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-neo4j/src/main/java/com/bytechef/component/ai/agent/chat/memory/neo4j/cluster/Neo4jChatMemory.java index c751698da19..a76b6246781 100644 --- a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-neo4j/src/main/java/com/bytechef/component/ai/agent/chat/memory/neo4j/cluster/Neo4jChatMemory.java +++ b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-neo4j/src/main/java/com/bytechef/component/ai/agent/chat/memory/neo4j/cluster/Neo4jChatMemory.java @@ -26,6 +26,7 @@ import com.bytechef.platform.component.definition.ai.agent.ChatMemoryFunction; import java.util.Map; import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; +import org.springframework.ai.chat.client.advisor.api.BaseAdvisor; import org.springframework.ai.chat.memory.ChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; @@ -55,6 +56,7 @@ protected static PromptChatMemoryAdvisor apply( .build(); return PromptChatMemoryAdvisor.builder(chatMemory) + .order(BaseAdvisor.HIGHEST_PRECEDENCE + 200) .build(); } } diff --git a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-redis/src/main/java/com/bytechef/component/ai/agent/chat/memory/redis/cluster/RedisChatMemory.java b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-redis/src/main/java/com/bytechef/component/ai/agent/chat/memory/redis/cluster/RedisChatMemory.java index d8a59e84ecf..bd63b4eea0b 100644 --- a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-redis/src/main/java/com/bytechef/component/ai/agent/chat/memory/redis/cluster/RedisChatMemory.java +++ b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-redis/src/main/java/com/bytechef/component/ai/agent/chat/memory/redis/cluster/RedisChatMemory.java @@ -26,6 +26,7 @@ import com.bytechef.platform.component.definition.ai.agent.ChatMemoryFunction; import java.util.Map; import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; +import org.springframework.ai.chat.client.advisor.api.BaseAdvisor; import org.springframework.ai.chat.memory.ChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; @@ -52,6 +53,7 @@ protected static PromptChatMemoryAdvisor apply( .build(); return PromptChatMemoryAdvisor.builder(chatMemory) + .order(BaseAdvisor.HIGHEST_PRECEDENCE + 200) .build(); } diff --git a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-vectorstore/src/main/java/com/bytechef/component/ai/chat/memory/cluster/VectorStoreChatMemory.java b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-vectorstore/src/main/java/com/bytechef/component/ai/chat/memory/cluster/VectorStoreChatMemory.java index 57dd88cbf25..7e2e4557722 100644 --- a/server/libs/modules/components/ai/agent/chat-memory/chat-memory-vectorstore/src/main/java/com/bytechef/component/ai/chat/memory/cluster/VectorStoreChatMemory.java +++ b/server/libs/modules/components/ai/agent/chat-memory/chat-memory-vectorstore/src/main/java/com/bytechef/component/ai/chat/memory/cluster/VectorStoreChatMemory.java @@ -30,6 +30,7 @@ import com.bytechef.platform.configuration.domain.ClusterElement; import com.bytechef.platform.configuration.domain.ClusterElementMap; import java.util.Map; +import org.springframework.ai.chat.client.advisor.api.BaseAdvisor; import org.springframework.ai.chat.client.advisor.vectorstore.VectorStoreChatMemoryAdvisor; import org.springframework.ai.chat.client.advisor.vectorstore.VectorStoreChatMemoryAdvisor.Builder; @@ -82,7 +83,8 @@ protected VectorStoreChatMemoryAdvisor apply( ParametersFactory.create(componentConnectionConnectionParameters), ParametersFactory.create(clusterElement.getExtensions()), componentConnections)) .defaultTopK( - inputParameters.getInteger(CHAT_MEMORY_RETRIEVE_SIZE, 20)); + inputParameters.getInteger(CHAT_MEMORY_RETRIEVE_SIZE, 20)) + .order(BaseAdvisor.HIGHEST_PRECEDENCE + 200); return builder.build(); } diff --git a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/AiAgentComponentHandler.java b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/AiAgentComponentHandler.java index 6c39177c9fc..3407ea47739 100644 --- a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/AiAgentComponentHandler.java +++ b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/AiAgentComponentHandler.java @@ -30,6 +30,7 @@ import com.bytechef.platform.component.definition.AbstractComponentDefinitionWrapper; import com.bytechef.platform.component.definition.AiAgentComponentDefinition; import com.bytechef.platform.component.service.ClusterElementDefinitionService; +import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.stereotype.Component; /** @@ -41,10 +42,11 @@ public class AiAgentComponentHandler implements ComponentHandler { private final AiAgentComponentDefinition componentDefinition; public AiAgentComponentHandler( - ClusterElementDefinitionService clusterElementDefinitionService, AiAgentToolFacade aiAgentToolFacade) { + AiAgentToolFacade aiAgentToolFacade, ClusterElementDefinitionService clusterElementDefinitionService, + ToolCallingManager toolCallingManager) { final ActionDefinition aiAgentChatActionDefinition = - AiAgentChatAction.of(clusterElementDefinitionService, aiAgentToolFacade); + AiAgentChatAction.of(aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager); this.componentDefinition = new AiAgentComponentDefinitionImpl( component(AI_AGENT) @@ -54,7 +56,7 @@ public AiAgentComponentHandler( .categories(ComponentCategory.ARTIFICIAL_INTELLIGENCE) .actions( aiAgentChatActionDefinition, - AiAgentStreamChatAction.of(clusterElementDefinitionService, aiAgentToolFacade)) + AiAgentStreamChatAction.of(clusterElementDefinitionService, aiAgentToolFacade, toolCallingManager)) .clusterElements(AiAgentChatTool.of(aiAgentChatActionDefinition))); } diff --git a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatAction.java b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatAction.java index a4575a9fc86..2fc9f7326c6 100644 --- a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatAction.java +++ b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatAction.java @@ -63,11 +63,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.client.advisor.ToolCallAdvisor; import org.springframework.ai.chat.client.advisor.api.Advisor; +import org.springframework.ai.chat.client.advisor.api.BaseAdvisor; import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.augment.AugmentedToolCallbackProvider; import org.springframework.ai.tool.definition.ToolDefinition; @@ -83,12 +86,15 @@ public abstract class AbstractAiAgentChatAction { private final ClusterElementDefinitionService clusterElementDefinitionService; private final AiAgentToolFacade aiAgentToolFacade; + private final ToolCallingManager toolCallingManager; protected AbstractAiAgentChatAction( - ClusterElementDefinitionService clusterElementDefinitionService, AiAgentToolFacade aiAgentToolFacade) { + AiAgentToolFacade aiAgentToolFacade, ClusterElementDefinitionService clusterElementDefinitionService, + ToolCallingManager toolCallingManager) { - this.clusterElementDefinitionService = clusterElementDefinitionService; this.aiAgentToolFacade = aiAgentToolFacade; + this.clusterElementDefinitionService = clusterElementDefinitionService; + this.toolCallingManager = toolCallingManager; } protected ChatClient.ChatClientRequestSpec getChatClientRequestSpec( @@ -155,11 +161,25 @@ private List getAdvisors( .map(clusterElement -> getGuardrailsAdvisor(connectionParameters, clusterElement)) .ifPresent(advisors::add); + // tool call + + ToolCallAdvisor.Builder toolCallAdvisorBuilder = ToolCallAdvisor.builder() + .toolCallingManager(toolCallingManager) + .advisorOrder(BaseAdvisor.HIGHEST_PRECEDENCE + 300); + // memory - clusterElementMap.fetchClusterElement(CHAT_MEMORY) + Advisor chatMemoryAdvisor = clusterElementMap.fetchClusterElement(CHAT_MEMORY) .map(clusterElement -> getChatMemoryAdvisor(connectionParameters, clusterElement)) - .ifPresent(advisors::add); + .orElse(null); + + if (chatMemoryAdvisor != null) { + advisors.add(chatMemoryAdvisor); + + toolCallAdvisorBuilder.disableInternalConversationHistory(); + } + + advisors.add(toolCallAdvisorBuilder.build()); // RAG diff --git a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentChatAction.java b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentChatAction.java index c0b722ce522..b8078d2d927 100644 --- a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentChatAction.java +++ b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentChatAction.java @@ -42,6 +42,7 @@ import org.jspecify.annotations.Nullable; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.ChatClient.ChatClientRequestSpec; +import org.springframework.ai.model.tool.ToolCallingManager; /** * @author Ivica Cardic @@ -49,14 +50,17 @@ public class AiAgentChatAction extends AbstractAiAgentChatAction { public static ChatActionDefinitionWrapper of( - ClusterElementDefinitionService clusterElementDefinitionService, AiAgentToolFacade aiAgentToolFacade) { + AiAgentToolFacade aiAgentToolFacade, ClusterElementDefinitionService clusterElementDefinitionService, + ToolCallingManager toolCallingManager) { - return new AiAgentChatAction(clusterElementDefinitionService, aiAgentToolFacade).build(); + return new AiAgentChatAction(clusterElementDefinitionService, aiAgentToolFacade, toolCallingManager).build(); } - private AiAgentChatAction(ClusterElementDefinitionService clusterElementDefinitionService, - AiAgentToolFacade aiAgentToolFacade) { - super(clusterElementDefinitionService, aiAgentToolFacade); + private AiAgentChatAction( + ClusterElementDefinitionService clusterElementDefinitionService, AiAgentToolFacade aiAgentToolFacade, + ToolCallingManager toolCallingManager) { + + super(aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager); } private ChatActionDefinitionWrapper build() { diff --git a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentStreamChatAction.java b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentStreamChatAction.java index dcd2534548c..32dd0d30a40 100644 --- a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentStreamChatAction.java +++ b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentStreamChatAction.java @@ -45,6 +45,7 @@ import org.springframework.ai.chat.client.ChatClient.ChatClientRequestSpec; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.model.Generation; +import org.springframework.ai.model.tool.ToolCallingManager; import reactor.core.publisher.Flux; /** @@ -53,15 +54,18 @@ public class AiAgentStreamChatAction extends AbstractAiAgentChatAction { public static ActionDefinition of( - ClusterElementDefinitionService clusterElementDefinitionService, AiAgentToolFacade aiAgentToolFacade) { + ClusterElementDefinitionService clusterElementDefinitionService, AiAgentToolFacade aiAgentToolFacade, + ToolCallingManager toolCallingManager) { - return new AiAgentStreamChatAction(clusterElementDefinitionService, aiAgentToolFacade).build(); + return new AiAgentStreamChatAction(aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager) + .build(); } private AiAgentStreamChatAction( - ClusterElementDefinitionService clusterElementDefinitionService, AiAgentToolFacade aiAgentToolFacade) { + AiAgentToolFacade aiAgentToolFacade, ClusterElementDefinitionService clusterElementDefinitionService, + ToolCallingManager toolCallingManager) { - super(clusterElementDefinitionService, aiAgentToolFacade); + super(aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager); } private ChatActionDefinitionWrapper build() { diff --git a/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatActionTest.java b/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatActionTest.java index 05061b35b74..83b89c44d52 100644 --- a/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatActionTest.java +++ b/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatActionTest.java @@ -41,6 +41,7 @@ import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.model.tool.ToolCallingManager; /** * @author Ivica Cardic @@ -48,11 +49,14 @@ @ExtendWith(MockitoExtension.class) class AbstractAiAgentChatActionTest { + @Mock + private AiAgentToolFacade aiAgentToolFacade; + @Mock private ClusterElementDefinitionService clusterElementDefinitionService; @Mock - private AiAgentToolFacade aiAgentToolFacade; + private ToolCallingManager toolCallingManager; @Test void testGetChatClientRequestSpecWithNullParameterValues() throws Exception { @@ -92,7 +96,8 @@ void testGetChatClientRequestSpecWithNullParameterValues() throws Exception { ActionContext actionContext = mock(ActionContext.class); - TestAiAgentChatAction action = new TestAiAgentChatAction(clusterElementDefinitionService, aiAgentToolFacade); + TestAiAgentChatAction action = new TestAiAgentChatAction( + aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager); try (MockedStatic modelUtilsMockedStatic = mockStatic(ModelUtils.class)) { modelUtilsMockedStatic.when(() -> ModelUtils.getMessages(any(), any())) @@ -105,9 +110,11 @@ void testGetChatClientRequestSpecWithNullParameterValues() throws Exception { private static class TestAiAgentChatAction extends AbstractAiAgentChatAction { - TestAiAgentChatAction(ClusterElementDefinitionService clusterElementDefinitionService, - AiAgentToolFacade aiAgentToolFacade) { - super(clusterElementDefinitionService, aiAgentToolFacade); + TestAiAgentChatAction( + AiAgentToolFacade aiAgentToolFacade, ClusterElementDefinitionService clusterElementDefinitionService, + ToolCallingManager toolCallingManager) { + + super(aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager); } } } diff --git a/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/chat/AiAgentComponentHandlerTest.java b/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/chat/AiAgentComponentHandlerTest.java index 47dcf59d080..89847b00f15 100644 --- a/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/chat/AiAgentComponentHandlerTest.java +++ b/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/chat/AiAgentComponentHandlerTest.java @@ -25,6 +25,6 @@ public class AiAgentComponentHandlerTest { @Test public void testGetComponentDefinition() { JsonFileAssert.assertEquals( - "definition/ai-agent_v1.json", new AiAgentComponentHandler(null, null).getDefinition()); + "definition/ai-agent_v1.json", new AiAgentComponentHandler(null, null, null).getDefinition()); } } From 5e1b40b85c1b53ef84c3a316b2f10897057273f8 Mon Sep 17 00:00:00 2001 From: Ivica Cardic Date: Fri, 8 May 2026 17:01:23 +0200 Subject: [PATCH 2/2] 1652 Align AiAgent action factory parameter order and test ToolCallAdvisor wiring Standardize (AiAgentToolFacade, ClusterElementDefinitionService, ToolCallingManager) across AiAgentChatAction.of, AiAgentStreamChatAction.of, their constructors, and the AiAgentComponentHandler call sites. Add tests asserting ToolCallAdvisor is present in the advisor list and that disableInternalConversationHistory() is applied only when a chat-memory advisor is configured. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../ai/agent/AiAgentComponentHandler.java | 2 +- .../action/AbstractAiAgentChatAction.java | 2 +- .../ai/agent/action/AiAgentChatAction.java | 4 +- .../agent/action/AiAgentStreamChatAction.java | 2 +- .../action/AbstractAiAgentChatActionTest.java | 100 ++++++++++++++++++ 5 files changed, 105 insertions(+), 5 deletions(-) diff --git a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/AiAgentComponentHandler.java b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/AiAgentComponentHandler.java index 3407ea47739..932d1be030a 100644 --- a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/AiAgentComponentHandler.java +++ b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/AiAgentComponentHandler.java @@ -56,7 +56,7 @@ public AiAgentComponentHandler( .categories(ComponentCategory.ARTIFICIAL_INTELLIGENCE) .actions( aiAgentChatActionDefinition, - AiAgentStreamChatAction.of(clusterElementDefinitionService, aiAgentToolFacade, toolCallingManager)) + AiAgentStreamChatAction.of(aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager)) .clusterElements(AiAgentChatTool.of(aiAgentChatActionDefinition))); } diff --git a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatAction.java b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatAction.java index 2fc9f7326c6..7e4f6b38074 100644 --- a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatAction.java +++ b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatAction.java @@ -149,7 +149,7 @@ private static ChatClient.ChatClientRequestSpec createPrompt( return chatClient.prompt(converter.getFormat()); } - private List getAdvisors( + List getAdvisors( ClusterElementMap clusterElementMap, Map connectionParameters, ActionContext context) { diff --git a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentChatAction.java b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentChatAction.java index b8078d2d927..4f65a35be3b 100644 --- a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentChatAction.java +++ b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentChatAction.java @@ -53,11 +53,11 @@ public static ChatActionDefinitionWrapper of( AiAgentToolFacade aiAgentToolFacade, ClusterElementDefinitionService clusterElementDefinitionService, ToolCallingManager toolCallingManager) { - return new AiAgentChatAction(clusterElementDefinitionService, aiAgentToolFacade, toolCallingManager).build(); + return new AiAgentChatAction(aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager).build(); } private AiAgentChatAction( - ClusterElementDefinitionService clusterElementDefinitionService, AiAgentToolFacade aiAgentToolFacade, + AiAgentToolFacade aiAgentToolFacade, ClusterElementDefinitionService clusterElementDefinitionService, ToolCallingManager toolCallingManager) { super(aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager); diff --git a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentStreamChatAction.java b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentStreamChatAction.java index 32dd0d30a40..463093eea16 100644 --- a/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentStreamChatAction.java +++ b/server/libs/modules/components/ai/agent/src/main/java/com/bytechef/component/ai/agent/action/AiAgentStreamChatAction.java @@ -54,7 +54,7 @@ public class AiAgentStreamChatAction extends AbstractAiAgentChatAction { public static ActionDefinition of( - ClusterElementDefinitionService clusterElementDefinitionService, AiAgentToolFacade aiAgentToolFacade, + AiAgentToolFacade aiAgentToolFacade, ClusterElementDefinitionService clusterElementDefinitionService, ToolCallingManager toolCallingManager) { return new AiAgentStreamChatAction(aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager) diff --git a/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatActionTest.java b/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatActionTest.java index 83b89c44d52..50e3531afd8 100644 --- a/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatActionTest.java +++ b/server/libs/modules/components/ai/agent/src/test/java/com/bytechef/component/ai/agent/action/AbstractAiAgentChatActionTest.java @@ -16,6 +16,7 @@ package com.bytechef.component.ai.agent.action; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -30,8 +31,11 @@ import com.bytechef.component.definition.Parameters; import com.bytechef.component.test.definition.MockParametersFactory; import com.bytechef.platform.component.ComponentConnection; +import com.bytechef.platform.component.definition.ai.agent.ChatMemoryFunction; import com.bytechef.platform.component.definition.ai.agent.ModelFunction; import com.bytechef.platform.component.service.ClusterElementDefinitionService; +import com.bytechef.platform.configuration.domain.ClusterElementMap; +import java.lang.reflect.Field; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -40,6 +44,9 @@ import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.ai.chat.client.advisor.ToolCallAdvisor; +import org.springframework.ai.chat.client.advisor.api.Advisor; +import org.springframework.ai.chat.client.advisor.api.BaseChatMemoryAdvisor; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.model.tool.ToolCallingManager; @@ -108,6 +115,99 @@ void testGetChatClientRequestSpecWithNullParameterValues() throws Exception { } } + @Test + void testGetAdvisorsIncludesToolCallAdvisorWithDefaultConversationHistoryWhenNoChatMemory() { + ClusterElementMap clusterElementMap = ClusterElementMap.of( + Map.of("clusterElements", Map.of("model", buildModelClusterElement()))); + + ActionContext actionContext = mock(ActionContext.class); + + TestAiAgentChatAction action = new TestAiAgentChatAction( + aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager); + + List advisors = action.getAdvisors(clusterElementMap, Map.of(), actionContext); + + ToolCallAdvisor toolCallAdvisor = findToolCallAdvisor(advisors); + + assertThat(toolCallAdvisor).isNotNull(); + assertThat(advisors).noneMatch(BaseChatMemoryAdvisor.class::isInstance); + assertThat(readConversationHistoryEnabled(toolCallAdvisor)).isTrue(); + } + + @Test + void testGetAdvisorsAddsChatMemoryBeforeToolCallAdvisorAndDisablesInternalConversationHistory() throws Exception { + Map chatMemoryElement = new HashMap<>(); + + chatMemoryElement.put("name", "memory_1"); + chatMemoryElement.put("type", "memoryComponent/v1/memoryElement"); + chatMemoryElement.put("parameters", Map.of()); + + ClusterElementMap clusterElementMap = ClusterElementMap.of( + Map.of( + "clusterElements", + Map.of("model", buildModelClusterElement(), "chatMemory", chatMemoryElement))); + + BaseChatMemoryAdvisor chatMemoryAdvisor = mock(BaseChatMemoryAdvisor.class); + + ChatMemoryFunction chatMemoryFunction = mock(ChatMemoryFunction.class); + + when(chatMemoryFunction.apply(any(), any(), any(), any())).thenReturn(chatMemoryAdvisor); + when(clusterElementDefinitionService.getClusterElement( + eq("memoryComponent"), eq(1), eq("memoryElement"))).thenReturn(chatMemoryFunction); + + ComponentConnection memoryConnection = new ComponentConnection( + "memoryComponent", 1, 2L, Map.of(), null); + + Map connectionParameters = Map.of("memory_1", memoryConnection); + ActionContext actionContext = mock(ActionContext.class); + + TestAiAgentChatAction action = new TestAiAgentChatAction( + aiAgentToolFacade, clusterElementDefinitionService, toolCallingManager); + + List advisors = action.getAdvisors(clusterElementMap, connectionParameters, actionContext); + + int chatMemoryIndex = advisors.indexOf(chatMemoryAdvisor); + ToolCallAdvisor toolCallAdvisor = findToolCallAdvisor(advisors); + int toolCallIndex = advisors.indexOf(toolCallAdvisor); + + assertThat(chatMemoryIndex).isGreaterThanOrEqualTo(0); + assertThat(toolCallIndex).isGreaterThan(chatMemoryIndex); + assertThat(readConversationHistoryEnabled(toolCallAdvisor)).isFalse(); + } + + private static Map buildModelClusterElement() { + Map modelElement = new HashMap<>(); + + modelElement.put("name", "model_1"); + modelElement.put("type", "testComponent/v1/testModel"); + modelElement.put("parameters", Map.of()); + + return modelElement; + } + + private static ToolCallAdvisor findToolCallAdvisor(List advisors) { + return advisors.stream() + .filter(ToolCallAdvisor.class::isInstance) + .map(ToolCallAdvisor.class::cast) + .findFirst() + .orElseThrow(() -> new AssertionError("Expected ToolCallAdvisor in advisor list")); + } + + private static boolean readConversationHistoryEnabled(ToolCallAdvisor toolCallAdvisor) { + try { + Field field = ToolCallAdvisor.class.getDeclaredField("conversationHistoryEnabled"); + + field.setAccessible(true); + + return field.getBoolean(toolCallAdvisor); + } catch (ReflectiveOperationException exception) { + throw new AssertionError( + "Unable to read conversationHistoryEnabled from ToolCallAdvisor — " + + "field name changed in Spring AI?", + exception); + } + } + private static class TestAiAgentChatAction extends AbstractAiAgentChatAction { TestAiAgentChatAction(