基于express调用chatgpt文字流输出和有道智云语音合成

express是基于node.js的一个web框架,可以更加简洁的去创建一个后台服务,由于项目的需要,引入和typescript,经过几天的努力实现了chatgpt文字流输出+有道智云语音合成的结合(略有遗憾),下面我记载以下,以供参考

后端实现:

    要出现chatgpt原生接口的流式效果(也就是一个字一个字往外面蹦),就得只能使用SSE(event-stream)和Websocket,其实采用轮询(短轮询和长轮询)也是可以的只是占用资源,下面我先来介绍这记得交互方法

  • 轮询:是由客户端每隔一段时间向服务器发出HTTP请求,服务端接收到请求后向客户端返回最新的数据。
    客户端的轮询方式一般分为短轮询和长轮询。

    • 短轮询:一般是由客户端每隔一段时间向服务器发起一次普通HTTP请求。服务端查询当前接口是否有数据更新,若有数据更新则向客户端返回最新数据,若无则提示客户端无数据更新。
      优点:比较简单,通过定时器在固定的间隔里不断发送请求。
      缺点:多条请求并不是每条都是有用的,会有很多无用请求,占据服务器资源和宽带,并且维护困难,响应的结果没有顺寻(因为是异步请求)只适用与小型应用。

    • 长轮询:一般是由客户端向服务器发出一个设置较长网络超时时间的HTTP请求,并在Http连接超时前,不主动断开连接;带颗段超时或有数据返回后,再次建立一个同样的Http请求,重复以上过程。
      优点:无消息时不会频繁请求,占用资源较少。
      缺点:服务器滞留信息会耗费资源,返回信息顺序无法保证,维护困难。

  • SSE(event-stream):SSE(Server-Sent Events)是一种单向通信协议,其中服务器可以将消息推送到客户端。与轮询不同,客户端只需发送一个请求,服务器可以随时发送新消息。这种方法可以减少网络流量和服务器负载。

  • Websocket:WebSocket 是一种双向通信协议,它允许服务器和客户端在连接打开的情况下实时通信。WebSocket 可以减少网络流量和服务器负载,因为它不需要客户端发送大量的 HTTP 请求来获取新消息。

可以看出SSE和Websocket两种协议在实时通讯中起着很大作用,下面介绍这两种协议在express的应用:

SSE:
import { Stream } from 'node:stream';
import api from '../ToolClass/base.js'
async function sendTextBymodel1(req,res){const params=res.data   //获取前端传过来的数据,其中包含一个属性Stream,要设置为trueconst {data}=await api.post<Stream>("/v1/chat/completions",params,{responseType:"stream"})res.send(readStream(data))   //这里进行对返回值的处理,可以在前端处理
}
function readStream(decoded) {let response=""let decodedArray = decoded.split("data: ");let longstr = "";decodedArray.forEach(decoded => {try {decoded = decoded.trim();if ( longstr == "" ){JSON.parse(decoded);}else{decoded = longstr + decoded;longstr = "";JSON.parse(decoded);}}catch ( e ){longstr = decoded;decoded = "";}if(decoded!==""){if(decoded.trim()==="[DONE]"){return;}else{response = JSON.parse(decoded).choices[0].delta.content ? JSON.parse(decoded).choices[0].delta.content : ""   return response}}})return response
}
export {sendTextBymodel1}

 返回的就是一个一个字符

前端通过fetch或者EventSource来进行接收,对于普通的浏览器还是行的,不过使用在uniapp中打包成安卓就不行了,此时的解决方案就是Websocket:

下载包:

npm i express-ws

注入,使用:

