VUE3和SpringBoot实现ChatGPT页面打字效果SSE流式数据展示

在做这个功能之前,本人也是走了很多弯路(花了好几天才搞好),你能看到本篇博文,那你就是找对地方了。百度上很多都是使用SseEmitter这种方式,这种方式使用的是websocket,使用这种方式就搞复杂了,会为后面项目分布式布署上埋下坑,什么坑,下面会说明。要实现【VUE3和SpringBoot实现ChatGPT页面打字效果SSE流式数据展示】这种效果,其实就是要使用SSE这种协议,这种协议很简单。我们先明白websocket和sse有什么区别,说几个主要的区别(太细的自己百度,这不是本博文的重点)

1、SSE是一种基于HTTP的单向通信机制,用于服务器向客户端推送数据,是单向通信

2、WebSocket是一种全双工的通信协议,它通过在客户端和服务器之间建立持久连接,实现双向通信

3、使用SSE在SpringBoot端,就像平时写接口一样,不需要啥配置,不需要保存用户的连接Session,WebSocket在SpringBoot端配置太多,而且需要保存用户的连接session,保存连接seesion在分布式布署上就比较麻烦。

博文最后会有整个项目源代码下载地址,下载之后里面有两个文件夹,myserver是后端springboot的,myweb是vue3前端页面的,如下截图

一、先看效果

二、环境搭建

1、在使用之前,我们还是安装一个大模型,我们这里使用的是LmStudio工具,这里面可以下载阿里开源的通义千问的语言模型,而且是在windows操作,简单;

2、下载地址:https://releases.lmstudio.ai/windows/0.2.17/latest/LM-Studio-0.2.17-Setup.exe

3、安装之后,就是下载大模型(下载需要魔法上网),选择Qwen 1.5

4、启动大模型,按箭头顺序,大模型启动后端口是1234

三、后端服务SpringBoot实现

用idea打开myserver项目,直接运行ServerApplication这个类,关键代码如下:

复制代码
        //构建请求对象
        ChatRequest chatRequest = new ChatRequest();
        chatRequest.setModel("qwen:latest");//设置模型
        chatRequest.setStream(true);//设置流式返回

        ReqMessage reqMessage = new ReqMessage();//设置请求消息,在此可以加入自己的prompt
        reqMessage.setRole("user");//用户消息
        reqMessage.setContent(message);//用户请求内容
        ArrayList<ReqMessage> messageList = new ArrayList<>();
        messageList.add(reqMessage);
        chatRequest.setMessages(messageList);//设置请求消息
        //构建请求json
        String paramJson = JSONUtil.toJsonStr(chatRequest);;

        Flux<String> response = webClient.post()
                .uri("/chat/completions")//请求uri
                .header("Authorization", "Bearer sk-**************")//设置成自己的key,获得key的方式可以在下文查看
                .header(HttpHeaders.ACCEPT, MediaType.TEXT_EVENT_STREAM_VALUE)//设置流式响应
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(paramJson))
                .retrieve()
                .bodyToFlux(String.class)
                .flatMap(res->{
                    if (StrUtil.equals("[DONE]",res)){//[DONE]是消息结束标识
                        return Flux.empty();
                    }
                    ObjectMapper objectMapper = new ObjectMapper();
                    try {
                        //System.out.println(res);
                        JsonNode jsonNode = objectMapper.readTree(res);
                        Answer aiAnswer = objectMapper.treeToValue(jsonNode, Answer.class);
                        List<Choices> choicesList = aiAnswer.getChoices();
                        Choices choices = choicesList.get(0);
                        Delta delta = choices.getDelta();
                        String json = objectMapper.writeValueAsString(delta);
                        System.out.println(json);
                        return Flux.just(json);
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    }

                    return Flux.empty();
                });

三、Vue3前端关键代码

1、打开myweb项目,先cmd命令,进入到当前目录执行如下命令

复制代码
npm install

2、运行项目,执行如下命令

复制代码
npm run dev

3、前端页面关键代码

复制代码
const abortController = new AbortController();
  eventSource.value = fetchEventSource('http://localhost:8080/ck/chat?message='+sendMsg.value,
      {
        method: "GET",
        signal: abortController.signal,
        openWhenHidden: true,
        onmessage(event) {
          let res = event.data;
          console.log(res)
          if(res!='[DONE]'&&res!=null){
            let data = JSON.parse(event.data);
            let content = data.content
            console.log('content=='+content)
            if(content!=null&&content.indexOf('\n')!=-1){
              let text = tableRightData.value[tableRightData.value.length - 1].content + content
              content = DOMPurify.sanitize(marked.parse(text))
              tableRightData.value[tableRightData.value.length - 1].content = content
              tableRightData.value.push({"role": "assistant", "content": '', "showPhoto": false, "error": null});
            }else if(content!=null&&content!=''){
              tableRightData.value[tableRightData.value.length - 1].content += content
            }
          }
          nextTick(()=>{
            //滚动条置最下面
            const container = rightContainerRef.value
            container.scrollTop = container.scrollHeight
          })
        },
        onclose() {
          console.log('结束了***************************')
          deleteBlankRow()
          // eventSource.close();
          eventSource.value = null; // 重置eventSource变量,允许重建连接
          console.log('结束了2*****************')
          abortController.abort();


        },
        onerror(event){
          console.log("EventSource failed:", event);
          abortController.abort();
          eventSource.value.close(); // 关闭出错的连接
          eventSource.value = null; // 重置eventSource变量,允许重建连接
        }
      }
  );

四、源代码下载:

下载地址:百度网盘 请输入提取码

相关推荐
Olrookie14 分钟前
若依前后端分离版学习笔记(二十)——实现滑块验证码(vue3)
java·前端·笔记·后端·学习·vue·ruoyi
LucianaiB29 分钟前
招聘可以AI面试,那么我制作了一个AI面试教练不过分吧
后端
无奈何杨1 小时前
CoolGuard更新,ip2region升级、名单增加过期时间
后端
摇滚侠2 小时前
Spring Boot 3零基础教程,WEB 开发 自定义静态资源目录 笔记31
spring boot·笔记·后端·spring
摇滚侠2 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 遍历 笔记40
spring boot·笔记·thymeleaf
Anthony_49262 小时前
逻辑清晰地梳理Golang Context
后端·go
Github项目推荐2 小时前
你的错误处理一团糟-是时候修复它了-🛠️
前端·后端
进击的圆儿2 小时前
高并发内存池项目开发记录01
后端
左灯右行的爱情2 小时前
4-Spring SPI机制解读
java·后端·spring
用户68545375977692 小时前
🎯 Class文件结构大揭秘:打开Java的"身份证" 🪪
后端