|
@@ -1,6 +1,5 @@
|
|
|
package org.jeecg.modules.adweb.gpt.service.impl;
|
|
|
|
|
|
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.google.common.collect.Lists;
|
|
|
import com.unfbx.chatgpt.OpenAiStreamClient;
|
|
|
import com.unfbx.chatgpt.entity.chat.ChatCompletion;
|
|
@@ -14,11 +13,10 @@ import org.apache.shiro.SecurityUtils;
|
|
|
import org.jeecg.common.api.vo.Result;
|
|
|
import org.jeecg.common.exception.JeecgBootException;
|
|
|
import org.jeecg.common.system.vo.LoginUser;
|
|
|
-import org.jeecg.common.util.FastJsonUtil;
|
|
|
import org.jeecg.common.util.SpringContextUtils;
|
|
|
import org.jeecg.common.util.UUIDGenerator;
|
|
|
import org.jeecg.modules.adweb.common.util.AdwebRedisUtil;
|
|
|
-import org.jeecg.modules.adweb.gpt.cache.LocalCache;
|
|
|
+import org.jeecg.modules.adweb.gpt.cache.SseEmitterCache;
|
|
|
import org.jeecg.modules.adweb.gpt.entity.ChatHistory;
|
|
|
import org.jeecg.modules.adweb.gpt.listeners.OpenAISSEEventSourceListener;
|
|
|
import org.jeecg.modules.adweb.gpt.service.ChatService;
|
|
@@ -33,27 +31,26 @@ import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
- * AI助手聊天Service
|
|
|
+ * AI聊天助手Service
|
|
|
*
|
|
|
* @author chenrui
|
|
|
* @date 2024/1/26 20:07
|
|
|
*/
|
|
|
-@Service
|
|
|
@Slf4j
|
|
|
+@Service
|
|
|
public class ChatServiceImpl implements ChatService {
|
|
|
|
|
|
- @Autowired private IChatHistoryService chatHistoryService;
|
|
|
-
|
|
|
// private static final String CACHE_KEY_PREFIX = "ai:chat:";
|
|
|
-
|
|
|
// private static final String CACHE_KEY_MSG_HISTORY = "msg_history";
|
|
|
|
|
|
- private static final String REDIS_KEY_PREFIX_MSG_CONTEXT = "ai:chat:msg_content";
|
|
|
+ private static final String REDIS_KEY_PREFIX_TOPIC_CONTEXT = "ai:chat:topic-context";
|
|
|
|
|
|
@Autowired AdwebRedisUtil adwebRedisUtil;
|
|
|
|
|
|
@Autowired private OpenAiStreamClient openAiStreamClient;
|
|
|
|
|
|
+ @Autowired private IChatHistoryService chatHistoryService;
|
|
|
+
|
|
|
/** 防止client不能成功注入 */
|
|
|
private OpenAiStreamClient ensureClient() {
|
|
|
if (Objects.isNull(this.openAiStreamClient)) {
|
|
@@ -64,14 +61,14 @@ public class ChatServiceImpl implements ChatService {
|
|
|
|
|
|
@Override
|
|
|
public SseEmitter createChat() {
|
|
|
- String uid = getUserId();
|
|
|
+ String uid = this.getUserId();
|
|
|
// 默认30秒超时,设置为0L则永不超时
|
|
|
SseEmitter sseEmitter = new SseEmitter(-0L);
|
|
|
// 完成后回调
|
|
|
sseEmitter.onCompletion(
|
|
|
() -> {
|
|
|
log.info("[{}]结束连接...................", uid);
|
|
|
- LocalCache.CACHE.remove(uid);
|
|
|
+ SseEmitterCache.CACHE.remove(uid);
|
|
|
});
|
|
|
// 超时回调
|
|
|
sseEmitter.onTimeout(
|
|
@@ -89,7 +86,7 @@ public class ChatServiceImpl implements ChatService {
|
|
|
.name("发生异常!")
|
|
|
.data(Message.builder().content("发生异常请重试!").build())
|
|
|
.reconnectTime(3000));
|
|
|
- LocalCache.CACHE.put(uid, sseEmitter);
|
|
|
+ SseEmitterCache.CACHE.put(uid, sseEmitter);
|
|
|
} catch (IOException e) {
|
|
|
log.error(e.getMessage(), e);
|
|
|
}
|
|
@@ -99,25 +96,25 @@ public class ChatServiceImpl implements ChatService {
|
|
|
} catch (IOException e) {
|
|
|
log.error(e.getMessage(), e);
|
|
|
}
|
|
|
- LocalCache.CACHE.put(uid, sseEmitter);
|
|
|
+ SseEmitterCache.CACHE.put(uid, sseEmitter);
|
|
|
log.info("[{}]创建sse连接成功!", uid);
|
|
|
return sseEmitter;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void closeChat() {
|
|
|
- String uid = getUserId();
|
|
|
- SseEmitter sse = (SseEmitter) LocalCache.CACHE.get(uid);
|
|
|
- if (sse != null) {
|
|
|
+ String uid = this.getUserId();
|
|
|
+ SseEmitter sse = SseEmitterCache.CACHE.get(uid);
|
|
|
+ if (Objects.nonNull(sse)) {
|
|
|
sse.complete();
|
|
|
// 移除
|
|
|
- LocalCache.CACHE.remove(uid);
|
|
|
+ SseEmitterCache.CACHE.remove(uid);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void sendMessage(String topicId, String message) {
|
|
|
- String uid = getUserId();
|
|
|
+ String uid = this.getUserId();
|
|
|
if (StringUtils.isBlank(message)) {
|
|
|
log.info("参数异常,message为null");
|
|
|
throw new BaseException("参数异常,message不能为空~");
|
|
@@ -127,40 +124,41 @@ public class ChatServiceImpl implements ChatService {
|
|
|
}
|
|
|
|
|
|
log.info("话题id:{}", topicId);
|
|
|
- // 1. 获取当前话题下的message context
|
|
|
- String contextKey = String.format("%s:%s:%s", REDIS_KEY_PREFIX_MSG_CONTEXT, uid, topicId);
|
|
|
- String contextValue = adwebRedisUtil.getString(contextKey);
|
|
|
- List<Message> messageContext =
|
|
|
- StringUtils.isNotEmpty(contextValue)
|
|
|
- ? FastJsonUtil.parseList(contextValue, Message.class)
|
|
|
- : Lists.newArrayList();
|
|
|
+ // 1. 获取当前话题的context - 最近100条用户message
|
|
|
+ String contextKey = String.format("%s:%s:%s", REDIS_KEY_PREFIX_TOPIC_CONTEXT, uid, topicId);
|
|
|
+ List<Message> topicContext =
|
|
|
+ Optional.ofNullable(adwebRedisUtil.lGet(contextKey, 0, -1))
|
|
|
+ .orElse(Lists.newArrayList())
|
|
|
+ .stream()
|
|
|
+ .map(Message.class::cast)
|
|
|
+ .collect(Collectors.toList());
|
|
|
// 1.1 添加当前message到context
|
|
|
- messageContext.add(Message.builder().content(message).role(Message.Role.USER).build());
|
|
|
+ topicContext.add(Message.builder().content(message).role(Message.Role.USER).build());
|
|
|
|
|
|
// 2. 发送GPT请求
|
|
|
- SseEmitter sseEmitter = (SseEmitter) LocalCache.CACHE.get(uid);
|
|
|
- if (sseEmitter == null) {
|
|
|
- log.info("聊天消息推送失败uid:[{}],没有创建连接,请重试。", uid);
|
|
|
- throw new JeecgBootException("聊天消息推送失败uid:[{}],没有创建连接,请重试。~");
|
|
|
+ SseEmitter sseEmitter = SseEmitterCache.CACHE.get(uid);
|
|
|
+ if (Objects.isNull(sseEmitter)) {
|
|
|
+ log.info("聊天消息推送失败uid:[{}],没有创建连接,请重试。", uid);
|
|
|
+ throw new JeecgBootException("聊天消息推送失败uid:[{}],没有创建连接,请重试~");
|
|
|
}
|
|
|
OpenAISSEEventSourceListener openAIEventSourceListener =
|
|
|
new OpenAISSEEventSourceListener(topicId, sseEmitter);
|
|
|
ChatCompletion completion =
|
|
|
ChatCompletion.builder()
|
|
|
- .messages(messageContext)
|
|
|
+ .messages(topicContext)
|
|
|
.model(ChatCompletion.Model.GPT_3_5_TURBO.getName())
|
|
|
.build();
|
|
|
ensureClient().streamChatCompletion(completion, openAIEventSourceListener);
|
|
|
|
|
|
- // 3. 将message context保存到Redis
|
|
|
- adwebRedisUtil.set(
|
|
|
+ // 3. 将当前话题的context保存到Redis
|
|
|
+ adwebRedisUtil.del(contextKey);
|
|
|
+ adwebRedisUtil.lSet(
|
|
|
contextKey,
|
|
|
- FastJsonUtil.toJSONString(
|
|
|
- messageContext.stream()
|
|
|
- // 每个话题仅保存最后100条message
|
|
|
- .skip(Math.max(0, messageContext.size() - 100))
|
|
|
- .collect(Collectors.toList())),
|
|
|
- // 话题 TTL设置为3小时
|
|
|
+ topicContext.stream()
|
|
|
+ // 每个话题仅保存最后100条message
|
|
|
+ .skip(Math.max(0, topicContext.size() - 100))
|
|
|
+ .collect(Collectors.toList()),
|
|
|
+ // 话题context TTL设置为3小时
|
|
|
3 * 60 * 60);
|
|
|
|
|
|
Result.ok(completion.tokens());
|
|
@@ -173,20 +171,10 @@ public class ChatServiceImpl implements ChatService {
|
|
|
// redisTemplate.opsForValue().set(cacheKey, chatHistoryVO.getContent());
|
|
|
// return Result.OK("保存成功");
|
|
|
|
|
|
- // AdWeb重构 - 聊天记录保存到DB,不存Redis
|
|
|
- String uid = getUserId();
|
|
|
- ChatHistory chatHistory =
|
|
|
- chatHistoryService.getOne(
|
|
|
- new LambdaQueryWrapper<ChatHistory>().eq(ChatHistory::getUid, uid));
|
|
|
-
|
|
|
- if (Objects.isNull(chatHistory)) {
|
|
|
- chatHistory = new ChatHistory();
|
|
|
- chatHistory.setUid(uid);
|
|
|
- chatHistory.setRole(Message.Role.USER.getName());
|
|
|
- }
|
|
|
-
|
|
|
+ // AdWeb重构 - 聊天记录按周保存到DB,不存Redis
|
|
|
+ ChatHistory chatHistory = chatHistoryService.getChatHistoryOfWeek(this.getUserId());
|
|
|
chatHistory.setContent(chatHistoryVO.getContent());
|
|
|
- chatHistoryService.saveOrUpdate(chatHistory);
|
|
|
+ chatHistoryService.updateById(chatHistory);
|
|
|
|
|
|
return Result.OK("保存成功");
|
|
|
}
|
|
@@ -200,17 +188,16 @@ public class ChatServiceImpl implements ChatService {
|
|
|
// chatHistoryVO.setContent(historyContent);
|
|
|
// return Result.OK(chatHistoryVO);
|
|
|
|
|
|
- // AdWeb重构 - 聊天记录保存到DB,不存Redis
|
|
|
- String uid = getUserId();
|
|
|
- ChatHistory chatHistory =
|
|
|
- chatHistoryService.getOne(
|
|
|
- new LambdaQueryWrapper<ChatHistory>().eq(ChatHistory::getUid, uid));
|
|
|
+ // AdWeb重构 - 聊天记录按周保存到DB,不存Redis
|
|
|
+ ChatHistory chatHistory = chatHistoryService.getChatHistoryOfWeek(this.getUserId());
|
|
|
|
|
|
ChatHistoryVO chatHistoryVO = new ChatHistoryVO();
|
|
|
chatHistoryVO.setContent(Objects.nonNull(chatHistory) ? chatHistory.getContent() : null);
|
|
|
+
|
|
|
return Result.OK(chatHistoryVO);
|
|
|
}
|
|
|
|
|
|
+ /** 获取当前登陆用户ID */
|
|
|
private String getUserId() {
|
|
|
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
|
|
return sysUser.getId();
|