package llm import ( "bytes" "encoding/json" "eta/eta_api/utils" "eta/eta_api/utils/llm/eta_llm" "eta/eta_api/utils/llm/eta_llm/eta_llm_http" "fmt" "io" "mime/multipart" "net/http" "os" "strings" ) var ( llmService = eta_llm.GetInstance() ) type UploadTempDocsResp struct { Code int `json:"code"` Msg string `json:"msg"` Data UploadTempDocDataResp `json:"data"` } type UploadTempDocDataResp struct { Id string `json:"id"` FailedFiles []interface{} `json:"failed_files"` } // UploadTempDocs // @Description: 上传到临时知识库 // @author: Roc // @datetime 2025-03-10 14:42:18 // @param filePath string // @return resp UploadDocsResp // @return err error func UploadTempDocs(filePath string) (resp UploadTempDocsResp, err error) { postUrl := utils.LLM_SERVER + "/knowledge_base/upload_temp_docs" params := make(map[string]string) //params[`prev_id`] = `` params[`chunk_size`] = `750` params[`chunk_overlap`] = `150` params[`zh_title_enhance`] = `true` files := make(map[string]string) files[`files`] = filePath result, err := PostFormData(postUrl, params, files) if err != nil { return } str := string(result) fmt.Println(str) err = json.Unmarshal(result, &resp) if err != nil { return } return } type LlmBaseResp struct { Code int `json:"code"` Msg string `json:"msg"` } type UploadDocsResp struct { LlmBaseResp Data UploadDocDataResp `json:"data"` } type UploadDocDataResp struct { Id string `json:"id"` FailedFiles map[string]string `json:"failed_files"` } // UploadDocsToKnowledge // @Description: 上传文章到知识库 // @author: Roc // @datetime 2025-03-10 14:40:44 // @param filePath string // @param knowledgeName string // @return resp UploadTempDocsResp // @return err error func UploadDocsToKnowledge(filePath, knowledgeName string) (updateResp UploadDocDataResp, err error) { postUrl := utils.LLM_SERVER + "/knowledge_base/upload_docs" params := make(map[string]string) params[`knowledge_base_name`] = knowledgeName params[`override`] = `true` // 覆盖已有文件 params[`to_vector_store`] = `true` // 上传文件后是否进行向量化 params[`chunk_size`] = `750` // 知识库中单段文本最大长度 params[`chunk_overlap`] = `150` // 知识库中相邻文本重合长度 params[`zh_title_enhance`] = `true` // 是否开启中文标题加强 params[`docs`] = `` // 自定义的docs,需要转为json字符串 params[`not_refresh_vs_cache`] = `false` // 暂不保存向量库(用于FAISS) files := make(map[string]string) files[`files`] = filePath result, err := PostFormData(postUrl, params, files) if err != nil { return } str := string(result) fmt.Println(str) var resp UploadDocsResp err = json.Unmarshal(result, &resp) if err != nil { return } if resp.Code != 200 { err = fmt.Errorf(`上传文件失败: %s`, resp.Msg) return } updateResp = resp.Data return } // DelDocsToKnowledge // @Description: 从知识库中删除文件 // @author: Roc // @datetime 2025-03-12 15:03:19 // @param knowledgeName string // @param filePathList []string // @return resp LlmBaseResp // @return err error func DelDocsToKnowledge(knowledgeName string, filePathList []string) (resp LlmBaseResp, err error) { postUrl := utils.LLM_SERVER + "/knowledge_base/delete_docs" params := make(map[string]interface{}) params[`knowledge_base_name`] = knowledgeName params[`file_names`] = filePathList params[`delete_content`] = `true` // params[`not_refresh_vs_cache`] = `false` // postData, err := json.Marshal(params) if err != nil { return } result, err := LlmHttpPost(postUrl, string(postData)) if err != nil { return } utils.FileLog.Info("DelDocsToKnowledge:" + postUrl + ";" + string(postData) + ";result:" + string(result)) err = json.Unmarshal(result, &resp) if err != nil { return } if resp.Code != 200 { err = fmt.Errorf(`上传文件失败: %s`, resp.Msg) return } return } // ChatResp 问答响应 type ChatResp struct { Answer string `json:"answer"` Docs []string `json:"docs"` } type HistoryContent struct { Content string `json:"content"` Role string `json:"role"` } func ChatByFile(knowledgeId, question string, historyList []eta_llm_http.HistoryContent) (answerStr string, answer ChatResp, err error) { // 没有问题那就直接返回 if question == `` { return } history := make([]json.RawMessage, 0) for _, v := range historyList { tmpHistory, tmpErr := json.Marshal(v) if tmpErr != nil { return } history = append(history, json.RawMessage(string(tmpHistory))) } resp, err := llmService.DocumentChat(question, knowledgeId, history, false) if err != nil { return } defer func() { if resp != nil && resp.Body != nil { _ = resp.Body.Close() } }() if resp == nil { err = fmt.Errorf(`知识库问答失败: 无应答`) return } result, err := io.ReadAll(resp.Body) if err != nil { err = fmt.Errorf(`知识库问答数据解析失败: %s`, err.Error()) return } answerStr = string(result) // 找到"data:"关键字的位置 dataIndex := bytes.Index([]byte(answerStr), []byte("data: ")) if dataIndex == -1 { err = fmt.Errorf(`未找到"data:"关键字`) return } // 提取"data:"关键字之后的部分 answerStr = answerStr[dataIndex+len("data: "):] // 解析JSON数据 err = json.Unmarshal([]byte(answerStr), &answer) if err != nil { err = fmt.Errorf(`解析JSON数据失败: %s`, err.Error()) return } return } // PostFormData sends a POST request with form-data func PostFormData(url string, params map[string]string, files map[string]string) ([]byte, error) { body := &bytes.Buffer{} writer := multipart.NewWriter(body) for key, val := range params { if err := writer.WriteField(key, val); err != nil { return nil, err } } for fieldName, filePath := range files { file, err := os.Open(filePath) if err != nil { return nil, err } defer file.Close() part, err := writer.CreateFormFile(fieldName, filePath) if err != nil { return nil, err } _, err = io.Copy(part, file) if err != nil { return nil, err } } err := writer.Close() if err != nil { return nil, err } req, err := http.NewRequest("POST", url, body) if err != nil { return nil, err } //req.Header.Set("accept", `application/json`) req.Header.Set("Content-Type", writer.FormDataContentType()) client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() result, err := io.ReadAll(resp.Body) return result, nil } func LlmHttpPost(url, postData string) ([]byte, error) { body := io.NopCloser(strings.NewReader(postData)) client := &http.Client{} req, err := http.NewRequest("POST", url, body) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("authorization", utils.MD5(utils.APP_EDB_LIB_NAME_EN+utils.EDB_LIB_Md5_KEY)) resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() b, err := io.ReadAll(resp.Body) utils.FileLog.Debug("HttpPost:" + string(b)) return b, err }