当前位置: 首页 > news >正文

基于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博客

相关文章:

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

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

(学习笔记-内存管理)内存分段、分页、管理与布局

内存分段 程序是由若干个逻辑分段组成的&#xff0c;比如可由代码分段、数据分段、栈段、堆段组成。不同的段是有不同的属性的&#xff0c;所以就用分段的形式把这些分段分离出来。 分段机制下&#xff0c;虚拟地址和物理地址是如何映射的&#xff1f; 分段机制下的虚拟地址由…...

PHP使用Redis实战实录1:宝塔环境搭建、6379端口配置、Redis服务启动失败解决方案

宝塔环境搭建、6379端口配置、Redis服务启动失败解决方案 前言一、Redis安装部署1.安装Redis2.php安装Redis扩展3.启动Redis 二、避坑指南1.6379端口配置2.Redis服务启动&#xff08;1&#xff09;Redis服务启动失败&#xff08;2&#xff09;Redis启动日志排查&#xff08;3&a…...

【数据结构】这堆是什么

目录 1.二叉树的顺序结构 2.堆的概念及结构 3.堆的实现 3.1 向上调整算法与向下调整算法 3.2 堆的创建 3.3 建堆的空间复杂度 3.4 堆的插入 3.5 堆的删除 3.6 堆的代码的实现 4.堆的应用 4.1 堆排序 4.2 TOP-K问题 首先&#xff0c;堆是一种数据结构&#xff0c;一种特…...

FFmpeg 音视频开发工具

目录 FFmpeg 下载与安装 ffmpeg 使用快速入门 ffplay 使用快速入门 FFmpeg 全套下载与安装 1、FFmpeg 是处理音频、视频、字幕和相关元数据等多媒体内容的库和工具的集合。一个完整的跨平台解决方案&#xff0c;用于录制、转换和流式传输音频和视频。 官网&#xff1a;http…...

Go 语言 select 都能做什么?

原文链接&#xff1a; Go 语言 select 都能做什么&#xff1f; 在 Go 语言中&#xff0c;select 是一个关键字&#xff0c;用于监听和 channel 有关的 IO 操作。 通过 select 语句&#xff0c;我们可以同时监听多个 channel&#xff0c;并在其中任意一个 channel 就绪时进行相…...

Hive之窗口函数lag()/lead()

一、函数介绍 lag()与lead函数是跟偏移量相关的两个分析函数 通过这两个函数可以在一次查询中取出同一字段的前N行的数据(lag)和后N行的数据(lead)作为独立的列,从而更方便地进行进行数据过滤&#xff0c;该操作可代替表的自联接&#xff0c;且效率更高 lag()/lead() lag(c…...

Vite+Typescript+Vue3学习笔记

ViteTypescriptVue3学习笔记 1、项目搭建 1.1、创建项目(yarn) D:\WebstromProject>yarn create vite yarn create v1.22.19 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages...success Installed…...

二、SQL-6.DCL-2).权限控制

*是数据库和表的通配符&#xff0c;出现在数据库位置上表示所有数据库&#xff0c;出现在表名位置上&#xff0c;表示所有表 %是主机名的通配符&#xff0c;表示所有主机。 e.g.所有数据库&#xff08;*&#xff09;的所有表&#xff08;*&#xff09;的所有权限&#xff08;a…...

[OpenStack] GPU透传

GPU透传本质就是PCI设备透传&#xff0c;不算是什么新技术。之前按照网上方法都没啥问题&#xff0c;但是这次测试NVIDIA A100遇到坑了。 首先是禁用nouveau 把intel_iommuon rdblacklistnouveau写入/etc/default/grub的cmdline&#xff0c;然后grub2-mkconfig -o /etc/grub2.c…...

无涯教程-jQuery - Progressbar组件函数

小部件进度条功能可与JqueryUI中的小部件一起使用。一个简单的进度条显示有关进度的信息。一个简单的进度条如下所示。 Progressbar - 语法 $( "#progressbar" ).progressbar({value: 37 }); Progressbar - 示例 以下是显示进度条用法的简单示例- <!doctype …...

