node对接ChatGpt的流式输出的配置

node对接ChatGpt的流式输出的配置

首先看一下效果

image-20241115161821543

将数据用流的方式返回给客户端,这种技术需求在传统的管理项目中不多见,但是在媒体或者有实时消息等功能上就会用到,这个知识点对于前端还是很重要的。

即时你不写服务端,但是服务端如果给你这样的接口,你也得知道怎么去使用联调。

1. nodejs实现简单的SSE服务,使用write返回流式

SSE服务(Server-Sent Events),是一种服务器向客户端推送实时更新的机制模式。

const express = require('express');  
const app = express();  
const port = 8002;  let strArr = ['你好','吃饭了吗','What are you doing?','My name is yy','8888','hello'
]
let setTask = nullapp.get('/events', (req, res) => {  res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');  res.setHeader('Cache-Control', 'no-cache');  res.setHeader('Connection', 'keep-alive');  let num = 0setTask =  setInterval(()=>{res.write(`data:${strArr[num]}\n\n`)num++if(num > 5){res.write(`data:end\n\n`)res.end()// res.closed()clearInterval(setTask)setTask = null}},1000)
});  app.listen(port, () => {  console.log(`${port}端口已启动`);  
});

2. 前端实现接收数据流

这里使用一个叫做EventSource的api去实现流式接口的调用和数据获取**。**

配置代理(重要)

如果我们用vue,react等等框架开发时,需要在代理处做一些配置,确保数据会以流式的返回。如果不做这层代理的配置,那么你获取的数据就会是执行完所有的res.write,一次性的全部返回给前端,就不是我们想要的效果。