import expressWs from 'express-ws'
import {sendTextBymodel1} from './Controller/ChatAI.js'
const app=express()
expressWs(app)
app.ws("/chat",sendTextBymodel1)import { Stream } from 'node:stream';
import api from '../ToolClass/base.js'
async function char(params,ws){ /* <Stream> */try {// speecher("有道词典API使有道词典API使有道词典API使有")const {data}=await api.post<Stream>("/v1/chat/completions",JSON.parse(params),{responseType:"stream"})data.on("data", async (dat)=>{ await ws.send(dat.toString('utf8'))})data.on('close',async () => {await ws.close();});} catch (error) { ws.send({status:402,meaasge:"Websocket服务出现错误"})}
}function sendTextBymodel1(ws,res){// 使用 ws 的 send 方法向连接另一端的客户端发送数据// ws.send("connect to express server with WebSocket success")let flag=falsews.on("message",async (msg)=>{char(msg,ws)})ws.on("close",(e)=>{})
}
export {sendTextBymodel1}

前端实现:

uni.connectSocket({url:"ws://43.155.177.34:8085/chat",header: {'content-type': 'application/json'}
})
uni.onSocketOpen((res)=>{uni.sendSocketMessage({data: param});
});
uni.onSocketError((res)=>{console.log('WebSocket连接打开失败,请检查!');});
uni.onSocketMessage((res)=>{this.readStream(res.data,_this, currentResLocation,"chat");  //与上面SSE的后端代码方法一样
})

关于有道智云语音合成API的代码如下:

import axios from 'axios'
import { generateUUID } from './util.js'
import { config } from 'dotenv';
import crypto from 'crypto'
import id3 from 'node-id3'
import fs from 'fs'
config()
const setting={q:"",appKey:"",salt:"",sign:"",signType:"v3",curtime:"",voiceName:"youxiaoqin",format:"mp3"
}
async function speecher(q:string){initData(q)const response=await axios.post("https://openapi.youdao.com/ttsapi",setting,{headers:{'Content-Type': 'application/x-www-form-urlencoded',},responseType: 'arraybuffer'})let name=Math.floor(Date.now() / 1000)let outputFilePath = 'public/'+name+'.mp3';try {fs.writeFileSync(outputFilePath,response.data,'binary');// const tags = id3.read(outputFilePath);// const durationInSeconds = tags && tags.duration ? tags.duration : 0;// console.log(durationInSeconds);} catch (error) {console.log(error);}return outputFilePath
}function calculateSHA256(input) {const hash = crypto.createHash('sha256');hash.update(input);return hash.digest('hex'); // 返回十六进制表示的哈希值
}function initData(q){setting.q=Buffer.from(q, 'utf8').toString();setting.appKey="应用key"let salt=generateUUID()setting.salt=saltsetting.voiceName="youxiaoqin"setting.curtime=Math.floor(Date.now() / 1000).toString()let input=getInput(q)const hashedData = calculateSHA256("应用key"+input+salt+setting.curtime+"应用秘钥");setting.sign=hashedDatasetting.signType="v3"
}function getInput(q){if (q.length<=20) {return q}return q.slice(0, 10)+q.length+q.slice(-10)}
export default speecher
export function generateUUID() {let d = new Date().getTime();let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {let r = (d + Math.random() * 16) % 16 | 0;d = Math.floor(d / 16);return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);});return uuid;
}
export const AI_HEAD_IMG_URL="https://th.bing.com/th?id=ODL.3e2fbff4543f0d3632d34be6d02adc93&w=100&h=100&c=12&pcl=faf9f7&o=6&dpr=1.5&pid=13.1"

其中有些变量可以不使用硬编码的形式,express可以使用环境变量,使用dotenv包

整个demo做下来,本想做成流式输出文字将文字流传给流式合成语言,然后将语言传给前端,达到实时对话,但是网上找了一遍支持流式语音的API都是国外的谷歌、微软、亚马逊,但是这些调用其API需要进行注册,注册过程中需要用到国外信用卡,悲痛,国内支持的流式传输的有百度,阿里的,只是是百度和阿里的声音比较简单,所以就没做了

本文参考了:

