API 代理转发 | ChatGPT 应用实战

API 代理转发 | ChatGPT 应用实战_第1张图片

由于 OpenAI 的 API 会限制访问 IP,除了在本地访问时开启代理外,还可以使用代理链接的方式。

教程需要一台国外服务器,个人主要用 Azure,内容一览:

  • 将用户所有请求转发给 OpenAI,不做任何处理

  • 将用户请求发送给 OpenAI,在这过程中记录请求日志(包括请求密钥和请求内容)

  • 越过 ChatGPT 的 Cloudflare 限制

注:本篇配合 openai_api_call 工具使用更佳。

直接转发请求

参考知乎:Nginx 反向代理 OpenAI API

设置 http 转发

配置 Nginx 信息如下,vim /etc/nginx/sites-enabled/default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
    listen 80;
    server_name {your_domain_name};
    location / {
        proxy_pass  https://api.openai.com/;
        proxy_ssl_server_name on;
        proxy_set_header Host api.openai.com;
        proxy_set_header Connection '';
        proxy_http_version 1.1;
        chunked_transfer_encoding off;
        proxy_buffering off;
        proxy_cache off;
        ; proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

将第一处的 {your_domain_name} 替换为你的域名,保存退出。

参数说明:

  • proxy_pass:转发的上游服务器地址

  • proxy_ssl_server_name on 和 SSL/TLS 握手相关(待了解)

  • proxy_set_header Host,默认将上游地址作为 Host,这里手动设置为 api.openai.com

  • proxy_set_header X-Forwarded-For,默认将客户端 IP 作为 X-Forwarded-For,这里注释掉,避免暴露实际 IP

  • proxy_set_header X-Forwarded-Proto,默认将客户端协议作为 X-Forwarded-Proto

设置 ssl 证书

申请 ssl 证书后,将其放在 your_cert_path 和 your_cert_key_path 处,并修改 Nginx 配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
    listen 443 ssl;
    server_name {your_domain_name};
    ssl_certificate {your_cert_path};
    ssl_certificate_key {your_cert_key_path};
    ssl_session_cache shared:le_nginx_SSL:1m;
    ssl_session_timeout 1440m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
    location / {
        proxy_pass  https://api.openai.com/;
        proxy_ssl_server_name on;
        proxy_set_header Host api.openai.com;
        proxy_set_header Connection '';
        proxy_http_version 1.1;
        chunked_transfer_encoding off;
        proxy_buffering off;
        proxy_cache off;
        # proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

监测数据并转发

比起直接用 nginx 转发,我们通过 Python 脚本转发请求,这样可以监测和处理交互的数据。

  • 使用 Python 的 flask 包编写脚本文件,将请求转发到 OpenAI

  • 使用 Nginx 配置域名

flask 转发请求

编写 main.py 文件,下载链接:main.py。

下边是对文件内容的解释。

加载头部

导入必要的 Python 包

1
2
3
4
5
#!/usr/bin/env python
from flask import Flask, request, Response
import requests, random

app = Flask(__name__)

设置 API 请求日志的保存路径

1
2
3
4
5
6
7
8
9
# file for saving API requests
home = os.path.expanduser("~")
# api path
api_path = os.path.join(home, '.chatlog/apilist.log')
# request log
logfile = 'chatdata-' + datetime.datetime.now().strftime("%Y-%m-%d") + '.log'
log_path = os.path.join(home, '.chatlog', logfile)
os.makedirs(os.path.dirname(log_path), exist_ok=True)
os.makedirs(os.path.dirname(api_path), exist_ok=True)
替换密钥

在 API_LIST 中填写实际的 API 密钥,当请求密钥为 fake_keys 中的值时,会随机返回 API_LIST 中的一个密钥。

1
2
3
4
# real API keys
API_LIST = [ 'sk-real-1', 'sk-real-2', 'sk-real-3']
# fake API keys
fake_keys = [ 'sk-fake-1', 'sk-fake-2', 'sk-fake-3']
转发 chat 请求

将请求转发到 OpenAI,将返回结果保存到日志文件中。

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
# wrap chat requests
@app.route('/v1/chat/completions', methods=['POST'])
def forward_request():
    try:
        # get request headers, body and query parameters
        api_key = request.headers.get('Authorization').strip("Bearer ").strip()
        url = 'https://api.openai.com/v1/chat/completions'
        data = request.get_json()
        params = request.args
        # If the API key starts with fake-, randomly select one key
        if api_key in fake_keys:
            api_key = random.choice(API_LIST)

        # Forward the data to the target API and get the response
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {api_key}"
        }
        response = requests.post(url, json=data, params=params, headers=headers)

        # Save API request log
        if not api_key in API_LIST:
            with open(api_path, 'a') as f:
                f.write(f'{api_key}\n\n')
        
        # Save request data
        with open(log_path, 'a', encoding="utf-8") as f:
            f.write(f'#Request\n{data}\n#Request\n')
            f.write(f'#Response\n{response.content}\n#Response\n\n')

        # return the response
        return Response(response.content, status=response.status_code)
    except Exception as e: # hide the error message
        return Response(f"invalid request!\n", status=400)
转发其他请求

这部分用于获取 API 的使用信息。

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
# wrap dashboard requests
@app.route('/', methods=['GET'])
def handle_all(path):
    try:
        api_key = request.headers.get('Authorization').strip("Bearer ").strip()
        if api_key in fake_keys:
            api_key = random.choice(API_LIST)
        headers = {
                "Content-Type": "application/json",
                "Authorization": f"Bearer {api_key}"}
        # get recent usage
        if path == 'v1/dashboard/billing/usage':
            start_date = request.args.get('start_date')
            end_date = request.args.get('end_date')
            url = f'https://api.openai.com/v1/dashboard/billing/usage?start_date={start_date}&end_date={end_date}'
            response = requests.get(url, headers=headers)
            return Response(response.content, status=response.status_code)
        # get subscription info
        elif path == 'v1/dashboard/billing/subscription':
            url = 'https://api.openai.com/v1/dashboard/billing/subscription'
            response = requests.get(url, headers=headers)
            return Response(response.content, status=response.status_code)
        else:
            return Response(f"Unsupported url!\n", status=400)
    except Exception as e:
        return Response(f"invalid request!\n", status=400)
启动服务

最后,加上启动服务的代码:

1
2
if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=False, port=5000)

