Browse Source

完成多个对话模型整合;新增删除对话接口

tsurumure 6 months ago
parent
commit
c3f124f66f

+ 1 - 1
pom.xml

@@ -72,7 +72,7 @@
         <dependency>
             <groupId>cn.hutool</groupId>
             <artifactId>hutool-all</artifactId>
-            <version>5.8.20</version>
+            <version>5.8.36</version>
         </dependency>
 
         <!-- fastJSON解析库 -->

+ 15 - 5
src/main/java/com/backendsys/modules/ai/chat/controller/ChatController.java

@@ -7,10 +7,7 @@ import io.swagger.v3.oas.annotations.Operation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
-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.RestController;
+import org.springframework.web.bind.annotation.*;
 
 @Validated
 @RestController
@@ -19,6 +16,12 @@ public class ChatController {
     @Autowired
     private ChatService chatService;
 
+
+    /**
+     * 前端判断:
+     * 当在对话历史中切换模型时,(内容 > 0 时)
+     * 提示:"不同模型会受之前的回答影响,导致准确度下降,是否要清空当前的对话内容?"
+     */
     @Operation(summary = "发起对话")
     @PreAuthorize("@sr.hasPermission('31')")
     @PostMapping("/api/ai/chat/sendChat")
@@ -30,7 +33,14 @@ public class ChatController {
     @PreAuthorize("@sr.hasPermission('31')")
     @GetMapping("/api/ai/chat/getChatList")
     public Result getChatList(@Validated(Chat.Detail.class) Chat chat) {
-        return Result.success().put("data", chatService.selectChatList(chat.getHistory_code()));
+        return Result.success().put("data", chatService.selectChatList(chat));
+    }
+
+    @Operation(summary = "删除我的对话内容 (软删除)")
+    @PreAuthorize("@sr.hasPermission('31')")
+    @DeleteMapping("/api/ai/chat/deleteChat")
+    public Result deleteChat(@Validated(Chat.Delete.class) @RequestBody Chat chat) {
+        return Result.success().put("data", chatService.deleteChat(chat));
     }
 
 }

+ 1 - 2
src/main/java/com/backendsys/modules/ai/chat/controller/ChatHistoryController.java

@@ -27,12 +27,11 @@ public class ChatHistoryController {
         return Result.success().put("data", chatHistoryService.selectChatHistoryList());
     }
 
-    @Operation(summary = "删除我的对话历史")
+    @Operation(summary = "删除我的对话历史及内容 (软删除)")
     @PreAuthorize("@sr.hasPermission('31')")
     @DeleteMapping("/api/ai/chat/deleteChatHistory")
     public Result deleteChatHistory(@Validated(ChatHistory.Delete.class) @RequestBody ChatHistory chatHistory) {
         return Result.success().put("data", chatHistoryService.deleteChatHistory(chatHistory));
     }
 
-
 }

+ 2 - 2
src/main/java/com/backendsys/modules/ai/chat/entity/Chat.java