[SQL挖掘机] - 窗口函数 - rank

介绍: rank() 是一种常用的窗口函数&#xff0c;它为结果集中的每一行分配一个排名&#xff08;rank&#xff09;。这个排名基于指定的排序顺序&#xff0c;并且在遇到相同的值时&#xff0c;会跳过相同的排名。 用法: rank() 函数的语法如下&#xff1a; rank() over ([pa…...

VBAC多层防火墙技术的研究-状态检测

黑客技术的提升和黑客工具的泛滥,造成大量的企业、机构和个人的电脑系统遭受程度不同的入侵和攻击,或面临随时被攻击的危险。迫使大家不得不加强对自身电脑网络系统的安全防护,根据系统管理者设定的安全规则把守企业网络,提供强大的、应用选通、信息过滤、流量控制、网络侦…...

PHP8的数据类型-PHP8知识详解

在PHP8中&#xff0c;变量不需要事先声明&#xff0c;赋值即声明。 不同的数据类型其实就是所储存数据的不同种类。在PHP8.0、8.1中都有所增加。以下是PHP8的15种数据类型&#xff1a; 1、字符串&#xff08;String&#xff09;&#xff1a;用于存储文本数据&#xff0c;可以使…...

明晚直播:可重构计算芯片的AI创新应用分享!

大模型技术的不断升级及应用落地&#xff0c;正在推动人工智能技术发展进入新的阶段&#xff0c;而智能化快速增长和发展的市场对芯片提出了更高的要求&#xff1a;高算力、高性能、灵活性、安全性。可重构计算区别于传统CPU、GPU&#xff0c;以指令驱动的串行执行方式&#xf…...

flask 点赞系统

dianzan.html页面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>点赞系统</title> </head> <body><h2>这是一个点赞系统</h2><table border"1"><…...

关于Java的多线程实现

多线程介绍 进程&#xff1a;进程指正在运行的程序。确切的来说&#xff0c;当一个程序进入内存运行&#xff0c;即变成一个进程&#xff0c;进程是处于运行过程中的程序&#xff0c;并且具有一定独立功能。 线程&#xff1a;线程是进程中的一个执行单元&#xff0c;负责当前进…...

如何判断某个视频是深度伪造的?

目录 一、前言 二、仔细检查面部动作 三、声音可以提供线索 四、观察视频中人物的身体姿势 五、小心无意义的词语 深造伪造危险吗&#xff1f; 一、前言 制作深度伪造视频就像在Word文档中编辑文本一样简单。换句话说&#xff0c;您可以拍下任何人的视频&#xff0c;让他…...

ESP32(MicroPython) 四足机器人(一)

最近决定研究一下四足机器人&#xff0c;但市面上的产品&#xff0c;要么性价比低&#xff0c;要么性能达不到要求。本人就另外买了零件&#xff0c;安装到之前的一个麦克纳姆轮底盘的底板上。&#xff08;轮子作为装饰&#xff0c;使用铜柱固定&#xff09; 舵机使用MG996R&a…...

力扣刷题记录---利用python实现链表的基本操作

文章目录 前言一、利用python实现链表的基本操作1.节点的定义使用类实现&#xff1a;1.链表的定义使用类实现&#xff1a;3.判断是否为空函数实现&#xff1a;4.链表长度函数实现&#xff1a;5.遍历链表函数实现&#xff1a;6.头插法函数实现&#xff1a;7.尾插法函数实现&…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

深入理解 React 样式方案

React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...

mcts蒙特卡洛模拟树思想

您这个观察非常敏锐&#xff0c;而且在很大程度上是正确的&#xff01;您已经洞察到了MCTS算法在不同阶段的两种不同行为模式。我们来把这个关系理得更清楚一些&#xff0c;您的理解其实离真相只有一步之遥。 您说的“select是在二次选择的时候起作用”&#xff0c;这个观察非…...