# node调用chatgpt
# 后台技术架构
express
web 服务器axios
请求接口dotenv
全局环境变量解析https-proxy-agent
接口代理
安装命令:
pnpm i express axios dotenv https-proxy-agent
pnpm i @types/express @types/node -D
# 1、配置 .env 环境变量
在根目录下创建 .env
文件,配置如下
# openai
OPENAI_API_BASEURL=https://api.openai.com
OPENAI_API_KEY=sk-xxx
OPENAI_API_ORG=org-xxx
# proxy
PROXY_OPEN=1 # 1启用 0不启用
PROXY_HOST=127.0.0.1
PROXY_PORT=1081
# server
SERVER_PORT=18001
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 2、配置 TypeScript 环境
详见 nodejs 使用 typescript (opens new window)
# 3、配置运行命令和打包命令
package.json
{
"scripts": {
"start": "node ./dist/app.js", // 运行编译后的 js
"dev": "ts-node ./src/app.ts", // 直接运行 ts
"build": "tsc", // 编译
"startup": "tsc && node ./dist/app.js" // 编译并运行
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 4、创建 src 目录,导出 env 配置
在 src 目录创建 env.ts
文件
./src/env.ts
import dotenv from 'dotenv';
dotenv.config();
export const OPENAI_API_BASEURL = process.env.OPENAI_API_BASEURL; // openai 接口地址
export const OPENAI_API_KEY = process.env.OPENAI_API_KEY; // openai key
export const OPENAI_API_ORG = process.env.OPENAI_API_ORG; // openai 组织代码
export const PROXY_OPEN = process.env.PROXY_OPEN; // 是否开启代理
export const PROXY_HOST = process.env.PROXY_HOST; // 代理 host
export const PROXY_PORT = process.env.PROXY_PORT; // 代理 端口
export const SERVER_PORT = process.env.SERVER_PORT; // 本地服务启动端口
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 5、封装 axios
(1)创建 axios 目录,然后在其目录下创建 request.ts
文件
./src/axios/request.ts
import { HttpsProxyAgent, HttpsProxyAgentOptions } from 'https-proxy-agent';
import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
import {
OPENAI_API_KEY,
OPENAI_API_ORG,
PROXY_OPEN,
PROXY_HOST,
PROXY_PORT,
} from '../env'; // 导出环境配置
// 代理配置
const proxyOpts: HttpsProxyAgentOptions = {
host: PROXY_HOST,
port: PROXY_PORT,
};
// 拦截请求和响应
function instances(instance: AxiosInstance) {
// 请求拦截
instance.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
config.headers.Authorization = 'Bearer ' + OPENAI_API_KEY;
config.headers['OpenAI-Organization'] = OPENAI_API_ORG;
PROXY_OPEN === '1' &&
(config.httpsAgent = new HttpsProxyAgent(proxyOpts));
return config;
},
(error: any) => {
return Promise.reject(error);
},
);
}
// 创建请求
const request = (config: any) => {
const instance = axios.create(config);
instances(instance);
return instance;
};
export default request;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
(2)再创建 index.ts
导出 axios
./src/axios/index.ts
import { OPENAI_API_BASEURL } from '../env';
import request from './request';
const axios = request({ baseURL: OPENAI_API_BASEURL });
export default axios;
1
2
3
4
2
3
4
# 6、封装 openai 接口
创建 api.ts
./src/api.ts
import axios from './axios';
// 所有模型列表
export const models = (params: any) => {
return axios.request({
method: 'get',
url: '/v1/models',
params,
});
};
// 聊天
export const chat = (data: any) => {
return axios.request({
method: 'post',
url: '/v1/chat/completions',
data,
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 7、创建请求 code
创建 res-code.ts
./src/res-code.ts
import { Response } from 'express';
export const success = (res: Response, data: any) => {
res.json({
code: 200,
msg: '请求成功',
data,
});
};
export const error = (res: Response, msg: any) => {
res.json({
code: -1,
msg: msg || '请求失败',
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 8、创建主程序代码
创建 app.ts
./src/app.ts
import express, { NextFunction, Request, Response } from "express"; //web框架
import { SERVER_PORT } from "./env";
import { models, chat } from "./api";
import { success, error } from "./res-code";
const app = express();
const multipart = require("connect-multiparty");
//解析form-data参数
const multipartMiddleware = multipart({ uploadDir: "./temp" });
app.use(multipartMiddleware); // 解析form-data参数
app.use(express.json({ limit: "50mb" })); // 解析 application/json 参数
app.use(express.urlencoded({ limit: "50mb", extended: true })); // 解析 www-form-urlencoded 参数
app.use("/", express.static("web")); // 开放web文件夹目录,可运行 vue 打包后的静态站点
app.post("/api/v1/models", multipartMiddleware, async (req, res) => {
try {
const response = await models({});
success(res, response.data);
} catch (e: any) {
error(res, e.response.data);
}
});
app.post("/api/v1/chat/completions", multipartMiddleware, async (req, res) => {
const {
messages,
max_tokens,
frequency_penalty,
presence_penalty,
temperature,
top_p,
model,
} = req.body;
try {
const params = {
model: model ?? "gpt-3.5-turbo", // 模型
messages, // messages: [{ role: "user", content }],
temperature: temperature ?? 0.8, // 随机性,值越大回复越随机
max_tokens: max_tokens ?? 2048, // 单次请求最大 token
top_p: top_p ?? 1, // 一个可用于代替 temperature 的参数
frequency_penalty: frequency_penalty ?? 0, // 单词重复控制
presence_penalty: presence_penalty ?? 0, // 话题新鲜度,值越大,越有可能拓展到新话题
};
const response = await chat(params);
const msg = resolveData(response.data);
if (msg) {
success(res, msg);
} else {
error(res, "无数据");
}
} catch (e: any) {
error(res, e.response.data);
}
});
function resolveData(data: any) {
let msg = null;
if (data) {
const { choices } = data;
if (choices && choices.length) {
msg = choices;
}
}
return msg;
}
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(err.stack);
res.status(500).send({
code: -1,
msg: "程序运行错误",
});
});
//启动应用程序
app.listen(SERVER_PORT, function () {
console.log("app listening on " + SERVER_PORT);
});
process.on("uncaughtException", function (err) {
//打印出错误
console.error("uncaughtException:" + err);
//打印出错误的调用栈方便调试
console.error(err.stack);
});
async function testModels() {
try {
const response = await models({});
console.log(response.data);
} catch (e: any) {
console.log(e.response.data);
}
}
async function testChat() {
try {
const params = {
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "你好" }],
};
const { data } = await chat(params);
console.log(data);
} catch (e: any) {
console.log(e.response.data);
}
}
// testChat()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
← node调用c++库 node调用动态库 →