mirror of https://github.com/jeecgboot/jeecg-boot
兼容deepseek-r1模型,ai助手支持think标签
parent
1a4abfc89c
commit
48a9ba6253
|
@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
//update-begin---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------
|
//update-begin---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------
|
||||||
/**
|
/**
|
||||||
|
@ -29,6 +30,20 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
|
||||||
private SseEmitter sseEmitter;
|
private SseEmitter sseEmitter;
|
||||||
|
|
||||||
private String topicId;
|
private String topicId;
|
||||||
|
/**
|
||||||
|
* 回复消息内容
|
||||||
|
*/
|
||||||
|
private String messageContent = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成回复回调
|
||||||
|
*/
|
||||||
|
private Consumer<String> doneCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否正在思考
|
||||||
|
*/
|
||||||
|
private boolean isThinking = false;
|
||||||
|
|
||||||
public OpenAISSEEventSourceListener(SseEmitter sseEmitter) {
|
public OpenAISSEEventSourceListener(SseEmitter sseEmitter) {
|
||||||
this.sseEmitter = sseEmitter;
|
this.sseEmitter = sseEmitter;
|
||||||
|
@ -39,12 +54,23 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
|
||||||
this.sseEmitter = sseEmitter;
|
this.sseEmitter = sseEmitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置消息完成响应时的回调
|
||||||
|
* for [QQYUN-11102/QQYUN-11109]兼容deepseek模型,支持tink标签
|
||||||
|
* @param doneCallback
|
||||||
|
* @author chenrui
|
||||||
|
* @date 2025/2/7 18:14
|
||||||
|
*/
|
||||||
|
public void onDone(Consumer<String> doneCallback){
|
||||||
|
this.doneCallback = doneCallback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) {
|
public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) {
|
||||||
log.info("OpenAI建立sse连接...");
|
log.info("ai-chat建立sse连接...");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,10 +79,12 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(@NotNull EventSource eventSource, String id, String type, @NotNull String data) {
|
public void onEvent(@NotNull EventSource eventSource, String id, String type, @NotNull String data) {
|
||||||
log.debug("OpenAI返回数据:{}", data);
|
log.debug("ai-chat返回数据:{}", data);
|
||||||
tokens += 1;
|
tokens += 1;
|
||||||
if (data.equals("[DONE]")) {
|
if (data.equals("[DONE]")) {
|
||||||
log.info("OpenAI返回数据结束了");
|
log.info("ai-chat返回数据结束了");
|
||||||
|
this.doneCallback.accept(messageContent);
|
||||||
|
messageContent = "";
|
||||||
sseEmitter.send(SseEmitter.event()
|
sseEmitter.send(SseEmitter.event()
|
||||||
.id("[TOKENS]")
|
.id("[TOKENS]")
|
||||||
.data("<br/><br/>tokens:" + tokens())
|
.data("<br/><br/>tokens:" + tokens())
|
||||||
|
@ -72,10 +100,40 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class); // 读取Json
|
ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class); // 读取Json
|
||||||
try {
|
try {
|
||||||
sseEmitter.send(SseEmitter.event()
|
//update-begin---author:chenrui ---date:20250207 for:[QQYUN-11102/QQYUN-11109]兼容deepseek模型,支持think标签------------
|
||||||
.id(this.topicId)
|
// 兼容think标签
|
||||||
.data(completionResponse.getChoices().get(0).getDelta())
|
Message delta = completionResponse.getChoices().get(0).getDelta();
|
||||||
.reconnectTime(3000));
|
if (null != delta) {
|
||||||
|
String content = delta.getContent();
|
||||||
|
if("<think>".equals(content)){
|
||||||
|
isThinking = true;
|
||||||
|
content = "> ";
|
||||||
|
delta.setContent(content);
|
||||||
|
}
|
||||||
|
if("</think>".equals(content)){
|
||||||
|
isThinking = false;
|
||||||
|
content = "\n\n";
|
||||||
|
delta.setContent(content);
|
||||||
|
}
|
||||||
|
if (isThinking) {
|
||||||
|
if (null != content && content.contains("\n")) {
|
||||||
|
content = "\n> ";
|
||||||
|
delta.setContent(content);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
// 响应消息体不记录思考过程
|
||||||
|
messageContent += null == content ? "" : content;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
log.info("ai-chat返回数据,发送给前端:" + content);
|
||||||
|
sseEmitter.send(SseEmitter.event()
|
||||||
|
.id(this.topicId)
|
||||||
|
.data(delta)
|
||||||
|
.reconnectTime(3000));
|
||||||
|
}
|
||||||
|
//update-end---author:chenrui ---date:20250207 for:[QQYUN-11102/QQYUN-11109]兼容deepseek模型,支持think标签------------
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(),e);
|
log.error(e.getMessage(),e);
|
||||||
eventSource.cancel();
|
eventSource.cancel();
|
||||||
|
@ -86,7 +144,7 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
|
||||||
@Override
|
@Override
|
||||||
public void onClosed(@NotNull EventSource eventSource) {
|
public void onClosed(@NotNull EventSource eventSource) {
|
||||||
log.info("流式输出返回值总共{}tokens", tokens() - 2);
|
log.info("流式输出返回值总共{}tokens", tokens() - 2);
|
||||||
log.info("OpenAI关闭sse连接...");
|
log.info("ai-chat关闭sse连接...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,10 +154,10 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
|
||||||
String errMsg = "";
|
String errMsg = "";
|
||||||
ResponseBody body = null == response ? null:response.body();
|
ResponseBody body = null == response ? null:response.body();
|
||||||
if (Objects.nonNull(body)) {
|
if (Objects.nonNull(body)) {
|
||||||
log.error("OpenAI sse连接异常data:{},异常:{}", body.string(), t.getMessage());
|
log.error("ai-chat sse连接异常data:{},异常:{}", body.string(), t.getMessage());
|
||||||
errMsg = body.string();
|
errMsg = body.string();
|
||||||
} else {
|
} else {
|
||||||
log.error("OpenAI sse连接异常data:{},异常:{}", response, t.getMessage());
|
log.error("ai-chat sse连接异常data:{},异常:{}", response, t.getMessage());
|
||||||
errMsg = t.getMessage();
|
errMsg = t.getMessage();
|
||||||
}
|
}
|
||||||
eventSource.cancel();
|
eventSource.cancel();
|
||||||
|
|
|
@ -173,13 +173,18 @@ public class ChatServiceImpl implements ChatService {
|
||||||
//update-begin---author:chenrui ---date:20240625 for:[TV360X-1570]给于更友好的提示,提示未配置ai------------
|
//update-begin---author:chenrui ---date:20240625 for:[TV360X-1570]给于更友好的提示,提示未配置ai------------
|
||||||
if (null != openAiStreamClient) {
|
if (null != openAiStreamClient) {
|
||||||
OpenAISSEEventSourceListener openAIEventSourceListener = new OpenAISSEEventSourceListener(topicId, sseEmitter);
|
OpenAISSEEventSourceListener openAIEventSourceListener = new OpenAISSEEventSourceListener(topicId, sseEmitter);
|
||||||
|
List<Message> finalMsgHistory = msgHistory;
|
||||||
|
openAIEventSourceListener.onDone(respMessage -> {
|
||||||
|
Message tempMessage = Message.builder().content(respMessage).role(Message.Role.ASSISTANT).build();
|
||||||
|
finalMsgHistory.add(tempMessage);
|
||||||
|
redisTemplate.opsForHash().put(cacheKey, CACHE_KEY_MSG_CONTEXT, JSONUtil.toJsonStr(finalMsgHistory));
|
||||||
|
});
|
||||||
ChatCompletion completion = ChatCompletion
|
ChatCompletion completion = ChatCompletion
|
||||||
.builder()
|
.builder()
|
||||||
.messages(msgHistory)
|
.messages(msgHistory)
|
||||||
.model(aiChatProperties.getModel())
|
.model(aiChatProperties.getModel())
|
||||||
.build();
|
.build();
|
||||||
openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener);
|
openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener);
|
||||||
redisTemplate.opsForHash().put(cacheKey, CACHE_KEY_MSG_CONTEXT, JSONUtil.toJsonStr(msgHistory));
|
|
||||||
//update-end---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------
|
//update-end---author:chenrui ---date:20240223 for:[QQYUN-8225]聊天记录保存------------
|
||||||
}
|
}
|
||||||
//update-end---author:chenrui ---date:20240625 for:[TV360X-1570]给于更友好的提示,提示未配置ai------------
|
//update-end---author:chenrui ---date:20240625 for:[TV360X-1570]给于更友好的提示,提示未配置ai------------
|
||||||
|
|
Loading…
Reference in New Issue