短轮询和长轮询_长轮询和短轮询_白鲸ld的博客-CSDN博客

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

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

相关文章

现行业供应链数字化转型难的最根本原因是什么?

由于各种原因&#xff0c;行业供应链的数字化转型可能充满挑战。然而&#xff0c;最根本的原因之一是供应链本身固有的复杂性和碎片化。以下是造成这一困难的一些关键因素&#xff1a; 1.缺乏标准化&#xff1a;供应链通常涉及多个组织、系统和流程&#xff0c;这些组织、系统…

删除主表 子表外键没有索引的性能优化

整个表147M&#xff0c;执行时一个CPU耗尽&#xff0c; buffer gets 超过1个G&#xff0c; 启用并行也没有用 今天开发的同事问有个表上的数据为什么删不掉&#xff1f;我看了一下&#xff0c;也就不到100000条数据&#xff0c;表上有外键&#xff0c;等了5分钟hang在那里&…

网络安全系统教程+学习路线(自学笔记)

一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

Upload-Labs通关

问题 记录BUG—在uploadlabs第三关中—关于phpstudy中修改httpd.conf依旧无法解析.php3d等问题_upload第三关常见错误_dfzy$_$的博客-CSDN博客 我们首先先来了解一下什么是文件上传 首先 很简单 文件上传就是 需要用户进行上传文件 图片或视频等信息但是如果用户恶意上传木马…

Kafka的TimingWheel

Kafka的TimingWheel是Kafka中的一个时间轮实现,用于管理和处理延迟消息。时间轮是一种定时器的数据结构,可以高效地管理和触发定时事件。 在Kafka中,TimingWheel用于处理延迟消息的重试。当Kafka生产者发送消息到Kafka集群,但由于某些原因导致消息发送失败,生产者会将这些…

C++中的数学问题---进制转换

