前端对接fastGPT流式数据+打字机效果

首先在对接api时 参数要设置stream: true,

      const data = {chatId: 'abc',stream: true,//这里true返回流式数据detail: false,variables: {uid: 'sfdsdf',name: 'zhaoyunyao,'},messages: [{ content: text, role: 'user' }]};

不要用axios发请求 不然处理不了流式数据 我这里使用fetch

        const response = await fetch(`${url}`, {method: 'post',headers: headers,body: JSON.stringify(data)});const reader = response.body.getReader();//创建了一个读取器对象,用于从响应主体中读取数据。response.body 是一个 ReadableStream 对象,通过调用 getReader() 方法可以获取一个读取器对象,以便逐步读取响应的内容。// 循环读取响应流while (true) {const { done, value } = await reader.read();if (done) break;// 将ArrayBuffer转为文本const chunk = new TextDecoder('utf-8').decode(value);// 处理文本为json格式const jsonArr = chunk.trim().replace(/\n/g, '').split('data: ').splice(1)for (let index = 0; index < jsonArr.length; index++) {const json = jsonArr[index];try {if (JSON.parse(json).choices) {const text = JSON.parse(json).choices[0].delta.content ?? ''content += text.replace(/^\n/g, '')} else {content = "内部出了问题o(╥﹏╥)o"}} catch {// 处理转json不报错}}obj.content = content //这里的content就是最终输出的文本}

然后我们再加一个打字机的光标 用html+css实现

              <div class="chat-item-details">{{ item.content }}/** 这里的span就是光标 **/<span class="cursor-blink" v-show="item.awaitReply">  </span></div>

再写上对应的css 

              .cursor-blink {display: inline-block;height: 16px;margin-bottom: -3px;width: 2px;animation: blink 1s infinite steps(1, start);}/*这里设置动画blink*/@keyframes blink {0%,100% {background-color: #000;color: #aaa;}50% {background-color: #bbb;/* not #aaa because it's seem there is Google Chrome bug */color: #000;}}

最后呈现的效果

上图呈现的差不多是打字机的效果了 不过呢 但在传输过程中每次停顿后会跳出一串内容然后又停顿一会,阅读体验有些不流畅, 就像玩游戏时帧数低卡顿的感觉, 我们用一个队列让它逐字地展示出来,并且根据传输速度控制输出的速度

  1. 需要一个打字机队列
  2. 队列提供入队和消费功能
  3. 需要一个动态时间来控制文字输出

 

    // 打字机队列// 添加队列addQueue(str,obj) {obj.queue.push(...str.split(''))},// 消费队列consume(obj) {if (obj.queue.length > 0) {let str = obj.queue.shift()str && this.onConsume(str,obj)} else if (obj.isDone) {obj.consuming = falseclearTimeout(obj.timmer)obj.awaitReply = falsethis.scrollBottom()}},// 消费间隔time(obj) {let time = 1000 / obj.queue.lengthreturn time > 100 ? 100 : time},// 消费下一个next(obj) {this.consume(obj)obj.timmer = setTimeout(() => {if (obj.consuming) {this.next(obj)}}, this.time(obj))},start(obj) {obj.consuming = trueobj.isDone=falsethis.next(obj)},done(obj) {obj.isDone=true},onConsume(str,obj) {obj.content += str},

 加了过后的效果

最后附上完整代码

export default {data() {return {key: "xxx",AppId: "xx",text: "",readonly: false,messages: [{ content: "您好,我是小环!请问需要什么帮助呢?", role: 'assistant', awaitReply: false },],userImg: this.$store.getters.avatar,username: this.$store.getters.nickname,awaitReply: false,timmer: null,obj: null,queue: [],consuming: false,isDone: false}},mounted() {const messageTextarea = document.getElementById('messageTextarea');messageTextarea.addEventListener('keydown', (event) => {// 如果按下的是回车键(Enter)if (event.key === 'Enter' && !event.ctrlKey) {event.preventDefault(); // 阻止默认的换行行为// 在这里可以添加发送消息的逻辑this.send();} else if (event.key === 'Enter' && event.ctrlKey) {const cursorPosition = messageTextarea.selectionStart; // 获取光标位置const textBeforeCursor = messageTextarea.value.substring(0, cursorPosition); // 获取光标前的文本const textAfterCursor = messageTextarea.value.substring(cursorPosition); // 获取光标后的文本messageTextarea.value = textBeforeCursor + '\n' + textAfterCursor; // 在光标位置插入换行符messageTextarea.selectionStart = cursorPosition + 1; // 设置光标位置为插入换行符后的位置messageTextarea.selectionEnd = cursorPosition + 1;}});},methods: {// 打字机队列// 添加队列addQueue(str, obj) {obj.queue.push(...str.split(''))},// 消费队列consume(obj) {if (obj.queue.length > 0) {let str = obj.queue.shift()str && this.onConsume(str, obj)} else if (obj.isDone) {obj.consuming = falseclearTimeout(obj.timmer)obj.awaitReply = falsethis.scrollBottom()}},// 消费间隔time(obj) {let time = 500 / obj.queue.lengthreturn time > 50 ? 50 : time},// 消费下一个next(obj) {this.consume(obj)obj.timmer = setTimeout(() => {if (obj.consuming) {this.next(obj)}}, this.time(obj))},start(obj) {obj.consuming = trueobj.isDone = falsethis.next(obj)},done(obj) {obj.isDone = true},onConsume(str, obj) {obj.content += str},async send() {if (this.text === "" || /^\s+$/.test(this.text)) {this.$message.warning('请输入内容')return}const text = this.textthis.text = ""const url = 'https://api.fastgpt.in/api/v1/chat/completions';this.messages.push({ role: 'user', content: text });let obj = { content: "", role: 'assistant', awaitReply: true, queue: [], consuming: false, isDone: false, timmer: null }this.messages.push(obj);this.scrollBottom()const data = {// 这里可以设置请求参数chatId: 'abc',stream: true,detail: false,variables: {uid: 'sfdsdf',name: 'zhaoyunyao,'},messages: [{ content: text, role: 'user' }]};const headers = {// 这里可以设置请求头Authorization: `Bearer ${this.key}`,"Content-Type": "application/json"};try {const response = await fetch(`${url}`, {method: 'post',headers: headers,body: JSON.stringify(data)});const reader = response.body.getReader();//let content = ""// 开始打字机队列this.start(obj)// 循环读取响应流while (true) {const { done, value } = await reader.read();if (done) break;// 将ArrayBuffer转为文本const chunk = new TextDecoder('utf-8').decode(value);// 处理文本为json格式const jsonArr = chunk.trim().replace(/\n/g, '').split('data: ').splice(1)for (let index = 0; index < jsonArr.length; index++) {const json = jsonArr[index];try {if (JSON.parse(json).choices) {const text = JSON.parse(json).choices[0].delta.content ?? ''this.addQueue(text.replace(/^\n/g, ''), obj)} else {this.addQueue('内部出了问题o(╥﹏╥)o', obj)}} catch {// 处理转json不报错}}this.scrollBottom()}} catch (error) {console.error('请求错误:', error);}this.done(obj)},// 滚到最底部scrollBottom() {setTimeout(() => {const mainChat = this.$refs.mainChatmainChat.scrollTop = mainChat.scrollHeight}, 0)},}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/809006.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PairAug:增强图像-文本对对放射学有什么用?

论文链接 代码链接GitHub - YtongXie/PairAug: [CVPR2024] PairAug: What Can Augmented Image-Text Pairs Do for Radiology? 发表于CVPR2024 机构 1) 澳大利亚机器学习研究所(AIML)&#xff0c;澳大利亚阿德莱德大学 2) 西北工业大学计算机科学与工程学院 3) 西北工业…

useRef总结

一、使用ref引用值 在react中ref的主要用途是用来获取DOM元素或者某个组件实例的引用。当你想访问真实的DOM节点&#xff0c;或者需要在组件之间共享可变数据且不需要触发重新渲染时&#xff0c;通常会使用ref。在vue中ref是响应式的&#xff0c;当数据发生改变时&#xff0c;相…

【简明图文教程】Node.js的下载、安装、环境配置及测试

文章目录 前言下载Node.js安装Node.js配置Node.js配置环境变量测试后言 前言 本教程适用于小白第一次从零开始进行Node.js的下载、安装、环境配置及测试。 如果你之前已经安装过了Node.js或删除掉了Node.js想重新安装&#xff0c;需要先参考以下博客进行处理后&#xff0c;再根…

渗透测试实战——第一站

仅供交流学习使用&#xff0c;请勿用于非法用途 前言&#xff1a;刚学了sql注入&#xff0c;只听理论总感觉没啥用&#xff0c;今天花了一半个多小时&#xff0c;去尝试寻找有漏洞的网站&#xff0c;最终找到了一个&#xff1b;实践是检验真理的唯一标准。 我是通过黑客常用语法…

牛客 NC36 在两个长度相等的排序数组中找到上中位数【中等 模拟 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/6fbe70f3a51d44fa9395cfc49694404f 思路 直接模拟2个数组有顺序放到一个数组中&#xff0c;然后返回中间的数参考答案java import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息 pu…

深入剖析UDP反射放大攻击原理及其有效防护策略

引言 随着互联网的飞速发展和业务复杂性的提升&#xff0c;网络安全问题日益凸显&#xff0c;其中分布式拒绝服务&#xff08;DDoS&#xff09;攻击成为危害最为严重的一类网络威胁之一。UDP反射放大攻击作为一种高效的DDoS手段&#xff0c;因其攻击成本低廉、威力巨大&#x…

IDEA中无法保存设置 Cannot Save Settings

确定原因: 在IDEA中父工程不应该存在有子工程的相关东西 首先,这是我的DCYJ项目(观察右侧的Content Root) 其次,这是我的EAPOFode项目(观察右侧的Content Root爆红处) 最后我将DCYJ项目右侧的Content Root全部删掉

es 倒排索引

es 倒排索引TRee 倒排索引树&#xff08;TRee&#xff09;通常指的是Elasticsearch中用于支持高速搜索的一种数据结构。它是一种树状结构&#xff0c;可以通过特定的词项&#xff08;terms&#xff09;来快速定位包含这些词项的文档。 在Elasticsearch中&#xff0c;倒排索引…

关于部署ELK和EFLKD的相关知识

文章目录 一、ELK日志分析系统1、ELK简介1.2 ElasticSearch1.3 Logstash1.4 Kibana&#xff08;展示数据可视化界面&#xff09;1.5 Filebeat 2、使用ELK的原因3、完整日志系统的基本特征4、ELK的工作原理 二、部署ELK日志分析系统1、服务器配置2、关闭防火墙3、ELK ElasticSea…

TS基础1-基础环境搭建

目录 1&#xff0c;编译选项1.1&#xff0c;target1.2&#xff0c;module1.3&#xff0c;lib1.4&#xff0c;输入目录1.5&#xff0c;include1.6&#xff0c;files 2&#xff0c;使用第三方库简化流程2.1&#xff0c;ts-node2.2&#xff0c;nodemon 使用 tsc --init 初始化配置…

ASUS华硕ROG幻16Air笔记本电脑GU605M原装出厂Win11系统工厂包下载,带有ASUSRecovery一键重置还原

适用型号&#xff1a;GU605MI、GU605MY、GU605MZ、GU605MV、GU605MU 链接&#xff1a;https://pan.baidu.com/s/1YBmZZbTKpIu883jYCS9KfA?pwd9jd4 提取码&#xff1a;9jd4 华硕原厂Windows11系统带有ASUS RECOVERY恢复功能、自带所有驱动、出厂主题壁纸、系统属性联机支持…

Vue3学习02 路由

Vue3学习02 路由 路由基本使用两个注意点路由器工作模式to的两种写法【命名路由】【嵌套路由】路由传参query参数params参数 路由规则的props配置replace属性编程式导航重定向 路由 路由器检测到路由的变化&#xff0c;路径变化&#xff0c;对应的组件也变化 基本使用 想使用…

Java后端搭建流程

目录 一、后端开发准备工作 1.下载 2.安装jdk &#xff0c;配置JAVA-HOME path 3.启动Tomcat 4.访问ip和端口 二、创建web项目 1.新建一个项目 2.发布web应用到服务器 &#xff08;1&#xff09;对LoginServlet继承HttpServlet &#xff08;2&#xff09;重写父类方法…

如何让视频流媒体平台免受网络攻击

在各国&#xff0c;流媒体服务已越来越受到大众的欢迎。有统计表明&#xff0c;目前视频流已占网络整体流量的80%以上。不过如您所见&#xff0c;近年来&#xff0c;数字威胁的不断增加&#xff0c;也让网络攻击逐年递增。单个视频用户受到的危险&#xff0c;往往会危及到整个服…

故障诊断 | 基于小波包结合卷积神经网络DWT-CNN实现电缆故障诊断算法研究附matlab代码

步骤 电缆故障诊断算法的实现步骤如下: 步骤1:数据预处理 首先,收集电缆的传感器数据。这些数据可以是电流、电压或其他与电缆状态相关的测量值。 对数据进行预处理,包括去除噪声、滤波和归一化等操作。 步骤2:小波包分解 使用小波包分解(Discrete Wavelet Packet Tr…

Vue 项目build打包发布到giteepages ,首页正常显示,其他路由页面报错404的解决方法

直接上解决方法&#xff1a; 打包之后dist上传之后&#xff0c;还要新创一个.spa文件&#xff0c;注意&#xff01;是 .spa 有个. 点&#xff0c;如下图 一般这样就可以开始部署了&#xff0c;然后开启giteepages服务。如果出现了首页正常显示&#xff0c;其他页面显示…

全新华为MateBook X Pro发布,将Ultra9放入980g超轻薄机身

2024年4月11日&#xff0c;在华为鸿蒙生态春季沟通会上全新的华为MateBook X Pro正式发布。该机以美学设计、创新科技以及智慧体验&#xff0c;追求重新定义Pro、重新定义旗舰&#xff0c;将颠覆消费者对传统轻薄本的认知。 华为MateBook X Pro追求极致轻薄与强大性能的完美结合…

【架构-10】DDOS(分布式拒绝服务攻击)

什么是分布式拒绝服务攻击&#xff1f; 分布式拒绝服务攻击&#xff08;Distributed Denial of Service&#xff0c;简称DDoS攻击&#xff09;是一种恶意行为&#xff0c;旨在通过同时向目标系统发送大量的请求或网络流量&#xff0c;以使该系统无法正常提供服务。与传统的拒绝…

Java安全管理器-SecurityManager

定义&#xff1a; SecurityManager是Java中的一个类&#xff0c;用于实现安全管理功能。它允许应用程序在运行时对安全策略进行动态管理&#xff0c;并控制哪些操作可以执行&#xff0c;哪些应该被拒绝。主要功能包括&#xff1a; 安全策略管理&#xff1a;SecurityManager允许…

D-LinkNAS 远程命令执行漏洞(CVE-2024-3273)RCE漏

声明&#xff1a; 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 简介 D-LinkNAS是由D-Link公司制造的网络附加存储设备。…