@@ -19,8 +19,8 @@ public class Chat {
 
     @TableId(type = IdType.AUTO)
     private Long id;
-    @Size(max = 36, message = "对话历史记录ID长度不超过 {max} 字符", groups = { Create.class })
-    @NotEmpty(message = "history_code 不能为空", groups = { Detail.class })
+    @Size(max = 36, message = "对话历史记录ID长度不超过 {max} 字符", groups = { Detail.class, Delete.class })
+    @NotEmpty(message = "history_code 不能为空", groups = { Detail.class, Delete.class })
     private String history_code;
 
     @RangeStringArray(message="模型取值有误,范围应是(HUNYUAN, DEEPSEEK)", value = {"HUNYUAN", "DEEPSEEK"}, groups = { Create.class, Update.class })

+ 2 - 3
src/main/java/com/backendsys/modules/sdk/deepseek/entity/DSResponse.java → src/main/java/com/backendsys/modules/ai/chat/entity/ChatResult.java

@@ -1,10 +1,9 @@
-package com.backendsys.modules.sdk.deepseek.entity;
+package com.backendsys.modules.ai.chat.entity;
 
 import lombok.Data;
 
 @Data
-public class DSResponse {
-
+public class ChatResult {
     private String reasoning_content;
     private Long reasoning_duration;
     private String content;

+ 2 - 2
src/main/java/com/backendsys/modules/sdk/deepseek/entity/DSContent.java → src/main/java/com/backendsys/modules/ai/chat/entity/ChatSseMessage.java

@@ -1,9 +1,9 @@
-package com.backendsys.modules.sdk.deepseek.entity;
+package com.backendsys.modules.ai.chat.entity;
 
 import lombok.Data;
 
 @Data
-public class DSContent {
+public class ChatSseMessage {
 
     private String content;
     private String content_type;    // (loading: 加载中, reply: 回复, think: 思考)

+ 1 - 1
src/main/java/com/backendsys/modules/ai/chat/service/ChatHistoryService.java

@@ -11,7 +11,7 @@ public interface ChatHistoryService {
     // 获取我的对话历史列表
     PageEntity selectChatHistoryList();
 
-    // 删除我的对话历史
+    // 删除我的对话历史 (软删除)
     Map<String, Object> deleteChatHistory(ChatHistory chatHistory);
 
 }

+ 3 - 1
src/main/java/com/backendsys/modules/ai/chat/service/ChatService.java

@@ -12,6 +12,8 @@ public interface ChatService {
     Map<String, Object> sendChat(Chat chat);
 
     // 获取我的对话列表
-    PageEntity selectChatList(String history_code);
+    PageEntity selectChatList(Chat chat);
 
+    // 删除我的对话 (软删除)
+    Map<String, Object> deleteChat(Chat chat);
 }

+ 1 - 9
src/main/java/com/backendsys/modules/ai/chat/service/impl/ChatHistoryServiceImpl.java

@@ -1,17 +1,11 @@
 package com.backendsys.modules.ai.chat.service.impl;
 
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.util.StrUtil;
 import com.backendsys.exception.CustException;
 import com.backendsys.modules.ai.chat.dao.ChatDao;
 import com.backendsys.modules.ai.chat.dao.ChatHistoryDao;
 import com.backendsys.modules.ai.chat.entity.Chat;
 import com.backendsys.modules.ai.chat.entity.ChatHistory;
 import com.backendsys.modules.ai.chat.service.ChatHistoryService;
-import com.backendsys.modules.ai.chat.service.ChatService;
-import com.backendsys.modules.common.config.security.utils.SecurityUtil;
-import com.backendsys.modules.sdk.deepseek.entity.DSResponse;
-import com.backendsys.modules.sdk.deepseek.service.DeepSeekClient;
 import com.backendsys.utils.response.PageEntity;
 import com.backendsys.utils.response.PageInfoResult;
 import com.backendsys.utils.v2.PageUtils;
@@ -22,8 +16,6 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
 
 @Service
 public class ChatHistoryServiceImpl implements ChatHistoryService {
@@ -48,7 +40,7 @@ public class ChatHistoryServiceImpl implements ChatHistoryService {
     }
 
     /**
-     * 删除我的对话历史
+     * 删除我的对话历史 (软删除)
      */
     @Override
     @Transactional

+ 90 - 57
src/main/java/com/backendsys/modules/ai/chat/service/impl/ChatServiceImpl.java

@@ -7,11 +7,11 @@ import com.backendsys.modules.ai.chat.dao.ChatDao;
 import com.backendsys.modules.ai.chat.dao.ChatHistoryDao;
 import com.backendsys.modules.ai.chat.entity.Chat;
 import com.backendsys.modules.ai.chat.entity.ChatHistory;
+import com.backendsys.modules.ai.chat.entity.ChatResult;
 import com.backendsys.modules.ai.chat.service.ChatService;
 import com.backendsys.modules.common.config.security.utils.SecurityUtil;
-import com.backendsys.modules.sdk.deepseek.entity.DSRequestMessage;
-import com.backendsys.modules.sdk.deepseek.entity.DSResponse;
 import com.backendsys.modules.sdk.deepseek.service.DeepSeekClient;
+import com.backendsys.modules.sdk.tencentcloud.huanyuan.service.HunYuanClient;
 import com.backendsys.utils.response.PageEntity;
 import com.backendsys.utils.response.PageInfoResult;
 import com.backendsys.utils.v2.PageUtils;
@@ -19,7 +19,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -28,6 +27,8 @@ import java.util.concurrent.CompletableFuture;
 @Service
 public class ChatServiceImpl implements ChatService {
 
+    @Autowired
+    private HunYuanClient hunYuanClient;
     @Autowired
     private DeepSeekClient deepSeekClient;
 
@@ -51,6 +52,10 @@ public class ChatServiceImpl implements ChatService {
         String model_version = chat.getModel_version();
         String prompt = chat.getContent();
 
+        if (("DEEPSEEK".equals(model)) && StrUtil.isEmpty(model_version)) {
+            throw new CustException("请选择(Deepseek)模型版本");
+        }
+
         // -- 是否首次创建 (如果历史记录code为空,即是首次创建) ---------------
         Boolean is_create = false;
         String history_code = chat.getHistory_code();
@@ -86,65 +91,71 @@ public class ChatServiceImpl implements ChatService {
 
         // -- 对话 -----------------------------------------------------
 
-        // 再记录一下耗时
+        // 创建一个 CompletableFuture 来执行异步任务
+        String finalHistory_code = history_code;
+        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+            try {
 
-        // ------------------------------------------------------------
-        if ("DEEPSEEK".equals(model)) {
-            // 创建一个 CompletableFuture 来执行异步任务
-            String finalHistory_code = history_code;
-            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                // [DB] 查询对话历史记录 (按时间升序排序,最新的在最后面) (用于对话上下文)
+                LambdaQueryWrapper<Chat> wrapper = new LambdaQueryWrapper<>();
+                wrapper.eq(Chat::getDel_flag, -1);
+                wrapper.eq(Chat::getHistory_code, finalHistory_code);
+                wrapper.orderByDesc(Chat::getCreate_time);
+                List<Chat> chatList = chatDao.selectList(wrapper);
 
-                try {
 
-                    // [DB] 查询对话历史记录 (按时间升序排序,最新的在最后面) (用于对话上下文)
-                    LambdaQueryWrapper<Chat> wrapper = new LambdaQueryWrapper<>();
-                    wrapper.eq(Chat::getDel_flag, -1);
-                    wrapper.eq(Chat::getHistory_code, finalHistory_code);
-                    wrapper.orderByDesc(Chat::getCreate_time);
-                    List<Chat> chatList = chatDao.selectList(wrapper);
+                // [INSERT][提问]
+                Chat userChat = new Chat();
+                BeanUtil.copyProperties(chat, userChat);
+                chatDao.insert(userChat);
 
+
+                ChatResult chatResult = null;
+                // ------------------------------------------------------------
+                if ("DEEPSEEK".equals(model)) {
                     // [DeepSeek] 发起对话
-                    DSResponse dsResponse = deepSeekClient.chatCompletion(model_version, prompt, chatList);
-
-                    // [DB][提问]
-                    Chat userChat = new Chat();
-                    BeanUtil.copyProperties(chat, userChat);
-                    chatDao.insert(userChat);
-
-                    // [DB][思考] THINK
-                    String reasoningContent = dsResponse.getReasoning_content();
-                    if (StrUtil.isNotEmpty(reasoningContent)) {
-                        //
-                        Chat thinkChat = new Chat();
-                        BeanUtil.copyProperties(chat, thinkChat);
-                        //
-                        thinkChat.setRole("assistant");
-                        thinkChat.setContent_type("THINK");
-                        thinkChat.setContent(reasoningContent);
-                        thinkChat.setDuration(dsResponse.getReasoning_duration());
-                        chatDao.insert(thinkChat);
-                    }
-
-                    // [DB][回复] REPLY
-                    String content = dsResponse.getContent();
-                    if (StrUtil.isNotEmpty(content)) {
-                        //
-                        Chat replyChat = new Chat();
-                        BeanUtil.copyProperties(chat, replyChat);
-                        //
-                        replyChat.setRole("assistant");
-                        replyChat.setContent_type("REPLY");
-                        replyChat.setContent(content);
-                        replyChat.setDuration(dsResponse.getContent_duration());
-                        chatDao.insert(replyChat);
-                    }
-
-                } catch (Exception e) {
-                    System.out.println(e.getMessage());
+                    chatResult = deepSeekClient.chatCompletion(model_version, prompt, chatList);
+                }
+                // ------------------------------------------------------------
+                if ("HUNYUAN".equals(model)) {
+                    // [混元] 发起对话
+                    chatResult = hunYuanClient.chatCompletion(prompt, chatList);
+                }
+                // ------------------------------------------------------------
+
+                // [INSERT][思考] THINK
+                String reasoningContent = chatResult.getReasoning_content();
+                if (StrUtil.isNotEmpty(reasoningContent)) {
+                    //
+                    Chat thinkChat = new Chat();
+                    BeanUtil.copyProperties(chat, thinkChat);
+                    //
+                    thinkChat.setRole("assistant");
+                    thinkChat.setContent_type("THINK");
+                    thinkChat.setContent(reasoningContent);
+                    thinkChat.setDuration(chatResult.getReasoning_duration());
+                    chatDao.insert(thinkChat);
                 }
 
-            });
-        }
+                // [INSERT][回复] REPLY
+                String content = chatResult.getContent();
+                if (StrUtil.isNotEmpty(content)) {
+                    //
+                    Chat replyChat = new Chat();
+                    BeanUtil.copyProperties(chat, replyChat);
+                    //
+                    replyChat.setRole("assistant");
+                    replyChat.setContent_type("REPLY");
+                    replyChat.setContent(content);
+                    replyChat.setDuration(chatResult.getContent_duration());
+                    chatDao.insert(replyChat);
+                }
+
+
+            } catch (Exception e) {
+                System.out.println(e.getMessage());
+            }
+        });
         // ------------------------------------------------------------
 
         return Map.of("history_code", history_code);
@@ -156,12 +167,14 @@ public class ChatServiceImpl implements ChatService {
      * 获取我的对话列表 (升序, 最新的在最后面出现)
      */
     @Override
-    public PageEntity selectChatList(String history_code) {
+    public PageEntity selectChatList(Chat chat) {
+
+        String history_code = chat.getHistory_code();
 
         ChatHistory chatHistory = chatHistoryDao.selectOne(new LambdaQueryWrapper<ChatHistory>()
                 .eq(ChatHistory::getDel_flag, -1)
                 .eq(ChatHistory::getHistory_code, history_code));
-        if (chatHistory == null) throw new CustException("history_code 不存在");
+        if (chatHistory == null) throw new CustException("对话历史不存在");
 
         PageUtils.startPage();  // 分页
         LambdaQueryWrapper<Chat> wrapper = new LambdaQueryWrapper<>();
@@ -174,4 +187,24 @@ public class ChatServiceImpl implements ChatService {
     }
 
 
+    /**
+     * 删除我的对话 (软删除)
+     */
+    @Override
+    public Map<String, Object> deleteChat(Chat chat) {
+
+        String history_code = chat.getHistory_code();
+
+        LambdaQueryWrapper<Chat> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(Chat::getDel_flag, -1);
+        wrapper.eq(Chat::getHistory_code, history_code);
+
+        // [软删除] 删除对话
+        chat.setDel_flag(1);
+        chatDao.update(chat, wrapper);
+
+        return Map.of("history_code", chat.getHistory_code());
+    }
+
+
 }

+ 4 - 2
src/main/java/com/backendsys/modules/sdk/deepseek/service/DeepSeekClient.java

@@ -2,14 +2,16 @@ package com.backendsys.modules.sdk.deepseek.service;
 
 import cn.hutool.json.JSONArray;
 import com.backendsys.modules.ai.chat.entity.Chat;
-import com.backendsys.modules.sdk.deepseek.entity.DSResponse;
+import com.backendsys.modules.ai.chat.entity.ChatResult;
 
 import java.util.List;
 
 public interface DeepSeekClient {
 
-    DSResponse chatCompletion(String model, String prompt, List<Chat> chatList);
+    // [DeepSeek] 发起对话
+    ChatResult chatCompletion(String model, String prompt, List<Chat> chatList);
 
+    // [DeepSeek] 获得模型
     JSONArray getModels();
 
 }

+ 38 - 41
src/main/java/com/backendsys/modules/sdk/deepseek/service/impl/DeepSeekClientImpl.java

@@ -1,18 +1,15 @@
 package com.backendsys.modules.sdk.deepseek.service.impl;
 
-import cn.hutool.core.convert.Convert;
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.backendsys.exception.CustException;
-import com.backendsys.modules.ai.chat.dao.ChatDao;
 import com.backendsys.modules.ai.chat.entity.Chat;
-import com.backendsys.modules.sdk.deepseek.entity.DSContent;
+import com.backendsys.modules.ai.chat.entity.ChatResult;
+import com.backendsys.modules.ai.chat.entity.ChatSseMessage;
 import com.backendsys.modules.sdk.deepseek.entity.DSRequest;
 import com.backendsys.modules.sdk.deepseek.entity.DSRequestMessage;
-import com.backendsys.modules.sdk.deepseek.entity.DSResponse;
 import com.backendsys.modules.sdk.deepseek.service.DeepSeekClient;
-import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.sse.entity.SseResponse;
 import com.backendsys.modules.sse.entity.SseResponseEnum;
 import com.backendsys.modules.sse.utils.SseUtil;
@@ -34,7 +31,6 @@ import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.CompletableFuture;
 
 /**
  * DeepseekAPI 工具类 (by Mure 2025/02/27)
@@ -53,14 +49,13 @@ public class DeepSeekClientImpl implements DeepSeekClient {
     private SseUtil sseUtil;
 
     /**
-     * 对话 (文档:https://api-docs.deepseek.com/zh-cn/api/create-chat-completion)
-     *
-     * @param prompt
+     * [DeepSeek] 发起对话
+     * - 文档:https://api-docs.deepseek.com/zh-cn/api/create-chat-completion
      */
     @Override
-    public DSResponse chatCompletion(String model, String prompt, List<Chat> chatList) {
+    public ChatResult chatCompletion(String model, String prompt, List<Chat> chatList) {
 
-        DSResponse dsResponse = new DSResponse();
+        ChatResult chatResult = new ChatResult();
         try {
             System.out.println("向模型: " + model + " 提问: " + prompt);
 
@@ -70,9 +65,8 @@ public class DeepSeekClientImpl implements DeepSeekClient {
             // - 接口做最大问答限制 (1-字数限制, 2-次数限制)
 
 
-            List<DSRequestMessage> messages = new ArrayList<>();
-
             // 加入上下文历史对话
+            List<DSRequestMessage> messages = new ArrayList<>();
             if (chatList != null && !chatList.isEmpty()) {
                 chatList.stream().forEach(chat -> {
                     if (!"THINK".equals(chat.getContent_type())) {
@@ -83,13 +77,15 @@ public class DeepSeekClientImpl implements DeepSeekClient {
                 Collections.reverse(messages);
             }
 
-            messages.add(0, new DSRequestMessage("user", prompt));
+            // 新的对话内容
+            messages.add(new DSRequestMessage("user", prompt));
 
+            System.out.println("---------------------- 历史对话: ----------------------");
+            messages.stream().forEach(msg -> {
+                System.out.println("[" + msg.getRole() + "]: " + msg.getContent());
+            });
+            System.out.println("-----------------------------------------------------");
 
-            System.out.println("------------------");
-            System.out.println("messages:");
-            System.out.println(messages);
-            System.out.println("------------------");
 
             // 构建请求体
             DSRequest body = new DSRequest();
@@ -128,10 +124,10 @@ public class DeepSeekClientImpl implements DeepSeekClient {
                     System.out.println("-------------------- 开始流式回答: --------------------");
 
                     // [SSE] 发送消息
-                    DSContent initContent = new DSContent();
-                    initContent.setContent_type("loading");
-                    initContent.setContent("正在思考");
-                    sseUtil.send(new SseResponse(SseResponseEnum.DEEPSEEK, initContent).toJsonStr());
+                    ChatSseMessage chatLoadingSseMessage = new ChatSseMessage();
+                    chatLoadingSseMessage.setContent_type("loading");
+                    chatLoadingSseMessage.setContent("正在思考");
+                    sseUtil.send(new SseResponse(SseResponseEnum.DEEPSEEK, chatLoadingSseMessage).toJsonStr());
 
                     StringBuilder allContent = new StringBuilder();
                     StringBuilder allReasoningContent = new StringBuilder();
@@ -175,10 +171,10 @@ public class DeepSeekClientImpl implements DeepSeekClient {
                                 System.out.println("reasoning_content: " + reasoning_content);
 
                                 // [SSE] 发送消息
-                                DSContent dsContent = new DSContent();
-                                dsContent.setContent_type("think");
-                                dsContent.setContent(reasoning_content);
-                                sseUtil.send(new SseResponse(SseResponseEnum.DEEPSEEK, dsContent).toJsonStr());
+                                ChatSseMessage chatSseMessage = new ChatSseMessage();
+                                chatSseMessage.setContent_type("think");
+                                chatSseMessage.setContent(reasoning_content);
+                                sseUtil.send(new SseResponse(SseResponseEnum.DEEPSEEK, chatSseMessage).toJsonStr());
 
                                 // 收集推理内容
                                 allReasoningContent.append(reasoning_content);
@@ -199,10 +195,10 @@ public class DeepSeekClientImpl implements DeepSeekClient {
                                 System.out.println("content: " + content);
 
                                 // [SSE] 发送消息
-                                DSContent dsContent = new DSContent();
-                                dsContent.setContent_type("reply");
-                                dsContent.setContent(content);
-                                sseUtil.send(new SseResponse(SseResponseEnum.DEEPSEEK, dsContent).toJsonStr());
+                                ChatSseMessage chatSseMessage = new ChatSseMessage();
+                                chatSseMessage.setContent_type("reply");
+                                chatSseMessage.setContent(content);
+                                sseUtil.send(new SseResponse(SseResponseEnum.DEEPSEEK, chatSseMessage).toJsonStr());
 
                                 // 收集回答内容
                                 allContent.append(content);
@@ -219,30 +215,31 @@ public class DeepSeekClientImpl implements DeepSeekClient {
                     System.out.println("全部回答: " + allContent);
                     System.out.println("总输出耗时: " + contentDuration + " 毫秒");
 
-
-
-                    dsResponse.setReasoning_content(allReasoningContent.toString());
-                    dsResponse.setReasoning_duration(reasoningContentDuration);
-                    dsResponse.setContent(allContent.toString());
-                    dsResponse.setContent_duration(contentDuration);
-                    return dsResponse;
+                    chatResult.setReasoning_content(allReasoningContent.toString());
+                    chatResult.setReasoning_duration(reasoningContentDuration);
+                    chatResult.setContent(allContent.toString());
+                    chatResult.setContent_duration(contentDuration);
+                    return chatResult;
 
                 }
             } catch (Exception e) {
                 sseUtil.send((new SseResponse(SseResponseEnum.DEEPSEEK, e.getMessage())).toJsonStr());
                 System.out.println(e.getMessage());
-                dsResponse.setContent(e.getMessage());
-                return dsResponse;
+                chatResult.setContent(e.getMessage());
+                return chatResult;
             }
         } catch (Exception e) {
             sseUtil.send((new SseResponse(SseResponseEnum.DEEPSEEK, e.getMessage())).toJsonStr());
             System.out.println(e.getMessage());
-            dsResponse.setContent(e.getMessage());
-            return dsResponse;
+            chatResult.setContent(e.getMessage());
+            return chatResult;
         }
 
     }
 
+    /**
+     * [DeepSeek] 获得模型
+     */
     @Override
     public JSONArray getModels() {
 

+ 13 - 0
src/main/java/com/backendsys/modules/sdk/tencentcloud/huanyuan/service/HunYuanClient.java

@@ -0,0 +1,13 @@
+package com.backendsys.modules.sdk.tencentcloud.huanyuan.service;
+
+import com.backendsys.modules.ai.chat.entity.Chat;
+import com.backendsys.modules.ai.chat.entity.ChatResult;
+
+import java.util.List;
+
+public interface HunYuanClient {
+
+    // [HunYuan] 发起对话
+    ChatResult chatCompletion(String prompt, List<Chat> chatList);
+
+}

+ 159 - 0
src/main/java/com/backendsys/modules/sdk/tencentcloud/huanyuan/service/impl/HunYuanClientImpl.java

@@ -0,0 +1,159 @@
+package com.backendsys.modules.sdk.tencentcloud.huanyuan.service.impl;
+
+import cn.hutool.core.util.ArrayUtil;
+import com.backendsys.modules.ai.chat.entity.Chat;
+import com.backendsys.modules.ai.chat.entity.ChatResult;
+import com.backendsys.modules.ai.chat.entity.ChatSseMessage;
+import com.backendsys.modules.sdk.deepseek.entity.DSRequestMessage;
+import com.backendsys.modules.sdk.tencentcloud.huanyuan.service.HunYuanClient;
+import com.backendsys.modules.sse.entity.SseResponse;
+import com.backendsys.modules.sse.entity.SseResponseEnum;
+import com.backendsys.modules.sse.utils.SseUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tencentcloudapi.common.Credential;
+import com.tencentcloudapi.common.SSEResponseModel;
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
+import com.tencentcloudapi.common.profile.ClientProfile;
+import com.tencentcloudapi.hunyuan.v20230901.HunyuanClient;
+import com.tencentcloudapi.hunyuan.v20230901.models.ChatStdRequest;
+import com.tencentcloudapi.hunyuan.v20230901.models.ChatStdResponse;
+import com.tencentcloudapi.hunyuan.v20230901.models.Message;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Service
+public class HunYuanClientImpl implements HunYuanClient {
+
+    @Value("${tencent.hunyuan.secret-id}")
+    private String SECRET_ID;
+    @Value("${tencent.hunyuan.secret-key}")
+    private String SECRET_KEY;
+    @Value("${tencent.hunyuan.region}")
+    private String REGION;
+
+    @Autowired
+    private SseUtil sseUtil;
+
+    private Message setMessage(String role, String content) {
+        Message msg = new Message();
+        msg.setRole(role);
+        msg.setContent(content);
+        return msg;
+    }
+
+    /**
+     * [HunYuan] 发起对话
+     * https://cloud.tencent.com/document/product/1729/101836
+     */
+    @Override
+    public ChatResult chatCompletion(String prompt, List<Chat> chatList) {
+
+        ChatResult chatResult = new ChatResult();
+        try {
+
+            // System.out.println("向模型: " + model + " 提问: " + prompt);
+            System.out.println("向混元模型 提问: " + prompt);
+
+            Credential cred = new Credential(SECRET_ID, SECRET_KEY);
+            ClientProfile clientProfile = new ClientProfile();
+            HunyuanClient client = new HunyuanClient(cred, REGION, clientProfile);
+
+            // 加入上下文历史对话
+            List<Message> messages = new ArrayList<>();
+            if (chatList != null && !chatList.isEmpty()) {
+                chatList.stream().forEach(chat -> {
+//                if (!"THINK".equals(chat.getContent_type())) {
+                    messages.add(setMessage(chat.getRole(), chat.getContent()));
+//                }
+                });
+                // 反转列表
+                 Collections.reverse(messages);
+            }
+
+            // 新的对话内容
+            messages.add(setMessage("user", prompt));
+
+            System.out.println("---------------------- 历史对话: ----------------------");
+            messages.stream().forEach(msg -> {
+                System.out.println("[" + msg.getRole() + "]: " + msg.getContent());
+            });
+            System.out.println("-----------------------------------------------------");
+
+
+            // 构建请求体
+            ChatStdRequest req = new ChatStdRequest();
+            req.setMessages(ArrayUtil.toArray(messages, Message.class));
+
+            ChatStdResponse resp = client.ChatStd(req);     // 发送对话 (标准版)
+//            ChatProResponse resp = client.ChatPro(req);     // 发送对话 (标准版)
+
+            // 记录请求开始时间
+            long allStartTime = System.currentTimeMillis();
+
+            StringBuilder allContent = new StringBuilder();
+            for (SSEResponseModel.SSE e : resp) {
+
+//                 System.out.println(e.Data);
+                /**
+                 *
+                 * .Data:
+                 *  {"Note":"以上内容为AI生成,不代表开发者立场,请勿删除或修改本标记","Choices":[{"FinishReason":"","Delta":{"Role":"assistant","Content":"当然"}}],"Created":1709618061,"Id":"e73c0a71-5c98-4893-ba90-ad5056d5871a","Usage":{"PromptTokens":7,"CompletionTokens":1,"TotalTokens":8}}
+                 * e.Data:
+                 *  {"Note":"以上内容为AI生成,不代表开发者立场,请勿删除或修改本标记","Choices":[{"FinishReason":"","Delta":{"Role":"assistant","Content":"可以"}}],"Created":1709618061,"Id":"e73c0a71-5c98-4893-ba90-ad5056d5871a","Usage":{"PromptTokens":7,"CompletionTokens":2,"TotalTokens":9}}
+                 * e.Data:
+                 *  {"Note":"以上内容为AI生成,不代表开发者立场,请勿删除或修改本标记","Choices":[{"FinishReason":"","Delta":{"Role":"assistant","Content":","}}],"Created":1709618061,"Id":"e73c0a71-5c98-4893-ba90-ad5056d5871a","Usage":{"PromptTokens":7,"CompletionTokens":3,"TotalTokens":10}}
+                 * e.Data:
+                 */
+
+//                JSONObject dataObject = JSONUtil.parseObj(e.Data);
+
+                ObjectMapper objectMapper = new ObjectMapper();
+                JsonNode node = objectMapper.readTree(e.Data.toString());
+                JsonNode delta = node.path("Choices").path(0).path("Delta");
+                String content = delta.path("Content").asText("");
+
+                // [SSE] 发送消息
+                ChatSseMessage chatSseMessage = new ChatSseMessage();
+                chatSseMessage.setContent_type("reply");
+                chatSseMessage.setContent(content);
+                sseUtil.send(new SseResponse(SseResponseEnum.HUNYUAN, chatSseMessage).toJsonStr());
+
+                // 收集回答内容
+                allContent.append(content);
+
+            }
+
+            System.out.println("-------------------- 结束流式回答. --------------------");
+            long contentDuration = System.currentTimeMillis() - allStartTime;
+
+            System.out.println("全部回答: " + allContent);
+            System.out.println("总输出耗时: " + contentDuration + " 毫秒");
+
+            chatResult.setContent(allContent.toString());
+            chatResult.setContent_duration(contentDuration);
+            return chatResult;
+
+        } catch (TencentCloudSDKException e) {
+            System.out.println(e.getMessage());
+            chatResult.setContent(e.getMessage());
+            return chatResult;
+        } catch (JsonMappingException e) {
+            System.out.println(e.getMessage());
+            chatResult.setContent(e.getMessage());
+            return chatResult;
+        } catch (JsonProcessingException e) {
+            System.out.println(e.getMessage());
+            chatResult.setContent(e.getMessage());
+            return chatResult;
+        }
+
+    }
+}

+ 2 - 1
src/main/java/com/backendsys/modules/sse/entity/SseResponseEnum.java

@@ -7,7 +7,8 @@ public enum SseResponseEnum {
     LOGOUT("logout", "退出登录"),
     NOTICE("notice", "通知"),
     UPLOAD("upload", "上传"),
-    DEEPSEEK("deepseek", "Deepseek");
+    DEEPSEEK("deepseek", "Deepseek"),
+    HUNYUAN("hunyuan", "Hunyuan");
 
     private String type;
     private String message;