diff --git a/bigtop-manager-ai/pom.xml b/bigtop-manager-ai/pom.xml
index 1ba22accb..6eb68ca83 100644
--- a/bigtop-manager-ai/pom.xml
+++ b/bigtop-manager-ai/pom.xml
@@ -48,30 +48,12 @@
bigtop-manager-dao
- dev.langchain4j
- langchain4j
+ org.springframework.ai
+ spring-ai-openai
- dev.langchain4j
- langchain4j-reactor
-
-
- dev.langchain4j
- langchain4j-open-ai
-
-
- dev.langchain4j
- langchain4j-community-qianfan
-
-
- dev.langchain4j
- langchain4j-community-dashscope
-
-
- org.slf4j
- slf4j-simple
-
-
+ org.springframework.ai
+ spring-ai-deepseek
org.springframework.boot
diff --git a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/GeneralAssistantFactory.java b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/GeneralAssistantFactory.java
index 5bad559b4..38951c8ec 100644
--- a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/GeneralAssistantFactory.java
+++ b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/GeneralAssistantFactory.java
@@ -34,8 +34,6 @@
import org.springframework.stereotype.Component;
-import dev.langchain4j.service.tool.ToolProvider;
-
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@@ -70,8 +68,7 @@ private AIAssistant.Builder initializeBuilder(PlatformType platformType) {
}
@Override
- public AIAssistant createWithPrompt(
- AIAssistantConfig config, ToolProvider toolProvider, SystemPrompt systemPrompt) {
+ public AIAssistant createWithPrompt(AIAssistantConfig config, Object toolProvider, SystemPrompt systemPrompt) {
GeneralAssistantConfig generalAssistantConfig = (GeneralAssistantConfig) config;
PlatformType platformType = generalAssistantConfig.getPlatformType();
Object id = generalAssistantConfig.getId();
@@ -81,9 +78,8 @@ public AIAssistant createWithPrompt(
AIAssistant.Builder builder = initializeBuilder(platformType);
builder.id(id)
- .memoryStore(chatMemoryStoreProvider.createPersistentChatMemoryStore())
- .withConfig(generalAssistantConfig)
- .withToolProvider(toolProvider);
+ .memoryStore(chatMemoryStoreProvider.createPersistentChatMemoryStore(id))
+ .withConfig(generalAssistantConfig);
configureSystemPrompt(builder, systemPrompt, generalAssistantConfig.getLanguage());
@@ -91,15 +87,14 @@ public AIAssistant createWithPrompt(
}
@Override
- public AIAssistant createForTest(AIAssistantConfig config, ToolProvider toolProvider) {
+ public AIAssistant createForTest(AIAssistantConfig config, Object toolProvider) {
GeneralAssistantConfig generalAssistantConfig = (GeneralAssistantConfig) config;
PlatformType platformType = generalAssistantConfig.getPlatformType();
AIAssistant.Builder builder = initializeBuilder(platformType);
builder.id(null)
.memoryStore(chatMemoryStoreProvider.createInMemoryChatMemoryStore())
- .withConfig(generalAssistantConfig)
- .withToolProvider(toolProvider);
+ .withConfig(generalAssistantConfig);
return builder.build();
}
diff --git a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/ChatMemoryStoreProvider.java b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/ChatMemoryStoreProvider.java
index 67003f10e..6e4a73aad 100644
--- a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/ChatMemoryStoreProvider.java
+++ b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/ChatMemoryStoreProvider.java
@@ -22,11 +22,11 @@
import org.apache.bigtop.manager.dao.repository.ChatMessageDao;
import org.apache.bigtop.manager.dao.repository.ChatThreadDao;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
+import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.stereotype.Component;
-import dev.langchain4j.store.memory.chat.ChatMemoryStore;
-import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;
-
import jakarta.annotation.Resource;
@Component
@@ -37,11 +37,17 @@ public class ChatMemoryStoreProvider {
@Resource
private ChatMessageDao chatMessageDao;
- public ChatMemoryStore createPersistentChatMemoryStore() {
- return new PersistentChatMemoryStore(chatThreadDao, chatMessageDao);
+ public ChatMemory createPersistentChatMemoryStore(Object conversationId) {
+ PersistentChatMemoryStore repository =
+ new PersistentChatMemoryStore((Long) conversationId, chatThreadDao, chatMessageDao);
+ return MessageWindowChatMemory.builder()
+ .chatMemoryRepository(repository)
+ .build();
}
- public ChatMemoryStore createInMemoryChatMemoryStore() {
- return new InMemoryChatMemoryStore();
+ public ChatMemory createInMemoryChatMemoryStore() {
+ return MessageWindowChatMemory.builder()
+ .chatMemoryRepository(new InMemoryChatMemoryRepository())
+ .build();
}
}
diff --git a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/store/PersistentChatMemoryStore.java b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/store/PersistentChatMemoryStore.java
index 9349d4adc..aaf6f6e7f 100644
--- a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/store/PersistentChatMemoryStore.java
+++ b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/assistant/store/PersistentChatMemoryStore.java
@@ -24,12 +24,12 @@
import org.apache.bigtop.manager.dao.repository.ChatMessageDao;
import org.apache.bigtop.manager.dao.repository.ChatThreadDao;
-import dev.langchain4j.data.message.AiMessage;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.ChatMessageType;
-import dev.langchain4j.data.message.SystemMessage;
-import dev.langchain4j.data.message.UserMessage;
-import dev.langchain4j.store.memory.chat.ChatMemoryStore;
+import org.springframework.ai.chat.memory.ChatMemoryRepository;
+import org.springframework.ai.chat.messages.AssistantMessage;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
@@ -38,41 +38,49 @@
import java.util.stream.Collectors;
@Slf4j
-public class PersistentChatMemoryStore implements ChatMemoryStore {
+public class PersistentChatMemoryStore implements ChatMemoryRepository {
- private final List messagesInMemory = new ArrayList<>();
+ private final List messagesInMemory = new ArrayList<>();
private final ChatThreadDao chatThreadDao;
private final ChatMessageDao chatMessageDao;
+ private final Long conversationId;
- public PersistentChatMemoryStore(ChatThreadDao chatThreadDao, ChatMessageDao chatMessageDao) {
+ public PersistentChatMemoryStore(Long conversationId, ChatThreadDao chatThreadDao, ChatMessageDao chatMessageDao) {
+ this.conversationId = conversationId;
this.chatThreadDao = chatThreadDao;
this.chatMessageDao = chatMessageDao;
}
- private ChatMessage convertToChatMessage(ChatMessagePO chatMessagePO) {
+ private Message convertToChatMessage(ChatMessagePO chatMessagePO) {
String sender = chatMessagePO.getSender().toLowerCase();
if (sender.equals(MessageType.AI.getValue())) {
- return new AiMessage(chatMessagePO.getMessage());
+ return new AssistantMessage(chatMessagePO.getMessage());
} else if (sender.equals(MessageType.USER.getValue())) {
return new UserMessage(chatMessagePO.getMessage());
+ } else if (sender.equals(MessageType.SYSTEM.getValue())) {
+ return new SystemMessage(chatMessagePO.getMessage());
} else {
return null;
}
}
- private ChatMessagePO convertToChatMessagePO(ChatMessage chatMessage, Long chatThreadId) {
+ private ChatMessagePO convertToChatMessagePO(Message message, Long chatThreadId) {
ChatMessagePO chatMessagePO = new ChatMessagePO();
- if (chatMessage.type().equals(ChatMessageType.AI)) {
+ if (message.getMessageType() == org.springframework.ai.chat.messages.MessageType.ASSISTANT) {
chatMessagePO.setSender(MessageType.AI.getValue());
- AiMessage aiMessage = (AiMessage) chatMessage;
- if (aiMessage.text() == null) {
+ AssistantMessage assistantMessage = (AssistantMessage) message;
+ if (assistantMessage.getText() == null) {
return null;
}
- chatMessagePO.setMessage(aiMessage.text());
- } else if (chatMessage.type().equals(ChatMessageType.USER)) {
+ chatMessagePO.setMessage(assistantMessage.getText());
+ } else if (message.getMessageType() == org.springframework.ai.chat.messages.MessageType.USER) {
chatMessagePO.setSender(MessageType.USER.getValue());
- UserMessage userMessage = (UserMessage) chatMessage;
- chatMessagePO.setMessage(userMessage.singleText());
+ UserMessage userMessage = (UserMessage) message;
+ chatMessagePO.setMessage(userMessage.getText());
+ } else if (message.getMessageType() == org.springframework.ai.chat.messages.MessageType.SYSTEM) {
+ chatMessagePO.setSender(MessageType.SYSTEM.getValue());
+ SystemMessage systemMessage = (SystemMessage) message;
+ chatMessagePO.setMessage(systemMessage.getText());
} else {
return null;
}
@@ -82,11 +90,11 @@ private ChatMessagePO convertToChatMessagePO(ChatMessage chatMessage, Long chatT
return chatMessagePO;
}
- private List sortMessages(List messages) {
- List systemMessages = messages.stream()
+ private List sortMessages(List messages) {
+ List systemMessages = messages.stream()
.filter(message -> message instanceof SystemMessage)
.collect(Collectors.toList());
- List otherMessages = messages.stream()
+ List otherMessages = messages.stream()
.filter(message -> !(message instanceof SystemMessage))
.toList();
@@ -95,9 +103,15 @@ private List sortMessages(List messages) {
}
@Override
- public List getMessages(Object threadId) {
- List chatMessages = chatMessageDao.findAllByThreadId((Long) threadId);
- List allChatMessages = new ArrayList<>();
+ public List findConversationIds() {
+ // Return the current conversation ID as a list
+ return List.of(String.valueOf(conversationId));
+ }
+
+ @Override
+ public List findByConversationId(String conversationId) {
+ List chatMessages = chatMessageDao.findAllByThreadId(this.conversationId);
+ List allChatMessages = new ArrayList<>();
if (!chatMessages.isEmpty()) {
allChatMessages.addAll(chatMessages.stream()
.map(this::convertToChatMessage)
@@ -111,20 +125,22 @@ public List getMessages(Object threadId) {
}
@Override
- public void updateMessages(Object threadId, List messages) {
- ChatMessage newMessage = messages.get(messages.size() - 1);
- ChatMessagePO chatMessagePO = convertToChatMessagePO(newMessage, (Long) threadId);
- if (chatMessagePO == null) {
- messagesInMemory.add(newMessage);
- return;
+ public void saveAll(String conversationId, List messages) {
+ for (Message message : messages) {
+ ChatMessagePO chatMessagePO = convertToChatMessagePO(message, this.conversationId);
+ if (chatMessagePO == null) {
+ messagesInMemory.add(message);
+ continue;
+ }
+ chatMessageDao.save(chatMessagePO);
}
- chatMessageDao.save(chatMessagePO);
}
@Override
- public void deleteMessages(Object threadId) {
- List chatMessagePOS = chatMessageDao.findAllByThreadId((Long) threadId);
+ public void deleteByConversationId(String conversationId) {
+ List chatMessagePOS = chatMessageDao.findAllByThreadId(this.conversationId);
chatMessagePOS.forEach(chatMessage -> chatMessage.setIsDeleted(true));
chatMessageDao.partialUpdateByIds(chatMessagePOS);
+ messagesInMemory.clear();
}
}
diff --git a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/AbstractAIAssistant.java b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/AbstractAIAssistant.java
index 58e8268a4..30ec7ab62 100644
--- a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/AbstractAIAssistant.java
+++ b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/AbstractAIAssistant.java
@@ -21,18 +21,20 @@
import org.apache.bigtop.manager.ai.core.config.AIAssistantConfig;
import org.apache.bigtop.manager.ai.core.factory.AIAssistant;
-import dev.langchain4j.memory.ChatMemory;
-import dev.langchain4j.memory.chat.MessageWindowChatMemory;
-import dev.langchain4j.service.tool.ToolProvider;
-import dev.langchain4j.store.memory.chat.ChatMemoryStore;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
+import org.springframework.ai.chat.memory.MessageWindowChatMemory;
+
import reactor.core.publisher.Flux;
public abstract class AbstractAIAssistant implements AIAssistant {
protected final AIAssistant.Service aiServices;
protected static final Integer MEMORY_LEN = 10;
protected final ChatMemory chatMemory;
+ protected final Object memoryId;
- protected AbstractAIAssistant(ChatMemory chatMemory, AIAssistant.Service aiServices) {
+ protected AbstractAIAssistant(Object memoryId, ChatMemory chatMemory, AIAssistant.Service aiServices) {
+ this.memoryId = memoryId;
this.chatMemory = chatMemory;
this.aiServices = aiServices;
}
@@ -44,7 +46,7 @@ public boolean test() {
@Override
public Object getId() {
- return chatMemory.id();
+ return memoryId;
}
@Override
@@ -60,19 +62,13 @@ public String ask(String chatMessage) {
public abstract static class Builder implements AIAssistant.Builder {
protected Object id;
- protected ChatMemoryStore chatMemoryStore;
+ protected ChatMemory chatMemory;
protected AIAssistantConfig config;
- protected ToolProvider toolProvider;
protected String systemPrompt;
public Builder() {}
- public Builder withToolProvider(ToolProvider toolProvider) {
- this.toolProvider = toolProvider;
- return this;
- }
-
public Builder withSystemPrompt(String systemPrompt) {
this.systemPrompt = systemPrompt;
return this;
@@ -88,19 +84,18 @@ public Builder id(Object id) {
return this;
}
- public Builder memoryStore(ChatMemoryStore chatMemoryStore) {
- this.chatMemoryStore = chatMemoryStore;
+ public Builder memoryStore(ChatMemory chatMemory) {
+ this.chatMemory = chatMemory;
return this;
}
- public MessageWindowChatMemory getChatMemory() {
- MessageWindowChatMemory.Builder builder = MessageWindowChatMemory.builder()
- .chatMemoryStore(chatMemoryStore)
- .maxMessages(MEMORY_LEN);
- if (id != null) {
- builder.id(id);
+ public ChatMemory getChatMemory() {
+ if (chatMemory == null) {
+ chatMemory = MessageWindowChatMemory.builder()
+ .chatMemoryRepository(new InMemoryChatMemoryRepository())
+ .build();
}
- return builder.build();
+ return chatMemory;
}
}
}
diff --git a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistant.java b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistant.java
index 6092590ae..ff2b96f5c 100644
--- a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistant.java
+++ b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistant.java
@@ -21,11 +21,10 @@
import org.apache.bigtop.manager.ai.core.config.AIAssistantConfig;
import org.apache.bigtop.manager.ai.core.enums.PlatformType;
-import dev.langchain4j.memory.ChatMemory;
-import dev.langchain4j.model.chat.ChatModel;
-import dev.langchain4j.model.chat.StreamingChatModel;
-import dev.langchain4j.service.tool.ToolProvider;
-import dev.langchain4j.store.memory.chat.ChatMemoryStore;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.StreamingChatModel;
+
import reactor.core.publisher.Flux;
public interface AIAssistant {
@@ -72,12 +71,10 @@ interface Service {
interface Builder {
Builder id(Object id);
- Builder memoryStore(ChatMemoryStore memoryStore);
+ Builder memoryStore(ChatMemory memoryStore);
Builder withConfig(AIAssistantConfig configProvider);
- Builder withToolProvider(ToolProvider toolProvider);
-
Builder withSystemPrompt(String systemPrompt);
AIAssistant build();
diff --git a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistantFactory.java b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistantFactory.java
index d947e778f..06e1dafef 100644
--- a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistantFactory.java
+++ b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistantFactory.java
@@ -21,15 +21,13 @@
import org.apache.bigtop.manager.ai.core.config.AIAssistantConfig;
import org.apache.bigtop.manager.ai.core.enums.SystemPrompt;
-import dev.langchain4j.service.tool.ToolProvider;
-
public interface AIAssistantFactory {
- AIAssistant createWithPrompt(AIAssistantConfig config, ToolProvider toolProvider, SystemPrompt systemPrompt);
+ AIAssistant createWithPrompt(AIAssistantConfig config, Object toolProvider, SystemPrompt systemPrompt);
- AIAssistant createForTest(AIAssistantConfig config, ToolProvider toolProvider);
+ AIAssistant createForTest(AIAssistantConfig config, Object toolProvider);
- default AIAssistant createAIService(AIAssistantConfig config, ToolProvider toolProvider) {
+ default AIAssistant createAIService(AIAssistantConfig config, Object toolProvider) {
return createWithPrompt(config, toolProvider, SystemPrompt.DEFAULT_PROMPT);
}
}
diff --git a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/DashScopeAssistant.java b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/DashScopeAssistant.java
index cad0a9ee2..6661e6e02 100644
--- a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/DashScopeAssistant.java
+++ b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/DashScopeAssistant.java
@@ -22,18 +22,29 @@
import org.apache.bigtop.manager.ai.core.enums.PlatformType;
import org.apache.bigtop.manager.ai.core.factory.AIAssistant;
-import dev.langchain4j.community.model.dashscope.QwenChatModel;
-import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel;
-import dev.langchain4j.internal.ValidationUtils;
-import dev.langchain4j.memory.ChatMemory;
-import dev.langchain4j.model.chat.ChatModel;
-import dev.langchain4j.model.chat.StreamingChatModel;
-import dev.langchain4j.service.AiServices;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.StreamingChatModel;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import org.springframework.util.Assert;
+
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
public class DashScopeAssistant extends AbstractAIAssistant {
- public DashScopeAssistant(ChatMemory chatMemory, AIAssistant.Service aiServices) {
- super(chatMemory, aiServices);
+ private static final String BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode";
+
+ public DashScopeAssistant(Object memoryId, ChatMemory chatMemory, AIAssistant.Service aiServices) {
+ super(memoryId, chatMemory, aiServices);
}
@Override
@@ -47,39 +58,101 @@ public static Builder builder() {
public static class Builder extends AbstractAIAssistant.Builder {
- public AIAssistant build() {
- AIAssistant.Service aiService = AiServices.builder(AIAssistant.Service.class)
- .chatModel(getChatModel())
- .streamingChatModel(getStreamingChatModel())
- .chatMemory(getChatMemory())
- .toolProvider(toolProvider)
- .systemMessageProvider(threadId -> {
- if (threadId != null) {
- return systemPrompt;
- }
- return null;
- })
- .build();
- return new DashScopeAssistant(getChatMemory(), aiService);
- }
-
@Override
public ChatModel getChatModel() {
- String model = ValidationUtils.ensureNotNull(config.getModel(), "model");
- String apiKey =
- ValidationUtils.ensureNotNull(config.getCredentials().get("apiKey"), "apiKey");
- return QwenChatModel.builder().apiKey(apiKey).modelName(model).build();
+ String model = config.getModel();
+ Assert.notNull(model, "model must not be null");
+ String apiKey = config.getCredentials().get("apiKey");
+ Assert.notNull(apiKey, "apiKey must not be null");
+
+ OpenAiApi openAiApi =
+ OpenAiApi.builder().baseUrl(BASE_URL).apiKey(apiKey).build();
+ OpenAiChatOptions options = OpenAiChatOptions.builder().model(model).build();
+ return OpenAiChatModel.builder()
+ .openAiApi(openAiApi)
+ .defaultOptions(options)
+ .build();
}
@Override
public StreamingChatModel getStreamingChatModel() {
- String model = ValidationUtils.ensureNotNull(config.getModel(), "model");
- String apiKey =
- ValidationUtils.ensureNotNull(config.getCredentials().get("apiKey"), "apiKey");
- return QwenStreamingChatModel.builder()
- .apiKey(apiKey)
- .modelName(model)
- .build();
+ // In Spring AI, OpenAiChatModel handles both sync and streaming
+ return getChatModel();
+ }
+
+ public AIAssistant build() {
+ ChatModel chatModel = getChatModel();
+ StreamingChatModel streamingChatModel = getStreamingChatModel();
+ ChatMemory memory = getChatMemory();
+
+ AIAssistant.Service aiService = new AIAssistant.Service() {
+ @Override
+ public String chat(String userMessage) {
+ List messages = new ArrayList<>();
+ if (systemPrompt != null) {
+ messages.add(new SystemMessage(systemPrompt));
+ }
+ // Add conversation history
+ String convId = String.valueOf(id);
+ List history = memory.get(convId);
+ messages.addAll(history);
+ // Add new user message
+ UserMessage newUserMessage = new UserMessage(userMessage);
+ messages.add(newUserMessage);
+
+ Prompt prompt = new Prompt(messages);
+ String response =
+ chatModel.call(prompt).getResult().getOutput().getText();
+
+ // Save to memory
+ memory.add(
+ convId,
+ List.of(
+ newUserMessage,
+ new org.springframework.ai.chat.messages.AssistantMessage(response)));
+
+ return response;
+ }
+
+ @Override
+ public Flux streamChat(String userMessage) {
+ List messages = new ArrayList<>();
+ if (systemPrompt != null) {
+ messages.add(new SystemMessage(systemPrompt));
+ }
+ // Add conversation history
+ String convId = String.valueOf(id);
+ List history = memory.get(convId);
+ messages.addAll(history);
+ // Add new user message
+ UserMessage newUserMessage = new UserMessage(userMessage);
+ messages.add(newUserMessage);
+
+ Prompt prompt = new Prompt(messages);
+
+ StringBuilder responseBuilder = new StringBuilder();
+ return streamingChatModel.stream(prompt)
+ .map(chatResponse -> {
+ String content =
+ chatResponse.getResult().getOutput().getText();
+ if (content != null) {
+ responseBuilder.append(content);
+ }
+ return content;
+ })
+ .doOnComplete(() -> {
+ // Save to memory when streaming completes
+ memory.add(
+ convId,
+ List.of(
+ newUserMessage,
+ new org.springframework.ai.chat.messages.AssistantMessage(
+ responseBuilder.toString())));
+ });
+ }
+ };
+
+ return new DashScopeAssistant(id, memory, aiService);
}
}
}
diff --git a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/DeepSeekAssistant.java b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/DeepSeekAssistant.java
index ed39621c2..962ad8258 100644
--- a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/DeepSeekAssistant.java
+++ b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/DeepSeekAssistant.java
@@ -16,26 +16,34 @@
* specific language governing permissions and limitations
* under the License.
*/
+
package org.apache.bigtop.manager.ai.platform;
import org.apache.bigtop.manager.ai.core.AbstractAIAssistant;
import org.apache.bigtop.manager.ai.core.enums.PlatformType;
import org.apache.bigtop.manager.ai.core.factory.AIAssistant;
-import dev.langchain4j.internal.ValidationUtils;
-import dev.langchain4j.memory.ChatMemory;
-import dev.langchain4j.model.chat.ChatModel;
-import dev.langchain4j.model.chat.StreamingChatModel;
-import dev.langchain4j.model.openai.OpenAiChatModel;
-import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
-import dev.langchain4j.service.AiServices;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.StreamingChatModel;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.deepseek.DeepSeekChatModel;
+import org.springframework.ai.deepseek.DeepSeekChatOptions;
+import org.springframework.ai.deepseek.api.DeepSeekApi;
+import org.springframework.util.Assert;
-public class DeepSeekAssistant extends AbstractAIAssistant {
+import reactor.core.publisher.Flux;
- private static final String BASE_URL = "https://api.deepseek.com/v1";
+import java.util.ArrayList;
+import java.util.List;
+
+public class DeepSeekAssistant extends AbstractAIAssistant {
- public DeepSeekAssistant(ChatMemory chatMemory, AIAssistant.Service aiServices) {
- super(chatMemory, aiServices);
+ public DeepSeekAssistant(Object memoryId, ChatMemory chatMemory, AIAssistant.Service aiServices) {
+ super(memoryId, chatMemory, aiServices);
}
@Override
@@ -51,42 +59,99 @@ public static class Builder extends AbstractAIAssistant.Builder {
@Override
public ChatModel getChatModel() {
- String model = ValidationUtils.ensureNotNull(config.getModel(), "model");
- String apiKey =
- ValidationUtils.ensureNotNull(config.getCredentials().get("apiKey"), "apiKey");
- return OpenAiChatModel.builder()
- .apiKey(apiKey)
- .baseUrl(BASE_URL)
- .modelName(model)
+ String model = config.getModel();
+ Assert.notNull(model, "model must not be null");
+ String apiKey = config.getCredentials().get("apiKey");
+ Assert.notNull(apiKey, "apiKey must not be null");
+
+ DeepSeekApi deepSeekApi = DeepSeekApi.builder().apiKey(apiKey).build();
+ DeepSeekChatOptions options =
+ DeepSeekChatOptions.builder().model(model).build();
+ return DeepSeekChatModel.builder()
+ .deepSeekApi(deepSeekApi)
+ .defaultOptions(options)
.build();
}
@Override
public StreamingChatModel getStreamingChatModel() {
- String model = ValidationUtils.ensureNotNull(config.getModel(), "model");
- String apiKey =
- ValidationUtils.ensureNotNull(config.getCredentials().get("apiKey"), "apiKey");
- return OpenAiStreamingChatModel.builder()
- .apiKey(apiKey)
- .baseUrl(BASE_URL)
- .modelName(model)
- .build();
+ // DeepSeekChatModel handles both sync and streaming
+ return getChatModel();
}
public AIAssistant build() {
- AIAssistant.Service aiService = AiServices.builder(AIAssistant.Service.class)
- .chatModel(getChatModel())
- .streamingChatModel(getStreamingChatModel())
- .chatMemory(getChatMemory())
- .toolProvider(toolProvider)
- .systemMessageProvider(threadId -> {
- if (threadId != null) {
- return systemPrompt;
- }
- return null;
- })
- .build();
- return new DeepSeekAssistant(getChatMemory(), aiService);
+ ChatModel chatModel = getChatModel();
+ StreamingChatModel streamingChatModel = getStreamingChatModel();
+ ChatMemory memory = getChatMemory();
+
+ AIAssistant.Service aiService = new AIAssistant.Service() {
+ @Override
+ public String chat(String userMessage) {
+ List messages = new ArrayList<>();
+ if (systemPrompt != null) {
+ messages.add(new SystemMessage(systemPrompt));
+ }
+ // Add conversation history
+ String convId = String.valueOf(id);
+ List history = memory.get(convId);
+ messages.addAll(history);
+ // Add new user message
+ UserMessage newUserMessage = new UserMessage(userMessage);
+ messages.add(newUserMessage);
+
+ Prompt prompt = new Prompt(messages);
+ String response =
+ chatModel.call(prompt).getResult().getOutput().getText();
+
+ // Save to memory
+ memory.add(
+ convId,
+ List.of(
+ newUserMessage,
+ new org.springframework.ai.chat.messages.AssistantMessage(response)));
+
+ return response;
+ }
+
+ @Override
+ public Flux streamChat(String userMessage) {
+ List messages = new ArrayList<>();
+ if (systemPrompt != null) {
+ messages.add(new SystemMessage(systemPrompt));
+ }
+ // Add conversation history
+ String convId = String.valueOf(id);
+ List history = memory.get(convId);
+ messages.addAll(history);
+ // Add new user message
+ UserMessage newUserMessage = new UserMessage(userMessage);
+ messages.add(newUserMessage);
+
+ Prompt prompt = new Prompt(messages);
+
+ StringBuilder responseBuilder = new StringBuilder();
+ return streamingChatModel.stream(prompt)
+ .map(chatResponse -> {
+ String content =
+ chatResponse.getResult().getOutput().getText();
+ if (content != null) {
+ responseBuilder.append(content);
+ }
+ return content;
+ })
+ .doOnComplete(() -> {
+ // Save to memory when streaming completes
+ memory.add(
+ convId,
+ List.of(
+ newUserMessage,
+ new org.springframework.ai.chat.messages.AssistantMessage(
+ responseBuilder.toString())));
+ });
+ }
+ };
+
+ return new DeepSeekAssistant(id, memory, aiService);
}
}
}
diff --git a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/OpenAIAssistant.java b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/OpenAIAssistant.java
index ce2738ecc..b51fa75ef 100644
--- a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/OpenAIAssistant.java
+++ b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/OpenAIAssistant.java
@@ -22,20 +22,29 @@
import org.apache.bigtop.manager.ai.core.enums.PlatformType;
import org.apache.bigtop.manager.ai.core.factory.AIAssistant;
-import dev.langchain4j.internal.ValidationUtils;
-import dev.langchain4j.memory.ChatMemory;
-import dev.langchain4j.model.chat.ChatModel;
-import dev.langchain4j.model.chat.StreamingChatModel;
-import dev.langchain4j.model.openai.OpenAiChatModel;
-import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
-import dev.langchain4j.service.AiServices;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.StreamingChatModel;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import org.springframework.util.Assert;
+
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
public class OpenAIAssistant extends AbstractAIAssistant {
- private static final String BASE_URL = "https://api.openai.com/v1";
+ private static final String BASE_URL = "https://api.openai.com";
- public OpenAIAssistant(ChatMemory chatMemory, AIAssistant.Service aiServices) {
- super(chatMemory, aiServices);
+ public OpenAIAssistant(Object memoryId, ChatMemory chatMemory, AIAssistant.Service aiServices) {
+ super(memoryId, chatMemory, aiServices);
}
@Override
@@ -51,42 +60,99 @@ public static class Builder extends AbstractAIAssistant.Builder {
@Override
public ChatModel getChatModel() {
- String model = ValidationUtils.ensureNotNull(config.getModel(), "model");
- String apiKey =
- ValidationUtils.ensureNotNull(config.getCredentials().get("apiKey"), "apiKey");
+ String model = config.getModel();
+ Assert.notNull(model, "model must not be null");
+ String apiKey = config.getCredentials().get("apiKey");
+ Assert.notNull(apiKey, "apiKey must not be null");
+
+ OpenAiApi openAiApi =
+ OpenAiApi.builder().baseUrl(BASE_URL).apiKey(apiKey).build();
+ OpenAiChatOptions options = OpenAiChatOptions.builder().model(model).build();
return OpenAiChatModel.builder()
- .apiKey(apiKey)
- .baseUrl(BASE_URL)
- .modelName(model)
+ .openAiApi(openAiApi)
+ .defaultOptions(options)
.build();
}
@Override
public StreamingChatModel getStreamingChatModel() {
- String model = ValidationUtils.ensureNotNull(config.getModel(), "model");
- String apiKey =
- ValidationUtils.ensureNotNull(config.getCredentials().get("apiKey"), "apiKey");
- return OpenAiStreamingChatModel.builder()
- .apiKey(apiKey)
- .baseUrl(BASE_URL)
- .modelName(model)
- .build();
+ // In Spring AI, OpenAiChatModel handles both sync and streaming
+ return getChatModel();
}
public AIAssistant build() {
- AIAssistant.Service aiService = AiServices.builder(AIAssistant.Service.class)
- .chatModel(getChatModel())
- .streamingChatModel(getStreamingChatModel())
- .chatMemory(getChatMemory())
- .toolProvider(toolProvider)
- .systemMessageProvider(threadId -> {
- if (threadId != null) {
- return systemPrompt;
- }
- return null;
- })
- .build();
- return new OpenAIAssistant(getChatMemory(), aiService);
+ ChatModel chatModel = getChatModel();
+ StreamingChatModel streamingChatModel = getStreamingChatModel();
+ ChatMemory memory = getChatMemory();
+
+ AIAssistant.Service aiService = new AIAssistant.Service() {
+ @Override
+ public String chat(String userMessage) {
+ List messages = new ArrayList<>();
+ if (systemPrompt != null) {
+ messages.add(new SystemMessage(systemPrompt));
+ }
+ // Add conversation history
+ String convId = String.valueOf(id);
+ List history = memory.get(convId);
+ messages.addAll(history);
+ // Add new user message
+ UserMessage newUserMessage = new UserMessage(userMessage);
+ messages.add(newUserMessage);
+
+ Prompt prompt = new Prompt(messages);
+ String response =
+ chatModel.call(prompt).getResult().getOutput().getText();
+
+ // Save to memory
+ memory.add(
+ convId,
+ List.of(
+ newUserMessage,
+ new org.springframework.ai.chat.messages.AssistantMessage(response)));
+
+ return response;
+ }
+
+ @Override
+ public Flux streamChat(String userMessage) {
+ List messages = new ArrayList<>();
+ if (systemPrompt != null) {
+ messages.add(new SystemMessage(systemPrompt));
+ }
+ // Add conversation history
+ String convId = String.valueOf(id);
+ List history = memory.get(convId);
+ messages.addAll(history);
+ // Add new user message
+ UserMessage newUserMessage = new UserMessage(userMessage);
+ messages.add(newUserMessage);
+
+ Prompt prompt = new Prompt(messages);
+
+ StringBuilder responseBuilder = new StringBuilder();
+ return streamingChatModel.stream(prompt)
+ .map(chatResponse -> {
+ String content =
+ chatResponse.getResult().getOutput().getText();
+ if (content != null) {
+ responseBuilder.append(content);
+ }
+ return content;
+ })
+ .doOnComplete(() -> {
+ // Save to memory when streaming completes
+ memory.add(
+ convId,
+ List.of(
+ newUserMessage,
+ new org.springframework.ai.chat.messages.AssistantMessage(
+ responseBuilder.toString())));
+ });
+ }
+ };
+
+ return new OpenAIAssistant(id, memory, aiService);
}
}
}
diff --git a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/QianFanAssistant.java b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/QianFanAssistant.java
index cf5db353e..468020eb1 100644
--- a/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/QianFanAssistant.java
+++ b/bigtop-manager-ai/src/main/java/org/apache/bigtop/manager/ai/platform/QianFanAssistant.java
@@ -22,18 +22,29 @@
import org.apache.bigtop.manager.ai.core.enums.PlatformType;
import org.apache.bigtop.manager.ai.core.factory.AIAssistant;
-import dev.langchain4j.community.model.qianfan.QianfanChatModel;
-import dev.langchain4j.community.model.qianfan.QianfanStreamingChatModel;
-import dev.langchain4j.internal.ValidationUtils;
-import dev.langchain4j.memory.ChatMemory;
-import dev.langchain4j.model.chat.ChatModel;
-import dev.langchain4j.model.chat.StreamingChatModel;
-import dev.langchain4j.service.AiServices;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.StreamingChatModel;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import org.springframework.util.Assert;
+
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
public class QianFanAssistant extends AbstractAIAssistant {
- public QianFanAssistant(ChatMemory chatMemory, AIAssistant.Service aiServices) {
- super(chatMemory, aiServices);
+ private static final String BASE_URL = "https://qianfan.baidubce.com";
+
+ public QianFanAssistant(Object memoryId, ChatMemory chatMemory, AIAssistant.Service aiServices) {
+ super(memoryId, chatMemory, aiServices);
}
@Override
@@ -47,48 +58,103 @@ public static Builder builder() {
public static class Builder extends AbstractAIAssistant.Builder {
- public AIAssistant build() {
- AIAssistant.Service aiService = AiServices.builder(AIAssistant.Service.class)
- .chatModel(getChatModel())
- .streamingChatModel(getStreamingChatModel())
- .chatMemory(getChatMemory())
- .toolProvider(toolProvider)
- .systemMessageProvider(threadId -> {
- if (threadId != null) {
- return systemPrompt;
- }
- return null;
- })
- .build();
- return new QianFanAssistant(getChatMemory(), aiService);
- }
-
@Override
public ChatModel getChatModel() {
- String model = ValidationUtils.ensureNotNull(config.getModel(), "model");
- String apiKey =
- ValidationUtils.ensureNotNull(config.getCredentials().get("apiKey"), "apiKey");
- String secretKey =
- ValidationUtils.ensureNotNull(config.getCredentials().get("secretKey"), "secretKey");
- return QianfanChatModel.builder()
+ String model = config.getModel();
+ Assert.notNull(model, "model must not be null");
+ String apiKey = config.getCredentials().get("apiKey");
+ Assert.notNull(apiKey, "apiKey must not be null");
+
+ OpenAiApi openAiApi = OpenAiApi.builder()
+ .baseUrl(BASE_URL)
+ .completionsPath("/v2/chat/completions")
.apiKey(apiKey)
- .secretKey(secretKey)
- .modelName(model)
+ .build();
+ OpenAiChatOptions options = OpenAiChatOptions.builder().model(model).build();
+ return OpenAiChatModel.builder()
+ .openAiApi(openAiApi)
+ .defaultOptions(options)
.build();
}
@Override
public StreamingChatModel getStreamingChatModel() {
- String model = ValidationUtils.ensureNotNull(config.getModel(), "model");
- String apiKey =
- ValidationUtils.ensureNotNull(config.getCredentials().get("apiKey"), "apiKey");
- String secretKey =
- ValidationUtils.ensureNotNull(config.getCredentials().get("secretKey"), "secretKey");
- return QianfanStreamingChatModel.builder()
- .apiKey(apiKey)
- .secretKey(secretKey)
- .modelName(model)
- .build();
+ return getChatModel();
+ }
+
+ public AIAssistant build() {
+ ChatModel chatModel = getChatModel();
+ StreamingChatModel streamingChatModel = getStreamingChatModel();
+ ChatMemory memory = getChatMemory();
+
+ AIAssistant.Service aiService = new AIAssistant.Service() {
+ @Override
+ public String chat(String userMessage) {
+ List messages = new ArrayList<>();
+ if (systemPrompt != null) {
+ messages.add(new SystemMessage(systemPrompt));
+ }
+ // Add conversation history
+ String convId = String.valueOf(id);
+ List history = memory.get(convId);
+ messages.addAll(history);
+ // Add new user message
+ UserMessage newUserMessage = new UserMessage(userMessage);
+ messages.add(newUserMessage);
+
+ Prompt prompt = new Prompt(messages);
+ String response =
+ chatModel.call(prompt).getResult().getOutput().getText();
+
+ // Save to memory
+ memory.add(
+ convId,
+ List.of(
+ newUserMessage,
+ new org.springframework.ai.chat.messages.AssistantMessage(response)));
+
+ return response;
+ }
+
+ @Override
+ public Flux streamChat(String userMessage) {
+ List messages = new ArrayList<>();
+ if (systemPrompt != null) {
+ messages.add(new SystemMessage(systemPrompt));
+ }
+ // Add conversation history
+ String convId = String.valueOf(id);
+ List history = memory.get(convId);
+ messages.addAll(history);
+ // Add new user message
+ UserMessage newUserMessage = new UserMessage(userMessage);
+ messages.add(newUserMessage);
+
+ Prompt prompt = new Prompt(messages);
+
+ StringBuilder responseBuilder = new StringBuilder();
+ return streamingChatModel.stream(prompt)
+ .map(chatResponse -> {
+ String content =
+ chatResponse.getResult().getOutput().getText();
+ if (content != null) {
+ responseBuilder.append(content);
+ }
+ return content;
+ })
+ .doOnComplete(() -> {
+ // Save to memory when streaming completes
+ memory.add(
+ convId,
+ List.of(
+ newUserMessage,
+ new org.springframework.ai.chat.messages.AssistantMessage(
+ responseBuilder.toString())));
+ });
+ }
+ };
+
+ return new QianFanAssistant(id, memory, aiService);
}
}
}
diff --git a/bigtop-manager-ai/src/test/java/assistant/GeneralAssistantFactoryTest.java b/bigtop-manager-ai/src/test/java/assistant/GeneralAssistantFactoryTest.java
index 778c1ffe3..e87d2c418 100644
--- a/bigtop-manager-ai/src/test/java/assistant/GeneralAssistantFactoryTest.java
+++ b/bigtop-manager-ai/src/test/java/assistant/GeneralAssistantFactoryTest.java
@@ -58,7 +58,6 @@ void testCreateAIAssistant() {
when(mockBuilder.id(any())).thenReturn(mockBuilder);
when(mockBuilder.memoryStore(any())).thenReturn(mockBuilder);
when(mockBuilder.withConfig(any())).thenReturn(mockBuilder);
- when(mockBuilder.withToolProvider(any())).thenReturn(mockBuilder);
when(mockBuilder.withSystemPrompt(any())).thenReturn(mockBuilder);
when(mockBuilder.build()).thenReturn(mock(AIAssistant.class));
diff --git a/bigtop-manager-ai/src/test/java/assistant/store/PersistentChatMemoryStoreTest.java b/bigtop-manager-ai/src/test/java/assistant/store/PersistentChatMemoryStoreTest.java
index 242bfc53b..deeeab640 100644
--- a/bigtop-manager-ai/src/test/java/assistant/store/PersistentChatMemoryStoreTest.java
+++ b/bigtop-manager-ai/src/test/java/assistant/store/PersistentChatMemoryStoreTest.java
@@ -27,22 +27,18 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
-
-import dev.langchain4j.data.message.AiMessage;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.ChatMessageType;
-import dev.langchain4j.data.message.SystemMessage;
-import dev.langchain4j.data.message.UserMessage;
+import org.springframework.ai.chat.messages.AssistantMessage;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@@ -54,12 +50,12 @@ class PersistentChatMemoryStoreTest {
@Mock
private ChatMessageDao chatMessageDao;
- @InjectMocks
private PersistentChatMemoryStore persistentChatMemoryStore;
@BeforeEach
void setUp() {
- persistentChatMemoryStore = new PersistentChatMemoryStore(chatThreadDao, chatMessageDao);
+ Long threadId = 1L;
+ persistentChatMemoryStore = new PersistentChatMemoryStore(threadId, chatThreadDao, chatMessageDao);
}
@Test
@@ -81,37 +77,34 @@ void testGetMessages() {
chatMessagePOS.add(messagePO3);
when(chatMessageDao.findAllByThreadId(threadId)).thenReturn(chatMessagePOS);
- List result = persistentChatMemoryStore.getMessages(threadId);
+ List result = persistentChatMemoryStore.findByConversationId(String.valueOf(threadId));
assertEquals(2, result.size());
- assertTrue(result.get(0) instanceof AiMessage);
- assertEquals("Hello from AI", ((AiMessage) result.get(0)).text());
+ assertTrue(result.get(0) instanceof AssistantMessage);
+ assertEquals("Hello from AI", ((AssistantMessage) result.get(0)).getText());
}
@Test
- void testUpdateMessages() {
+ void testAddMessages() {
Long threadId = 1L;
ChatThreadPO chatThreadPO = new ChatThreadPO();
chatThreadPO.setUserId(123L);
when(chatThreadDao.findById(threadId)).thenReturn(chatThreadPO);
- List messages = new ArrayList<>();
+ List messages = new ArrayList<>();
messages.add(new SystemMessage("Hello System"));
- persistentChatMemoryStore.updateMessages(threadId, messages);
+ persistentChatMemoryStore.saveAll(String.valueOf(threadId), messages);
+ messages.clear();
messages.add(new UserMessage("Hello User"));
- persistentChatMemoryStore.updateMessages(threadId, messages);
- messages.add(new AiMessage("Hello AI"));
- persistentChatMemoryStore.updateMessages(threadId, messages);
-
- ChatMessage mockMessage = mock(ChatMessage.class);
- when(mockMessage.type()).thenReturn(ChatMessageType.TOOL_EXECUTION_RESULT);
- messages.add(mockMessage);
- persistentChatMemoryStore.updateMessages(threadId, messages);
+ persistentChatMemoryStore.saveAll(String.valueOf(threadId), messages);
+ messages.clear();
+ messages.add(new AssistantMessage("Hello AI"));
+ persistentChatMemoryStore.saveAll(String.valueOf(threadId), messages);
}
@Test
- void testDeleteMessages() {
+ void testClearMessages() {
Long threadId = 1L;
List chatMessagePOS = new ArrayList<>();
@@ -121,7 +114,7 @@ void testDeleteMessages() {
when(chatMessageDao.findAllByThreadId(threadId)).thenReturn(chatMessagePOS);
- persistentChatMemoryStore.deleteMessages(threadId);
+ persistentChatMemoryStore.deleteByConversationId(String.valueOf(threadId));
Assertions.assertTrue(chatMessagePOS.get(0).getIsDeleted());
}
@@ -130,12 +123,16 @@ void testDeleteMessages() {
void testSystemMessage() {
Long threadId = 1L;
- when(chatMessageDao.findAllByThreadId(threadId)).thenReturn(new ArrayList<>());
- persistentChatMemoryStore.updateMessages(threadId, List.of(new SystemMessage("Hello from System")));
- List result = persistentChatMemoryStore.getMessages(threadId);
+ ChatMessagePO systemMessagePO = new ChatMessagePO();
+ systemMessagePO.setSender("system");
+ systemMessagePO.setMessage("Hello from System");
+
+ when(chatMessageDao.findAllByThreadId(threadId)).thenReturn(List.of(systemMessagePO));
+
+ List result = persistentChatMemoryStore.findByConversationId(String.valueOf(threadId));
assertEquals(1, result.size());
assertTrue(result.get(0) instanceof SystemMessage);
- assertEquals("Hello from System", ((SystemMessage) result.get(0)).text());
+ assertEquals("Hello from System", ((SystemMessage) result.get(0)).getText());
}
}
diff --git a/bigtop-manager-bom/pom.xml b/bigtop-manager-bom/pom.xml
index d701c4f01..878ae561b 100644
--- a/bigtop-manager-bom/pom.xml
+++ b/bigtop-manager-bom/pom.xml
@@ -32,7 +32,7 @@
1.0.0-RC1
- 3.1.1
+ 3.2.0
2.2.0
2.3.32
3.14.0
@@ -52,8 +52,8 @@
1.12.4
8.1.2.192
2.15.0
- 1.0.1
- 1.0.1-beta6
+ 1.0.1
+ 24.0.1
3.0.3
2.1.0
4.29.0
@@ -261,19 +261,23 @@
${grpc-spring-boot.version}
- dev.langchain4j
- langchain4j
- ${langchain4j-core.version}
+ org.springframework.ai
+ spring-ai-bom
+ ${spring-ai.version}
+ pom
+ import
+
- dev.langchain4j
- langchain4j-open-ai
- ${langchain4j-core.version}
+ org.springframework.ai
+ spring-ai-openai
+ ${spring-ai.version}
+
- dev.langchain4j
- langchain4j-community-qianfan
- ${langchain4j.version}
+ org.springframework.ai
+ spring-ai-deepseek
+ ${spring-ai.version}
@@ -282,22 +286,23 @@
${spring-ai.version}
+
- com.github.victools
- jsonschema-module-jackson
- ${victools.version}
+ dev.langchain4j
+ langchain4j
+ ${langchain4j.version}
- dev.langchain4j
- langchain4j-community-dashscope
- ${langchain4j.version}
+ org.jetbrains
+ annotations
+ ${jetbrains-annotations.version}
- dev.langchain4j
- langchain4j-reactor
- ${langchain4j.version}
+ com.github.victools
+ jsonschema-module-jackson
+ ${victools.version}
diff --git a/bigtop-manager-server/pom.xml b/bigtop-manager-server/pom.xml
index 7d4545749..e6d3ff384 100644
--- a/bigtop-manager-server/pom.xml
+++ b/bigtop-manager-server/pom.xml
@@ -63,6 +63,14 @@
org.apache.bigtop
bigtop-manager-ai
+
+ dev.langchain4j
+ langchain4j
+
+
+ org.jetbrains
+ annotations
+
com.github.victools
jsonschema-module-jackson
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/ChatbotController.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/ChatbotController.java
index 2206970eb..c6b835d55 100644
--- a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/ChatbotController.java
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/ChatbotController.java
@@ -62,7 +62,7 @@ public ResponseEntity createChatThread(@RequestBody ChatbotThreadR
@Operation(summary = "update thread", description = "Update a chat thread")
@PutMapping("/threads/{threadId}")
public ResponseEntity updateChatThread(
- @PathVariable Long threadId, @RequestBody ChatbotThreadReq chatbotThreadReq) {
+ @PathVariable(name = "threadId") Long threadId, @RequestBody ChatbotThreadReq chatbotThreadReq) {
ChatThreadDTO chatThreadDTO = ChatThreadConverter.INSTANCE.fromReq2DTO(chatbotThreadReq);
chatThreadDTO.setId(threadId);
return ResponseEntity.success(chatbotService.updateChatThread(chatThreadDTO));
@@ -70,13 +70,13 @@ public ResponseEntity updateChatThread(
@Operation(summary = "delete thread", description = "Delete a chat thread")
@DeleteMapping("/threads/{threadId}")
- public ResponseEntity deleteChatThread(@PathVariable Long threadId) {
+ public ResponseEntity deleteChatThread(@PathVariable(name = "threadId") Long threadId) {
return ResponseEntity.success(chatbotService.deleteChatThread(threadId));
}
@Operation(summary = "get thread", description = "Get a chat thread")
@GetMapping("/threads/{threadId}")
- public ResponseEntity getChatThread(@PathVariable Long threadId) {
+ public ResponseEntity getChatThread(@PathVariable(name = "threadId") Long threadId) {
return ResponseEntity.success(chatbotService.getChatThread(threadId));
}
@@ -88,7 +88,7 @@ public ResponseEntity> getAllChatThreads() {
@Operation(summary = "talk", description = "Talk with Chatbot")
@PostMapping("/threads/{threadId}/talk")
- public SseEmitter talk(@PathVariable Long threadId, @RequestBody ChatbotMessageReq messageReq) {
+ public SseEmitter talk(@PathVariable(name = "threadId") Long threadId, @RequestBody ChatbotMessageReq messageReq) {
ChatbotCommand command = ChatbotCommand.getCommandFromMessage(messageReq.getMessage());
if (command != null) {
messageReq.setMessage(
@@ -99,7 +99,7 @@ public SseEmitter talk(@PathVariable Long threadId, @RequestBody ChatbotMessageR
@Operation(summary = "history", description = "Get chat records")
@GetMapping("/threads/{threadId}/history")
- public ResponseEntity> history(@PathVariable Long threadId) {
+ public ResponseEntity> history(@PathVariable(name = "threadId") Long threadId) {
return ResponseEntity.success(chatbotService.history(threadId));
}
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/LLMConfigController.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/LLMConfigController.java
index 2b27cbb66..141d5341a 100644
--- a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/LLMConfigController.java
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/LLMConfigController.java
@@ -58,13 +58,14 @@ public ResponseEntity> platforms() {
@Operation(summary = "get platform", description = "Get platform")
@GetMapping("/platforms/{id}")
- public ResponseEntity getPlatform(@PathVariable Long id) {
+ public ResponseEntity getPlatform(@PathVariable(name = "id") Long id) {
return ResponseEntity.success(llmConfigService.getPlatform(id));
}
@Operation(summary = "platform credentials", description = "Get platform auth credentials")
@GetMapping("/platforms/{platformId}/auth-credentials")
- public ResponseEntity> platformsAuthCredential(@PathVariable Long platformId) {
+ public ResponseEntity> platformsAuthCredential(
+ @PathVariable(name = "platformId") Long platformId) {
return ResponseEntity.success(llmConfigService.platformsAuthCredentials(platformId));
}
@@ -91,7 +92,7 @@ public ResponseEntity addAuthorizedPlatform(@RequestBody AuthPla
@Operation(summary = "update auth platform", description = "Update authorized platform")
@PutMapping("/auth-platforms/{authId}")
public ResponseEntity updateAuthorizedPlatform(
- @PathVariable Long authId, @RequestBody AuthPlatformReq authPlatformReq) {
+ @PathVariable(name = "authId") Long authId, @RequestBody AuthPlatformReq authPlatformReq) {
AuthPlatformDTO authPlatformDTO = AuthPlatformConverter.INSTANCE.fromReq2DTO(authPlatformReq);
authPlatformDTO.setId(authId);
return ResponseEntity.success(llmConfigService.updateAuthorizedPlatform(authPlatformDTO));
@@ -99,25 +100,25 @@ public ResponseEntity updateAuthorizedPlatform(
@Operation(summary = "get auth platform", description = "Get a authorized platform")
@GetMapping("/auth-platforms/{authId}")
- public ResponseEntity getAuthorizedPlatform(@PathVariable Long authId) {
+ public ResponseEntity getAuthorizedPlatform(@PathVariable(name = "authId") Long authId) {
return ResponseEntity.success(llmConfigService.getAuthorizedPlatform(authId));
}
@Operation(summary = "delete auth platform", description = "Delete a authorized platform")
@DeleteMapping("/auth-platforms/{authId}")
- public ResponseEntity deleteAuthorizedPlatform(@PathVariable Long authId) {
+ public ResponseEntity deleteAuthorizedPlatform(@PathVariable(name = "authId") Long authId) {
return ResponseEntity.success(llmConfigService.deleteAuthorizedPlatform(authId));
}
@Operation(summary = "activate auth platform", description = "Activate authorized platform")
@PostMapping("/auth-platforms/{authId}/activate")
- public ResponseEntity activateAuthorizedPlatform(@PathVariable Long authId) {
+ public ResponseEntity activateAuthorizedPlatform(@PathVariable(name = "authId") Long authId) {
return ResponseEntity.success(llmConfigService.activateAuthorizedPlatform(authId));
}
@Operation(summary = "deactivate auth platform", description = "Deactivate authorized platform")
@PostMapping("/auth-platforms/{authId}/deactivate")
- public ResponseEntity deactivateAuthorizedPlatform(@PathVariable Long authId) {
+ public ResponseEntity deactivateAuthorizedPlatform(@PathVariable(name = "authId") Long authId) {
return ResponseEntity.success(llmConfigService.deactivateAuthorizedPlatform(authId));
}
}
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/LoginController.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/LoginController.java
index 08be70246..0627bb585 100644
--- a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/LoginController.java
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/LoginController.java
@@ -36,6 +36,7 @@
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
@@ -53,14 +54,14 @@ public class LoginController {
@Operation(summary = "salt", description = "Generate salt")
@GetMapping(value = "/salt")
- public ResponseEntity salt(String username) {
+ public ResponseEntity salt(@RequestParam("username") String username) {
String salt = Pbkdf2Utils.generateSalt(username);
return ResponseEntity.success(salt);
}
@Operation(summary = "nonce", description = "Generate nonce")
@GetMapping(value = "/nonce")
- public ResponseEntity nonce(String username) {
+ public ResponseEntity nonce(@RequestParam("username") String username) {
String nonce = PasswordUtils.randomString(16);
String cacheKey = username + ":" + nonce;
CacheUtils.setCache(Caches.CACHE_NONCE, cacheKey, nonce, Caches.NONCE_EXPIRE_TIME_MINUTES, TimeUnit.MINUTES);
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/converter/JsonToolCallResultConverter.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/converter/JsonToolCallResultConverter.java
index 3632001e5..6cf6bce99 100644
--- a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/converter/JsonToolCallResultConverter.java
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/converter/JsonToolCallResultConverter.java
@@ -20,7 +20,6 @@
import org.apache.bigtop.manager.common.utils.JsonUtils;
-import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.execution.ToolCallResultConverter;
@@ -43,7 +42,7 @@ public class JsonToolCallResultConverter implements ToolCallResultConverter {
private static final Logger logger = LoggerFactory.getLogger(JsonToolCallResultConverter.class);
- @NotNull @Override
+ @Override
public String convert(@Nullable Object result, @Nullable Type returnType) {
if (returnType == Void.TYPE) {
logger.debug("The tool has no return type. Converting to conventional response.");
diff --git a/bigtop-manager-server/src/main/resources/ddl/MySQL-DDL-CREATE.sql b/bigtop-manager-server/src/main/resources/ddl/MySQL-DDL-CREATE.sql
index aeab69311..773dd94f7 100644
--- a/bigtop-manager-server/src/main/resources/ddl/MySQL-DDL-CREATE.sql
+++ b/bigtop-manager-server/src/main/resources/ddl/MySQL-DDL-CREATE.sql
@@ -355,8 +355,8 @@ VALUES
INSERT INTO llm_platform (credential, name, support_models)
VALUES
('{"apiKey": "API Key"}', 'OpenAI', 'gpt-3.5-turbo,gpt-4,gpt-4o,gpt-3.5-turbo-16k,gpt-4-turbo-preview,gpt-4-32k,gpt-4o-mini'),
-('{"apiKey": "API Key"}', 'DashScope', 'qwen-1.8b-chat,qwen-max,qwen-plus,qwen-turbo,qwen3-235b-a22b,qwen3-30b-a3b,qwen-plus-latest,qwen-turbo-latest'),
-('{"apiKey": "API Key", "secretKey": "Secret Key"}', 'QianFan','Yi-34B-Chat,ERNIE-4.0-8K,ERNIE-3.5-128K,ERNIE-Speed-8K,Llama-2-7B-Chat,Fuyu-8B'),
+('{"apiKey": "API Key"}', 'DashScope', 'qwen-max,qwen-plus,qwen-turbo,qwen3-235b-a22b,qwen3-30b-a3b,qwen-plus-latest,qwen-turbo-latest'),
+('{"apiKey": "API Key"}', 'QianFan','ernie-speed-8k'),
('{"apiKey": "API Key"}','DeepSeek','deepseek-chat,deepseek-reasoner');
UPDATE `llm_platform`
@@ -368,7 +368,7 @@ SET `desc` = 'Get your API Key in https://bailian.console.aliyun.com/?apiKey=1#/
WHERE `name` = 'DashScope';
UPDATE `llm_platform`
-SET `desc` = 'Get API Key and Secret Key in https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application/v1'
+SET `desc` = 'Get API Key and Secret Key in https://console.bce.baidu.com/iam/#/iam/apikey/list'
WHERE `name` = 'QianFan';
UPDATE `llm_platform`
diff --git a/bigtop-manager-server/src/main/resources/ddl/PostgreSQL-DDL-CREATE.sql b/bigtop-manager-server/src/main/resources/ddl/PostgreSQL-DDL-CREATE.sql
index 35feb63ff..eb34c0bac 100644
--- a/bigtop-manager-server/src/main/resources/ddl/PostgreSQL-DDL-CREATE.sql
+++ b/bigtop-manager-server/src/main/resources/ddl/PostgreSQL-DDL-CREATE.sql
@@ -366,8 +366,8 @@ VALUES
INSERT INTO llm_platform (credential, name, support_models)
VALUES
('{"apiKey": "API Key"}','OpenAI','gpt-3.5-turbo,gpt-4,gpt-4o,gpt-3.5-turbo-16k,gpt-4-turbo-preview,gpt-4-32k,gpt-4o-mini'),
-('{"apiKey": "API Key"}','DashScope','qwen-1.8b-chat,qwen-max,qwen-plus,qwen-turbo,qwen3-235b-a22b,qwen3-30b-a3b,qwen-plus-latest,qwen-turbo-latest'),
-('{"apiKey": "API Key", "secretKey": "Secret Key"}','QianFan','Yi-34B-Chat,ERNIE-4.0-8K,ERNIE-3.5-128K,ERNIE-Speed-8K,Llama-2-7B-Chat,Fuyu-8B'),
+('{"apiKey": "API Key"}','DashScope','qwen-max,qwen-plus,qwen-turbo,qwen3-235b-a22b,qwen3-30b-a3b,qwen-plus-latest,qwen-turbo-latest'),
+('{"apiKey": "API Key"}', 'QianFan','ernie-speed-8k'),
('{"apiKey": "API Key"}','DeepSeek','deepseek-chat,deepseek-reasoner');
UPDATE llm_platform
@@ -379,7 +379,7 @@ SET "desc" = 'Get your API Key in https://bailian.console.aliyun.com/?apiKey=1#/
WHERE "name" = 'DashScope';
UPDATE llm_platform
-SET "desc" = 'Get API Key and Secret Key in https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application/v1'
+SET "desc" = 'Get API Key and Secret Key in https://console.bce.baidu.com/iam/#/iam/apikey/list'
WHERE "name" = 'QianFan';
UPDATE llm_platform