邀请懂 TypeScript 的程序员帮忙改代码

2023-05-26 20:20:32 +08:00
 x77

一个 ChatGPT 的 Web UI 项目,搞得还不错支持语音,我小试了一下 Azure 的语音合成,效果真不错。不过这项目设计是在客户端(浏览器)向 OpenAI 发起请求,有没有人帮忙改成由服务端发起请求,类似这个项目。具体的要修改的代码可能在这里: https://github.com/hahahumble/speechgpt/blob/main/src/apis/openai.ts

我知道代码里有个代理设置,在云端开个 Function 服务器就可以转发,但还是有些麻烦,另外还得传输 APIKey 不是很安全。

我不懂 Typescript ,虽然语言都是相通的,能看懂能小改 Typescript ,但是要用不懂的语言来表达还是有些吃力,也容易搞得低效、不合理。也有人给这项目提类似需求的 Issue (希望在服务端设置 API ),但是开发方更新有点慢。

那就自个搞一下把,希望有兴趣的开发者帮忙改下,感谢。

1661 次点击
所在节点    程序员
15 条回复
ByteCat
2023-05-26 20:27:51 +08:00
Node.js 18 开始也自带 fetch 支持了呀
a632079
2023-05-26 20:40:07 +08:00
https://nodejs.org/dist/latest-v18.x/docs/api/globals.html#fetch

亦或者添加 node-fetch 模块。

P.S 这和 TS 无关,纯粹模块问题……
leokun
2023-05-26 20:48:23 +08:00
这个如果改成 next 的还是有一点工作量的
最简单最快的方法就是 njs 写一个转发,打成容器就好了
x77
2023-05-26 20:48:23 +08:00
@ByteCat
@a632079
我 nodejs 开发环境都没折腾啊
Aloento
2023-05-26 20:52:35 +08:00
@x77 #4 他们应该听不懂你在说什么
CLMan
2023-05-26 20:52:49 +08:00
这软件架构就是这样,服务端提供的只是一个 UI ,由本地向 OpenAI API 地址发起请求的。

你要改成服务端请求,最简单的办法是搭建一个 api 代理,将其密钥放在代理的服务端,UI 层只需要根据你的需求进行小改。
Puteulanus
2023-05-26 20:53:35 +08:00
你这能算自个搞一下吗哥 😂
leokun
2023-05-26 20:57:11 +08:00
a632079
2023-05-26 21:18:46 +08:00
@Aloento 他说服务端,也没提什么框架,什么 Runtime 。只提了一嘴小改 TS ,那不就默认 node 了嘛😨
zbinlin
2023-05-26 21:28:40 +08:00
这个项目不就是一个纯前端项目吗,如果你要改成从服务端发起请求,不就需要添加服务端 API 了(或者用 nginx 的 njs 来添加一个转发接口?)
AS4694lAS4808
2023-05-26 21:48:04 +08:00
代码只需要加一行,把 base_url 改成你机器的。

然后在对应的机器上开一个 nginx ,后端代理到 api.openai.com ,location 里添加密钥头,完事。
ruoxie
2023-05-26 21:52:27 +08:00
代码直接给你了
import * as https from 'https';
import { TextDecoder } from 'util';

export const createChatCompletion = (options: {
host: string;
apiKey: string;
model: string;
text: string;
context?: string;
maxTokens: number;
handleChunk?: (data: { text?: string; hasMore: boolean }) => void;
}) =>
new Promise<string>((resolve, reject) => {
let combinedResult = '';
const request = https.request(
{
hostname: options.host,
port: 443,
path: '/v1/chat/completions',
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${options.apiKey}`,
},
},
(res) => {
res.on('data', async (chunk) => {
const text = new TextDecoder('utf-8').decode(chunk);
const data = text.split('\n\n').filter((s) => s);
for (let i = 0; i < data.length; i++) {
try {
let element = data[i];
if (element.includes('data: ')) {
if (element.trim() === 'data:') {
// 处理只返回了 data: 的情况
return;
}
} else if (element.includes('delta')) {
// 处理没有 data 开头
element = `data: ${element}`;
}
if (element.includes('data: ')) {
if (element.includes('[DONE]')) {
options.handleChunk &&
options.handleChunk({ hasMore: false, text: '' });
return;
}
// remove 'data: '
const data = JSON.parse(element.replace('data: ', ''));
if (data.finish_reason === 'stop') {
options.handleChunk &&
options.handleChunk({ hasMore: false, text: '' });
return;
}
const openaiRes = data.choices[0].delta.content;
if (openaiRes) {
options.handleChunk &&
options.handleChunk({
text: openaiRes.replaceAll('\\n', '\n'),
hasMore: true,
});
combinedResult += openaiRes;
}
} else {
options.handleChunk &&
options.handleChunk({ hasMore: false, text: element });
return;
}
} catch (e) {
console.error({
e,
element: data[i],
});
}
}
});
res.on('error', (e) => {
options.handleChunk &&
options.handleChunk({ hasMore: false, text: e.toString() });
reject(e);
});
res.on('end', () => {
resolve(combinedResult);
});
},
);
const body = {
model: options.model,
messages: [
{
role: 'system',
content: options.context || '',
},
{
role: 'user',
content: options.text,
},
],
stream: true,
max_tokens: options.maxTokens,
};
request.write(JSON.stringify(body));
request.end();
});
ruoxie
2023-05-26 21:58:04 +08:00
这个项目没有用流模式,体验太差了
x77
2023-05-27 19:19:06 +08:00
@Puteulanus 咱们自个儿
x77
2023-05-27 19:24:32 +08:00
@ruoxie 感谢。
我这边只能编译 docker 镜像,把您的代码替换掉 openai.ts 文件就可以了吗?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/943295

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX