diff --git a/ruoyi-modules-api/ruoyi-chat-api/pom.xml b/ruoyi-modules-api/ruoyi-chat-api/pom.xml index b47d9c3e..7e08c9af 100644 --- a/ruoyi-modules-api/ruoyi-chat-api/pom.xml +++ b/ruoyi-modules-api/ruoyi-chat-api/pom.xml @@ -34,11 +34,11 @@ - - - - - + + + + + org.ruoyi @@ -83,6 +83,63 @@ 0.3.1 + + dev.langchain4j + langchain4j-open-ai + 1.0.1 + + + + dev.langchain4j + langchain4j-core + 1.0.0 + + + dev.langchain4j + langchain4j-http-client-jdk + 1.0.0 + + + dev.langchain4j + langchain4j-community-zhipu-ai + 1.0.1-beta6 + + + + + + dev.langchain4j + langchain4j-community-dashscope + 1.0.1-beta6 + + + + io.jsonwebtoken + jjwt + 0.12.6 + + + io.jsonwebtoken + jjwt-api + 0.12.6 + + + io.jsonwebtoken + jjwt-impl + 0.12.6 + + + io.jsonwebtoken + jjwt-jackson + 0.12.6 + + + + org.springframework.boot + spring-boot-starter-webflux + + + diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/enums/ChatModeType.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/enums/ChatModeType.java index d29bccd3..8388ad2f 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/enums/ChatModeType.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/enums/ChatModeType.java @@ -8,6 +8,13 @@ public enum ChatModeType { CHAT("chat", "中转模型"), DIFY("dify", "DIFY"), COZE("coze", "扣子"), + + ZHIPU("zhipu", "智谱清言"), + + DEEPSEEK("deepseek", "深度求索"), + + QIANWEN("qianwen", "通义千问"), + VECTOR("vector", "知识库向量模型"); private final String code; diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/IChatService.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/IChatService.java index 55ea2ac8..df49209e 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/IChatService.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/IChatService.java @@ -3,6 +3,9 @@ package org.ruoyi.chat.service.chat; import org.ruoyi.common.chat.request.ChatRequest; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + /** * 对话Service接口 * diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/StreamAssistant.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/StreamAssistant.java new file mode 100644 index 00000000..b1854278 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/StreamAssistant.java @@ -0,0 +1,7 @@ +package org.ruoyi.chat.service.chat; + +import dev.langchain4j.service.TokenStream; + +public interface StreamAssistant { + TokenStream chat(String message); +} \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/CozeServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/CozeServiceImpl.java index 6da0988c..7cbb5927 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/CozeServiceImpl.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/CozeServiceImpl.java @@ -5,7 +5,6 @@ import com.coze.openapi.client.chat.model.ChatEvent; import com.coze.openapi.client.chat.model.ChatEventType; import com.coze.openapi.client.connversations.message.model.Message; import com.coze.openapi.service.auth.TokenAuth; -import com.coze.openapi.service.config.Consts; import com.coze.openapi.service.service.CozeAPI; import io.reactivex.Flowable; import lombok.extern.slf4j.Slf4j; @@ -37,7 +36,6 @@ public class CozeServiceImpl implements IChatService { @Override public SseEmitter chat(ChatRequest chatRequest, SseEmitter emitter) { ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel()); - TokenAuth authCli = new TokenAuth(chatModelVo.getApiKey()); CozeAPI coze = new CozeAPI.Builder() @@ -49,7 +47,7 @@ public class CozeServiceImpl implements IChatService { CreateChatReq.builder() .botID(chatModelVo.getModelName()) .userID(chatRequest.getUserId().toString()) - .messages(Collections.singletonList(Message.buildUserQuestionText("What can you do?"))) + .messages(Collections.singletonList(Message.buildUserQuestionText(chatRequest.getPrompt()))) .build(); Flowable resp = coze.chat().stream(req); diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/DeepSeekChatImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/DeepSeekChatImpl.java new file mode 100644 index 00000000..8e805dd9 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/DeepSeekChatImpl.java @@ -0,0 +1,77 @@ +package org.ruoyi.chat.service.chat.impl; + + +import dev.langchain4j.model.chat.StreamingChatModel; +import dev.langchain4j.model.chat.response.ChatResponse; +import dev.langchain4j.model.chat.response.StreamingChatResponseHandler; +import dev.langchain4j.model.openai.OpenAiStreamingChatModel; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.ruoyi.chat.enums.ChatModeType; +import org.ruoyi.chat.service.chat.IChatService; +import org.ruoyi.common.chat.request.ChatRequest; +import org.ruoyi.domain.vo.ChatModelVo; +import org.ruoyi.service.IChatModelService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.HashMap; +import java.util.Map; + +/** + * deepseek + */ +@Service +@Slf4j +public class DeepSeekChatImpl implements IChatService { + + @Autowired + private IChatModelService chatModelService; + + @Override + public SseEmitter chat(ChatRequest chatRequest, SseEmitter emitter) { + ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel()); + StreamingChatModel chatModel = OpenAiStreamingChatModel.builder() + .baseUrl(chatModelVo.getApiHost()) + .apiKey(chatModelVo.getApiKey()) + .modelName(chatModelVo.getModelName()) + .logRequests(true) + .logResponses(true) + .temperature(0.8) + .build(); + // 发送流式消息 + try { + chatModel.chat(chatRequest.getPrompt(), new StreamingChatResponseHandler() { + @SneakyThrows + @Override + public void onPartialResponse(String partialResponse) { + emitter.send(partialResponse); + log.info("收到消息片段: {}", partialResponse); + System.out.print(partialResponse); + } + + @Override + public void onCompleteResponse(ChatResponse completeResponse) { + emitter.complete(); + log.info("消息结束,完整消息ID: {}", completeResponse); + } + + @Override + public void onError(Throwable error) { + System.err.println("错误: " + error.getMessage()); + } + }); + + } catch (Exception e) { + log.error("deepseek请求失败:{}", e.getMessage()); + } + + return emitter; + } + + @Override + public String getCategory() { + return ChatModeType.DEEPSEEK.getCode(); + } +} diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/QianWenAiChatServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/QianWenAiChatServiceImpl.java new file mode 100644 index 00000000..6e4308d4 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/QianWenAiChatServiceImpl.java @@ -0,0 +1,84 @@ +package org.ruoyi.chat.service.chat.impl; + +import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel; +import dev.langchain4j.community.model.zhipu.ZhipuAiStreamingChatModel; +import dev.langchain4j.model.chat.StreamingChatModel; +import dev.langchain4j.model.chat.response.ChatResponse; +import dev.langchain4j.model.chat.response.StreamingChatResponseHandler; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.ruoyi.chat.enums.ChatModeType; +import org.ruoyi.chat.service.chat.IChatService; +import org.ruoyi.common.chat.request.ChatRequest; +import org.ruoyi.domain.ChatMessage; +import org.ruoyi.domain.vo.ChatModelVo; +import org.ruoyi.service.IChatModelService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import static dev.langchain4j.data.message.SystemMessage.systemMessage; +import static dev.langchain4j.data.message.UserMessage.userMessage; +import static java.util.Arrays.asList; + + +/** + * 阿里通义千问 + */ +@Service +@Slf4j +public class QianWenAiChatServiceImpl implements IChatService { + + @Autowired + private IChatModelService chatModelService; + + + @Override + public SseEmitter chat(ChatRequest chatRequest, SseEmitter emitter) { + ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel()); + StreamingChatModel model = QwenStreamingChatModel.builder() + .apiKey(chatModelVo.getApiKey()) + .modelName(chatModelVo.getModelName()) + .build(); + + // 发送流式消息 + try { + model.chat(chatRequest.getPrompt(), new StreamingChatResponseHandler() { + @SneakyThrows + @Override + public void onPartialResponse(String partialResponse) { + emitter.send(partialResponse); + log.info("收到消息片段: {}", partialResponse); + } + + @Override + public void onCompleteResponse(ChatResponse completeResponse) { + emitter.complete(); + log.info("消息结束,完整消息ID: {}", completeResponse); + } + + @Override + public void onError(Throwable error) { + error.printStackTrace(); + } + }); + } catch (Exception e) { + log.error("千问请求失败:{}", e.getMessage()); + } + + return emitter; + + } + + @Override + public String getCategory() { + return ChatModeType.QIANWEN.getCode(); + } + + + +} diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ZhipuAiChatServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ZhipuAiChatServiceImpl.java new file mode 100644 index 00000000..b09d9610 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ZhipuAiChatServiceImpl.java @@ -0,0 +1,85 @@ +package org.ruoyi.chat.service.chat.impl; + +import dev.langchain4j.agent.tool.ToolSpecification; +import dev.langchain4j.community.model.zhipu.ZhipuAiStreamingChatModel; +import dev.langchain4j.model.chat.StreamingChatModel; +import dev.langchain4j.model.chat.response.ChatResponse; +import dev.langchain4j.model.chat.response.StreamingChatResponseHandler; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.ruoyi.chat.enums.ChatModeType; +import org.ruoyi.chat.service.chat.IChatService; +import org.ruoyi.common.chat.request.ChatRequest; +import org.ruoyi.domain.vo.ChatModelVo; +import org.ruoyi.service.IChatModelService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + + +/** + * 智谱AI + */ +@Service +@Slf4j +public class ZhipuAiChatServiceImpl implements IChatService { + + @Autowired + private IChatModelService chatModelService; + + + ToolSpecification currentTime = ToolSpecification.builder() + .name("currentTime") + .description("currentTime") + .build(); + + + @Override + public SseEmitter chat(ChatRequest chatRequest, SseEmitter emitter){ + ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel()); + // 发送流式消息 + try { + StreamingChatResponseHandler handler = new StreamingChatResponseHandler() { + @SneakyThrows + @Override + public void onPartialResponse(String token) { + System.out.println(token); + emitter.send(token); + } + + @SneakyThrows + @Override + public void onError(Throwable error) { + System.out.println(error.getMessage()); + emitter.send(error.getMessage()); + } + + @Override + public void onCompleteResponse(ChatResponse response) { + emitter.complete(); + log.info("消息结束,完整消息ID: {}", response.aiMessage()); + } + }; + + StreamingChatModel model = ZhipuAiStreamingChatModel.builder() + .model(chatModelVo.getModelName()) + .apiKey(chatModelVo.getApiKey()) + .logRequests(true) + .logResponses(true) + .build(); + model.chat(chatRequest.getPrompt(), handler); + } catch (Exception e) { + log.error("智谱清言请求失败:{}", e.getMessage()); + } + + return emitter; + } + + @Override + public String getCategory() { + return ChatModeType.ZHIPU.getCode(); + } +}