1
0
This repository has been archived on 2024-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
chatgpt-to-api/internal/chatgpt/request.go

210 lines
5.9 KiB
Go
Raw Normal View History

2023-04-02 15:15:09 +00:00
package chatgpt
import (
2023-05-03 22:36:17 +00:00
"bufio"
2023-04-02 15:15:09 +00:00
"bytes"
"encoding/json"
2023-06-12 14:59:40 +00:00
"freechatgpt/typings"
2023-06-09 18:45:37 +00:00
chatgpt_types "freechatgpt/typings/chatgpt"
2023-06-09 19:30:45 +00:00
"io"
2023-04-20 00:22:03 +00:00
"os"
2023-05-03 22:36:17 +00:00
"strings"
2023-04-02 15:15:09 +00:00
2023-06-28 09:10:02 +00:00
arkose "github.com/acheong08/funcaptcha"
2023-04-02 15:15:09 +00:00
http "github.com/bogdanfinn/fhttp"
tls_client "github.com/bogdanfinn/tls-client"
2023-06-09 19:30:45 +00:00
"github.com/gin-gonic/gin"
chatgpt_response_converter "freechatgpt/conversion/response/chatgpt"
// chatgpt "freechatgpt/internal/chatgpt"
official_types "freechatgpt/typings/official"
2023-04-02 15:15:09 +00:00
)
var (
jar = tls_client.NewCookieJar()
options = []tls_client.HttpClientOption{
tls_client.WithTimeoutSeconds(360),
2023-08-07 03:31:03 +00:00
tls_client.WithClientProfile(tls_client.Okhttp4Android13),
2023-04-02 15:15:09 +00:00
tls_client.WithNotFollowRedirects(),
tls_client.WithCookieJar(jar), // create cookieJar instance and pass it as argument
2023-04-20 06:24:04 +00:00
// Disable SSL verification
tls_client.WithInsecureSkipVerify(),
2023-04-02 15:15:09 +00:00
}
2023-04-22 02:30:23 +00:00
client, _ = tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
2023-04-20 00:22:03 +00:00
API_REVERSE_PROXY = os.Getenv("API_REVERSE_PROXY")
2023-04-02 15:15:09 +00:00
)
2023-05-03 22:36:17 +00:00
func init() {
2023-06-28 09:10:02 +00:00
arkose.SetTLSClient(&client)
2023-05-03 22:36:17 +00:00
}
func POSTconversation(message chatgpt_types.ChatGPTRequest, access_token string, puid string, proxy string) (*http.Response, error) {
2023-05-18 03:42:54 +00:00
if proxy != "" {
client.SetProxy(proxy)
2023-05-03 22:36:17 +00:00
}
2023-05-01 08:41:48 +00:00
apiUrl := "https://chat.openai.com/backend-api/conversation"
2023-04-20 00:22:03 +00:00
if API_REVERSE_PROXY != "" {
apiUrl = API_REVERSE_PROXY
}
2023-04-02 15:15:09 +00:00
// JSONify the body and add it to the request
2023-04-02 16:37:16 +00:00
body_json, err := json.Marshal(message)
2023-04-02 15:15:09 +00:00
if err != nil {
2023-04-02 16:37:16 +00:00
return &http.Response{}, err
2023-04-02 15:15:09 +00:00
}
request, err := http.NewRequest(http.MethodPost, apiUrl, bytes.NewBuffer(body_json))
2023-04-02 16:37:16 +00:00
if err != nil {
return &http.Response{}, err
}
2023-05-10 06:03:06 +00:00
// Clear cookies
if puid != "" {
request.Header.Set("Cookie", "_puid="+puid+";")
2023-05-24 15:23:03 +00:00
}
2023-04-02 15:15:09 +00:00
request.Header.Set("Content-Type", "application/json")
2023-05-01 08:42:09 +00:00
request.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36")
2023-04-02 16:37:16 +00:00
request.Header.Set("Accept", "*/*")
if access_token != "" {
request.Header.Set("Authorization", "Bearer "+access_token)
}
2023-04-02 15:15:09 +00:00
if err != nil {
return &http.Response{}, err
}
response, err := client.Do(request)
return response, err
}
2023-06-09 19:30:45 +00:00
2023-06-09 19:55:39 +00:00
// Returns whether an error was handled
2023-06-09 19:57:36 +00:00
func Handle_request_error(c *gin.Context, response *http.Response) bool {
2023-06-09 19:30:45 +00:00
if response.StatusCode != 200 {
// Try read response body as JSON
var error_response map[string]interface{}
2023-06-09 19:55:39 +00:00
err := json.NewDecoder(response.Body).Decode(&error_response)
2023-06-09 19:30:45 +00:00
if err != nil {
// Read response body
body, _ := io.ReadAll(response.Body)
c.JSON(500, gin.H{"error": gin.H{
"message": "Unknown error",
"type": "internal_server_error",
"param": nil,
"code": "500",
"details": string(body),
}})
2023-06-09 19:55:39 +00:00
return true
2023-06-09 19:30:45 +00:00
}
c.JSON(response.StatusCode, gin.H{"error": gin.H{
"message": error_response["detail"],
"type": response.Status,
"param": nil,
"code": "error",
}})
2023-06-09 19:55:39 +00:00
return true
}
return false
}
2023-06-09 20:10:55 +00:00
type ContinueInfo struct {
ConversationID string `json:"conversation_id"`
ParentID string `json:"parent_id"`
}
func Handler(c *gin.Context, response *http.Response, token string, translated_request chatgpt_types.ChatGPTRequest, stream bool) (string, *ContinueInfo) {
2023-06-09 19:55:39 +00:00
max_tokens := false
2023-06-09 19:30:45 +00:00
// Create a bufio.Reader from the response body
reader := bufio.NewReader(response.Body)
// Read the response byte by byte until a newline character is encountered
if stream {
// Response content type is text/event-stream
c.Header("Content-Type", "text/event-stream")
} else {
// Response content type is application/json
c.Header("Content-Type", "application/json")
}
2023-06-09 19:49:13 +00:00
var finish_reason string
2023-06-12 14:59:40 +00:00
var previous_text typings.StringStruct
2023-06-09 20:10:55 +00:00
var original_response chatgpt_types.ChatGPTResponse
2023-06-20 07:24:37 +00:00
var isRole = true
2023-06-09 19:30:45 +00:00
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
2023-06-09 20:10:55 +00:00
return "", nil
2023-06-09 19:30:45 +00:00
}
if len(line) < 6 {
continue
}
// Remove "data: " from the beginning of the line
line = line[6:]
// Check if line starts with [DONE]
if !strings.HasPrefix(line, "[DONE]") {
// Parse the line as JSON
2023-06-09 20:10:55 +00:00
2023-06-09 19:30:45 +00:00
err = json.Unmarshal([]byte(line), &original_response)
if err != nil {
continue
}
if original_response.Error != nil {
2023-06-09 20:26:54 +00:00
c.JSON(500, gin.H{"error": original_response.Error})
2023-06-09 20:10:55 +00:00
return "", nil
2023-06-09 19:30:45 +00:00
}
2023-06-09 20:26:54 +00:00
if original_response.Message.Author.Role != "assistant" || original_response.Message.Content.Parts == nil {
continue
}
2023-06-20 07:24:37 +00:00
if original_response.Message.Metadata.MessageType != "next" && original_response.Message.Metadata.MessageType != "continue" || original_response.Message.EndTurn != nil {
2023-06-09 19:30:45 +00:00
continue
}
2023-06-20 07:24:37 +00:00
response_string := chatgpt_response_converter.ConvertToString(&original_response, &previous_text, isRole)
isRole = false
2023-06-09 19:30:45 +00:00
if stream {
2023-06-09 19:45:03 +00:00
_, err = c.Writer.WriteString(response_string)
2023-06-09 19:30:45 +00:00
if err != nil {
2023-06-09 20:10:55 +00:00
return "", nil
2023-06-09 19:30:45 +00:00
}
}
// Flush the response writer buffer to ensure that the client receives each line as it's written
c.Writer.Flush()
2023-06-09 19:49:13 +00:00
if original_response.Message.Metadata.FinishDetails != nil {
if original_response.Message.Metadata.FinishDetails.Type == "max_tokens" {
max_tokens = true
}
finish_reason = original_response.Message.Metadata.FinishDetails.Type
}
2023-06-09 19:30:45 +00:00
} else {
if stream {
2023-06-09 19:49:13 +00:00
final_line := official_types.StopChunk(finish_reason)
2023-06-09 19:30:45 +00:00
c.Writer.WriteString("data: " + final_line.String() + "\n\n")
}
}
}
2023-06-09 20:10:55 +00:00
if !max_tokens {
2023-06-12 14:59:40 +00:00
return previous_text.Text, nil
2023-06-09 20:10:55 +00:00
}
2023-06-12 14:59:40 +00:00
return previous_text.Text, &ContinueInfo{
2023-06-09 20:10:55 +00:00
ConversationID: original_response.ConversationID,
ParentID: original_response.Message.ID,
}
2023-06-09 19:30:45 +00:00
}
2023-06-16 14:49:08 +00:00
func GETengines() (interface{}, int, error) {
2023-06-16 15:45:27 +00:00
url := "https://api.openai.com/v1/models"
2023-06-16 14:49:08 +00:00
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer "+os.Getenv("OFFICIAL_API_KEY"))
resp, err := client.Do(req)
if err != nil {
return nil, 0, err
}
defer resp.Body.Close()
var result interface{}
json.NewDecoder(resp.Body).Decode(&result)
return result, resp.StatusCode, nil
}