二进制转十六进制 string binToHex(string bin){string hex"";if(bin.size()%4!0){for(int i0;i<(4-bin.size()%4);i){bin"0"bin;}}for(int i0;i<bin.size();i4){string tmpbin.substr(i,4);bitset<4>b(tmp);hexb.to_ulong()<10?char(b.t…

解决报错:Can‘t connect to HTTPS URL because the SSL module is not available.

本人今天准备打开安装一个label-studio包&#xff0c;试了很多次&#xff0c;接连报如下错误&#xff0c;因此我就去找了一些解决方案&#xff0c;现在总结如下&#xff1a; 1、报错信息如下 2、解决方案如下&#xff1a; github上有对应的解决方案&#xff0c;链接&#xff…

操作系统18:磁盘I/O速度、磁盘可靠性、数据一致性

目录 1、提高磁盘I/O速度的途径 &#xff08;1&#xff09;磁盘高速缓存(Disk Cache) 1.1 - 数据交付(Data Delivery)方式 1.2 - 置换算法 1.3 - 周期性地写回磁盘 &#xff08;2&#xff09;提高磁盘I/O速度的其它方法 2.1 - 提前读 2.2 - 延迟写 2.3 - 优化物理块的…

安全渗透--正则表达式

什么是正则表达式&#xff1f; 正则表达式是一组由字母和符号组成的特殊文本&#xff0c;它可以用来从文本中找出满足你想要的格式的句子。 一个正则表达式是一种从左到右匹配主体字符串的模式。 “Regular expression”这个词比较拗口&#xff0c;我们常使用缩写的术语“regex…

面试算法题

1 使用栈实现队列 #include <iostream> #include <stack> class MyQueue { public:MyQueue() {}void push(int x){in.push(x); // 直接将元素push入in栈}int pop(){int data peek(); // 先查一遍&#xff0c;就是更新一遍out栈out.pop();return data;}// 查找队…

吴恩达ChatGPT《LangChain Chat with Your Data》笔记

文章目录 1. Introduction2. Document Loading2.1 Retrieval Augmented Generation&#xff08;RAG&#xff09;2.2 Load PDFs2.3 Load YouTube2.4 Load URLs2.5 Load Notion 3. Document Splitting3.1 Splitter Flow3.2 Character Splitter3.3 Token Splitter3.4 Markdown Spl…

数据库技术的基本概念、原理、方法和技术

一、基本概念 1. DB&#xff1a;数据库&#xff0c;保存一组有组织的数据的容器&#xff1b; 2. DBMS: 数据库管理系统&#xff0c;又称为数据库软件&#xff08;数据库产品)&#xff0c;用于管理DB中的数据&#xff1b; 3. SQL: 结构化查询语言&#xff0c;用于和DBMS通信的…

Ubuntu-解决包依赖关系

Ubuntu-解决包依赖关系的办法 安装软件包的时候&#xff0c;有时会遇到类似下图的依赖问题&#xff0c;无法正常安装&#xff0c;下面提供三种方法解决依赖问题。 1.可以尝试用下面方法处理依赖问题&#xff0c;紧跟前一条安装命令后面输入下面命令&#xff0c;然后再执行安装…

数据结构:计数排序(详解)

思路详解&#xff1a; 1 找到数组中的最大值、最小值 2 开辟一个统计每个数据出现次数的数组&#xff08;总个数是最大值-最小值1&#xff0c;因为下标范围是0~最大值-最小值&#xff0c;闭区间统计个数要1&#xff09; 3 遇到一个元素&#xff0c;在此元素-最小值作为下标的…

TSN -促进IT/OT 融合的网络技术

时间敏感网络&#xff08;tsn&#xff09;技术是IT/OT 融合的一项关键的基础网络技术&#xff0c;它实现了在一个异构网络中&#xff0c;实现OT的实时数据和IT系统的交互数据的带宽共享。 TSN允许将经典的高确定性现场总线系统和IT应用&#xff08;如大数据传输&#xff09;的功…

java 页面html常用写法总结

​(注意&#xff1a;本文章默认base html中已经引入bootstrap.min.css、style.css等css样式) input &#xff1a;输入标签 <#input required"必填" id"cycle" name"周期" underline"true" style"width:75%" itype&quo…

找第三大的数

给你一个非空数组&#xff0c;返回此数组中 第三大的数 。如果不存在&#xff0c;则返回数组中最大的数。 示例 1&#xff1a; 输入&#xff1a;[3, 2, 1] 输出&#xff1a;1 解释&#xff1a;第三大的数是 1 。 示例 2&#xff1a; 输入&#xff1a;[1, 2] 输出&#xff1…

了解 3DS MAX 3D摄像机跟踪设置:第 2 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 项目设置 步骤 1 打开“后效”。 打开后效果 步骤 2 转到合成>新合成以创建新合成。 将“宽度”和“高度”值分别设置为 1280 和 720。将帧速率设置为 25&#xff0c;将持续时间设置为 12 秒。单…

windows系统安装指定的vue/cli、node和npm;vue/cli脚手架搭建项目所涉及的vue/cli、node、npm依赖版本等问题

文章目录 前言一、安装vue/cli脚手架1.安装指定版本脚手架&#xff0c;我是用的3.12.0版本2.查看版本是否安装成功&#xff0c;成功有版本号2.1问题&#xff1a;安装失败2.2解决方案2.3 安装成功 二、安装指定node和npm1.为什么需要安装指定node和npm版本&#xff0c;同时匹配v…

脑电信号处理与特征提取——2.脑电的神经起源与测量(夏晓磊)

目录 二、脑电的神经起源与测量 2.1 脑电的神经起源 2.2 脑电的测量 二、脑电的神经起源与测量 2.1 脑电的神经起源 脑电起源于大脑皮层大量神经元的同步突触活动&#xff0c;主要贡献来自锥体细胞。 静息电位&#xff1a;内负外正&#xff0c;K内流。 动作电位&…