package service import ( "bytes" "encoding/base64" "encoding/json" "errors" "fmt" "github.com/carmel/gooxml/document" "github.com/fonchain_enterprise/fonchain-main/pkg/config" "github.com/fonchain_enterprise/fonchain-main/pkg/e" "github.com/fonchain_enterprise/fonchain-main/pkg/model" "github.com/fonchain_enterprise/fonchain-main/pkg/request" "github.com/fonchain_enterprise/fonchain-main/pkg/response" "github.com/fonchain_enterprise/fonchain-main/pkg/service/aliyun" "github.com/fonchain_enterprise/fonchain-main/pkg/utils" "github.com/fonchain_enterprise/fonchain-main/pkg/utils/stringutils" "github.com/gin-gonic/gin" "go.uber.org/zap" "io" "io/ioutil" "mime/multipart" "net/http" "os" "path/filepath" "regexp" "strings" "time" ) type ChatRequestData struct { Model string `json:"model"` MaxTokens int `json:"max_tokens"` Temperature float64 `json:"temperature"` TopP float64 `json:"top_p"` PresencePenalty float64 `json:"presence_penalty"` FrequencyPenalty float64 `json:"frequency_penalty"` Messages []Message `json:"messages"` Stream bool `json:"stream"` ListUuid string `json:"listUuid"` Type string `json:"type"` } type Message struct { Role string `json:"role"` Content interface{} `json:"content"` } type RequestData struct { Model string `json:"model"` MaxTokens int `json:"max_tokens"` Temperature float64 `json:"temperature"` TopP float64 `json:"top_p"` PresencePenalty float64 `json:"presence_penalty"` FrequencyPenalty float64 `json:"frequency_penalty"` } type ChatMsg struct { ID string `json:"id"` Object string `json:"object"` Created int `json:"created"` Model string `json:"model"` Choices []Choice `json:"choices"` } type Choice struct { Index int `json:"index"` Delta Delta `json:"delta"` FinishReason string `json:"finish_reason"` } type Delta struct { Content string `json:"content"` } type DefineRequest struct { Model string `json:"model"` Stream bool `json:"stream,omitempty"` Stop []string `json:"stop,omitempty"` PresencePenalty float32 `json:"presence_penalty,omitempty"` FrequencyPenalty float32 `json:"frequency_penalty,omitempty"` // LogitBias is must be a token id string (specified by their token ID in the tokenizer), not a word string. // incorrect: `"logit_bias":{"You": 6}`, correct: `"logit_bias":{"1639": 6}` // refs: https://platform.openai.com/docs/api-reference/chat/create#chat/create-logit_bias LogitBias map[string]int `json:"logit_bias,omitempty"` User string `json:"user,omitempty"` } type MessageContentImage struct { Type string `json:"type"` Text string `json:"text"` ImageUrl string `json:"image_url"` } func Completion(ctx *gin.Context) { var request ChatRequestData bodyByte, _ := io.ReadAll(ctx.Request.Body) ctx.Request.Body = io.NopCloser(bytes.NewBuffer(bodyByte)) _ = json.Unmarshal(bodyByte, &request) ctx.Header("Content-Type", "text/event-stream") ctx.Header("Cache-Control", "no-cache") ctx.Header("Connection", "keep-alive") //url := config.ChatGptHost + "/chat/completion" url := config.ChatGptHost + "/baidu-chat/chat" //url := "http://114.217.150.188:9011" + "/baidu-chat/chat" var jsonData []byte var err error request, err = NewRequest(request) if err != nil { ResponseQuickMsg(ctx, e.Failed, err.Error(), nil) return } if request.ListUuid == "" { request.ListUuid = "web" } jsonData, err = json.Marshal(request) if err != nil { ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil) return } zap.L().Info("Completion", zap.Any("url", url)) zap.L().Info("Completion", zap.Any("jsonData", jsonData)) resp, errA := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if errA != nil { zap.L().Error("Completion", zap.Error(errA)) ResponseQuickMsg(ctx, e.Failed, errA.Error(), nil) return } defer resp.Body.Close() ctx.Stream(func(w io.Writer) bool { buf := make([]byte, 4096) for { n, errS := resp.Body.Read(buf) if n > 0 { msg := string(buf[:n]) ctx.Stream(func(w io.Writer) bool { if strings.Contains(msg, "DONE") { fmt.Println(msg) fmt.Fprint(w, msg) return false } fmt.Println(msg) zap.L().Info("Completion", zap.Any("msg", msg)) fmt.Fprint(w, msg) return false }) } if errS != nil { return false } } }) } func AppCompletion(ctx *gin.Context) { var request ChatRequestData err := ctx.BindJSON(&request) if err != nil { ResponseQuickMsg(ctx, e.Failed, err.Error(), nil) return } ctx.Header("Content-Type", "text/event-stream") ctx.Header("Cache-Control", "no-cache") ctx.Header("Connection", "keep-alive") url := config.ChatGptHost + "/baidu-chat/chat" //url := "http://127.0.0.1:9010/chat/completion" request, err = NewRequest(request) if err != nil { ResponseQuickMsg(ctx, e.Failed, err.Error(), nil) return } jsonData, err := json.Marshal(request) if err != nil { ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil) return } if request.ListUuid == "" { request.ListUuid = "app" } resp, errA := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if err != nil { ResponseQuickMsg(ctx, e.Failed, errA.Error(), nil) return } defer resp.Body.Close() /*var chatData ChatMsg var content string ctx.Stream(func(w io.Writer) bool { buf := make([]byte, 4096) for { n, errS := resp.Body.Read(buf) if errS != nil { return false } if n > 0 { msg := string(buf[:n]) msg = strings.Replace(msg, `\n`, "", -1) if len(msg) > 0 && msg[len(msg)-1] == '\n' { msg = msg[:len(msg)-1] } re := regexp.MustCompile(`data:\s*({.+?}]})`) matches := re.FindAllStringSubmatch(msg, -1) for _, match := range matches { msg = match[1] fmt.Println("msg1111", msg) if !strings.Contains(msg, "[DONE]") { fmt.Println("msg222", msg) _ = json.Unmarshal([]byte(msg), &chatData) fmt.Println("msg333", chatData) for _, choice := range chatData.Choices { content = fmt.Sprintf("%s%s", content, choice.Delta.Content) } } } } } } if err != nil { response.ResponseQuickMsg(ctx, e.Failed, err.Error(), nil) return } //fmt.Println("ap-complete", string(jsonData), content) response.ResponseQuickMsg(ctx, e.Ok, "", map[string]interface{}{ "content": content, })*/ defer resp.Body.Close() ctx.Stream(func(w io.Writer) bool { buf := make([]byte, 4096) for { n, errS := resp.Body.Read(buf) if n > 0 { msg := string(buf[:n]) ctx.Stream(func(w io.Writer) bool { if strings.Contains(msg, "DONE") { fmt.Println(msg) fmt.Fprint(w, msg) return false } fmt.Println(msg) fmt.Fprint(w, msg) return false }) } if errS != nil { return false } } }) return } func AudioToText(c *gin.Context) { err := c.Request.ParseMultipartForm(32 << 20) // 限制最大文件大小 if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } file, handler, err := c.Request.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } defer file.Close() body := new(bytes.Buffer) writer := multipart.NewWriter(body) part, err := writer.CreateFormFile("file", handler.Filename) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } _, err = io.Copy(part, file) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } err = writer.Close() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // 创建一个新的请求,将文件发送到美国接口 client := &http.Client{} url := config.ChatGptHost + "/chat/audio-to-text" req, err := http.NewRequest("POST", url, body) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } req.Header.Set("Content-Type", writer.FormDataContentType()) resp, err := client.Do(req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { c.JSON(http.StatusInternalServerError, gin.H{"error": resp.Status}) return } data, err := ioutil.ReadAll(resp.Body) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } var audioToTextInfo model.AudioToTextResp if err = json.Unmarshal(data, &audioToTextInfo); err != nil { response.ResponseQuickMsg(c, e.Failed, e.GetMsg(e.JsonUnmarshal), nil) } response.ResponseQuickMsg(c, e.Failed, e.GetMsg(e.Success), map[string]interface{}{ "text": audioToTextInfo.Data.Text, }) return } func ImageUrlToBase64(imageURL string) (base64Val string, err error) { resp, err := http.Get(imageURL) if err != nil { err = errors.New("读取图片错误") return } defer resp.Body.Close() imageData, err := ioutil.ReadAll(resp.Body) if err != nil { err = errors.New("读取图片内容失败") return } base64Val = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imageData) return } func NewRequest(request ChatRequestData) (resp ChatRequestData, err error) { resp = request return if request.Model == "gpt-4-vision-preview" { //bodyByte, _ := io.ReadAll(ctx.Request.Body) for key, messages := range request.Messages { var contentData []map[string]string contentBytes, _ := json.Marshal(messages.Content) _ = json.Unmarshal(contentBytes, &contentData) var needModify bool for kk, content := range contentData { needModify = false if strings.Contains(content["image_url"], "http") { needModify = true contentData[kk]["image_url_origin"] = content["image_url"] if content["image_url"], err = ImageUrlToBase64(content["image_url"]); err != nil { return } contentData[kk]["image_url"] = content["image_url"] } fmt.Println(kk) fmt.Println(contentData) } fmt.Println(contentData) fmt.Println(key) if needModify { var mm []interface{} cc, _ := json.Marshal(contentData) _ = json.Unmarshal([]byte(cc), &mm) request.Messages[key].Content = mm } } } resp = request return } func ChatList(ctx *gin.Context) { var ( req request.ChatListReq err error ) if err = ctx.BindJSON(&req); err != nil { response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil) return } if req.UserUuid == "" { if mLoginInfoAny, exists := ctx.Get("mLoginInfo"); exists { userInfo := mLoginInfoAny.(model.LoginInfo) req.UserUuid = fmt.Sprint(userInfo.ID) } } if req.UserUuid == "" { response.ResponseQuickMsg(ctx, e.Failed, e.ErrNotLogin, nil) return } code, body, err := utils.PostBody(config.ChatGptHost+"/chat/list", req) if err != nil { response.ResponseQuickMsg(ctx, e.Failed, err.Error(), nil) return } fmt.Println(code) var newData interface{} _ = json.Unmarshal([]byte(body), &newData) ctx.JSON(http.StatusOK, newData) return } func ChatCreate(ctx *gin.Context) { var req request.ChatCreateReq var err error if err = ctx.BindJSON(&req); err != nil { response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil) return } if mLoginInfoAny, exists := ctx.Get("mLoginInfo"); exists { userInfo := mLoginInfoAny.(model.LoginInfo) req.UserUuid = fmt.Sprint(userInfo.ID) } if req.GptModel == "" { response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil) return } code, body, err := utils.PostBody(config.ChatGptHost+"/chat/create", req) if err != nil { response.ResponseQuickMsg(ctx, e.Failed, err.Error(), nil) return } fmt.Println(code) var newData interface{} _ = json.Unmarshal([]byte(body), &newData) ctx.JSON(http.StatusOK, newData) return } func ChatDel(ctx *gin.Context) { var ( req request.DelListReq err error ) if err = ctx.BindJSON(&req); err != nil { response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil) return } code, body, err := utils.PostBody(config.ChatGptHost+"/chat/del", req) if err != nil { response.ResponseQuickMsg(ctx, e.Failed, err.Error(), nil) return } fmt.Println(code) var newData interface{} _ = json.Unmarshal([]byte(body), &newData) ctx.JSON(http.StatusOK, newData) return } func ChatDetail(ctx *gin.Context) { var ( req request.ListDetailReq err error ) if err = ctx.BindJSON(&req); err != nil { response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil) return } var newData interface{} var body string var code int if req.GptModel == "gpt-4-vision-preview" { file, _ := os.Open("./data/mock/detail.txt") defer file.Close() bodyBytes, _ := io.ReadAll(file) body = string(bodyBytes) } else { code, body, err = utils.PostBody(config.ChatGptHost+"/chat/detail", req) if err != nil { response.ResponseQuickMsg(ctx, e.Failed, err.Error(), nil) return } fmt.Println(code) } _ = json.Unmarshal([]byte(body), &newData) ctx.JSON(http.StatusOK, newData) return } func GetFileText(ctx *gin.Context) { var ( filePath string text string subtexts []string ) var wordExts string = ".doc,.docx" // doc提交调用API解析 file, err := ctx.FormFile("file") if err != nil { response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.ErrorReadFile), nil) return } filePath = fmt.Sprintf("%s%s", model.MediaPath, file.Filename) fileExt := filepath.Ext(filePath) if !strings.Contains(wordExts, fileExt) { response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.ERROR_INVALID_FILE_EXT), nil) return } if fileExt == ".doc" { fileUrl, _err := quickBos(file, "doc", "file", "word", "") if _err != nil { response.ResponseQuickMsg(ctx, e.Failed, _err.Error(), nil) return } docId, _err := aliyun.DocStruct(fileUrl, file.Filename) if _err != nil { response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.ErrorUploadFile), nil) return } for { time.Sleep(1 * time.Second) complete, texts, errs := aliyun.DocResult(docId) if errs != nil { response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.ErrorUploadFile), nil) return } if complete { for _, v := range texts { text += strings.Trim(v, "") } break } } fmt.Println(text) } else { err = ctx.SaveUploadedFile(file, filePath) if err != nil { response.ResponseQuickMsg(ctx, e.Failed, e.ErrorSaveFile, nil) return } doc, _err := document.Open(filePath) defer os.Remove(filePath) if _err != nil { response.ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.ErrorReadFile), nil) return } for _, p := range doc.Paragraphs() { for _, run := range p.Runs() { if strings.Trim(run.Text(), "") != "" { text += strings.Trim(run.Text(), "") } } } } subtexts = stringutils.SplitText(text, 15000) response.ResponseQuickMsg(ctx, e.SUCCESS, e.GetMsg(e.Ok), map[string]interface{}{ "count": len(subtexts), "paragraph": subtexts, }) return } func CompletionText(ctx *gin.Context) { var request ChatRequestData bodyByte, _ := io.ReadAll(ctx.Request.Body) ctx.Request.Body = io.NopCloser(bytes.NewBuffer(bodyByte)) _ = json.Unmarshal(bodyByte, &request) ctx.Header("Content-Type", "text/event-stream") ctx.Header("Cache-Control", "no-cache") ctx.Header("Connection", "keep-alive") url := config.ChatGptHost + "/baidu-chat/chat" var jsonData []byte var err error request, err = NewRequest(request) if err != nil { ResponseQuickMsg(ctx, e.Failed, err.Error(), nil) return } if request.ListUuid == "" { request.ListUuid = "web" } jsonData, err = json.Marshal(request) if err != nil { ResponseQuickMsg(ctx, e.Failed, e.GetMsg(e.InvalidParams), nil) return } zap.L().Info("Completion", zap.Any("url", url)) zap.L().Info("Completion", zap.Any("jsonData", jsonData)) resp, errA := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if errA != nil { zap.L().Error("Completion", zap.Error(errA)) ResponseQuickMsg(ctx, e.Failed, errA.Error(), nil) return } defer resp.Body.Close() // 创建一个 bytes.Buffer,并将 resp.Body 中的内容复制到其中 var buf bytes.Buffer _, err = io.Copy(&buf, resp.Body) if err != nil { // 错误处理 ResponseQuickMsg(ctx, e.Failed, err.Error(), nil) return } // 获取所有的数据,并将其转化为字符串 allData := buf.String() // 打印所有的数据 re := regexp.MustCompile(`"content":"(.*?)"`) matches := re.FindAllStringSubmatch(allData, -1) var answer string for _, match := range matches { answer = fmt.Sprintf("%s%s", answer, match[1]) } ResponseQuickMsg(ctx, e.Ok, e.GetMsg(e.Success), map[string]string{ "answer": answer, }) return }