From 81b4cbbe35c87785518196affdad7642b6221b2e Mon Sep 17 00:00:00 2001 From: JEECG <445654970@qq.com> Date: Thu, 13 Feb 2025 20:00:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=97=AE=E9=A2=98=EF=BC=9A?= =?UTF-8?q?=E7=BA=BF=E4=B8=8Aai=E5=8A=A9=E6=89=8B=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E4=B8=80=E6=AC=A1=E6=80=A7=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=EF=BC=8C=E6=B6=88=E6=81=AF=E4=B8=8D=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OpenAISSEEventSourceListener.java | 63 ++++++++++-------- .../gpt/service/impl/ChatServiceImpl.java | 4 +- .../jeecg/AiChat/components/chat.vue | 66 +++++++++++-------- 3 files changed, 74 insertions(+), 59 deletions(-) diff --git a/jeecg-boot/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/listeners/OpenAISSEEventSourceListener.java b/jeecg-boot/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/listeners/OpenAISSEEventSourceListener.java index 97257e20..c2d0c1e1 100644 --- a/jeecg-boot/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/listeners/OpenAISSEEventSourceListener.java +++ b/jeecg-boot/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/listeners/OpenAISSEEventSourceListener.java @@ -17,6 +17,7 @@ import java.util.Objects; import java.util.function.Consumer; //update-begin---author:chenrui ---date:20240126 for:【QQYUN-7932】AI助手------------ + /** * OpenAI的SSE监听 * @author chenrui @@ -49,7 +50,7 @@ public class OpenAISSEEventSourceListener extends EventSourceListener { this.sseEmitter = sseEmitter; } - public OpenAISSEEventSourceListener(String topicId,SseEmitter sseEmitter){ + public OpenAISSEEventSourceListener(String topicId, SseEmitter sseEmitter){ this.topicId = topicId; this.sseEmitter = sseEmitter; } @@ -102,40 +103,44 @@ public class OpenAISSEEventSourceListener extends EventSourceListener { try { //update-begin---author:chenrui ---date:20250207 for:[QQYUN-11102/QQYUN-11109]兼容deepseek模型,支持think标签------------ // 兼容think标签 - Message delta = completionResponse.getChoices().get(0).getDelta(); - if (null != delta) { - String content = delta.getContent(); - if("".equals(content)){ - isThinking = true; - content = "> "; - delta.setContent(content); - } - if("".equals(content)){ - isThinking = false; - content = "\n\n"; - delta.setContent(content); - } - if (isThinking) { - if (null != content && content.contains("\n")) { - content = "\n> "; + //update-begin---author:chenrui ---date:20250210 for:判断空,防止反悔的内容为空报错.------------ + if(null != completionResponse.getChoices() + && !completionResponse.getChoices().isEmpty() + && null != completionResponse.getChoices().get(0)) { + //update-end---author:chenrui ---date:20250210 for:判断空,防止反悔的内容为空报错.------------ + Message delta = completionResponse.getChoices().get(0).getDelta(); + if (null != delta) { + String content = delta.getContent(); + if ("".equals(content)) { + isThinking = true; + content = "> "; + delta.setContent(content); + } + if ("".equals(content)) { + isThinking = false; + content = "\n\n"; delta.setContent(content); } - }else { - // 响应消息体不记录思考过程 - messageContent += null == content ? "" : 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)); } - - - - 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) { - log.error(e.getMessage(),e); + log.error("ai-chat返回数据,发生异常"+e.getMessage(),e); + sseEmitter.completeWithError(e); eventSource.cancel(); } } diff --git a/jeecg-boot/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/impl/ChatServiceImpl.java b/jeecg-boot/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/impl/ChatServiceImpl.java index 09465465..c22ace48 100644 --- a/jeecg-boot/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/impl/ChatServiceImpl.java +++ b/jeecg-boot/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/gpt/service/impl/ChatServiceImpl.java @@ -104,6 +104,7 @@ public class ChatServiceImpl implements ChatService { //超时回调 sseEmitter.onTimeout(() -> { log.info("[{}]连接超时...................", uid); + LocalCache.CACHE.remove(uid); }); //异常回调 sseEmitter.onError( @@ -115,7 +116,7 @@ public class ChatServiceImpl implements ChatService { .name("发生异常!") .data(Message.builder().content("发生异常请重试!").build()) .reconnectTime(3000)); - LocalCache.CACHE.put(uid, sseEmitter); + LocalCache.CACHE.remove(uid); } catch (IOException e) { log.error(e.getMessage(),e); } @@ -179,6 +180,7 @@ public class ChatServiceImpl implements ChatService { finalMsgHistory.add(tempMessage); redisTemplate.opsForHash().put(cacheKey, CACHE_KEY_MSG_CONTEXT, JSONUtil.toJsonStr(finalMsgHistory)); }); + log.info("话题:{},开始发送消息~~~", topicId); ChatCompletion completion = ChatCompletion .builder() .messages(msgHistory) diff --git a/jeecgboot-vue3/src/components/jeecg/AiChat/components/chat.vue b/jeecgboot-vue3/src/components/jeecg/AiChat/components/chat.vue index 0f219ee9..c89dda22 100644 --- a/jeecgboot-vue3/src/components/jeecg/AiChat/components/chat.vue +++ b/jeecgboot-vue3/src/components/jeecg/AiChat/components/chat.vue @@ -153,6 +153,7 @@ import presetQuestion from './presetQuestion.vue'; import { DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue'; import { message, Modal, Tabs } from 'ant-design-vue'; + import { isObject, isString } from '/@/utils/is'; import '../style/github-markdown.less'; import '../style/highlight.less'; import '../style/style.less'; @@ -250,40 +251,46 @@ // 当从事件源接收到数据时触发 evtSource.onmessage = function (e) { const data = e.data; - // console.log(e); - if (data === '[DONE]') { - updateChatSome(uuid, props.chatData.length - 1, { loading: false }); - scrollToBottom(); - handleStop(); - evtSource.close(); // 关闭连接 - } else { - try { - const _data = JSON.parse(data); - const content = _data.content; - if (content != undefined) { - lastText += content; - updateChat(uuid.value, props.chatData.length - 1, { - dateTime: new Date().toLocaleString(), - text: lastText, - inversion: false, - error: false, - loading: true, - conversationOptions: e.lastEventId == '[ERR]' ? null : { conversationId: data.conversationId, parentMessageId: e.lastEventId }, - requestOptions: { prompt: message, options: { ...options } }, - }); - scrollToBottom(); - } else { + let delay = 0; + setTimeout(() => { + if (data === '[DONE]') { + updateChatSome(uuid, props.chatData.length - 1, { loading: false }); + scrollToBottom(); + handleStop(); + evtSource.close(); // 关闭连接 + } else { + try { + const _data = JSON.parse(data); + const content = _data.content; + if (content != undefined) { + lastText += content; + updateChat(uuid.value, props.chatData.length - 1, { + dateTime: new Date().toLocaleString(), + text: lastText, + inversion: false, + error: false, + loading: true, + conversationOptions: e.lastEventId == '[ERR]' ? null : { conversationId: data.conversationId, parentMessageId: e.lastEventId }, + requestOptions: { prompt: message, options: { ...options } }, + }); + scrollToBottom(); + } else { + // updateChatSome(uuid.value, props.chatData.length - 1, { loading: false }); + // scrollToBottom(); + // handleStop(); + } + } catch (error: any) { + console.log('ai 聊天:::', error); + if (isObject(error) && isString(error.message) && error.message.endsWith('is not valid JSON')) { + return; + } updateChatSome(uuid.value, props.chatData.length - 1, { loading: false }); scrollToBottom(); handleStop(); + evtSource.close(); // 关闭连接 } - } catch (error) { - updateChatSome(uuid.value, props.chatData.length - 1, { loading: false }); - scrollToBottom(); - handleStop(); - evtSource.close(); // 关闭连接 } - } + }, delay); }; // 与事件源的连接无法打开时触发 evtSource.onerror = function (e) { @@ -347,6 +354,7 @@ }; // 停止响应 const handleStop = () => { + console.log('ai 聊天:::---停止响应'); if (loading.value) { loading.value = false; }