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

337 天前
 x77

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

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

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

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

1467 次点击
所在节点    程序员
15 条回复
ByteCat
337 天前
Node.js 18 开始也自带 fetch 支持了呀
a632079
337 天前
https://nodejs.org/dist/latest-v18.x/docs/api/globals.html#fetch

亦或者添加 node-fetch 模块。

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

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

然后在对应的机器上开一个 nginx ,后端代理到 api.openai.com ,location 里添加密钥头,完事。
ruoxie
337 天前
代码直接给你了
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
337 天前
这个项目没有用流模式,体验太差了
x77
336 天前
@Puteulanus 咱们自个儿
x77
336 天前
@ruoxie 感谢。
我这边只能编译 docker 镜像,把您的代码替换掉 openai.ts 文件就可以了吗?

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

https://www.v2ex.com/t/943295

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

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

© 2021 V2EX