效果如下,在配置代理中将compress设置为false

    devServer:{client:{overlay:false},port:8080,open:true,compress:false,  //流式数据返回的关键配置proxy:{'/server1':{target:'http://localhost:3001',ws:false,changeOrigin:true,pathRewrite:{'^/server1':''}},'/server2':{target:'http://localhost:3002',ws:false,changeOrigin:true,pathRewrite:{'^/server2':''}},'/sse':{target:'http://localhost:8002',ws:false,changeOrigin:true,pathRewrite:{'^/sse':''}}}}
前端实现接口调用

<template><div><el-button @click="sendMsg">发送消息</el-button><p v-for="(item,index) in msgList" :key="index">{{ item }}</p></div></template><script>export default{name:'admin',data(){return{msgList:[]}},methods:{sendMsg(){let vm = this//方案1:EventSourceconst eventSource = new EventSource('/sse/events');  //消息监听eventSource.onmessage = function(event) {  console.log(eventSource,vm,'状态')console.log(event.data); // 输出SSE发送的数据  if(event.data === 'end'){eventSource.close()}else{vm.msgList.push(event.data)}};  //连接成功eventSource.onopen = function(event){}//连接出错eventSource.onerror = function(error) {  if (eventSource.readyState === EventSource.CLOSED) {  // 连接已关闭,可能需要重新连接  console.error('SSE连接已关闭:', error);  }  }//方案2:xhr(不推荐)// const xhr = new XMLHttpRequest(); // const url = '/sse/events'; // xhr.open('GET', url,true); // xhr.setRequestHeader('Accept', 'text/event-stream');// xhr.onload = (event)=>{//   if(xhr.status === 200){//     console.log(xhr.responseText,'onload',event)//   }// }// xhr.onreadystatechange = (event)=>{//   // if(xhr.status === 200){//   //   console.log(xhr.responseText,'onreadystatechange',event)//   // }// }// xhr.onprogress = (event)=>{//   if(xhr.status === 200){//     console.log(xhr.responseText,'onreadystatechange',event)//   }// }// xhr.send()}}}</script><style lang="less"></style>

这样就大功告成了,如果以后要是做类似于chatgpt这种效果,就可以用到的。

3. 对接ai接口,使用write返回数据流格式

服务器端(node+express)与openai接口对接部分代码:

// const md5 = require('md5')
import express from 'express';
import mysql from 'mysql';
import cors from 'cors';
import jwt from 'jsonwebtoken';
import bodyParser from 'body-parser';
import OpenAI from "openai";
const app = express()app.use(bodyParser.json())              //解析json
app.use(bodyParser.urlencoded({ extended: true }));             //解析客户端传递过来的参数 function query(sql, callback) {// 从连接池中获取一个连接pool.getConnection((err, connection) => {if (err) {callback(err, null);} else {// 执行查询connection.query(sql, (err, results) => {// 释放连接connection.release();callback(err, results);});}});
}app.get('/open', (req, res) => {const openai = new OpenAI({// 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey: "sk-xxx",apiKey: "",baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1"});async function main() {const completion = await openai.chat.completions.create({model: "qwen-plus",messages: [{ "role": "system", "content": "You are a helpful assistant." },{ "role": "user", "content": "你是谁?" }],stream: true,});for await (const chunk of completion) {// console.log(JSON.stringify(chunk));// res.write(chunk);res.write(JSON.stringify(chunk));// res.end();}res.end();}main();
})app.listen(3001, () => {console.log('serve is running at http://127.0.0.1:3001')
})

上面的apikey值需要你申请密钥

然后在浏览器访问地址http://192.168.3.105:3001/open,就会输出流式数据,在前端中调用就行

4. res.write、res.end及res.send 使用以及区别?

首先,Express响应中常用的四种API:res.write() | res.end() | res.send() | res.json(),这4个API方法,都可以发送HTTP响应,返回浏览器的请求数据

一. res.write()方法

//引入express包
const express = require('express');
//创建路由器对象
const r = express.Router();
//往路由器中添加路由
//商品列表路由:get  /list
r.get('/list', (req, res) => {// 在响应头信息里设置响应返回的内容类型为html,编码为utf-8(在浏览器页面正常显示中文)// 设置内容解析的编码为utf-8,正确地告诉浏览器,服务器响应的内容是什么编码的,你浏览器应该按照我服务器设定的编码格式来解析给你的内容// res.writeHead(200, {//   'Content-Type': 'text/html;charset=utf-8'// });let show = "<h2>888</h2>";res.write(show);res.write('商品列表');res.end();//res.end('<div>该方法用于结束响应的浏览器请求</div>');
});//导出路由器对象
module.exports = r;

打开接口地址

image-20241115163302145

1. res.write()响应的数据“所见即所得”

res.write()的返回数据是没有经过处理的,原封不动的返回原数据,所见即所得

2. res.write()与res.end()总是且必须成对出现

如果要使用res.write()最后必须要有res.end,两者是成对出现的,缺一不可,也就是说使用res.write方法向前端返回数据,必须调用res.end方法结束请求。否则浏览器会一直处于处于请求状态

3. res.write()方法在结束浏览器响应请求之前,允许多次调用

如果想要输出多条语句,使用的是res.write(),也就是说在res.end() 之前,res.write() 可以被执行多次),且返回的数据会被拼接到一起。

4.res.write()是可以结合HTML标签显示的

res.write()输出内容可以结合HTML标签进行使用。

5. res.write()只支持输出字符串类型或是Buffer对象两种内容类型的数据
如果此时我们输出一个数字就会报错,查看报错信息,提醒我们不能输出number类型
res.write(123);

二. res.end方法

//引入express包
const express = require('express');
//创建路由器对象
const r = express.Router();
//往路由器中添加路由
//商品列表路由:get  /list
r.get('/list', (req, res) => {// 在响应头信息里设置响应返回的内容类型为html,编码为utf-8(在浏览器页面正常显示中文)// 设置内容解析的编码为utf-8,正确地告诉浏览器,服务器响应的内容是什么编码的,你浏览器应该按照我服务器设定的编码格式来解析给你的内容res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});res.end('<div>该方法用于结束响应的浏览器请求</div>');//下面语句将不会输出res.end("Hello world");
});//导出路由器对象
module.exports = r;

res.end()函数用于结束响应过程。该方法用于快速结束响应,而无需任何数据。也就是说用于在没有任何数据的情况下快速结束响应。如果有响应数据,就不能用 res.end,会报错,请使用res.send()和res.json()等方法。

1. res.end()响应的数据“所见即所得”

res.end()的返回数据同res.write()一样,也是没有经过处理的,原封不动的返回原数据,所见即所得

2. res.end()是不允许输出多行的

不同于res.write()方法,res.end()作为结束浏览器请求的方法,仅能调用一次

3. res.end()是可以结合HTML标签显示的

res.end()同res.write()一样,输出的内容可以是带HTML标签的内容

res.end(‘’

该方法用于结束响应的浏览器请求
‘’);

4. res.end()只支持输出字符串类型或是Buffer对象两种内容类型的数据

res.end()同res.write一样,不能输入除字符串类型或是Buffer对象类型外的其他内容类型的数据

三. res.send()方法

1. res.send()响应的数据是经过处理的

打开浏览器控制台,在响应头中被自动添加了context-type,也就是说,res.send()方法响应返回给页面数据时,在响应头信息里会被自动添加设置返回数据类型的context-type属性

2. res.send()只能被调用一次,因为它等同于res.write+res.end()

多个send输出只执行第一个send语句,后续send语句将不被执行

3.res.send()同res.write()、res.end()一样,可以结合HTML标签数据显示

*4. res.send**()支*持多种内容格式的输出

res.send()方法可以支持多种参数,比如可以传String、Array、Buffer对象、对象、json对象

当参数是Array或Object、json对象,Express以JSON表示响应

res.send({ user: ‘tobi’ });

res.send()只能被调用一次,因为它等同于res.write+res.end()**

多个send输出只执行第一个send语句,后续send语句将不被执行

3.res.send()同res.write()、res.end()一样,可以结合HTML标签数据显示

*4. res.send**()支*持多种内容格式的输出

res.send()方法可以支持多种参数,比如可以传String、Array、Buffer对象、对象、json对象

当参数是Array或Object、json对象,Express以JSON表示响应

res.send({ user: ‘tobi’ });

res.send([1,2,3]);

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

esp32c3安装micropython环境

esp32c3竟然支持micropython环境&#xff0c;真的太让人高兴了。主要是python开发比较友好&#xff0c;开发速度要快于C和C&#xff0c; 可以用来快速创意验证。 下载 首先到官网&#xff1a;MicroPython - Python for microcontrollers 点击“download”进入下载页面&#…

Linux运维工程师推荐学习的开发语言

前言&#xff1a;会开发的运维和不会开发的运维可以说是两个世界的运维。 个人推荐python和go&#xff0c;前者可以做自动化运维&#xff0c;后者可以深挖k8s&#xff1b;最近就不先演示运维服务技术的部署和架构搭建了&#xff0c;在深挖自动化运维&#xff0c;为了让现在的工…

新手小白学习docker第八弹------实现MySQL主从复制搭建

目录 0 引言1 实操1.1 新建主服务器容器1.2 书写配置文件1.3 重启master实例1.4 进入mysql-master容器master容器实例内创建数据同步用户 1.5 新建从服务器容器1.6 书写配置文件1.7 重启slave实例1.8 查看主从同步状态1.9 进入mysql-slave容器1.9.1 配置主从复制1.9.2 查看主从…

我谈二值形态学基本运算——腐蚀、膨胀、开运算、闭运算

Gonzalez从集合角度定义膨胀和腐蚀&#xff0c;不易理解。 Through these definitions, you can interpret dilation and erosion as sliding neighborhood operations analogous to convolution (or spatial filtering). 禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向…

力扣题目解析--合并两个链表

题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#xff…

基于yolov8、yolov5的鸟类分类系统(含UI界面、训练好的模型、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8、yolov8 SE注意力机制 或 yolov5、yolov5 SE注意力机制 &#xff0c; 直接提供最少两个训练好的模型。模型十分重要&#xff0c;因为有些同学的电脑没有 GPU&#xff0…

css:浮动

网页的本质上就是摆放盒子&#xff0c;把盒子摆到相应的位置上 css提供了三种传统的布局方式&#xff1a; 普通流&#xff08;标准流&#xff09;&#xff1a;标签按默认方式排列&#xff0c;最基本的布局方式 浮动 定位 实际开发中&#xff0c;一个网页基本包含了三种这种布局…

Essential Cell Biology--Fifth Edition--Chapter one (6)

1.1.4.4 Internal Membranes Create Intracellular Compartments with Different Functions [细胞膜形成具有不同功能的细胞内隔室] 细胞核、线粒体和叶绿体并不是真核细胞中唯一的膜包围细胞器。细胞质中含有大量的[ a profusion of]其他细胞器&#xff0c;这些细胞器被单层膜…

量子奇异值阈值算法

特征值分解只适用于方阵&#xff0c;如何扩展到任意形状的矩阵呢&#xff1f;奇异值分解能够解决此问题。量子奇异值阈值算法在奇异值分解的基础上将小的特征值设置为0&#xff0c;从而将小的特征值及其对应的特征向量去掉&#xff0c;进而降低矩阵的秩&#xff0c;达到降维的目…

Python_爬虫3_Requests库网络爬虫实战(5个实例)

目录 实例1&#xff1a;京东商品页面的爬取 实例2&#xff1a;亚马逊商品页面的爬取 实例3&#xff1a;百度360搜索关键词提交 实例4&#xff1a;网络图片的爬取和存储 实例5&#xff1a;IP地址归地的自动查询 实例1&#xff1a;京东商品页面的爬取 import requests url …

黑马微项目

目录 1 飞机票 2 生成一个五位数验证码 3 数字加密 4 数字解密 5 抢红包 6 双色球系统 7 用户登录 8 金额转换 9 手机号屏蔽 10 罗马数字转换 11 调整字符串 12 初级学生管理系统&#xff08;学生数据的管理&#xff09; 13 学生管理系统&#xff08;用户的相关操…

基于lighthouse搭建私有网盘Cloudreve【开源应用实践】

基于lighthouse搭建私有网盘Cloudreve【超高性价比】 今天给大家分享一款私人网盘神器&#xff0c;既能存放你的文件文档&#xff0c;也能替你保存那不可告人的秘密~ 香菇今天将手把手教给大家如何在腾讯云轻量应用服务器上搭建个人专属网盘 1. 既爱又恨的网盘存储 很多小伙伴…

博物馆实景复刻:开启沉浸式文化体验的新篇章

随着数字化技术的飞速发展&#xff0c;博物馆的展览形式正在经历一场前所未有的变革。3数字博物馆和3D线上展览&#xff0c;这种创新的展览方式不仅打破了时间和空间的限制&#xff0c;更让文化遗产的保护与传承迈上了一个新的台阶。 本文将深入探讨博物馆实景复刻虚拟展厅的兴…

java中设计模式的使用(持续更新中)

概述 设计模式的目的&#xff1a;编写软件过程中&#xff0c;程序员面临着来自耦合性&#xff0c;内聚性以及可维护性&#xff0c;可扩展性&#xff0c;重用性&#xff0c;灵活性等多方面的挑战&#xff0c;设计模式是为了让程序&#xff08;软件&#xff09;&#xff0c;具有…

linux基础io重定向

文章目录 目录 文章目录 前言 一、函数的认识 1、认识close函数和dup2函数 1、close函数&#xff1a; ​编辑 2、write、read函数 1、write函数 2、read函数 二、重定向 1.引入函数dup2 ​编辑 2、输出重定向 3.输出重定向 三、myshell重定向 总结 前言 接上一篇&#xff0c;…

[STM32] 定时器应用之输出比较 (五)

文章目录 1.输出比较2.PWM 介绍3.配置PWM 1.输出比较 OC: 输出比较。 输出比较可以通过比较CNT与CCR寄存器值的关系&#xff0c;来对输出电平进行置1、置0或翻转的操作&#xff0c;用于输出一定频率和占空比的PWM波形。每个高级定时器和通用定时器都拥有4个输出比较通道高级定…

【计算机毕设】无查重 基于python豆瓣电影评论舆情数据可视化系统(完整系统源码+数据库+开发笔记+详细部署教程)✅

目录 【计算机毕设】无查重 基于python豆瓣电影数据可视化系统&#xff08;完整系统源码数据库开发笔记详细部署教程&#xff09;✅ 一、项目背景 二、项目目标 三、项目功能 四、开发技术介绍 五、数据库设计 六、项目展示 七、开发笔记 八、启动步骤文档 九、权威教…

后台管理系统窗体程序:个人中心

目录 个人中心的功能介绍&#xff1a; 1、进入页面 2、页面内的各种功能设计 &#xff08;1&#xff09;修改按钮 &#xff08;2&#xff09;页面的进入退出操作 一、网页设计 二、html代码 三、css代码 四、js代码 本次项目为后台管理系统&#xff0c;在本系统内的第七…

PLC如何支持GEM300标准?SECS/GEM通讯协议

1. 提供技术服务&#xff0c;保证户使用没问题 2. 支持市场所有的常规PLC 3. 支持常规组态软件&#xff0c;如wincc、组态王、组态屏等 4. 支持各类传感器&#xff0c;私有协议、modbus、web等 5. 无需二次开发&#xff0c;只需配置映射到已有的PLC地址 GEM300协议是为了满…

用 Google Sheets 表格增强 Tableau 数据分析的 3 种玩法

轻松实现文本翻译、网页数据抓取&#xff0c;甚至创建高级日期表来增强 Tableau 可视化效果&#xff01; 作为一款强大的数据可视化工具&#xff0c;Tableau 的可视化能力毋庸置疑。然而&#xff0c;对于跟表格打交道的用户来说&#xff0c;它没有“创建表格”的功能&#xff0…