执行 python main.py 启动服务。

修改 Nginx 配置文件

这一步需要域名,根据监听的端口号编写 nginx 配置文件,当访问该域名时,自动转发到相应的端口。

在 /etc/nginx/sites-available/default 文件中,添加如下内容:

1
2
3
4
5
6
7
8
9
10
server {
    listen 80;
    server_name <域名地址>;

    location / {
        proxy_pass http://localhost:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

设置 ssl 证书

  1. 在域名所在服务商申请证书后,下载 nginx 证书文件,如 example.pem 和 example.key

  2. 将证书文件上传到服务器,比如到 /etc/nginx/cert 目录下

  3. 修改 nginx 配置文件,追加下边内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    server {
        listen 443 ssl;
        server_name <域名地址>;
    
        ssl_certificate /etc/nginx/cert/example.pem;
        ssl_certificate_key /etc/nginx/cert/example.key;
    
        location / {
            proxy_pass http://localhost:5000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    

    注意服务器需要开启 443 入站端口。

越过 Cloudflare 限制

借助 go-chatgpt-api 项目,只需要编写 docker-compose 文件:

1
2
3
4
5
6
7
8
9
10
version: "3"
services:
  go-chatgpt-api:
    container_name: go-chatgpt-api
    image: linweiyuan/go-chatgpt-api:latest # 最新
    ports:
      - 8080:8080 # 宿主机8080端口可按需改为其它端口
    environment:
      - GIN_MODE=release
    restart: unless-stopped

然后编写 nginx 配置文件,设置反向代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
   listen 80;
   server_name <域名地址>;
   location / {
       proxy_pass  http://127.0.0.1:8080;
       proxy_ssl_server_name on;
       proxy_set_header Host api.openai.com;
       proxy_set_header Connection '';
       proxy_http_version 1.1;
       chunked_transfer_encoding off;
       proxy_buffering off;
       proxy_cache off;
       proxy_set_header X-Forwarded-For $remote_addr;
       proxy_set_header X-Forwarded-Proto $scheme;
   }
}

443 端口类似配置。为避免代理被他人使用,这里也可以修改参数,比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
   listen 80;
   server_name <域名地址>;
   location /password {
       proxy_pass  http://127.0.0.1:8080;
       proxy_ssl_server_name on;
       proxy_set_header Host api.openai.com;
       proxy_set_header Connection '';
       proxy_http_version 1.1;
       chunked_transfer_encoding off;
       proxy_buffering off;
       proxy_cache off;
       proxy_set_header X-Forwarded-For $remote_addr;
       proxy_set_header X-Forwarded-Proto $scheme;
   }
}

另外,该项目不仅支持 API 代理,也支持 Access Token 代理,接口示例参看:

https://github.com/linweiyuan/go-chatgpt-api/blob/main/example/vscode

你可能感兴趣的:(chatgpt)