使用原生nodejs来对接ChatGPT API流式响应

55,469次阅读
没有评论

共计 11771 个字符,预计需要花费 30 分钟才能阅读完成。

我是跟着 B 站一个老师写的:B 站老师视频

文章末尾附全部代码,如有那个地方不对的或者说是不准确的还请各位大佬指出来哇(っ˘зʕ•̫͡•ʔ

目录

1. 首先得创建一个空的文件夹,在 vscode 中打开

2. 安装可以直接运行 TS 的开发工具

3. 新建 TS 文件

4. 在 vscode 中可以有 node 一些内置包的提示

5. 在创建的 TS 文件中输入代码

6. 在终端运行

6. 如果以上都正确的话,就说明我们基本的效果已经出来了

7. 接下来就可以对接 ChatGPT 的接口了

8. 新建一个窗口(终端),安装 axios

9. 接着就是在 TS 文件中导入 axios 和 api 实例

10. 创建一个 env 文件

11. 因为 nodejs 中无法直接使用 env 的文件,所以这里需要安装一个包

12. 接着就导入 dotenv

13. 接下来就是 createServer 方法中的了

14. 因为请求的速度会很慢,所以需要安装 proxy

15. 导入 socks-proxy-agent

16. 接着就是在 env 和 TS 中写入代理地址

17. 接着就试着一下输出 stream 格式

18. 也可以试着更改 message 中的内容

19. 接下来就开始前端的业务了

19.1 创建一个 html 文件

19.2 再来看一下 req

19.3 请求的是跟地址

19.4 请求的是 chat

19.5 如果请求的是其他地址

19.6 注意一点

19.7html 中的代码

19.8 返回字符串

19.9 在页面输出

19.10 用文本框的形式输出

20. 全部代码

TS 代码

env 代码

html 代码


1. 首先得创建一个空的文件夹,在 vscode 中打开

2. 安装可以直接运行 TS 的开发工具

npm i -g ts-node-dev

3. 新建 TS 文件

4. 在 vscode 中可以有 node 一些内置包的提示

npm i -D @types/node

5. 在创建的 TS 文件中输入代码

// 从 http 中导入 createServer 方法,目的是创建一个 web 服务
import {createServer} from 'http'

createServer((req, res) => {res.end('ok')
}).listen(9001)// 这个 9001 是监听的端口号
console.log('http://localhost:9001');

6. 在终端运行

tsnd server.ts => 我这里的 server 是我起的名字,你们根据自己起的名字写

最后效果如图所示:

因为我代码里面写的端口号是 9001,所以我输出的是 http://localhost:9001,最后运行结束后,终端里面会出现 log 出来的接口,Ctrl+ 单击终端中的链接,浏览器就会出现我代码中的 OK

使用原生 nodejs 来对接 ChatGPT API 流式响应

6. 如果以上都正确的话,就说明我们基本的效果已经出来了

7. 接下来就可以对接 ChatGPT 的接口了

https://platform.openai.com/docs/api-reference/making-requests

打开上面的链接(ChatGPT 的官方文档),找到下图这个地方,这个图片中就是 ChatGPT 的接口和其他一些东西了

使用原生 nodejs 来对接 ChatGPT API 流式响应

8. 新建一个窗口(终端),安装 axios

npm i axios

9. 接着就是在 TS 文件中导入 axios 和 api 实例

// 导入 axios
import axios from 'axios'
// 创建 axios 的一个 api 实例
const api = axios.create({
    baseURL:'https://api.openai.com/v1',// 这里写的就是 ChatGPT 的接口
    timeout: 5000,// 可以不写
    headers: {
        'Content-Type': 'application/json', // 这个可以不写,重要的是下面的 Authorization
        Authorization: `Bearer ${}`// 这里是咱们自己的 token, 看 env 文件,这里我把 key 放到了环境变量中}
})

10. 创建一个 env 文件

这里的 key 就是之前群里发的那个 Excel 表格里面的东西

OPENAI_API_KEY = 你自己的 key

11. 因为 nodejs 中无法直接使用 env 的文件,所以这里需要安装一个包

npm i dotenv

12. 接着就导入 dotenv

从一下代码中可以看到 Authorization 后面添加了东西,其实这是引入 dotenv/config 后,会自动加载 dotenv 文件,并且会输出到 process.env 中去,然后后面再加上 OPENAI_API_KEY 就可以了。这就设置好了最基本的 axios 实例

import 'dotenv/config'
​
const api = axios.create({
    baseURL:'https://api.openai.com/v1',// 这里写的就是 ChatGPT 的接口
    timeout: 5000,// 可以不写
    headers: {
        'Content-Type': 'application/json', // 这个可以不写,重要的是下面的 Authorization
        Authorization: `Bearer ${process.env.OPENAI_API_KEY}`// 这里是咱们自己的 token
    }
})

13. 接下来就是 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,
}

14. 因为请求的速度会很慢,所以需要安装 proxy

npm i socks-proxy-agent

15. 导入 socks-proxy-agent

import {SocksProxyAgent} from 'socks-proxy-agent'

16. 接着就是在 env 和 TS 中写入代理地址

使用原生 nodejs 来对接 ChatGPT API 流式响应

从上述图片中可以看到,红框框中的地址是 7890,所以我这里写的是 7890,你们需要根据自己的来写

SOCKS_PROXY = socks5://127.0.0.1:7890
httpsAgent: new SocksProxyAgent(process.env.SOCKS_PROXY)

如图所示:这就是运行出来的结果,ChatGPT 直接返回的是一个 JSON 对象,最重要的是 choices 中的。可以看到 message 下的 content 是直接返回的一个内容

使用原生 nodejs 来对接 ChatGPT API 流式响应

17. 接着就试着一下输出 stream 格式

// 导入 stream
import {Stream} from 'stream';

createServer(async (req, res) => {
  // 这里是 ChatGPT 的接口根地址, 返回的数据是 JSON 格式。post 默认的是任何格式,但是这里是 stream 格式
  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,
    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 是监听的端口号

如下图所示,这就是运行出来的结果:

使用原生 nodejs 来对接 ChatGPT API 流式响应

18. 也可以试着更改 message 中的内容

"messages": [{"role": "user", "content": "你可以讲一个爱情故事吗?"}],
max_tokens: 100,// 并且,我把这里的最大限度设成 100,在运行过程中就可以看到页面在不停输出,直到我设置的最大限度为止

19. 接下来就开始前端的业务了

19.1 创建一个 html 文件




    
    
    流式响应


    

流式响应是指服务器在发送响应时,可以不等待客户端请求,而是直接发送响应,这样可以减少服务器的响应时间,提高服务器的性能。

19.2 再来看一下 req

依然是在刚刚的 TS 文件当中,在 createServer 中所有代码之前加上下方代码即可

createServer(async (req, res) => {console.log(req.url);// 来看一下 req 的 url,看他请求的是什么
    return res.end(req.url);
}).listen(9001); // 这个 9001 是监听的端口号

如图所示,这就是浏览器页面中输出的东西

使用原生 nodejs 来对接 ChatGPT API 流式响应

下方图片是 vscode 终端中输出的东西

使用原生 nodejs 来对接 ChatGPT API 流式响应

所以我们需要实现一个界面,上方 url 中的地址是 / 的时候显示 html 页面,是 chat 的时候就是接口的东西

19.3 请求的是跟地址

但是如果地址后面有参数的话,会被忽略掉,所以我们需要将 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 中写的内容

使用原生 nodejs 来对接 ChatGPT API 流式响应

19.4 请求的是 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(
        "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;
  }

19.5 如果请求的是其他地址

如果请求的是根地址和 chat 之外的地址的话,直接返回一个空的字符串

 default:
	// 如果请求的接口地址不在上面定义的接口地址中,就返回一个空的字符串
	res.end('')

19.6 注意一点

messages: [{role: "user", content: "你可以讲一个爱情故事吗?"}]这一部分的内容应该是由前端那边传过来的,所以我们需要把他设置成变量

//   entries()是返回了键值对的一个数组;Object.fromEntries()是将键值对数组转化为一个对象
const query = Object.fromEntries(url.searchParams.entries());
console.log(query);

如下图所示,因为我 url 后面没有接参数,所以我终端输出的是一个空的对象。为什么会有两个呢?因为一般浏览器还会发一个 favicon 的请求,所以会多一个空的对象,不用管它

使用原生 nodejs 来对接 ChatGPT API 流式响应

所以下方的请求 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(
        "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;

19.7html 中的代码

 

但是后面你就会发现浏览器中的控制台会报错,这是因为在 TS 文件里面中的请求 chat 的那一块少写了东西res.setHeader("Content-Type", "text/event-stream"),这个代码我已经在上面写过了,不过你们可以尝试一下把这句话注了,然后看看报错。当把这个代码加上之后,再刷新一下页面,控制台就没有报错了,但是控制台中会输出一些东西(见下方图片)。但是有一个小 bug,就是它会持续不断的输出,因为我们没有关闭它。可以翻到控制台的最后,可以看到[DONE],这个是 openai 返回的一个特殊的标记,表示这个代码已经完成了

使用原生 nodejs 来对接 ChatGPT API 流式响应

使用原生 nodejs 来对接 ChatGPT API 流式响应

所以我们可以在 es.onmessage 中加一个判断,当输出 [DONE] 的时候就关闭 es,这样就不会有重复的内容了

es.onmessage = (e) => {
	// e.data 就是服务器返回的数据
	console.log(e.data);
	if(e.data ==='[DONE]'){// 当服务器推送 [DONE] 时,关闭 es
	return es.close();}

19.8 返回字符串

从浏览器的控制台中可得知,我们每次输出的都是一个对象,所以这里我们把对象中输出的内容拿出来

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('你好呀')

如图所示,这就是我们可以看到的一个实时输出的一个效果

使用原生 nodejs 来对接 ChatGPT API 流式响应

19.9 在页面输出

在 html 中用标签输出,给他设一个 id。然后在 js 代码中获取一下这个标签,在进行连接之前需要清空一下之前的记录,然后在收到任何 content 后,把这些内容加到这个标签当中。



19.10 用文本框的形式输出




20. 全部代码

TS 代码

// 从 http 中导入 createServer 方法,目的是创建一个 web 服务
import {createServer} from "http";
// 导入 axios
import axios from "axios";
// 导入 dotenv
import "dotenv/config";
// 导入 socks-proxy-agent
import {SocksProxyAgent} from "socks-proxy-agent";
// 导入 stream
import {Stream} from "stream";
// 导入 fs
import {createReadStream} from "fs";

const socksProxy = process.env.SOCKS_PROXY;

// 检查 socksProxy 是否为 undefined
if (socksProxy === undefined) {throw new Error("SOCKS_PROXY environment variable is not defined");
}

// 创建 axios 的一个 api 实例
const api = axios.create({
  baseURL: "https://api.openai.com/v1", // 这里写的就是 ChatGPT 的接口地址
  timeout: 5000, // 可以不写
  headers: {
    "Content-Type": "application/json", // 这个可以不写,重要的是下面的 Authorization
    Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, // 这里是咱们自己的 token
  },
  //   因为我们请求的是 https,所以需要设置代理
  httpsAgent: new SocksProxyAgent(socksProxy),
});

createServer(async (req, res) => {// console.log(req.url);// 来看一下 req 的 url,看他请求的是什么
  // 因为如果地址后面有参数的话,会被忽略掉,所以我们需要将 url 转化为一个 URL 对象
  const url = new URL(req.url!, "file:///");
//   entries()是返回了键值对的一个数组;Object.fromEntries()是将键值对数组转化为一个对象
  const query = Object.fromEntries(url.searchParams.entries());
  console.log(query);
  
  // return res.end(url.pathname),// 这里输出 url 的 pathname,也就是请求的接口地址,他就会是一个纯路径

  // 做个判断,看看请求的是什么接口, 这个 url 中会包含接口的很多信息
  switch (url.pathname) {
    // 让数据以流的形式返回给客户端
    case "/":
      createReadStream("./index.html").pipe(res);
      break;
    // 如果请求的是 /chat 接口的话,执行的就是之前写的 ChatGPT 接口那一部分的内容了
    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(
        "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;
    default:
        // 如果请求的接口地址不在上面定义的接口地址中,就返回一个空的字符串
      res.end('')
  }

  //res.end 无法直接返回 JSON 格式,需要利用 JSON.stringify()转成字符串。但是因为 ChatGPT 的返回是 stream 格式,所以需要用 pipe 方法将 stream 转成字符串,这里就把 JSON 给注销了
  //   res.end(JSON.stringify(data));
}).listen(9002); // 这个 9001 是监听的端口号

console.log("http://localhost:9002");

env 代码

OPENAI_API_KEY = 你自己的 key
# 这里我把 key 放到了.env 文件 (也就是环境变量) 中,这样在代码中就不需要写 key 了
# 但是如果要在代码中写 key,就需要把 key 写到代码中,这样不好,所以我把 key 写到了.env 文件中,这样在代码中就不需要写 key 了
# 这里的 key 是之前在 OpenAI 官网申请的,也就是群里那个文档中的东西,每个人都有的

# 这里写的是猫的地址
SOCKS_PROXY = socks5://127.0.0.1:7890

html 代码




    
    
    流式响应
    


    
    
    
    

原文地址: 使用原生 nodejs 来对接 ChatGPT API 流式响应

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-10-29发表,共计11771字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)