GPT旧版API转换器

目的

GPT API很好,但是很贵。网上有一些免费的GPT API服务,个人认为最好用的需要是完全兼容官方API调用方式的,目前本人找到了这些:

(2023.10.28更新,此白嫖方式已经被OpenAI封堵)

这两个服务的异同如下:

  • 两者实现方式类似(我猜的)
  • 两者都需要能正常使用ChatGPT的OpenAI账户,于是你也就知道了这两个服务的原理
  • 两者都受制于OpenAI,一个账号只能同时进行一个completion
  • 两者都贴心地提供了账户池的功能,于是API的容量取决于你的账户量
    • 第一个需要走第三方服务,不是那么安全,但是不需要bypass Cloudflare
    • 第二个是纯本地的,比较安全,但是需要一个开通Plus的账户来bypass Cloudflare(个人猜测Pandora也是这样实现的)

兼容官方调用方式是为了让我们能通过替换已有ChatGPT-based软件的API Endpoint实现免费使用(至少我是)。

问题

上面提到的这两个服务除了前面提到的共同点之外,还有一个共同点:只支持新的/v1/chat/completionsAPI不支持旧的/v1/completionsAPI然而有些软件就是用了旧版(比如Auto-GPT)。

当你的软件运行到调用/v1/completions的部分时,服务器会告诉你你没钱了,这不是我们所期望的。

解决方法

反正新版API已经支持了,自己写一个代理服务器,把对旧版request转到新版,再把response转成旧版即可。

一开始我用.NET实现了一个,但是因为我不熟悉ASP.NET,所以全都是用HttpListener做的,做完之后发现了一些很蛋疼的问题,最后选择用Node.js重写。

由于本人完全没有Node.js经验,所以一些功能的写法咨询了ChatGPT,全程一共产生了100KB左右的聊天记录。

实现代理服务器

由于本人完全没有Node.js经验,所以一开始用了一个网上别人做的简易代理服务器做底,这里放上原作者链接:手动实现nodejs代理服务器 (qq.com)

实现请求头转发

这里有个坑,Node.js的http会把这些头都变成小写,但是OpenAI的服务器不认小写,所以我们没法直接copy。不过好在头里面有用的东西比较少,我们选择官方文档中包含的手动添加即可。

1
2
3
4
5
6
7
8
9
10
11
12
...
let options = {
"method": req.method,
"hostname": parsedUrl.hostname,
"port": parsedUrl.port == "" ? (parsedUrl.protocol == "http:" ? "80" : "443") : parsedUrl.port,
"path": req.url,
"headers": {
"Authorization": req.headers.authorization || "", // fuck you lower case
"Content-Type": req.headers["content-type"] || ""
}
}
...

实现对旧版API的检查和转换

这部分逻辑比较简单,检测的方式就是看Url,转换就是按照OpenAI官方文档给出的json拼接起来。

1
2
3
4
5
6
7
8
9
10
11
...
let postbodyBuffer = Buffer.concat(postbody);
// convert
if (req.url == "/v1/completions") {
let postbodyJson = JSON.parse(postbodyBuffer.toString());
postbodyJson = convertPostbody(postbodyJson);
postbodyBuffer = Buffer.from(JSON.stringify(postbodyJson));
options.path = "/v1/chat/completions";
writeLog("Converted the request.");
}
...
1
2
3
4
5
6
7
8
9
10
11
12
function convertPostbody(postbodyJson) {
let newPostbodyJson = {
"model": "gpt-3.5-turbo"/*postbodyJson.model*/,
"messages": [
{
"role": "user",
"content": postbodyJson.prompt
}
]
};
return newPostbodyJson;
}

同样,收到response后也要把新版的response转为旧版。

1
2
3
4
5
6
7
let responsebodyBuffer = Buffer.concat(responsebody);
// convert
if (req.url == "/v1/completions") {
let responsebodyJson = convertResponsebody(responsebodyBuffer);
responsebodyBuffer = Buffer.from(JSON.stringify(responsebodyJson));
writeLog(`Converted the response.`);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function convertResponsebody(responsebodyBuffer) {
let responsebodyJson = JSON.parse(responsebodyBuffer.toString());
let newResponsebodyJson = {
"id": responsebodyJson.id,
"object": "text_completion",
"created": responsebodyJson.created,
"model": responsebodyJson.model,
"choices": [
{
"text": responsebodyJson.choices[0].message.content,
"index": 0,
"logprobs": null,
"finish_reason": responsebodyJson.choices[0].finish_reason
}
],
"usage": {
"prompt_tokens": responsebodyJson.usage.prompt_tokens,
"completion_tokens": responsebodyJson.usage.completion_tokens,
"total_tokens": responsebodyJson.usage.total_tokens
}
};
return newResponsebodyJson;
}

实现回复头转发

其实我写完上面两部分后就满怀自信开Auto-GPT去了,结果一开始get models的时候就炸了。调试后我发现OpenAI官方的Python库中包含对回复头的检查,而我实现的代理会把这部分东西丢掉,导致官方库的一个变量成了null,然后崩溃。

1
2
3
...
res.writeHead(response.statusCode, response.headers);
...

杂项

包含异常捕获、日志、命令行参数、http/https切换等等,这里不再赘述。

待续

其实OpenAI官方的库还不能用http的服务器,但是由于我没有Node.js的经验,所以干脆用了Nginx做反代。因为我是在Linux服务器上运行代理服务器,个人感觉这样实现比较方便。如果你有自带https的需求,欢迎pr。

最终结果

最后服务器确实写好了,能正常运行:ParaN3xus/gptApiConverter: A proxy server to convert /completions API to /chat/completions API. (github.com)

但是Auto-GPT不能。(本来最开始就是想免费玩Auto-GPT的…)原因是Auto-GPT中还用到了embedding,这是免费API 无论如何也无法实现的…

不过其他的软件,比如翻译器什么的,应该不会用到这样的API,本人还没有试过,不过应该问题不大。

总结

虽然最开始的目的没有达到,但是至少做出来个能满足预期需求的服务器,也算是填补了一个空缺,同时还水了一篇没有技术力的Blog,还算是有收获。

留言评论

0条搜索结果。