前端对接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;}}
最后呈现的效果

上图呈现的差不多是打字机的效果了 不过呢 但在传输过程中每次停顿后会跳出一串内容然后又停顿一会,阅读体验有些不流畅, 就像玩游戏时帧数低卡顿的感觉, 我们用一个队列让它逐字地展示出来,并且根据传输速度控制输出的速度
- 需要一个打字机队列
- 队列提供入队和消费功能
- 需要一个动态时间来控制文字输出
// 打字机队列// 添加队列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)},}
}
相关文章:
前端对接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 …...
避免使用第三方工具完成电脑环境检测
0. 简介 在之前配置各种深度学习环境的时候经常需要先检测一下电脑的软硬件环境,其实整个过程比较重复和固定,所以我们是否有可能一键检测Python版本、PIP版本、Conda版本、CUDA版本、电脑系统、CPU核数、CPU频率、内存、硬盘等内容这是很多Deepper苦恼…...
vue 中 mixin 的应用场景,原理和合并规则
应用场景 多个组件的相同逻辑可以提出去来一个公共的 mixin 原理 Mixin 的工作原理是将 Mixin 中的选项合并到组件的选项中 合并规则 优先处理 mixinsprops 、method、inject、computed 同名的使用组件内的,不使用mixin 的data 进行合并生命周期和watch 先执行…...
点击按钮(文字)调起elementUI大图预览
时隔一年,我又回来了 ~ 最近在做后台,遇到一个需求,就是点击“查看详情”按钮,调起elementUI的大图预览功能,预览多张图片,如下图: 首先想到的是使用element-ui的el-image组件,但它是…...
全面学习SpringCloud框架指南
要深入学习Spring Cloud框架,你需要系统地掌握其核心组件和概念,并了解如何在实际项目中应用这些知识。以下是一些关键的学习点和相应的学习内容: 一共分为10个模块包括: 1、微服务架构基础: 理解微服务架构的概念和优势。 学习单体架构向微服务架构演进的过程。 掌握…...
5G智慧水利数字孪生可视化平台,推进水利行业数字化转型
5G智慧水利数字孪生可视化平台,推进水利行业数字化转型。随着5G技术的快速发展,越来越多的行业开始探索数字化转型的道路。水利行业作为国民经济的重要支柱,也面临着数字化转型的迫切需求。5G智慧水利数字孪生可视化平台作为水利行业数字化转…...
新手入门:大语言模型训练指南
在这个信息爆炸的时代,人工智能技术正以前所未有的速度渗透到我们生活的方方面面。从智能手机上的语音助手到自动驾驶汽车,AI的应用无处不在。而在这些令人惊叹的技术背后,大语言模型(LLM)扮演着至关重要的角色。它们不…...
Win11 WSL2 install Ubuntu20.04 and Seismic Unix
Win11系统,先启用或关闭Windows功能,勾选“适用于Linux的Windows子系统”和“虚拟机平台”两项 设置wsl默认版本为wsl2,并更新 wsl --list --verbose # 查看安装版本及内容 wsl --set-default-version 2 # 设置wsl默认版本为wsl2 # 已安装…...
rust使用print控制台打印输出五颜六色的彩色红色字体
想要在控制台打印输出彩色的字体,可以使用一些已经封装好的依赖库,比如ansi_term这个依赖库,官方依赖库地址:https://crates.io/crates/ansi_term 安装依赖: cargo add ansi_term 或者在Cargo.toml文件中加入&#…...
贪心算法|435.无重叠区间
力扣题目链接 class Solution { public:// 按照区间右边界排序static bool cmp (const vector<int>& a, const vector<int>& b) {return a[1] < b[1];}int eraseOverlapIntervals(vector<vector<int>>& intervals) {if (intervals.siz…...
C++的并发世界(七)——互斥锁
0.死锁的由来 假设有两个线程T1和T2,它们需要对两个互斥量mtx1和mtx2进行访问。而且需要按照以下顺序获取互斥量的所有权: -T1先获取mte1的所有权,再获取mt2的所有权。 -T2先获取 mtx2的所有权。再铁取 mtx1的所有权。 如果两个线程同时执行,…...
NI-LabView的DAQ缺少或丢失的解决办法(亲测有效)
DAQmx在Labview中不显示或缺失 问题:在NI Packasge Manager安装完DAQ后在labview中不显示控件解决办法 问题:在NI Packasge Manager安装完DAQ后在labview中不显示控件 在打开测量I/O时,见不到 DAQmx,或者在Express中见不到DAQ助手…...
cesium 调整3dtiles的位置 世界坐标下 相对坐标下 平移矩阵
cesium调整3dtiles的位置用到的是平移矩阵,原理是在世界坐标系中用偏移点减去原始点得到一个平移向量,再根据这个向量得到平移矩阵。 原始点:一般是模型的中心点位置,可通过模型的包围盒得到偏移点:可分为两种情况&…...
flutter跑通腾讯云直播Demo
运行示例 前提条件 要求java jdk 11版本 并且配置到了环境变量 重要 要求flutter 版本 2.8.0 并且配置到了环境变量 重要 要求dart-sdk版本2.15 并且配置到了环境变量 重要 您已 注册腾讯云 账号,并完成 实名认证。 申请 SDKAPPID 和 SECRETKEY 登录实时音视频控…...
飞机降落蓝桥杯[2023蓝桥省赛B组]
2023蓝桥省赛B组 B题 飞机降落 题解 标准深搜板子题,难度不大 #include<bits/stdc.h> using namespace std; #define MAX 10 struct node{int t,d,l;//t:飞机到达时间 d:飞机最大盘旋时间 l:飞机降落所需时间bool v;//标记此架飞机是否被搜索过 用于剪枝 };…...
如何动态渲染HTML内容?用v-html!
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...
EFcore 6 连接oracle19 WinForm vs2022
用EFcore访问Oracle,终于不需要Oracle的什么安装包了,直接在VS2022中就可以轻松搞定。在csdn上看到一哥们的帖子,测试了一下,发现很方便。使用的场景是:VS2022中EFcore6。经过测试,同 Navicat Premium 16比…...
(delphi11最新学习资料) Object Pascal 学习笔记---第9章第2节(finally代码块)
9.2 finally 代码块 还有第四个用于异常处理的关键字,我已经提到过,但到目前为止还没有使用过,那就是 finally。finally块用于执行一些应始终执行的操作(通常是清理操作)。事实上,无论是否发生异常&…...
220 基于matlab的考虑直齿轮热弹耦合的动力学分析
基于matlab的考虑直齿轮热弹耦合的动力学分析,输入主动轮、从动轮各类参数,考虑润滑油温度、润滑油粘度系数等参数,输出接触压力、接触点速度、摩擦系数、对流传热系数等结果。程序已调通,可直接运行。 220直齿轮热弹耦合 接触压力…...
Intrigue Core:一款功能强大的攻击面枚举引擎
关于Intrigue Core Intrigue Core是一款功能强大的开源攻击面枚举引擎,该工具可以帮助广大研究人员更好地管理应用程序的攻击面。 Intrigue Core集成了各种各样的安全数据源,可以将这些数据提取到标准化的对象模型中,并通过图形数据库跟踪关…...
程序员必看!用UML类图破解Spring Boot领域模型设计难题
程序员必看!用UML类图破解Spring Boot领域模型设计难题 在Spring Boot项目中,领域模型设计往往是决定系统可维护性和扩展性的关键。许多Java开发者虽然熟练使用JPA和MyBatis,但当面对复杂的业务逻辑时,却常常陷入"贫血模型&q…...
DirectX兼容性解决方案:让经典游戏在Windows 10重获新生
DirectX兼容性解决方案:让经典游戏在Windows 10重获新生 【免费下载链接】dxwrapper Fixes compatibility issues with older games running on Windows 10 by wrapping DirectX dlls. Also allows loading custom libraries with the file extension .asi into gam…...
别再硬算螺栓预紧力了!用COMSOL 6.2快速搞定螺栓连接的有限元仿真(附模型文件)
COMSOL 6.2螺栓连接仿真实战:从理论陷阱到高效建模 螺栓连接在机械结构中无处不在,但传统的手动计算预紧力方法不仅耗时耗力,还容易忽略接触非线性、摩擦效应等关键因素。COMSOL Multiphysics 6.2版本针对这一工程痛点进行了专项优化…...
Sambert多情感语音合成镜像:在虚拟主播场景下的应用实践
Sambert多情感语音合成镜像:在虚拟主播场景下的应用实践 1. 引言:虚拟主播的“声音”难题 你有没有想过,那些在直播间里和你互动、讲段子、带货的虚拟主播,为什么有的声音听起来特别“假”,而有的却能让你感觉像在和…...
开源吐槽大会:从抱怨到贡献的进化之路
开源项目吐槽大会:技术文章大纲技术背景与吐槽文化开源社区的协作模式与挑战 吐槽文化的价值:促进改进与社区互动 典型案例:知名项目的争议与改进常见吐槽点分析代码质量与可维护性问题 文档缺失或过时 社区响应速度与维护者态度 版本迭代与兼…...
203 异构车辆队列分布式 MPC 优化控制约束复现之旅
203 异构车辆队列分布式 MPC 优化控制约束 复现的代码 .m 文件在自动驾驶和智能交通领域,异构车辆队列的分布式模型预测控制(MPC)是个热门话题。今天就来聊聊基于复现代码(.m文件)对203异构车辆队列分布式MPC优化控制约…...
Python程序员转战Mojo的最后1公里:自动转换工具mojoify上线首周已修复89%语法迁移阻塞点(限时开源)
第一章:Mojo与Python混合编程全景概览Mojo 是一种为 AI 系统量身打造的现代系统编程语言,兼具 Python 的易用性与 C/Rust 的执行效率。它原生兼容 Python 生态,允许开发者在同一个项目中无缝调用 Python 模块、复用 NumPy/Torch 接口…...
如何快速上手TegraRcmGUI:Switch破解注入完整指南
如何快速上手TegraRcmGUI:Switch破解注入完整指南 【免费下载链接】TegraRcmGUI C GUI for TegraRcmSmash (Fuse Gele exploit for Nintendo Switch) 项目地址: https://gitcode.com/gh_mirrors/te/TegraRcmGUI 你是否曾为Nintendo Switch的定制化需求而烦恼…...
联邦学习安全指南:5种对抗攻击防御策略实测(PySyft案例详解)
联邦学习安全实战:5类对抗攻击防御策略与PySyft代码实现 联邦学习作为分布式机器学习的前沿技术,在医疗、金融等隐私敏感领域展现出巨大潜力。然而,其去中心化的特性也带来了独特的安全挑战——恶意参与者可能通过精心设计的对抗样本破坏全局…...
CDN 报错 403/502/504 怎么解决?源站与防护策略排查
网站接入CDN后,原本访问流畅,突然出现403、502、504报错,用户反馈无法访问,自己排查半天找不到头绪——其实这类报错大多和「源站状态」「防护策略」「CDN配置」三个环节相关,今天就结合实操经验,把这三种常…...
