|
@@ -13,17 +13,17 @@ 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.google.gson.Gson;
|
|
|
+import com.google.gson.GsonBuilder;
|
|
|
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.ChatCompletionsRequest;
|
|
|
+import com.tencentcloudapi.hunyuan.v20230901.models.ChatCompletionsResponse;
|
|
|
+import com.tencentcloudapi.hunyuan.v20230901.models.Choice;
|
|
|
import com.tencentcloudapi.hunyuan.v20230901.models.Message;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
@@ -68,8 +68,10 @@ public class HunYuanClientImpl implements HunYuanClient {
|
|
|
public ChatResult chatCompletion(ChatCompletionParam chatCompletionParam) {
|
|
|
|
|
|
// 参数化
|
|
|
- Long user_id = chatCompletionParam.getUser_id();
|
|
|
String prompt = chatCompletionParam.getPrompt();
|
|
|
+ System.out.println("向混元模型 提问: " + prompt);
|
|
|
+
|
|
|
+ Long user_id = chatCompletionParam.getUser_id();
|
|
|
String history_code = chatCompletionParam.getHistory_code();
|
|
|
List<Chat> chatList = chatCompletionParam.getChatList();
|
|
|
Boolean internet = chatCompletionParam.getInternet();
|
|
@@ -79,121 +81,130 @@ public class HunYuanClientImpl implements HunYuanClient {
|
|
|
String requestOfRedisKey = APPLICATION_NAME + "-chat-history-" + history_code;
|
|
|
StringBuilder allReplyContent = new StringBuilder();
|
|
|
|
|
|
+ // 记录请求开始时间
|
|
|
+ long allStartTime = System.currentTimeMillis();
|
|
|
+
|
|
|
+ // 加入上下文历史对话
|
|
|
+ System.out.println("---- 历史对话 (history_code): " + history_code + " ----");
|
|
|
+
|
|
|
+ List<Message> messages = new ArrayList<>();
|
|
|
+ if (chatList != null && !chatList.isEmpty()) {
|
|
|
+ chatList.stream().forEach(chat -> {
|
|
|
+ // 混元没有 THINK
|
|
|
+ messages.add(setMessage(chat.getRole(), chat.getContent()));
|
|
|
+ });
|
|
|
+ // 反转列表
|
|
|
+ Collections.reverse(messages);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 返回值结构体
|
|
|
ChatResult chatResult = new ChatResult();
|
|
|
- try {
|
|
|
|
|
|
- // System.out.println("向模型: " + model + " 提问: " + prompt);
|
|
|
- System.out.println("向混元模型 提问: " + prompt);
|
|
|
+ // -- [博查] Web Search API ----------------------------------------------
|
|
|
+ if (internet) {
|
|
|
|
|
|
- // 记录请求开始时间
|
|
|
- long allStartTime = System.currentTimeMillis();
|
|
|
+ // 远程查询、统计接口时间、设置返回参数
|
|
|
+ long internetStartTime = System.currentTimeMillis();
|
|
|
+ JsonNode searchResult = bochaService.WebSearch(new BochaParam(prompt));
|
|
|
+ String context = bochaService.WebSearchToString(searchResult);
|
|
|
+ long internetEndTime = System.currentTimeMillis();
|
|
|
+ chatResult.setInternet_duration(internetStartTime - internetEndTime);
|
|
|
+ chatResult.setInternet_content(context);
|
|
|
|
|
|
- // 加入上下文历史对话
|
|
|
- System.out.println("---- 历史对话 (history_code): " + history_code + " ----");
|
|
|
+ // 将搜索结果作为上下文添加到消息中
|
|
|
+ messages.add(setMessage("system", context));
|
|
|
+ messages.add(setMessage("user", "在回答时引用以上全部数据进行分析")); // 的 "name"、"summary"
|
|
|
+ messages.add(setMessage("assistant", "好的"));
|
|
|
|
|
|
- List<Message> messages = new ArrayList<>();
|
|
|
- if (chatList != null && !chatList.isEmpty()) {
|
|
|
- chatList.stream().forEach(chat -> {
|
|
|
- // 混元没有 THINK
|
|
|
- messages.add(setMessage(chat.getRole(), chat.getContent()));
|
|
|
- });
|
|
|
- // 反转列表
|
|
|
- Collections.reverse(messages);
|
|
|
- }
|
|
|
+ // [SSE] 发送消息
|
|
|
+ ChatSseMessage chatSearchSseMessage = new ChatSseMessage("SEARCH", context, null, history_code);
|
|
|
+ sseUtil.send(user_id, new SseResponse(SseResponseEnum.OLLAMA, chatSearchSseMessage).toJsonStr());
|
|
|
+ }
|
|
|
+ // -----------------------------------------------------------------------
|
|
|
|
|
|
- // -- [博查] Web Search API ----------------------------------------------
|
|
|
- if (internet) {
|
|
|
-
|
|
|
- // 远程查询、统计接口时间、设置返回参数
|
|
|
- long internetStartTime = System.currentTimeMillis();
|
|
|
- JsonNode searchResult = bochaService.WebSearch(new BochaParam(prompt));
|
|
|
- String context = bochaService.WebSearchToString(searchResult);
|
|
|
- long internetEndTime = System.currentTimeMillis();
|
|
|
- chatResult.setInternet_duration(internetStartTime - internetEndTime);
|
|
|
- chatResult.setInternet_content(context);
|
|
|
-
|
|
|
- // 将搜索结果作为上下文添加到消息中
|
|
|
- messages.add(setMessage("system", context));
|
|
|
- messages.add(setMessage("user", "在回答时引用以上全部数据进行分析")); // 的 "name"、"summary"
|
|
|
- messages.add(setMessage("assistant", "好的"));
|
|
|
-
|
|
|
- // [SSE] 发送消息
|
|
|
- ChatSseMessage chatSearchSseMessage = new ChatSseMessage("SEARCH", context, null, history_code);
|
|
|
- sseUtil.send(user_id, new SseResponse(SseResponseEnum.OLLAMA, chatSearchSseMessage).toJsonStr());
|
|
|
- }
|
|
|
- // -----------------------------------------------------------------------
|
|
|
+ // 新的对话内容
|
|
|
+ messages.add(setMessage("user", prompt));
|
|
|
|
|
|
- // 新的对话内容
|
|
|
- messages.add(setMessage("user", prompt));
|
|
|
+ // 输出全部对话内容
|
|
|
+ messages.stream().forEach(msg -> {
|
|
|
+ System.out.println("[" + msg.getRole() + "]: " + msg.getContent());
|
|
|
+ });
|
|
|
+ System.out.println("---------------------------------------------------------------------");
|
|
|
|
|
|
- // 输出全部对话内容
|
|
|
- messages.stream().forEach(msg -> {
|
|
|
- System.out.println("[" + msg.getRole() + "]: " + msg.getContent());
|
|
|
- });
|
|
|
- System.out.println("---------------------------------------------------------------------");
|
|
|
|
|
|
+ // [混元大模型]
|
|
|
+ Credential cred = new Credential(SECRET_ID, SECRET_KEY);
|
|
|
+ ClientProfile clientProfile = new ClientProfile();
|
|
|
+ clientProfile.getHttpProfile().setReadTimeout(400); // 流式接口耗时可能较长
|
|
|
+
|
|
|
+ HunyuanClient client = new HunyuanClient(cred, REGION, clientProfile);
|
|
|
+
|
|
|
+ ChatCompletionsRequest req = new ChatCompletionsRequest();
|
|
|
+ // 模型名称,可选值包括 hunyuan-lite、hunyuan-standard、hunyuan-standard-256K、hunyuan-pro、
|
|
|
+ // hunyuan-code、 hunyuan-role、 hunyuan-functioncall、 hunyuan-vision、 hunyuan-turbo。
|
|
|
+ // 各模型介绍请阅读 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 中的说明。
|
|
|
+ // 注意:不同的模型计费不同,请根据 [购买指南](https://cloud.tencent.com/document/product/1729/97731) 按需调用。
|
|
|
+ req.setModel("hunyuan-standard");
|
|
|
+ req.setMessages(ArrayUtil.toArray(messages, Message.class));
|
|
|
+ req.setStream(true); // true:流 | false:非流体
|
|
|
|
|
|
- // [混元大模型]
|
|
|
- Credential cred = new Credential(SECRET_ID, SECRET_KEY);
|
|
|
- ClientProfile clientProfile = new ClientProfile();
|
|
|
- HunyuanClient client = new HunyuanClient(cred, REGION, clientProfile);
|
|
|
|
|
|
- ChatStdRequest req = new ChatStdRequest();
|
|
|
- req.setMessages(ArrayUtil.toArray(messages, Message.class));
|
|
|
+ // 发送对话
|
|
|
+ try (ChatCompletionsResponse resp = client.ChatCompletions(req)) {
|
|
|
|
|
|
- ChatStdResponse resp = client.ChatStd(req); // 发送对话 (标准版)
|
|
|
-// ChatProResponse resp = client.ChatPro(req); // 发送对话 (专业版)
|
|
|
+ // hunyuan ChatCompletions 同时支持 stream 和非 stream 的情况
|
|
|
+ if (req.getStream()) {
|
|
|
|
|
|
+ // stream 示例
|
|
|
+ Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
|
|
|
+ for (SSEResponseModel.SSE e : resp) {
|
|
|
|
|
|
- for (SSEResponseModel.SSE e : resp) {
|
|
|
+ ChatCompletionsResponse eventModel = gson.fromJson(e.Data, ChatCompletionsResponse.class);
|
|
|
+ Choice[] choices = eventModel.getChoices();
|
|
|
|
|
|
+ if (choices.length > 0) {
|
|
|
|
|
|
+ String content = choices[0].getDelta().getContent();
|
|
|
|
|
|
-// 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:
|
|
|
- */
|
|
|
+ // [SSE] 发送消息
|
|
|
+ ChatSseMessage chatSseMessage = new ChatSseMessage("REPLY", content, null, history_code);
|
|
|
+ sseUtil.send(user_id, new SseResponse(SseResponseEnum.HUNYUAN, chatSseMessage).toJsonStr());
|
|
|
|
|
|
- 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("");
|
|
|
+ // 收集回答内容
|
|
|
+ allReplyContent.append(content);
|
|
|
|
|
|
- // [SSE] 发送消息
|
|
|
- ChatSseMessage chatSseMessage = new ChatSseMessage("REPLY", content, null, history_code);
|
|
|
- sseUtil.send(user_id, new SseResponse(SseResponseEnum.HUNYUAN, chatSseMessage).toJsonStr());
|
|
|
+ // 判断是否中止
|
|
|
+ if (ObjectUtil.isEmpty(redisUtil.getCacheObject(requestOfRedisKey))) {
|
|
|
+ System.out.println("中止!");
|
|
|
+ allReplyContent.append(" (请求中止)");
|
|
|
+ // request.abort();
|
|
|
+ // 流程结束后,删除锁
|
|
|
+ redisUtil.delete(requestOfRedisKey);
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- // 收集回答内容
|
|
|
- allReplyContent.append(content);
|
|
|
+ }
|
|
|
|
|
|
- // 判断是否中止
|
|
|
- if (ObjectUtil.isEmpty(redisUtil.getCacheObject(requestOfRedisKey))) {
|
|
|
- System.out.println("中止!");
|
|
|
- allReplyContent.append(" (请求中止)");
|
|
|
-// request.abort();
|
|
|
- // 流程结束后,删除锁
|
|
|
- redisUtil.delete(requestOfRedisKey);
|
|
|
- break;
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
+ System.out.println("-------------------- 结束流式回答. --------------------");
|
|
|
+ replyDuration = System.currentTimeMillis() - allStartTime;
|
|
|
|
|
|
- System.out.println("-------------------- 结束流式回答. --------------------");
|
|
|
- replyDuration = System.currentTimeMillis() - allStartTime;
|
|
|
+ System.out.println("全部回答: " + allReplyContent);
|
|
|
+ System.out.println("总输出耗时: " + replyDuration + " 毫秒");
|
|
|
|
|
|
- System.out.println("全部回答: " + allReplyContent);
|
|
|
- System.out.println("总输出耗时: " + replyDuration + " 毫秒");
|
|
|
+ chatResult.setContent(allReplyContent.toString());
|
|
|
+ chatResult.setContent_duration(replyDuration);
|
|
|
+ return chatResult;
|
|
|
|
|
|
- chatResult.setContent(allReplyContent.toString());
|
|
|
- chatResult.setContent_duration(replyDuration);
|
|
|
- return chatResult;
|
|
|
+ } else {
|
|
|
+ // 非 stream 示例
|
|
|
+ // 通过 Stream=false 参数来指定非 stream 协议, 一次性拿到结果
|
|
|
+ String content = client.ChatCompletions(req).getChoices()[0].getMessage().getContent();
|
|
|
+ System.out.println("content = " + content);
|
|
|
+ chatResult.setContent(content);
|
|
|
+ return chatResult;
|
|
|
+ }
|
|
|
|
|
|
} catch (TencentCloudSDKException e) {
|
|
|
System.out.println("TencentCloudSDKException: " + e.getMessage());
|
|
@@ -201,22 +212,6 @@ public class HunYuanClientImpl implements HunYuanClient {
|
|
|
ChatSseMessage chatSseMessage = new ChatSseMessage("REPLY", e.getMessage(), replyDuration, history_code);
|
|
|
sseUtil.send(user_id, new SseResponse(SseResponseEnum.HUNYUAN, chatSseMessage).toJsonStr());
|
|
|
|
|
|
- chatResult.setContent(e.getMessage());
|
|
|
- return chatResult;
|
|
|
- } catch (JsonMappingException e) {
|
|
|
- System.out.println("JsonMappingException: " + e.getMessage());
|
|
|
- // [SSE] 发送消息
|
|
|
- ChatSseMessage chatSseMessage = new ChatSseMessage("REPLY", e.getMessage(), replyDuration, history_code);
|
|
|
- sseUtil.send(user_id, new SseResponse(SseResponseEnum.HUNYUAN, chatSseMessage).toJsonStr());
|
|
|
-
|
|
|
- chatResult.setContent(e.getMessage());
|
|
|
- return chatResult;
|
|
|
- } catch (JsonProcessingException e) {
|
|
|
- System.out.println("JsonProcessingException: " + e.getMessage());
|
|
|
- // [SSE] 发送消息
|
|
|
- ChatSseMessage chatSseMessage = new ChatSseMessage("REPLY", e.getMessage(), replyDuration, history_code);
|
|
|
- sseUtil.send(user_id, new SseResponse(SseResponseEnum.HUNYUAN, chatSseMessage).toJsonStr());
|
|
|
-
|
|
|
chatResult.setContent(e.getMessage());
|
|
|
return chatResult;
|
|
|
} finally {
|