让你反代的openai的API支持key轮询

利用Golang反代某AI的API中的反代只能用一个key,而前端项目那么多,总是改前端的代码让其支持轮询太麻烦了,干脆改造反代的代码,将轮询写到反代的环节中。

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main

import (
"log"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"sync"
)

type RProxy struct {
remote *url.URL
}

var (
mu sync.Mutex
count int
)

func get1Key(key string) string {
mu.Lock()
defer mu.Unlock()

arr := strings.Split(key, "")
randomIndex := count % len(arr)
count++
if count > 999999 {
count = 0
}
randomSubstr := arr[randomIndex]
return randomSubstr
}

func GoReverseProxy(this *RProxy) *httputil.ReverseProxy {
remote := this.remote

proxy := httputil.NewSingleHostReverseProxy(remote)

proxy.Director = func(request *http.Request) {
targetQuery := remote.RawQuery
request.URL.Scheme = remote.Scheme
request.URL.Host = remote.Host
request.Host = remote.Host // todo 这个是关键
request.URL.Path, request.URL.RawPath = joinURLPath(remote, request.URL)
keys := strings.Split(request.Header.Get("Authorization"), " ")
if len(keys) == 2 {
request.Header.Set("Authorization", "Bearer " + get1Key(keys[1]))
}
log.Println("request.Header.Authorization:", request.Header.Get("Authorization"))
if targetQuery == "" request.URL.RawQuery == "" {
request.URL.RawQuery = targetQuery + request.URL.RawQuery
} else {
request.URL.RawQuery = targetQuery + "&" + request.URL.RawQuery
}
if _, ok := request.Header["User-Agent"]; !ok {
// explicitly disable User-Agent so it's not set to default value
request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36")
}
log.Println("request.URL.Path:", request.URL.Path, "request.URL.RawQuery:", request.URL.RawQuery)
}

// 修改响应头
proxy.ModifyResponse = func(response *http.Response) error {
response.Header.Add("Access-Control-Allow-Origin", "*")
return nil
}

return proxy
}

// go sdk 源码
func joinURLPath(a, b *url.URL) (path, rawpath string) {
if a.RawPath == "" && b.RawPath == "" {
return singleJoiningSlash(a.Path, b.Path), ""
}
// Same as singleJoiningSlash, but uses EscapedPath to determine
// whether a slash should be added
apath := a.EscapedPath()
bpath := b.EscapedPath()

aslash := strings.HasSuffix(apath, "/")
bslash := strings.HasPrefix(bpath, "/")

switch {
case aslash && bslash:
return a.Path + b.Path[1:], apath + bpath[1:]
case !aslash && !bslash:
return a.Path + "/" + b.Path, apath + "/" + bpath
}
return a.Path + b.Path, apath + bpath
}

// go sdk 源码
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}

func main() {
port := "1874"
reverseUrl := "https://api.openai.com"

remote, err := url.Parse(reverseUrl)
if err != nil {
panic(err)
}

proxy := GoReverseProxy(&RProxy{
remote: remote,
})

log.Println("当前代理地址: " + reverseUrl + " 本地监听: http://127.0.0.1:" + port)

serveErr := http.ListenAndServe(":"+port, proxy)

if serveErr != nil {
panic(serveErr)
}
}
  • CC=musl-gcc /home/jovyan/go/bin/go1.20.1 build -tags musl -o openai -trimpath -ldflags ‘-linkmode “external” -extldflags “-static” -s -w -buildid=’ ./openai.go
  • docker build -t limour/openai .
  • sudo docker-compose up -d –remove-orphans
  • sudo docker image prune
  • docker-compose logs tail

让你反代的openai的API支持key轮询
https://occdn.limour.top/2664.html
Author
Limour
Posted on
March 26, 2023
Licensed under