-
Notifications
You must be signed in to change notification settings - Fork 0
/
translate.go
147 lines (135 loc) · 3.72 KB
/
translate.go
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package main
import (
"bytes"
"compress/gzip"
"encoding/json"
"github.com/andybalholm/brotli"
"github.com/gin-gonic/gin"
"io"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
)
func main() {
log.SetPrefix("[translate-debug] ")
r := gin.Default()
r.Use(CORS)
r.POST("/translate", OpenAiProxy(handleOpenAiResponse))
r.POST("/translate/openai", OpenAiProxy(identityResponseHandler))
r.GET("/translate/ping", func(c *gin.Context) {
c.String(http.StatusOK, "I am up!")
})
port := String(os.Getenv("HTTP_PORT")).orElse("8080").get()
err := r.Run("0.0.0.0:" + port)
if err != nil {
log.Fatal("server start failed with error", err)
}
}
func CORS(c *gin.Context) {
origin := os.Getenv("ALLOW_ORIGINS")
debug("ALLOW_ORIGINS", origin)
originalHeader := c.GetHeader("Origin")
if origin == "" {
originalHeader = "*"
} else if !String(origin).contains(",", originalHeader) {
debug("Request origin", originalHeader)
c.AbortWithStatus(http.StatusForbidden)
return
}
c.Header("Access-Control-Allow-Origin", originalHeader)
c.Header("Access-Control-Allow-Credentials", "true")
c.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Header("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
type ResponseCallBack func(*http.Response) error
func handleOpenAiResponse(c *gin.Context) ResponseCallBack {
return func(response *http.Response) error {
if response.StatusCode == 200 {
resp, err := parseOpenAiResponse(response)
ans := Answer{
Reply: resp.Choices[0].Message.Content,
Reason: resp.Choices[0].FinishReason,
}
c.JSON(http.StatusOK, ans)
return err
} else {
debug("Response:", response.Status)
dump, _ := httputil.DumpResponse(response, true)
debug(string(dump))
return nil
}
}
}
func identityResponseHandler(_ *gin.Context) ResponseCallBack {
return func(resp *http.Response) error {
return nil
}
}
func parseOpenAiResponse(resp *http.Response) (OpenAiResponse, error) {
var reader io.Reader
switch resp.Header.Get("Content-Encoding") {
case "br":
brReader := brotli.NewReader(resp.Body)
reader = brReader
case "gzip":
reader, _ = gzip.NewReader(resp.Body)
default:
// Handle other encodings or no encoding here, if needed.
reader = resp.Body
}
_bytes, _ := io.ReadAll(reader)
var ret OpenAiResponse
err := json.Unmarshal(_bytes, &ret)
return ret, err
}
// OpenAiProxy callback defines what to do with the proxy http.Response
func OpenAiProxy(callback func(c *gin.Context) ResponseCallBack) gin.HandlerFunc {
return func(c *gin.Context) {
openapi := "https://api.openai.com/v1/chat/completions"
director := func(req *http.Request) {
_url, err := url.Parse(openapi)
if err != nil {
panic(err)
}
req.URL = _url
req.Host = ""
req.Header.Set("Authorization", "Bearer "+OpenaiKey)
req.Header.Set("Content-Type", "application/json")
var ask Ask
err = c.ShouldBindJSON(&ask)
if err != nil {
panic(err)
}
ask.setDefault()
body := OpenAiRequest{
Model: ask.Model,
Temperature: OpenaiTemperature,
}
msg := Message{
Role: "user",
Content: ask.Content,
}
body.Messages = []Message{systemMsg(ask.Lang), msg}
var length int
req.Body, length = newRequestBody(body)
req.ContentLength = int64(length)
}
proxy := &httputil.ReverseProxy{
Director: director,
ModifyResponse: callback(c),
}
proxy.ServeHTTP(c.Writer, c.Request)
}
}
type Body io.ReadCloser
func newRequestBody(body any) (Body, int) {
_bytes, _ := json.Marshal(body)
return io.NopCloser(bytes.NewBuffer(_bytes)), len(_bytes)
}