chat.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. package llm
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "eta/eta_api/utils"
  6. "eta/eta_api/utils/llm/eta_llm"
  7. "eta/eta_api/utils/llm/eta_llm/eta_llm_http"
  8. "fmt"
  9. "io"
  10. "mime/multipart"
  11. "net/http"
  12. "os"
  13. "strings"
  14. )
  15. var (
  16. llmService = eta_llm.GetInstance()
  17. )
  18. type UploadTempDocsResp struct {
  19. Code int `json:"code"`
  20. Msg string `json:"msg"`
  21. Data UploadTempDocDataResp `json:"data"`
  22. }
  23. type UploadTempDocDataResp struct {
  24. Id string `json:"id"`
  25. FailedFiles []interface{} `json:"failed_files"`
  26. }
  27. // UploadTempDocs
  28. // @Description: 上传到临时知识库
  29. // @author: Roc
  30. // @datetime 2025-03-10 14:42:18
  31. // @param filePath string
  32. // @return resp UploadDocsResp
  33. // @return err error
  34. func UploadTempDocs(filePath string) (resp UploadTempDocsResp, err error) {
  35. postUrl := utils.LLM_SERVER + "/knowledge_base/upload_temp_docs"
  36. params := make(map[string]string)
  37. //params[`prev_id`] = ``
  38. params[`chunk_size`] = `750`
  39. params[`chunk_overlap`] = `150`
  40. params[`zh_title_enhance`] = `true`
  41. files := make(map[string]string)
  42. files[`files`] = filePath
  43. result, err := PostFormData(postUrl, params, files)
  44. if err != nil {
  45. return
  46. }
  47. str := string(result)
  48. fmt.Println(str)
  49. err = json.Unmarshal(result, &resp)
  50. if err != nil {
  51. return
  52. }
  53. return
  54. }
  55. type LlmBaseResp struct {
  56. Code int `json:"code"`
  57. Msg string `json:"msg"`
  58. }
  59. type UploadDocsResp struct {
  60. LlmBaseResp
  61. Data UploadDocDataResp `json:"data"`
  62. }
  63. type UploadDocDataResp struct {
  64. Id string `json:"id"`
  65. FailedFiles map[string]string `json:"failed_files"`
  66. }
  67. // UploadDocsToKnowledge
  68. // @Description: 上传文章到知识库
  69. // @author: Roc
  70. // @datetime 2025-03-10 14:40:44
  71. // @param filePath string
  72. // @param knowledgeName string
  73. // @return resp UploadTempDocsResp
  74. // @return err error
  75. func UploadDocsToKnowledge(filePath, knowledgeName string) (updateResp UploadDocDataResp, err error) {
  76. postUrl := utils.LLM_SERVER + "/knowledge_base/upload_docs"
  77. params := make(map[string]string)
  78. params[`knowledge_base_name`] = knowledgeName
  79. params[`override`] = `true` // 覆盖已有文件
  80. params[`to_vector_store`] = `true` // 上传文件后是否进行向量化
  81. params[`chunk_size`] = `750` // 知识库中单段文本最大长度
  82. params[`chunk_overlap`] = `150` // 知识库中相邻文本重合长度
  83. params[`zh_title_enhance`] = `true` // 是否开启中文标题加强
  84. params[`docs`] = `` // 自定义的docs,需要转为json字符串
  85. params[`not_refresh_vs_cache`] = `false` // 暂不保存向量库(用于FAISS)
  86. files := make(map[string]string)
  87. files[`files`] = filePath
  88. result, err := PostFormData(postUrl, params, files)
  89. if err != nil {
  90. return
  91. }
  92. str := string(result)
  93. fmt.Println(str)
  94. var resp UploadDocsResp
  95. err = json.Unmarshal(result, &resp)
  96. if err != nil {
  97. return
  98. }
  99. if resp.Code != 200 {
  100. err = fmt.Errorf(`上传文件失败: %s`, resp.Msg)
  101. return
  102. }
  103. updateResp = resp.Data
  104. return
  105. }
  106. // DelDocsToKnowledge
  107. // @Description: 从知识库中删除文件
  108. // @author: Roc
  109. // @datetime 2025-03-12 15:03:19
  110. // @param knowledgeName string
  111. // @param filePathList []string
  112. // @return resp LlmBaseResp
  113. // @return err error
  114. func DelDocsToKnowledge(knowledgeName string, filePathList []string) (resp LlmBaseResp, err error) {
  115. postUrl := utils.LLM_SERVER + "/knowledge_base/delete_docs"
  116. params := make(map[string]interface{})
  117. params[`knowledge_base_name`] = knowledgeName
  118. params[`file_names`] = filePathList
  119. params[`delete_content`] = `true` //
  120. params[`not_refresh_vs_cache`] = `false` //
  121. postData, err := json.Marshal(params)
  122. if err != nil {
  123. return
  124. }
  125. result, err := LlmHttpPost(postUrl, string(postData))
  126. if err != nil {
  127. return
  128. }
  129. utils.FileLog.Info("DelDocsToKnowledge:" + postUrl + ";" + string(postData) + ";result:" + string(result))
  130. err = json.Unmarshal(result, &resp)
  131. if err != nil {
  132. return
  133. }
  134. if resp.Code != 200 {
  135. err = fmt.Errorf(`上传文件失败: %s`, resp.Msg)
  136. return
  137. }
  138. return
  139. }
  140. // ChatResp 问答响应
  141. type ChatResp struct {
  142. Answer string `json:"answer"`
  143. Docs []string `json:"docs"`
  144. }
  145. type HistoryContent struct {
  146. Content string `json:"content"`
  147. Role string `json:"role"`
  148. }
  149. func ChatByFile(knowledgeId, question string, historyList []eta_llm_http.HistoryContent) (answerStr string, answer ChatResp, err error) {
  150. // 没有问题那就直接返回
  151. if question == `` {
  152. return
  153. }
  154. history := make([]json.RawMessage, 0)
  155. for _, v := range historyList {
  156. tmpHistory, tmpErr := json.Marshal(v)
  157. if tmpErr != nil {
  158. return
  159. }
  160. history = append(history, json.RawMessage(string(tmpHistory)))
  161. }
  162. resp, err := llmService.DocumentChat(question, knowledgeId, history, false)
  163. if err != nil {
  164. return
  165. }
  166. defer func() {
  167. if resp != nil && resp.Body != nil {
  168. _ = resp.Body.Close()
  169. }
  170. }()
  171. if resp == nil {
  172. err = fmt.Errorf(`知识库问答失败: 无应答`)
  173. return
  174. }
  175. result, err := io.ReadAll(resp.Body)
  176. if err != nil {
  177. err = fmt.Errorf(`知识库问答数据解析失败: %s`, err.Error())
  178. return
  179. }
  180. answerStr = string(result)
  181. // 找到"data:"关键字的位置
  182. dataIndex := bytes.Index([]byte(answerStr), []byte("data: "))
  183. if dataIndex == -1 {
  184. err = fmt.Errorf(`未找到"data:"关键字`)
  185. return
  186. }
  187. // 提取"data:"关键字之后的部分
  188. answerStr = answerStr[dataIndex+len("data: "):]
  189. // 解析JSON数据
  190. err = json.Unmarshal([]byte(answerStr), &answer)
  191. if err != nil {
  192. err = fmt.Errorf(`解析JSON数据失败: %s`, err.Error())
  193. return
  194. }
  195. return
  196. }
  197. // PostFormData sends a POST request with form-data
  198. func PostFormData(url string, params map[string]string, files map[string]string) ([]byte, error) {
  199. body := &bytes.Buffer{}
  200. writer := multipart.NewWriter(body)
  201. for key, val := range params {
  202. if err := writer.WriteField(key, val); err != nil {
  203. return nil, err
  204. }
  205. }
  206. for fieldName, filePath := range files {
  207. file, err := os.Open(filePath)
  208. if err != nil {
  209. return nil, err
  210. }
  211. defer file.Close()
  212. part, err := writer.CreateFormFile(fieldName, filePath)
  213. if err != nil {
  214. return nil, err
  215. }
  216. _, err = io.Copy(part, file)
  217. if err != nil {
  218. return nil, err
  219. }
  220. }
  221. err := writer.Close()
  222. if err != nil {
  223. return nil, err
  224. }
  225. req, err := http.NewRequest("POST", url, body)
  226. if err != nil {
  227. return nil, err
  228. }
  229. //req.Header.Set("accept", `application/json`)
  230. req.Header.Set("Content-Type", writer.FormDataContentType())
  231. client := &http.Client{}
  232. resp, err := client.Do(req)
  233. if err != nil {
  234. return nil, err
  235. }
  236. defer resp.Body.Close()
  237. result, err := io.ReadAll(resp.Body)
  238. return result, nil
  239. }
  240. func LlmHttpPost(url, postData string) ([]byte, error) {
  241. body := io.NopCloser(strings.NewReader(postData))
  242. client := &http.Client{}
  243. req, err := http.NewRequest("POST", url, body)
  244. if err != nil {
  245. return nil, err
  246. }
  247. req.Header.Set("Content-Type", "application/json")
  248. req.Header.Set("authorization", utils.MD5(utils.APP_EDB_LIB_NAME_EN+utils.EDB_LIB_Md5_KEY))
  249. resp, err := client.Do(req)
  250. if err != nil {
  251. return nil, err
  252. }
  253. defer resp.Body.Close()
  254. b, err := io.ReadAll(resp.Body)
  255. utils.FileLog.Debug("HttpPost:" + string(b))
  256. return b, err
  257. }