admin管理员组文章数量:1530012
一 、使用原生nodejs来对接ChatGPT API流式响应:
- 首先得创建一个空的文件夹,在vscode中打开
- 安装可以直接运行TS的开发工具
npm i -g ts-node-dev
- 新建TS文件
- 在vscode中可以有node一些内置包的提示
npm i -D @types/node
- 在创建的TS文件中输入代码
// 从http中导入createServer方法,目的是创建一个web服务
import {createServer} from 'http'
createServer((req, res) => {
res.end('ok')
}).listen(9001)// 这个9001是监听的端口号
console.log('http://localhost:9001');
- 在终端运行
tsnd server.ts => 我这里的server是我起的名字,你们根据自己起的名字写
最后效果如图所示:
因为我代码里面写的端口号是9001,所以我输出的是http://localhost:9001,最后运行结束后,终端里面会出现log出来的接口,Ctrl+单击终端中的链接,浏览器就会出现我代码中的OK。
7. 如果以上都正确的话,就说明我们基本的效果已经出来了
8. 接下来就可以对接ChatGPT的接口了
https://platform.openai/docs/api-reference/making-requests
这个网址需要开启魔法小猫🧙♂️
打开上面的链接(ChatGPT的官方文档),找到下图这个地方,这个图片中就是ChatGPT的接口和其他一些东西了
- 新建一个窗口(终端),安装axios
npm i axios
- 接着就是在TS文件中导入axios和api实例
// 导入axios
import axios from 'axios'
// 创建axios的一个api实例
const api = axios.create({
baseURL:'https://api.openai/v1',//这里写的就是ChatGPT的接口
timeout: 5000,// 可以不写
headers: {
'Content-Type': 'application/json', // 这个可以不写,重要的是下面的Authorization
Authorization: `Bearer ${}`// 这里是咱们自己的token,看env文件,这里我把key放到了环境变量中
}
})
- 创建一个env文件
这里的key就是之前群里发的那个Excel表格里面的东西
OPENAI_API_KEY = 你自己的key
- 因为nodejs中无法直接使用env的文件,所以这里需要安装一个包
npm i dotenv
- 接着就导入dotenv
从一下代码中可以看到Authorization后面添加了东西,其实这是引入dotenv/config后,会自动加载dotenv文件,并且会输出到process.env中去,然后后面再加上OPENAI_API_KEY
就可以了。这就设置好了最基本的axios实例
import 'dotenv/config'
const api = axios.create({
baseURL:'https://api.openai/v1',//这里写的就是ChatGPT的接口
timeout: 5000,// 可以不写
headers: {
'Content-Type': 'application/json', // 这个可以不写,重要的是下面的Authorization
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`// 这里是咱们自己的token
}
})
- 接下来就是createServer方法中的了
createServer(async (req, res) => {
// 这里是ChatGPT的接口根地址,返回的数据是JSON格式
const {data} = await api.post("chat/completions", {
// 这里是ChatGPT的JSON数据
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "Say this is a test!" }],
max_tokens: 30,// 测试的时候加一个max_tokens,目的是控制ChatGPT的输出长度,可以减少请求的时间
// temperature: 0.7,
});
// res.end无法直接返回JSON格式,需要利用JSON.stringify()转成字符串
res.end(JSON.stringify(data));
}).listen(9001); // 这个9001是监听的端口号
上面代码中的这个部分是ChatGPT官方文档中的-d这个部分的东西。content: “Say this is a test!” 中引号里面的东西就是咱们平时向小柴提问的问题
{
// 这里是ChatGPT的JSON数据
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "Say this is a test!" }],
max_tokens: 30,// 测试的时候加一个max_tokens,目的是控制ChatGPT的输出长度,可以减少请求的时间
// temperature: 0.7,
}
- 因为请求的速度会很慢,所以需要安装proxy
npm i socks-proxy-agent
- 导入socks-proxy-agent
import {SocksProxyAgent} from 'socks-proxy-agent'
- 接着就是在env和TS中写入代理地址
从上述图片中可以看到,红框框中的地址是7890,所以我这里写的是7890,你们需要根据自己的来写
SOCKS_PROXY = socks5://127.0.0.1:7890
httpsAgent: new SocksProxyAgent(process.env.SOCKS_PROXY)
如图所示:这就是运行出来的结果,ChatGPT直接返回的是一个JSON对象,最重要的是choices中的。可以看到message下的content是直接返回的一个内容
18. 接着就试着一下输出stream格式
// 导入stream
import { Stream } from 'stream';
createServer(async (req, res) => {
// 这里是ChatGPT的接口根地址,返回的数据是JSON格式。post默认的是任何格式,但是这里是stream格式
const {data} = await api.post<Stream>("chat/completions", {
// 这里是ChatGPT的JSON数据
"model": "gpt-3.5-turbo",
"messages": [{ "role": "user", "content": "Say this is a test!" }],
max_tokens: 30,// 测试的时候加一个max_tokens,目的是控制ChatGPT的输出长度,可以减少请求的时间
// temperature: 0.7,
stream:true,
},{
// 设置返回的数据类型为stream
responseType:'stream'
});
data.pipe(res);// 可以直接流向res响应的对象
// res.end无法直接返回JSON格式,需要利用JSON.stringify()转成字符串。但是因为ChatGPT的返回是stream格式,所以需要用pipe方法将stream转成字符串,这里就把JSON给注销了
// res.end(JSON.stringify(data));
}).listen(9001); // 这个9001是监听的端口号
19. 也可以试着更改message中的内容
"messages": [{ "role": "user", "content": "你可以讲一个爱情故事吗?" }],
max_tokens: 100,// 并且,我把这里的最大限度设成100,在运行过程中就可以看到页面在不停输出,直到我设置的最大限度为止
- 接下来就开始前端的业务了
- 创建一个html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>流式响应</title>
</head>
<body>
<p>流式响应是指服务器在发送响应时,可以不等待客户端请求,而是直接发送响应,这样可以减少服务器的响应时间,提高服务器的性能。</p>
</body>
</html>
- 再来看一下req
依然是在刚刚的TS文件当中,在createServer中所有代码之前加上下方代码即可
createServer(async (req, res) => {
console.log(req.url);// 来看一下req的url,看他请求的是什么
return res.end(req.url);
}).listen(9001); // 这个9001是监听的端口号
如图所示,这就是浏览器页面中输出的东西
所以我们需要实现一个界面,上方url中的地址是/的时候显示html页面,是chat的时候就是接口的东西
23. 请求的是跟地址
但是如果地址后面有参数的话,会被忽略掉,所以我们需要将url转化为一个URL对象。这里就需要改刚刚19.2步的代码了
// console.log(req.url);// 来看一下req的url,看他请求的是什么
// 因为如果地址后面有参数的话,会被忽略掉,所以我们需要将url转化为一个URL对象
const url = new URL(req.url!,'file:///')
// return res.end(url.pathname),// 这里输出url的pathname,也就是请求的接口地址,他就会是一个纯路径
// 做个判断,看看请求的是什么接口
switch (url.pathname) {
// 让数据以流的形式返回给客户端
case "/":
createReadStream("./index.html").pipe(res);
break;
}
- 如图所示,这就是运行的结果了。页面显示的是刚刚html中写的内容
- 请求的是chat
这里就是再加一个case,然后后面跟的是之前写的东西,就直接把那一部分的代码XC过来就可以
// 做个判断,看看请求的是什么接口
switch (url.pathname) {
// 让数据以流的形式返回给客户端
case "/":
createReadStream("./index.html").pipe(res);
break;
// 如果请求的是/chat接口的话,执行的就是之前写的ChatGPT接口那一部分的内容了
case "/chat":
// 这里是ChatGPT的接口根地址,返回的数据是JSON格式。post默认的是任何格式,但是这里是stream格式
const { data } = await api.post<Stream>(
"chat/completions",
{
// 这里是ChatGPT的JSON数据
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "你可以讲一个爱情故事吗?" }],
max_tokens: 100, // 测试的时候加一个max_tokens,目的是控制ChatGPT的输出长度,可以减少请求的时间
temperature: 0.7,
stream: true,
},
{
// 设置返回的数据类型为stream。stream是node.js中专门用来处理流数据的。他可以不断的将数据流输出到res响应的对象中,所以它可以实现流式的传输数据
responseType: "stream",
}
);
data.pipe(res); // 可以直接流向res响应的对象
break;
}
- 如果请求的是其他地址
如果请求的是根地址和chat之外的地址的话,直接返回一个空的字符串
default: // 如果请求的接口地址不在上面定义的接口地址中,就返回一个空的字符串 res.end('')
-
注意一点
messages: [{ role: "user", content: "你可以讲一个爱情故事吗?" }]
这一部分的内容应该是由前端那边传过来的,所以我们需要把他设置成变量
// entries()是返回了键值对的一个数组;Object.fromEntries()是将键值对数组转化为一个对象
const query = Object.fromEntries(url.searchParams.entries());
console.log(query);
如下图所示,因为我url后面没有接参数,所以我终端输出的是一个空的对象。为什么会有两个呢?因为一般浏览器还会发一个favicon的请求,所以会多一个空的对象,不用管它
所以下方的请求chat中的message就可以写成query.prompt,但是在写的时候一定要确保query.prompt存在,所以一定要在刚开始加上判断
case "/chat":
res.setHeader("Content-Type", "text/event-stream");// 一定要加,要不然浏览器控制台会报错,报错信息看下方图片
if(!query.prompt){
res.statusCode = 400;
return res.end(JSON.stringify({
message: "请输入提问内容"
}))
}
// 这里是ChatGPT的接口根地址,返回的数据是JSON格式。post默认的是任何格式,但是这里是stream格式
const { data } = await api.post<Stream>(
"chat/completions",
{
// 这里是ChatGPT的JSON数据
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: query.prompt}],// 但是一定要确保prompt是字符串格式且存在,所以在上面加一个判断
max_tokens: 100, // 测试的时候加一个max_tokens,目的是控制ChatGPT的输出长度,可以减少请求的时间
temperature: 0.7,
stream: true,
},
{
// 设置返回的数据类型为stream。stream是node.js中专门用来处理流数据的。他可以不断的将数据流输出到res响应的对象中,所以它可以实现流式的传输数据
responseType: "stream",
}
);
data.pipe(res); // 可以直接流向res响应的对象
break;
html中的代码
<script>
// 在这里定义一个函数,方便调用
function send(prompt){
// 定义一个url,根地址是当前页面的地址
const url = new URL('/chat',location.href)
// 将prompt添加到url中,直接可以拼接参数,不需要手写。设置一个参数,键名为prompt,值为上面传进来的prompt
url.searchParams.set('prompt',prompt)
// url定义好之后就直接给es就行了
const es = new EventSource(url)
// es就可以监听到服务器的推送了。得到一个e是message响应的事件
es.onmessage = (e) => {
// e.data就是服务器返回的数据
console.log(e.data);
}
}
send('你好呀')
</script>
但是后面你就会发现浏览器中的控制台会报错,这是因为在TS文件里面中的请求chat的那一块少写了东西res.setHeader("Content-Type", "text/event-stream")
,这个代码我已经在上面写过了,不过你们可以尝试一下把这句话注了,然后看看报错。当把这个代码加上之后,再刷新一下页面,控制台就没有报错了,但是控制台中会输出一些东西(见下方图片)。但是有一个小bug,就是它会持续不断的输出,因为我们没有关闭它。可以翻到控制台的最后,可以看到[DONE],这个是openai返回的一个特殊的标记,表示这个代码已经完成了
所以我们可以在es.onmessage中加一个判断,当输出[DONE]的时候就关闭es,这样就不会有重复的内容了
es.onmessage = (e) => {
// e.data就是服务器返回的数据
console.log(e.data);
if(e.data ==='[DONE]'){
// 当服务器推送[DONE]时,关闭es
return es.close();
}
返回字符串
从浏览器的控制台中可得知,我们每次输出的都是一个对象,所以这里我们把对象中输出的内容拿出来
es.onmessage = (e) => {
// 定义一个data变量,将服务器返回的数据转换为JSON格式
const data = JSON.parse(e.data);
// 获取content。从浏览器的控制台中可以看到服务器返回的数据,里面的内容就在choices下面的delta中的content里。如果content为空,则给一个空的字符串
const {content = ''} = data.choices[0].delta;
console.log(content);
}
send('你好呀')
如图所示,这就是我们可以看到的一个实时输出的一个效果
在页面输出
在html中用标签输出,给他设一个id。然后在js代码中获取一下这个标签,在进行连接之前需要清空一下之前的记录,然后在收到任何content后,把这些内容加到这个标签当中。
<!-- 这里用了pre标签,因为它可以保留换行符,方便查看 -->
<pre id="out"></pre>
<script>
// 获取输出元素
const out = document.getElementById('out');
// 在连接之前清空之前的记录
out.innerHTML = ''
es.onmessage = (e) => {
const {content = ''} = data.choices[0].delta;
// 然后在收到任何一个content之后把他加进去
out.innerHTML += content;
console.log(content);
}
send('你好呀')
</script>
用文本框的形式输出
<!-- 一定要把文本框放到pre的上面,要不然可能会导致答案在上面 -->
<input type="text" id="input">
<script>
const input = document.getElementById('input');
// 给input加一个事件监听
input.onkeydown = e => {
// 按下回车键,就触发事件
if (e.keyCode === 13) {
// 获取输入框的值
send(e.target.value)
}
}
</script>
下面是一个B站的视频,大家可以看着视频整一下!
B站的视频
版权声明:本文标题:Node.js对接ChatGPT的流式接口-----满满的干货!!!! 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1726696087a1081114.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论