diff --git a/bigtop-manager-ai/bigtop-manager-ai-assistant/pom.xml b/bigtop-manager-ai/bigtop-manager-ai-assistant/pom.xml index 62ca8b3f..f1810e8b 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-assistant/pom.xml +++ b/bigtop-manager-ai/bigtop-manager-ai-assistant/pom.xml @@ -39,6 +39,10 @@ org.apache.bigtop bigtop-manager-ai-core + + org.apache.bigtop + bigtop-manager-ai-dashscope + org.apache.bigtop bigtop-manager-dao diff --git a/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/GeneralAssistantFactory.java b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/GeneralAssistantFactory.java index 2bc988e8..7ff16bcf 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/GeneralAssistantFactory.java +++ b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/GeneralAssistantFactory.java @@ -19,6 +19,7 @@ package org.apache.bigtop.manager.ai.assistant; import org.apache.bigtop.manager.ai.assistant.provider.LocSystemPromptProvider; +import org.apache.bigtop.manager.ai.assistant.provider.PersistentStoreProvider; import org.apache.bigtop.manager.ai.core.AbstractAIAssistantFactory; import org.apache.bigtop.manager.ai.core.enums.PlatformType; import org.apache.bigtop.manager.ai.core.enums.SystemPrompt; @@ -26,37 +27,36 @@ import org.apache.bigtop.manager.ai.core.factory.AIAssistant; import org.apache.bigtop.manager.ai.core.factory.ToolBox; import org.apache.bigtop.manager.ai.core.provider.AIAssistantConfigProvider; +import org.apache.bigtop.manager.ai.core.provider.MessageStoreProvider; import org.apache.bigtop.manager.ai.core.provider.SystemPromptProvider; +import org.apache.bigtop.manager.ai.dashscope.DashScopeAssistant; import org.apache.bigtop.manager.ai.openai.OpenAIAssistant; import org.apache.commons.lang3.NotImplementedException; -import dev.langchain4j.data.message.SystemMessage; -import dev.langchain4j.store.memory.chat.ChatMemoryStore; -import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore; - import java.util.Objects; public class GeneralAssistantFactory extends AbstractAIAssistantFactory { private final SystemPromptProvider systemPromptProvider; - private final ChatMemoryStore chatMemoryStore; + private final MessageStoreProvider messageStoreProvider; public GeneralAssistantFactory() { - this(new LocSystemPromptProvider(), new InMemoryChatMemoryStore()); + this(new LocSystemPromptProvider(), new PersistentStoreProvider()); } public GeneralAssistantFactory(SystemPromptProvider systemPromptProvider) { - this(systemPromptProvider, new InMemoryChatMemoryStore()); + this(systemPromptProvider, new PersistentStoreProvider()); } - public GeneralAssistantFactory(ChatMemoryStore chatMemoryStore) { - this(new LocSystemPromptProvider(), chatMemoryStore); + public GeneralAssistantFactory(MessageStoreProvider messageStoreProvider) { + this(new LocSystemPromptProvider(), messageStoreProvider); } - public GeneralAssistantFactory(SystemPromptProvider systemPromptProvider, ChatMemoryStore chatMemoryStore) { + public GeneralAssistantFactory( + SystemPromptProvider systemPromptProvider, MessageStoreProvider messageStoreProvider) { this.systemPromptProvider = systemPromptProvider; - this.chatMemoryStore = chatMemoryStore; + this.messageStoreProvider = messageStoreProvider; } @Override @@ -69,14 +69,19 @@ public AIAssistant createWithPrompt( if (Objects.requireNonNull(platformType) == PlatformType.OPENAI) { aiAssistant = OpenAIAssistant.builder() .id(id) - .memoryStore(chatMemoryStore) + .memoryStore(messageStoreProvider.getChatMemoryStore()) .withConfigProvider(assistantConfig) .build(); + } else if (Objects.requireNonNull(platformType) == PlatformType.DASH_SCOPE) { + aiAssistant = DashScopeAssistant.builder() + .id(id) + .withConfigProvider(assistantConfig) + .messageRepository(messageStoreProvider.getMessageRepository()) + .build(); } else { throw new PlatformNotFoundException(platformType.getValue()); } - - SystemMessage systemPrompt = systemPromptProvider.getSystemPrompt(systemPrompts); + String systemPrompt = systemPromptProvider.getSystemMessage(systemPrompts); aiAssistant.setSystemPrompt(systemPrompt); String locale = assistantConfig.getLanguage(); if (locale != null) { diff --git a/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/AIAssistantConfig.java b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/AIAssistantConfig.java index fdd3b0da..132c37a1 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/AIAssistantConfig.java +++ b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/AIAssistantConfig.java @@ -109,7 +109,9 @@ public Builder addConfig(String key, String value) { } public Builder addConfigs(Map configMap) { - configs.putAll(configMap); + if (configMap != null) { + configs.putAll(configMap); + } return this; } diff --git a/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/LocSystemPromptProvider.java b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/LocSystemPromptProvider.java index 7163fb25..33e16e7d 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/LocSystemPromptProvider.java +++ b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/LocSystemPromptProvider.java @@ -23,7 +23,6 @@ import org.springframework.util.ResourceUtils; -import dev.langchain4j.data.message.SystemMessage; import lombok.extern.slf4j.Slf4j; import java.io.File; @@ -36,7 +35,7 @@ public class LocSystemPromptProvider implements SystemPromptProvider { @Override - public SystemMessage getSystemPrompt(SystemPrompt systemPrompt) { + public String getSystemMessage(SystemPrompt systemPrompt) { if (systemPrompt == SystemPrompt.DEFAULT_PROMPT) { systemPrompt = SystemPrompt.BIGDATA_PROFESSOR; } @@ -45,8 +44,8 @@ public SystemMessage getSystemPrompt(SystemPrompt systemPrompt) { } @Override - public SystemMessage getSystemPrompt() { - return getSystemPrompt(SystemPrompt.DEFAULT_PROMPT); + public String getSystemMessage() { + return getSystemMessage(SystemPrompt.DEFAULT_PROMPT); } private String loadTextFromFile(String fileName) { @@ -64,23 +63,23 @@ private String loadTextFromFile(String fileName) { } } - private SystemMessage loadPromptFromFile(String fileName) { + private String loadPromptFromFile(String fileName) { final String filePath = fileName + ".st"; String text = loadTextFromFile(filePath); if (text == null) { - return SystemMessage.from("You are a helpful assistant."); + return "You are a helpful assistant."; } else { - return SystemMessage.from(text); + return text; } } - public SystemMessage getLanguagePrompt(String locale) { + public String getLanguagePrompt(String locale) { final String filePath = SystemPrompt.LANGUAGE_PROMPT.getValue() + '-' + locale + ".st"; String text = loadTextFromFile(filePath); if (text == null) { - return SystemMessage.from("Answer in " + locale); + return "Answer in " + locale; } else { - return SystemMessage.from(text); + return text; } } } diff --git a/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/PersistentStoreProvider.java b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/PersistentStoreProvider.java new file mode 100644 index 00000000..3e24ced9 --- /dev/null +++ b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/provider/PersistentStoreProvider.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bigtop.manager.ai.assistant.provider; + +import org.apache.bigtop.manager.ai.assistant.store.PersistentChatMemoryStore; +import org.apache.bigtop.manager.ai.assistant.store.PersistentMessageRepository; +import org.apache.bigtop.manager.ai.core.provider.MessageStoreProvider; +import org.apache.bigtop.manager.ai.core.repository.MessageRepository; +import org.apache.bigtop.manager.dao.repository.ChatMessageDao; +import org.apache.bigtop.manager.dao.repository.ChatThreadDao; + +import dev.langchain4j.store.memory.chat.ChatMemoryStore; +import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class PersistentStoreProvider implements MessageStoreProvider { + private final ChatThreadDao chatThreadDao; + private final ChatMessageDao chatMessageDao; + + public PersistentStoreProvider(ChatThreadDao chatThreadDao, ChatMessageDao chatMessageDao) { + this.chatThreadDao = chatThreadDao; + this.chatMessageDao = chatMessageDao; + } + + public PersistentStoreProvider() { + chatMessageDao = null; + chatThreadDao = null; + } + + @Override + public MessageRepository getMessageRepository() { + return new PersistentMessageRepository(chatThreadDao, chatMessageDao); + } + + @Override + public ChatMemoryStore getChatMemoryStore() { + if (chatThreadDao == null) { + return new InMemoryChatMemoryStore(); + } + return new PersistentChatMemoryStore(chatThreadDao, chatMessageDao); + } +} diff --git a/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/store/PersistentMessageRepository.java b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/store/PersistentMessageRepository.java new file mode 100644 index 00000000..7b9c3715 --- /dev/null +++ b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/main/java/org/apache/bigtop/manager/ai/assistant/store/PersistentMessageRepository.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bigtop.manager.ai.assistant.store; + +import org.apache.bigtop.manager.ai.core.enums.MessageSender; +import org.apache.bigtop.manager.ai.core.repository.MessageRepository; +import org.apache.bigtop.manager.dao.po.ChatMessagePO; +import org.apache.bigtop.manager.dao.po.ChatThreadPO; +import org.apache.bigtop.manager.dao.repository.ChatMessageDao; +import org.apache.bigtop.manager.dao.repository.ChatThreadDao; + +public class PersistentMessageRepository implements MessageRepository { + private final ChatThreadDao chatThreadDao; + private final ChatMessageDao chatMessageDao; + + private boolean noPersistent() { + return chatThreadDao == null || chatMessageDao == null; + } + + public PersistentMessageRepository(ChatThreadDao chatThreadDao, ChatMessageDao chatMessageDao) { + this.chatThreadDao = chatThreadDao; + this.chatMessageDao = chatMessageDao; + } + + private ChatMessagePO getChatMessagePO(String message, Long threadId, MessageSender sender) { + if (noPersistent()) { + return null; + } + ChatThreadPO chatThreadPO = chatThreadDao.findById(threadId); + ChatMessagePO chatMessagePO = new ChatMessagePO(); + chatMessagePO.setUserId(chatThreadPO.getUserId()); + chatMessagePO.setThreadId(threadId); + chatMessagePO.setSender(sender.getValue()); + chatMessagePO.setMessage(message); + return chatMessagePO; + } + + @Override + public void saveUserMessage(String message, Long threadId) { + if (noPersistent()) { + return; + } + ChatMessagePO chatMessagePO = getChatMessagePO(message, threadId, MessageSender.USER); + chatMessageDao.save(chatMessagePO); + } + + @Override + public void saveAiMessage(String message, Long threadId) { + if (noPersistent()) { + return; + } + ChatMessagePO chatMessagePO = getChatMessagePO(message, threadId, MessageSender.AI); + chatMessageDao.save(chatMessagePO); + } + + @Override + public void saveSystemMessage(String message, Long threadId) { + if (noPersistent()) { + return; + } + ChatMessagePO chatMessagePO = getChatMessagePO(message, threadId, MessageSender.SYSTEM); + chatMessageDao.save(chatMessagePO); + } +} diff --git a/bigtop-manager-ai/bigtop-manager-ai-assistant/src/test/java/org/apache/bigtop/manager/ai/assistant/SystemPromptProviderTests.java b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/test/java/org/apache/bigtop/manager/ai/assistant/SystemPromptProviderTests.java index 1f768e17..6094a763 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-assistant/src/test/java/org/apache/bigtop/manager/ai/assistant/SystemPromptProviderTests.java +++ b/bigtop-manager-ai/bigtop-manager-ai-assistant/src/test/java/org/apache/bigtop/manager/ai/assistant/SystemPromptProviderTests.java @@ -24,8 +24,6 @@ import org.junit.jupiter.api.Test; -import dev.langchain4j.data.message.SystemMessage; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -35,12 +33,12 @@ public class SystemPromptProviderTests { @Test public void loadSystemPromptByIdTest() { - SystemMessage systemPrompt1 = systemPromptProvider.getSystemPrompt(SystemPrompt.BIGDATA_PROFESSOR); - assertFalse(systemPrompt1.text().isEmpty()); + String systemPrompt1 = systemPromptProvider.getSystemMessage(SystemPrompt.BIGDATA_PROFESSOR); + assertFalse(systemPrompt1.isEmpty()); - SystemMessage systemPrompt2 = systemPromptProvider.getSystemPrompt(); - assertFalse(systemPrompt2.text().isEmpty()); + String systemPrompt2 = systemPromptProvider.getSystemMessage(); + assertFalse(systemPrompt2.isEmpty()); - assertEquals(systemPrompt1.text(), systemPrompt2.text()); + assertEquals(systemPrompt1, systemPrompt2); } } diff --git a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/AbstractAIAssistant.java b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/AbstractAIAssistant.java index 5b0f383a..d7b155f8 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/AbstractAIAssistant.java +++ b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/AbstractAIAssistant.java @@ -20,85 +20,12 @@ import org.apache.bigtop.manager.ai.core.factory.AIAssistant; -import dev.langchain4j.data.message.AiMessage; -import dev.langchain4j.data.message.ChatMessage; -import dev.langchain4j.data.message.SystemMessage; -import dev.langchain4j.memory.ChatMemory; -import dev.langchain4j.model.StreamingResponseHandler; -import dev.langchain4j.model.chat.ChatLanguageModel; -import dev.langchain4j.model.chat.StreamingChatLanguageModel; -import dev.langchain4j.model.output.Response; -import reactor.core.publisher.Flux; -import reactor.core.publisher.FluxSink; - public abstract class AbstractAIAssistant implements AIAssistant { - private final ChatLanguageModel chatLanguageModel; - private final StreamingChatLanguageModel streamingChatLanguageModel; - private final Object assistantId; - private final ChatMemory chatMemory; protected static final Integer MEMORY_LEN = 10; - public AbstractAIAssistant( - ChatLanguageModel chatLanguageModel, - StreamingChatLanguageModel streamingChatLanguageModel, - ChatMemory chatMemory) { - this.chatLanguageModel = chatLanguageModel; - this.streamingChatLanguageModel = streamingChatLanguageModel; - this.chatMemory = chatMemory; - this.assistantId = this.chatMemory.id(); - } - - @Override - public Flux streamAsk(ChatMessage chatMessage) { - chatMemory.add(chatMessage); - return Flux.create( - emitter -> streamingChatLanguageModel.generate(chatMemory.messages(), new StreamingResponseHandler<>() { - @Override - public void onNext(String token) { - emitter.next(token); - } - - @Override - public void onError(Throwable error) { - emitter.error(error); - } - - @Override - public void onComplete(Response response) { - StreamingResponseHandler.super.onComplete(response); - chatMemory.add(response.content()); - } - }), - FluxSink.OverflowStrategy.BUFFER); - } - - @Override - public String ask(ChatMessage chatMessage) { - chatMemory.add(chatMessage); - Response generate = chatLanguageModel.generate(chatMemory.messages()); - String aiMessage = generate.content().text(); - chatMemory.add(AiMessage.from(aiMessage)); - return aiMessage; - } - - @Override - public void setSystemPrompt(SystemMessage systemPrompt) { - chatMemory.add(systemPrompt); - } - - @Override - public Object getId() { - return chatMemory.id(); - } - - @Override - public void resetMemory() { - chatMemory.clear(); - } - @Override - public ChatMemory getMemory() { - return this.chatMemory; + public boolean test() { + return ask("1+1=") != null; } } diff --git a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/enums/PlatformType.java b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/enums/PlatformType.java index cadbb2a6..83029c93 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/enums/PlatformType.java +++ b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/enums/PlatformType.java @@ -27,7 +27,8 @@ @Getter public enum PlatformType { - OPENAI("openai"); + OPENAI("openai"), + DASH_SCOPE("dashscope"); private final String value; diff --git a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistant.java b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistant.java index 6834b051..b79474c5 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistant.java +++ b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistant.java @@ -20,12 +20,11 @@ import org.apache.bigtop.manager.ai.core.enums.PlatformType; -import dev.langchain4j.data.message.ChatMessage; -import dev.langchain4j.data.message.SystemMessage; -import dev.langchain4j.data.message.UserMessage; -import dev.langchain4j.memory.ChatMemory; import reactor.core.publisher.Flux; +import java.util.HashMap; +import java.util.Map; + public interface AIAssistant { /** @@ -40,40 +39,38 @@ public interface AIAssistant { * @param userMessage * @return */ - Flux streamAsk(ChatMessage userMessage); + Flux streamAsk(String userMessage); /** * This is a conversation based on blocking output. * @param userMessage * @return */ - String ask(ChatMessage userMessage); + String ask(String userMessage); /** - * This is primarily used to retrieve the AI assistant's history of chat conversations. + * This is used to get the AIAssistant's Platform * @return */ - ChatMemory getMemory(); + PlatformType getPlatform(); /** - * This is used to get the AIAssistant's Platform + * This is used to create a thread * @return */ - PlatformType getPlatform(); - - void setSystemPrompt(SystemMessage systemPrompt); - - void resetMemory(); - - default Flux streamAsk(String message) { - return streamAsk(UserMessage.from(message)); + default Map createThread() { + return new HashMap<>(); } - default String ask(String message) { - return ask(UserMessage.from(message)); - } + /** + * This is used to set system prompt + * @return + */ + void setSystemPrompt(String systemPrompt); - default void setSystemPrompt(String systemPrompt) { - setSystemPrompt(SystemMessage.systemMessage(systemPrompt)); - } + /** + * This is used to test whether the configuration is correct + * @return + */ + boolean test(); } diff --git a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistantFactory.java b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistantFactory.java index d6b24034..5a7d63a0 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistantFactory.java +++ b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/factory/AIAssistantFactory.java @@ -22,8 +22,6 @@ import org.apache.bigtop.manager.ai.core.enums.SystemPrompt; import org.apache.bigtop.manager.ai.core.provider.AIAssistantConfigProvider; -import java.util.UUID; - public interface AIAssistantFactory { AIAssistant createWithPrompt( @@ -31,15 +29,8 @@ AIAssistant createWithPrompt( AIAssistant create(PlatformType platformType, AIAssistantConfigProvider assistantConfig, Object id); - /** - * TODO Create AIAssistant without memory, should delete UUID - * - * @param platformType platform type - * @param assistantConfig assistant config - * @return AIAssistant - */ default AIAssistant create(PlatformType platformType, AIAssistantConfigProvider assistantConfig) { - return create(platformType, assistantConfig, UUID.randomUUID().toString()); + return create(platformType, assistantConfig, null); } ToolBox createToolBox(PlatformType platformType); diff --git a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/provider/MessageStoreProvider.java b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/provider/MessageStoreProvider.java new file mode 100644 index 00000000..9e7fee15 --- /dev/null +++ b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/provider/MessageStoreProvider.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bigtop.manager.ai.core.provider; + +import org.apache.bigtop.manager.ai.core.repository.MessageRepository; + +import dev.langchain4j.store.memory.chat.ChatMemoryStore; + +public interface MessageStoreProvider { + MessageRepository getMessageRepository(); + + ChatMemoryStore getChatMemoryStore(); +} diff --git a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/provider/SystemPromptProvider.java b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/provider/SystemPromptProvider.java index 90f0013a..83bb0d50 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/provider/SystemPromptProvider.java +++ b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/provider/SystemPromptProvider.java @@ -20,14 +20,12 @@ import org.apache.bigtop.manager.ai.core.enums.SystemPrompt; -import dev.langchain4j.data.message.SystemMessage; - public interface SystemPromptProvider { - SystemMessage getSystemPrompt(SystemPrompt systemPrompt); + String getSystemMessage(SystemPrompt systemPrompt); // return default system prompt - SystemMessage getSystemPrompt(); + String getSystemMessage(); - SystemMessage getLanguagePrompt(String locale); + String getLanguagePrompt(String locale); } diff --git a/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/repository/MessageRepository.java b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/repository/MessageRepository.java new file mode 100644 index 00000000..1ad582f1 --- /dev/null +++ b/bigtop-manager-ai/bigtop-manager-ai-core/src/main/java/org/apache/bigtop/manager/ai/core/repository/MessageRepository.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bigtop.manager.ai.core.repository; + +public interface MessageRepository { + default void saveUserMessage(String message, Long threadId) {} + + default void saveAiMessage(String message, Long threadId) {} + + default void saveSystemMessage(String message, Long threadId) {} +} diff --git a/bigtop-manager-ai/bigtop-manager-ai-dashscope/pom.xml b/bigtop-manager-ai/bigtop-manager-ai-dashscope/pom.xml new file mode 100644 index 00000000..0a7259a1 --- /dev/null +++ b/bigtop-manager-ai/bigtop-manager-ai-dashscope/pom.xml @@ -0,0 +1,51 @@ + + + + 4.0.0 + + org.apache.bigtop + bigtop-manager-ai + ${revision} + + + bigtop-manager-ai-dashscope + ${project.artifactId} + Bigtop Manager AI DashScope + + + + org.apache.bigtop + bigtop-manager-ai-core + ${revision} + + + + com.alibaba + dashscope-sdk-java + 2.16.3 + + + org.slf4j + slf4j-simple + + + + + diff --git a/bigtop-manager-ai/bigtop-manager-ai-dashscope/src/main/java/org/apache/bigtop/manager/ai/dashscope/DashScopeAssistant.java b/bigtop-manager-ai/bigtop-manager-ai-dashscope/src/main/java/org/apache/bigtop/manager/ai/dashscope/DashScopeAssistant.java new file mode 100644 index 00000000..538a6747 --- /dev/null +++ b/bigtop-manager-ai/bigtop-manager-ai-dashscope/src/main/java/org/apache/bigtop/manager/ai/dashscope/DashScopeAssistant.java @@ -0,0 +1,325 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bigtop.manager.ai.dashscope; + +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 org.apache.bigtop.manager.ai.core.provider.AIAssistantConfigProvider; +import org.apache.bigtop.manager.ai.core.repository.MessageRepository; + +import com.alibaba.dashscope.aigc.generation.Generation; +import com.alibaba.dashscope.aigc.generation.GenerationParam; +import com.alibaba.dashscope.assistants.Assistant; +import com.alibaba.dashscope.assistants.AssistantParam; +import com.alibaba.dashscope.assistants.Assistants; +import com.alibaba.dashscope.common.GeneralListParam; +import com.alibaba.dashscope.common.ListResult; +import com.alibaba.dashscope.common.Message; +import com.alibaba.dashscope.common.Role; +import com.alibaba.dashscope.exception.InputRequiredException; +import com.alibaba.dashscope.exception.InvalidateParameter; +import com.alibaba.dashscope.exception.NoApiKeyException; +import com.alibaba.dashscope.threads.AssistantStreamEvents; +import com.alibaba.dashscope.threads.AssistantThread; +import com.alibaba.dashscope.threads.ContentBase; +import com.alibaba.dashscope.threads.ContentText; +import com.alibaba.dashscope.threads.ThreadParam; +import com.alibaba.dashscope.threads.Threads; +import com.alibaba.dashscope.threads.messages.Messages; +import com.alibaba.dashscope.threads.messages.TextMessageParam; +import com.alibaba.dashscope.threads.messages.ThreadMessage; +import com.alibaba.dashscope.threads.messages.ThreadMessageDelta; +import com.alibaba.dashscope.threads.runs.AssistantStreamMessage; +import com.alibaba.dashscope.threads.runs.Run; +import com.alibaba.dashscope.threads.runs.RunParam; +import com.alibaba.dashscope.threads.runs.Runs; +import dev.langchain4j.internal.ValidationUtils; +import io.reactivex.Flowable; +import reactor.core.publisher.Flux; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DashScopeAssistant extends AbstractAIAssistant { + private final Assistants assistants = new Assistants(); + private final Messages messages = new Messages(); + private final Threads threads = new Threads(); + private final Runs runs = new Runs(); + private final MessageRepository messageRepository; + private final DashScopeThreadParam dashScopeThreadParam; + + public DashScopeAssistant(MessageRepository messageRepository, DashScopeThreadParam dashScopeThreadParam) { + this.messageRepository = messageRepository; + this.dashScopeThreadParam = dashScopeThreadParam; + } + + private String getValueFromAssistantStreamMessage(AssistantStreamMessage assistantStreamMessage) { + ThreadMessageDelta threadMessageDelta = (ThreadMessageDelta) assistantStreamMessage.getData(); + StringBuilder streamMessage = new StringBuilder(); + + List contents = threadMessageDelta.getDelta().getContent(); + for (ContentBase content : contents) { + ContentText contentText = (ContentText) content; + streamMessage.append(contentText.getText().getValue()); + } + return streamMessage.toString(); + } + + @Override + public PlatformType getPlatform() { + return PlatformType.DASH_SCOPE; + } + + @Override + public void setSystemPrompt(String systemPrompt) { + if (dashScopeThreadParam.getAssistantId() == null) { + return; + } + TextMessageParam textMessageParam = TextMessageParam.builder() + .apiKey(dashScopeThreadParam.getApiKey()) + .role(Role.ASSISTANT.getValue()) + .content(systemPrompt) + .build(); + try { + messages.create(dashScopeThreadParam.getAssistantThreadId(), textMessageParam); + } catch (NoApiKeyException | InputRequiredException e) { + throw new RuntimeException(e); + } + RunParam runParam = RunParam.builder() + .apiKey(dashScopeThreadParam.getApiKey()) + .assistantId(dashScopeThreadParam.getAssistantId()) + .build(); + try { + runs.create(dashScopeThreadParam.getAssistantThreadId(), runParam); + } catch (NoApiKeyException | InputRequiredException | InvalidateParameter e) { + throw new RuntimeException(e); + } + messageRepository.saveSystemMessage(systemPrompt, (Long) dashScopeThreadParam.getThreadId()); + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public Object getId() { + return dashScopeThreadParam.getThreadId(); + } + + @Override + public Flux streamAsk(String userMessage) { + messageRepository.saveUserMessage(userMessage, (Long) dashScopeThreadParam.getThreadId()); + TextMessageParam textMessageParam = TextMessageParam.builder() + .apiKey(dashScopeThreadParam.getApiKey()) + .role(Role.USER.getValue()) + .content(userMessage) + .build(); + try { + ThreadMessage message = messages.create(dashScopeThreadParam.getAssistantThreadId(), textMessageParam); + } catch (NoApiKeyException | InputRequiredException e) { + throw new RuntimeException(e); + } + + RunParam runParam = RunParam.builder() + .apiKey(dashScopeThreadParam.getApiKey()) + .assistantId(dashScopeThreadParam.getAssistantId()) + .stream(true) + .build(); + Flowable runFlowable = null; + try { + runFlowable = runs.createStream(dashScopeThreadParam.getAssistantThreadId(), runParam); + } catch (NoApiKeyException | InputRequiredException | InvalidateParameter e) { + throw new RuntimeException(e); + } + StringBuilder finalMessage = new StringBuilder(); + return Flux.from(runFlowable) + .map(assistantStreamMessage -> { + String message = + assistantStreamMessage.getEvent().equals(AssistantStreamEvents.THREAD_MESSAGE_DELTA) + ? getValueFromAssistantStreamMessage(assistantStreamMessage) + : ""; + finalMessage.append(message); + return message; + }) + .doOnComplete(() -> { + messageRepository.saveAiMessage(finalMessage.toString(), (Long) dashScopeThreadParam.getThreadId()); + }); + } + + @Override + public String ask(String userMessage) { + messageRepository.saveUserMessage(userMessage, (Long) dashScopeThreadParam.getThreadId()); + TextMessageParam textMessageParam = TextMessageParam.builder() + .apiKey(dashScopeThreadParam.getApiKey()) + .role(Role.USER.getValue()) + .content(userMessage) + .build(); + try { + ThreadMessage message = messages.create(dashScopeThreadParam.getAssistantThreadId(), textMessageParam); + } catch (NoApiKeyException | InputRequiredException e) { + throw new RuntimeException(e); + } + + RunParam runParam = RunParam.builder() + .apiKey(dashScopeThreadParam.getApiKey()) + .assistantId(dashScopeThreadParam.getAssistantId()) + .build(); + Run run; + try { + run = runs.create(dashScopeThreadParam.getAssistantThreadId(), runParam); + } catch (NoApiKeyException | InputRequiredException | InvalidateParameter e) { + throw new RuntimeException(e); + } + while (true) { + if (run.getStatus().equals(Run.Status.CANCELLED) + || run.getStatus().equals(Run.Status.COMPLETED) + || run.getStatus().equals(Run.Status.FAILED) + || run.getStatus().equals(Run.Status.REQUIRES_ACTION) + || run.getStatus().equals(Run.Status.EXPIRED)) { + break; + } else { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + try { + run = runs.retrieve( + dashScopeThreadParam.getAssistantThreadId(), run.getId(), dashScopeThreadParam.getApiKey()); + } catch (NoApiKeyException e) { + throw new RuntimeException(e); + } + } + + ListResult threadMessages = null; + try { + threadMessages = messages.list( + dashScopeThreadParam.getAssistantThreadId(), + GeneralListParam.builder() + .apiKey(dashScopeThreadParam.getApiKey()) + .build()); + } catch (NoApiKeyException | InputRequiredException e) { + throw new RuntimeException(e); + } + List threadMessage = threadMessages.getData(); + if (threadMessage.isEmpty()) { + return null; + } + List contents = threadMessage.get(0).getContent(); + StringBuilder finalMessage = new StringBuilder(); + for (ContentBase content : contents) { + ContentText contentText = (ContentText) content; + finalMessage.append(contentText.getText().getValue()); + } + messageRepository.saveAiMessage(finalMessage.toString(), (Long) dashScopeThreadParam.getThreadId()); + return finalMessage.toString(); + } + + @Override + public boolean test() { + Generation generation = new Generation(); + GenerationParam param = GenerationParam.builder() + .apiKey(dashScopeThreadParam.getApiKey()) + .model(dashScopeThreadParam.getModel()) + .build(); + + Message userMsg = + Message.builder().role(Role.USER.getValue()).content("1+1=").build(); + param.setMessages(Collections.singletonList(userMsg)); + try { + generation.call(param); + } catch (NoApiKeyException | InputRequiredException e) { + throw new RuntimeException(e); + } + return true; + } + + @Override + public Map createThread() { + AssistantParam param = AssistantParam.builder() + .model(dashScopeThreadParam.getModel()) + .apiKey(dashScopeThreadParam.getApiKey()) + .name("DashScope Assistant") + .build(); + Map threadInfo = new HashMap<>(); + try { + Assistant assistant = assistants.create(param); + threadInfo.put("assistantId", assistant.getId()); + } catch (NoApiKeyException e) { + throw new RuntimeException(e); + } + ThreadParam threadParam = + ThreadParam.builder().apiKey(dashScopeThreadParam.getApiKey()).build(); + try { + AssistantThread assistantThread = threads.create(threadParam); + threadInfo.put("assistantThreadId", assistantThread.getId()); + } catch (NoApiKeyException e) { + throw new RuntimeException(e); + } + return threadInfo; + } + + public static class Builder { + private Object id; + private AIAssistantConfigProvider configProvider; + private MessageRepository messageRepository; + + public Builder() {} + + public Builder withConfigProvider(AIAssistantConfigProvider configProvider) { + this.configProvider = configProvider; + return this; + } + + public Builder id(Object id) { + this.id = id; + return this; + } + + public Builder messageRepository(MessageRepository messageRepository) { + this.messageRepository = messageRepository; + return this; + } + + public AIAssistant build() { + String model = ValidationUtils.ensureNotNull(configProvider.getModel(), "model"); + String apiKey = ValidationUtils.ensureNotNull( + configProvider.getCredentials().get("apiKey"), "apiKey"); + DashScopeThreadParam param = new DashScopeThreadParam(); + param.setApiKey(apiKey); + param.setModel(model); + String assistantThreadId = configProvider.getConfigs().get("assistantThreadId"); + if (assistantThreadId != null) { + param.setAssistantThreadId(assistantThreadId); + } + String assistantId = configProvider.getConfigs().get("assistantId"); + if (assistantId != null) { + param.setAssistantId(assistantId); + } + if (id != null) { + param.setThreadId(id); + } + return new DashScopeAssistant(messageRepository, param); + } + } +} diff --git a/bigtop-manager-ai/bigtop-manager-ai-dashscope/src/main/java/org/apache/bigtop/manager/ai/dashscope/DashScopeThreadParam.java b/bigtop-manager-ai/bigtop-manager-ai-dashscope/src/main/java/org/apache/bigtop/manager/ai/dashscope/DashScopeThreadParam.java new file mode 100644 index 00000000..06e6f088 --- /dev/null +++ b/bigtop-manager-ai/bigtop-manager-ai-dashscope/src/main/java/org/apache/bigtop/manager/ai/dashscope/DashScopeThreadParam.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bigtop.manager.ai.dashscope; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class DashScopeThreadParam { + private Object threadId; + + private String assistantId; + + private String assistantThreadId; + + private String model; + + private String apiKey; +} diff --git a/bigtop-manager-ai/bigtop-manager-ai-dashscope/src/main/java/org/apache/bigtop/manager/ai/dashscope/DashScopeToolBox.java b/bigtop-manager-ai/bigtop-manager-ai-dashscope/src/main/java/org/apache/bigtop/manager/ai/dashscope/DashScopeToolBox.java new file mode 100644 index 00000000..6a5ac84e --- /dev/null +++ b/bigtop-manager-ai/bigtop-manager-ai-dashscope/src/main/java/org/apache/bigtop/manager/ai/dashscope/DashScopeToolBox.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bigtop.manager.ai.dashscope; + +import org.apache.bigtop.manager.ai.core.factory.ToolBox; + +import reactor.core.publisher.Flux; + +import java.util.List; + +public class DashScopeToolBox implements ToolBox { + @Override + public List getTools() { + return null; + } + + @Override + public String invoke(String toolName) { + return null; + } + + @Override + public Flux streamInvoke(String toolName) { + return null; + } +} diff --git a/bigtop-manager-ai/bigtop-manager-ai-openai/src/main/java/org/apache/bigtop/manager/ai/openai/OpenAIAssistant.java b/bigtop-manager-ai/bigtop-manager-ai-openai/src/main/java/org/apache/bigtop/manager/ai/openai/OpenAIAssistant.java index f5325802..59eba580 100644 --- a/bigtop-manager-ai/bigtop-manager-ai-openai/src/main/java/org/apache/bigtop/manager/ai/openai/OpenAIAssistant.java +++ b/bigtop-manager-ai/bigtop-manager-ai-openai/src/main/java/org/apache/bigtop/manager/ai/openai/OpenAIAssistant.java @@ -20,27 +20,104 @@ import org.apache.bigtop.manager.ai.core.AbstractAIAssistant; import org.apache.bigtop.manager.ai.core.enums.PlatformType; +import org.apache.bigtop.manager.ai.core.exception.AssistantConfigNotSetException; import org.apache.bigtop.manager.ai.core.factory.AIAssistant; import org.apache.bigtop.manager.ai.core.provider.AIAssistantConfigProvider; +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.data.message.SystemMessage; +import dev.langchain4j.data.message.UserMessage; import dev.langchain4j.internal.ValidationUtils; import dev.langchain4j.memory.ChatMemory; import dev.langchain4j.memory.chat.MessageWindowChatMemory; +import dev.langchain4j.model.StreamingResponseHandler; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.chat.StreamingChatLanguageModel; import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.model.openai.OpenAiStreamingChatModel; +import dev.langchain4j.model.output.Response; import dev.langchain4j.store.memory.chat.ChatMemoryStore; +import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; public class OpenAIAssistant extends AbstractAIAssistant { + private final ChatLanguageModel chatLanguageModel; + private final StreamingChatLanguageModel streamingChatLanguageModel; + private final ChatMemory chatMemory; + private static final String BASE_URL = "https://api.openai.com/v1"; - private OpenAIAssistant( + public OpenAIAssistant( ChatLanguageModel chatLanguageModel, StreamingChatLanguageModel streamingChatLanguageModel, ChatMemory chatMemory) { - super(chatLanguageModel, streamingChatLanguageModel, chatMemory); + this.chatLanguageModel = chatLanguageModel; + this.streamingChatLanguageModel = streamingChatLanguageModel; + this.chatMemory = chatMemory; + } + + @Override + public Flux streamAsk(String chatMessage) { + if (chatMemory == null || streamingChatLanguageModel == null) { + throw new AssistantConfigNotSetException("threadId"); + } + chatMemory.add(UserMessage.from(chatMessage)); + return Flux.create( + emitter -> streamingChatLanguageModel.generate(chatMemory.messages(), new StreamingResponseHandler<>() { + @Override + public void onNext(String token) { + emitter.next(token); + } + + @Override + public void onError(Throwable error) { + emitter.error(error); + } + + @Override + public void onComplete(Response response) { + StreamingResponseHandler.super.onComplete(response); + chatMemory.add(response.content()); + } + }), + FluxSink.OverflowStrategy.BUFFER); + } + + @Override + public String ask(String chatMessage) { + if (chatMemory == null || chatLanguageModel == null) { + throw new AssistantConfigNotSetException("threadId"); + } + chatMemory.add(UserMessage.from(chatMessage)); + Response generate = chatLanguageModel.generate(chatMemory.messages()); + String aiMessage = generate.content().text(); + chatMemory.add(AiMessage.from(aiMessage)); + return aiMessage; + } + + @Override + public void setSystemPrompt(String systemPrompt) { + if (chatMemory == null) { + throw new AssistantConfigNotSetException("threadId"); + } + chatMemory.add(SystemMessage.systemMessage(systemPrompt)); + } + + public void setSystemPrompt(SystemMessage systemPrompt) { + if (chatMemory == null) { + throw new AssistantConfigNotSetException("threadId"); + } + chatMemory.add(systemPrompt); + } + + @Override + public Object getId() { + return chatMemory.id(); + } + + public void resetMemory() { + chatMemory.clear(); } @Override @@ -76,7 +153,6 @@ public Builder memoryStore(ChatMemoryStore chatMemoryStore) { } public AIAssistant build() { - ValidationUtils.ensureNotNull(id, "id"); String model = ValidationUtils.ensureNotNull(configProvider.getModel(), "model"); String apiKey = ValidationUtils.ensureNotNull( configProvider.getCredentials().get("apiKey"), "apiKey"); @@ -90,11 +166,13 @@ public AIAssistant build() { .baseUrl(BASE_URL) .modelName(model) .build(); - MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder() - .id(id) + MessageWindowChatMemory.Builder builder = MessageWindowChatMemory.builder() .chatMemoryStore(chatMemoryStore) - .maxMessages(MEMORY_LEN) - .build(); + .maxMessages(MEMORY_LEN); + if (id != null) { + builder.id(id); + } + MessageWindowChatMemory chatMemory = builder.build(); return new OpenAIAssistant(openAiChatModel, openaiStreamChatModel, chatMemory); } } diff --git a/bigtop-manager-ai/pom.xml b/bigtop-manager-ai/pom.xml index 58a96981..c731441f 100644 --- a/bigtop-manager-ai/pom.xml +++ b/bigtop-manager-ai/pom.xml @@ -32,6 +32,7 @@ Bigtop Manager AI bigtop-manager-ai-openai + bigtop-manager-ai-dashscope bigtop-manager-ai-core bigtop-manager-ai-assistant diff --git a/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/po/ChatThreadPO.java b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/po/ChatThreadPO.java index e4c8628e..f17cfb33 100644 --- a/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/po/ChatThreadPO.java +++ b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/po/ChatThreadPO.java @@ -25,6 +25,7 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; import java.io.Serializable; +import java.util.Map; @Data @EqualsAndHashCode(callSuper = true) @@ -37,6 +38,9 @@ public class ChatThreadPO extends BasePO implements Serializable { @Column(name = "model", nullable = false, length = 255) private String model; + @Column(name = "thread_info", columnDefinition = "json", nullable = false) + private Map threadInfo; + @Column(name = "user_id") private Long userId; diff --git a/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/ChatThreadDao.java b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/ChatThreadDao.java index a691b7b3..895cba8f 100644 --- a/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/ChatThreadDao.java +++ b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/ChatThreadDao.java @@ -29,6 +29,10 @@ public interface ChatThreadDao extends BaseDao { ChatThreadPO findById(Long id); + ChatThreadPO findByThreadId(@Param("id") Long id); + List findAllByPlatformAuthorizedIdAndUserId( @Param("platformId") Long platformAuthorizedId, @Param("userId") Long userId); + + void saveWithThreadInfo(ChatThreadPO chatThreadPO); } diff --git a/bigtop-manager-dao/src/main/resources/mapper/mysql/ChatThreadMapper.xml b/bigtop-manager-dao/src/main/resources/mapper/mysql/ChatThreadMapper.xml index be702079..5ecd9a2a 100644 --- a/bigtop-manager-dao/src/main/resources/mapper/mysql/ChatThreadMapper.xml +++ b/bigtop-manager-dao/src/main/resources/mapper/mysql/ChatThreadMapper.xml @@ -24,6 +24,11 @@ id, user_id, platform_id, model + + + + + + + + + INSERT INTO llm_chat_thread (platform_id, user_id, model, thread_info) + VALUES (#{platformId}, #{userId}, #{model}, #{threadInfo, typeHandler=org.apache.bigtop.manager.dao.handler.JsonTypeHandler}) + ON DUPLICATE KEY UPDATE + platform_id = VALUES(platform_id), + user_id = VALUES(user_id), + model = VALUES(model), + thread_info = VALUES(thread_info) + + \ No newline at end of file diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/impl/AIChatServiceImpl.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/impl/AIChatServiceImpl.java index 16ef8e54..025b4d48 100644 --- a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/impl/AIChatServiceImpl.java +++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/impl/AIChatServiceImpl.java @@ -20,7 +20,7 @@ import org.apache.bigtop.manager.ai.assistant.GeneralAssistantFactory; import org.apache.bigtop.manager.ai.assistant.provider.AIAssistantConfig; -import org.apache.bigtop.manager.ai.assistant.store.PersistentChatMemoryStore; +import org.apache.bigtop.manager.ai.assistant.provider.PersistentStoreProvider; import org.apache.bigtop.manager.ai.core.enums.MessageSender; import org.apache.bigtop.manager.ai.core.enums.PlatformType; import org.apache.bigtop.manager.ai.core.factory.AIAssistant; @@ -54,6 +54,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import lombok.extern.slf4j.Slf4j; import reactor.core.publisher.Flux; import jakarta.annotation.Resource; @@ -64,6 +65,7 @@ import java.util.Objects; @Service +@Slf4j public class AIChatServiceImpl implements AIChatService { @Resource private PlatformDao platformDao; @@ -79,21 +81,21 @@ public class AIChatServiceImpl implements AIChatService { private AIAssistantFactory aiAssistantFactory; - private final AIAssistantFactory aiTestFactory = new GeneralAssistantFactory(); - public AIAssistantFactory getAiAssistantFactory() { if (aiAssistantFactory == null) { aiAssistantFactory = - new GeneralAssistantFactory(new PersistentChatMemoryStore(chatThreadDao, chatMessageDao)); + new GeneralAssistantFactory(new PersistentStoreProvider(chatThreadDao, chatMessageDao)); } return aiAssistantFactory; } - private AIAssistantConfig getAIAssistantConfig(PlatformAuthorizedDTO platformAuthorizedDTO) { + private AIAssistantConfig getAIAssistantConfig( + PlatformAuthorizedDTO platformAuthorizedDTO, Map configs) { return AIAssistantConfig.builder() .setModel(platformAuthorizedDTO.getModel()) .setLanguage(LocaleContextHolder.getLocale().toString()) .addCredentials(platformAuthorizedDTO.getCredentials()) + .addConfigs(configs) .build(); } @@ -101,24 +103,26 @@ private PlatformType getPlatformType(String platformName) { return PlatformType.getPlatformType(platformName.toLowerCase()); } - private AIAssistant buildAIAssistant(PlatformAuthorizedDTO platformAuthorizedDTO, Long threadId) { + private AIAssistant buildAIAssistant( + PlatformAuthorizedDTO platformAuthorizedDTO, Long threadId, Map configs) { return getAiAssistantFactory() .create( getPlatformType(platformAuthorizedDTO.getPlatformName()), - getAIAssistantConfig(platformAuthorizedDTO), + getAIAssistantConfig(platformAuthorizedDTO, configs), threadId); } private Boolean testAuthorization(PlatformAuthorizedDTO platformAuthorizedDTO) { - AIAssistant aiAssistant = aiTestFactory.create( - getPlatformType(platformAuthorizedDTO.getPlatformName()), getAIAssistantConfig(platformAuthorizedDTO)); + AIAssistant aiAssistant = getAiAssistantFactory() + .create( + getPlatformType(platformAuthorizedDTO.getPlatformName()), + getAIAssistantConfig(platformAuthorizedDTO, null), + false); try { - aiAssistant.ask("1+1="); + return aiAssistant.test(); } catch (Exception e) { throw new ApiException(ApiExceptionEnum.CREDIT_INCORRECT, e.getMessage()); } - - return true; } @Override @@ -233,7 +237,13 @@ public ChatThreadVO createChatThreads(Long platformId, String model) { chatThreadPO.setUserId(userId); chatThreadPO.setModel(model); chatThreadPO.setPlatformId(platformAuthorizedPO.getId()); - chatThreadDao.save(chatThreadPO); + + PlatformAuthorizedDTO platformAuthorizedDTO = new PlatformAuthorizedDTO( + platformPO.getName(), platformAuthorizedPO.getCredentials(), chatThreadPO.getModel()); + AIAssistant aiAssistant = buildAIAssistant(platformAuthorizedDTO, null, null); + Map threadInfo = aiAssistant.createThread(); + chatThreadPO.setThreadInfo(threadInfo); + chatThreadDao.saveWithThreadInfo(chatThreadPO); return ChatThreadConverter.INSTANCE.fromPO2VO(chatThreadPO); } @@ -267,9 +277,9 @@ public List getAllChatThreads(Long platformId, String model) { @Override public SseEmitter talk(Long platformId, Long threadId, String message) { - ChatThreadPO chatThreadPO = chatThreadDao.findById(threadId); + ChatThreadPO chatThreadPO = chatThreadDao.findByThreadId(threadId); Long userId = SessionUserHolder.getUserId(); - if (chatThreadPO == null || !Objects.equals(userId, chatThreadPO.getUserId())) { + if (!Objects.equals(userId, chatThreadPO.getUserId())) { throw new ApiException(ApiExceptionEnum.CHAT_THREAD_NOT_FOUND); } PlatformAuthorizedPO platformAuthorizedPO = platformAuthorizedDao.findByPlatformId(platformId); @@ -280,7 +290,8 @@ public SseEmitter talk(Long platformId, Long threadId, String message) { PlatformPO platformPO = platformDao.findById(platformAuthorizedPO.getPlatformId()); PlatformAuthorizedDTO platformAuthorizedDTO = new PlatformAuthorizedDTO( platformPO.getName(), platformAuthorizedPO.getCredentials(), chatThreadPO.getModel()); - AIAssistant aiAssistant = buildAIAssistant(platformAuthorizedDTO, chatThreadPO.getId()); + AIAssistant aiAssistant = + buildAIAssistant(platformAuthorizedDTO, chatThreadPO.getId(), chatThreadPO.getThreadInfo()); Flux stringFlux = aiAssistant.streamAsk(message); SseEmitter emitter = new SseEmitter(); 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 3988894f..fb6c27f7 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 @@ -342,6 +342,7 @@ CREATE TABLE `llm_chat_thread` `platform_id` BIGINT(20) UNSIGNED NOT NULL, `user_id` BIGINT(20) UNSIGNED NOT NULL, `model` VARCHAR(255) NOT NULL, + `thread_info` JSON DEFAULT NULL, `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `create_by` BIGINT DEFAULT NULL, @@ -374,4 +375,5 @@ VALUES (1, now(), now(), 'Administrator', '21232f297a57a5a743894a0e4a801fc3', tr -- Adding default ai chat platform INSERT INTO bigtop_manager.llm_platform (id,credential,NAME,support_models) VALUES -(1,'{"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'); \ No newline at end of file +(1,'{"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'), +(2,'{"apiKey": "API Key"}','DashScope','qwen-max,qwen-plus,qwen-turbo'); diff --git a/pom.xml b/pom.xml index 66f14b04..bcf37b4f 100644 --- a/pom.xml +++ b/pom.xml @@ -136,6 +136,12 @@ ${project.version} + + org.apache.bigtop + bigtop-manager-ai-dashscope + ${project.version} + + org.apache.bigtop bigtop-manager-ai-assistant