Spaces:
Runtime error
Runtime error
| package api | |
| //goland:noinspection GoSnakeCaseUsage | |
| import ( | |
| "bufio" | |
| "os" | |
| "strings" | |
| "encoding/json" | |
| //"fmt" | |
| "github.com/gin-gonic/gin" | |
| _ "github.com/linweiyuan/go-chatgpt-api/env" | |
| http "github.com/bogdanfinn/fhttp" | |
| tls_client "github.com/bogdanfinn/tls-client" | |
| ) | |
| const ( | |
| defaultErrorMessageKey = "errorMessage" | |
| AuthorizationHeader = "Authorization" | |
| ContentType = "application/x-www-form-urlencoded" | |
| UserAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" | |
| Auth0Url = "https://auth0.openai.com" | |
| LoginUsernameUrl = Auth0Url + "/u/login/identifier?state=" | |
| LoginPasswordUrl = Auth0Url + "/u/login/password?state=" | |
| ParseUserInfoErrorMessage = "Failed to parse user login info." | |
| GetAuthorizedUrlErrorMessage = "Failed to get authorized url." | |
| GetStateErrorMessage = "Failed to get state." | |
| EmailInvalidErrorMessage = "Email is not valid." | |
| EmailOrPasswordInvalidErrorMessage = "Email or password is not correct." | |
| GetAccessTokenErrorMessage = "Failed to get access token." | |
| defaultTimeoutSeconds = 300 // 5 minutes | |
| ) | |
| var Client tls_client.HttpClient | |
| type LoginInfo struct { | |
| Username string `json:"username"` | |
| Password string `json:"password"` | |
| } | |
| type AuthLogin interface { | |
| GetAuthorizedUrl(csrfToken string) (string, int, error) | |
| GetState(authorizedUrl string) (string, int, error) | |
| CheckUsername(state string, username string) (int, error) | |
| CheckPassword(state string, username string, password string) (string, int, error) | |
| GetAccessToken(code string) (string, int, error) | |
| } | |
| //goland:noinspection GoUnhandledErrorResult | |
| func init() { | |
| Client, _ = tls_client.NewHttpClient(tls_client.NewNoopLogger(), []tls_client.HttpClientOption{ | |
| tls_client.WithCookieJar(tls_client.NewCookieJar()), | |
| tls_client.WithTimeoutSeconds(defaultTimeoutSeconds), | |
| tls_client.WithClientProfile(tls_client.Okhttp4Android13), | |
| }...) | |
| } | |
| func ReturnMessage(msg string) gin.H { | |
| return gin.H{ | |
| defaultErrorMessageKey: msg, | |
| } | |
| } | |
| func GetAccessToken(accessToken string) string { | |
| if !strings.HasPrefix(accessToken, "Bearer") { | |
| return "Bearer " + accessToken | |
| } | |
| return accessToken | |
| } | |
| //goland:noinspection GoUnhandledErrorResult | |
| func HandleConversationResponse(c *gin.Context, resp *http.Response) (bool, string, string) { | |
| Status := false | |
| ParentMessageID := "" | |
| oldpart := c.GetString("oldpart") | |
| part := "" | |
| if len(oldpart) == 0 { | |
| c.Writer.Header().Set("Content-Type", "text/event-stream; charset=utf-8") | |
| } | |
| reader := bufio.NewReader(resp.Body) | |
| for { | |
| if c.Request.Context().Err() != nil { | |
| // c.Writer.Write([]byte("data: [DONE]" + "\n\n")) | |
| // c.Writer.Flush() | |
| break | |
| } | |
| line, err := reader.ReadString('\n') | |
| if err != nil { | |
| break | |
| } | |
| line = strings.TrimSpace(line) | |
| if strings.HasPrefix(line, "event") || | |
| strings.HasPrefix(line, "data: 20") || | |
| line == "" { | |
| continue | |
| } | |
| // if len(oldpart) > 0 { | |
| // logger.Info(fmt.Sprintf("HandleConversationResponseContinue: %s", line)) | |
| // } | |
| if Status { | |
| continue | |
| } | |
| if strings.HasPrefix(line, "[DONE]") { | |
| } else { | |
| if len(oldpart) > 0 { | |
| var result map[string]interface{} | |
| err := json.Unmarshal([]byte(line[6:]), &result) | |
| if err == nil { | |
| message := result["message"].(map[string]interface{}) | |
| content := message["content"].(map[string]interface{}) | |
| parts := content["parts"].([]interface{}) | |
| part = parts[0].(string) | |
| parts[0] = oldpart + part | |
| // message["id"] = c.GetString("msg_id") | |
| // metadata := message["metadata"].(map[string]interface{}) | |
| // metadata["message_type"] = "next" | |
| resultJSON, err2 := json.Marshal(result) | |
| if err2 == nil { | |
| line = "data: " + string(resultJSON) | |
| // logger.Info(fmt.Sprintf("HandleConversationResponseAddon: %s", line)) | |
| } | |
| } | |
| } else { | |
| } | |
| } | |
| c.Writer.Write([]byte(line + "\n\n")) | |
| c.Writer.Flush() | |
| if strings.HasPrefix(line, "[DONE]") { | |
| continue | |
| } | |
| data := line[6:] | |
| var result map[string]interface{} | |
| err2 := json.Unmarshal([]byte(data), &result) | |
| if err2 != nil { | |
| continue | |
| } | |
| //fmt.Printf("result: %v\n", result) | |
| if message, ok := result["message"].(map[string]interface{}); ok { | |
| status := message["status"].(string) | |
| if status == "finished_successfully" { | |
| if message["metadata"] != nil { | |
| metadata := message["metadata"].(map[string]interface{}) | |
| if metadata["finish_details"] != nil { | |
| finishDetails := metadata["finish_details"].(map[string]interface{}) | |
| finishType := finishDetails["type"].(string) | |
| if finishType == "max_tokens" { | |
| // logger.Info(fmt.Sprintf("finish_details中type的值: %s", finishType)) | |
| // logger.Info(fmt.Sprintf("HandleConversationResponse: %s", line)) | |
| content := message["content"].(map[string]interface{}) | |
| parts := content["parts"].([]interface{}) | |
| part = parts[0].(string) | |
| Status = true | |
| ParentMessageID = message["id"].(string) | |
| if len(oldpart) == 0 { | |
| c.Set("msg_id", ParentMessageID) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return Status, ParentMessageID, part | |
| } | |
| //goland:noinspection GoUnhandledErrorResult,SpellCheckingInspection | |
| func NewHttpClient() tls_client.HttpClient { | |
| client, _ := tls_client.NewHttpClient(tls_client.NewNoopLogger(), []tls_client.HttpClientOption{ | |
| tls_client.WithCookieJar(tls_client.NewCookieJar()), | |
| tls_client.WithClientProfile(tls_client.Okhttp4Android13), | |
| }...) | |
| proxyUrl := os.Getenv("GO_CHATGPT_API_PROXY") | |
| if proxyUrl != "" { | |
| client.SetProxy(proxyUrl) | |
| } | |
| return client | |
| } | |