Ver Fonte

新增ai文件上传

hongze há 1 ano atrás
pai
commit
29eea3020e

+ 2 - 2
controllers/ai/ai.go

@@ -60,8 +60,8 @@ func (this *AiController) List() {
 		}
 
 		if putVal <= 0 {
-			br.Msg = "您今日50次问答已达上限,请明天再来!"
-			br.ErrMsg = "您今日50次问答已达上限,请明天再来!"
+			br.Msg = "您今日" + strconv.Itoa(utils.AiChatLimit) + "次问答已达上限,请明天再来!"
+			br.ErrMsg = "您今日" + strconv.Itoa(utils.AiChatLimit) + "次问答已达上限,请明天再来!"
 			return
 		}
 		lastSecond := utils.GetTodayLastSecond()

+ 308 - 0
controllers/ai/ai_file.go

@@ -0,0 +1,308 @@
+package ai
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/aimod"
+	"eta/eta_api/services"
+	"eta/eta_api/services/aiser"
+	"eta/eta_api/utils"
+	"fmt"
+	"os"
+	"path"
+	"strconv"
+	"time"
+)
+
+type AiFileController struct {
+	controllers.BaseAuthController
+}
+
+// @Title 文件上传
+// @Description 文件上传接口
+// @Param   file   query   file  true       "文件"
+// @Success 200 {object} models.ResourceResp
+// @router /file/upload [post]
+func (this *AiFileController) FileUpload() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	f, h, err := this.GetFile("file")
+	if err != nil {
+		br.Msg = "获取资源信息失败"
+		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
+		return
+	}
+	uploadFileName := h.Filename //上传的文件名
+	ext := path.Ext(h.Filename)
+	dateDir := time.Now().Format("20060102")
+	uploadDir := utils.STATIC_DIR + "ai/" + dateDir
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+	if err != nil {
+		br.Msg = "存储目录创建失败"
+		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+		return
+	}
+	randStr := utils.GetRandStringNoSpecialChar(28)
+	fileName := randStr + ext
+	fpath := uploadDir + "/" + fileName
+	defer f.Close() //关闭上传文件
+	err = this.SaveToFile("file", fpath)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+	resourceUrl := ``
+	ossClient := services.NewOssClient()
+	if ossClient == nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "初始化OSS服务失败"
+		return
+	}
+	resourceUrl, err = ossClient.UploadFile(fileName, fpath, "")
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+
+	defer func() {
+		os.Remove(fpath)
+	}()
+
+	item := new(models.Resource)
+	item.ResourceUrl = resourceUrl
+	item.ResourceType = 1
+	item.CreateTime = time.Now()
+	newId, err := models.AddResource(item)
+	if err != nil {
+		br.Msg = "资源上传失败"
+		br.ErrMsg = "资源上传失败,Err:" + err.Error()
+		return
+	}
+	//调用AI接口,上传文件
+	resp := models.ResourceResp{
+		Id:           newId,
+		ResourceUrl:  resourceUrl,
+		ResourceName: uploadFileName,
+	}
+	uploadResult, err := aiser.OpenAiFileUpload(resourceUrl, uploadFileName)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+	if uploadResult != nil {
+		uploadObj := new(aimod.FileUploadRecord)
+		uploadObj.AdminId = this.SysUser.AdminId
+		uploadObj.FileUrl = resourceUrl
+		uploadObj.FileName = uploadFileName
+		uploadObj.OpenaiFileId = uploadResult.Data.ID
+		uploadObj.OpenaiFileName = uploadResult.Data.FileName
+		uploadObj.OpenaiObject = uploadResult.Data.Object
+		uploadObj.OpenaiStatus = uploadResult.Data.Status
+		uploadObj.OpenaiPurpose = uploadResult.Data.Purpose
+		uploadObj.OpenaiStatusDetails = uploadResult.Data.StatusDetails
+		uploadObj.OpenaiCreatedAt = uploadResult.Data.CreatedAt
+		uploadObj.CreateTime = time.Now()
+		uploadObj.ModifyTime = time.Now()
+		_, err = uploadObj.AddFileUploadRecord()
+		if err != nil {
+			br.Msg = "上传失败"
+			br.ErrMsg = "上传失败,Err:" + err.Error()
+			return
+		}
+		resp.OpenaiFileId = uploadObj.OpenaiFileId
+	}
+	br.Msg = "上传成功"
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+	return
+}
+
+// @Title 文件检索
+// @Description 文件检索接口
+// @Param	request	body aimod.FileRetrieveReq true "type json string"
+// @Success 200 {object} models.ResourceResp
+// @router /file/retrieve [post]
+func (this *AiFileController) FileRetrieve() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req aimod.FileRetrieveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.Ask == "" {
+		br.Msg = "请输入提问内容!"
+		br.ErrMsg = "请输入提问内容"
+		return
+	}
+
+	if utils.Re == nil {
+		key := "CACHE_CHAT_" + strconv.Itoa(this.SysUser.AdminId)
+		cacheVal, err := utils.Rc.RedisInt(key)
+		fmt.Println("RedisString:", cacheVal, "err:", err)
+		if err != nil && err.Error() != "redigo: nil returned" {
+			br.Msg = "获取数据失败!"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		putVal := 0
+		if cacheVal <= 0 {
+			putVal = utils.AiChatLimit
+		} else {
+			putVal = cacheVal - 1
+		}
+
+		if putVal <= 0 {
+			br.Msg = "您今日" + strconv.Itoa(utils.AiChatLimit) + "次问答已达上限,请明天再来!"
+			br.ErrMsg = "您今日" + strconv.Itoa(utils.AiChatLimit) + "次问答已达上限,请明天再来!"
+			return
+		}
+		lastSecond := utils.GetTodayLastSecond()
+		utils.Rc.Put(key, putVal, lastSecond)
+	}
+
+	//根据提问,获取信息
+	askUuid := utils.MD5(req.Ask)
+	chatMode, err := aimod.GetAiChatByAsk(askUuid)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取数据失败!"
+		br.ErrMsg = "获取数据失败,GetAiChatByAsk,Err:" + err.Error()
+		return
+	}
+
+	var assistantId, threadId string
+	if req.AiChatTopicId > 0 {
+		aiChatTopicObj := new(aimod.AiChatTopic)
+		aiChatTopicObj.AiChatTopicId = req.AiChatTopicId
+		topic, err := aiChatTopicObj.GetAiChatTopicById()
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "获取数据失败!"
+				br.ErrMsg = "获取数据失败,主题不存在,Err:" + err.Error()
+				return
+			}
+			br.Msg = "获取数据失败!"
+			br.ErrMsg = "获取数据失败,GetAiChatTopicById,Err:" + err.Error()
+			return
+		}
+		assistantId = topic.AssistantId
+		threadId = topic.ThreadId
+	}
+
+	resp := new(aimod.ChatResp)
+	var answer string
+	if chatMode != nil && chatMode.Answer != "" {
+		answer = chatMode.Answer
+	} else {
+		//获取主题下的所有信息
+		//AiChatTopicId
+		historyList, err := aimod.GetAiChatList(req.AiChatTopicId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取主题历史数据失败!"
+			br.ErrMsg = "获取主题历史数据失败,Err:" + err.Error()
+			return
+		}
+
+		frList := make([]aimod.HistoryChat, 0)
+		frItem := new(aimod.HistoryChat)
+		frItem.Ask = req.Ask
+		frItem.Answer = ""
+		frList = append(frList, *frItem)
+
+		for _, v := range historyList {
+			historyFr := new(aimod.HistoryChat)
+			historyFr.Ask = v.Ask
+			historyFr.Answer = v.Answer
+			frList = append(frList, *historyFr)
+
+			if v.OpenaiFileId != "" {
+				req.OpenaiFileId = append(req.OpenaiFileId, v.OpenaiFileId)
+			}
+		}
+
+		//var assistantId,threadId string
+		fileRetrieveResp, err := aiser.FileRetrieve(assistantId, threadId, frList, req.OpenaiFileId)
+		if err != nil {
+			br.Msg = "获取数据失败!"
+			br.ErrMsg = "获取数据失败,FileRetrieve,Err:" + err.Error()
+			return
+		}
+
+		assistantId = fileRetrieveResp.Data.AssistantId
+		threadId = fileRetrieveResp.Data.ThreadId
+		answer = fileRetrieveResp.Data.Answer
+	}
+	resp.Ask = req.Ask
+	resp.Answer = answer
+
+	if req.AiChatTopicId <= 0 { //新增
+		topic := new(aimod.AiChatTopic)
+		topic.TopicName = req.Ask
+		topic.SysUserId = this.SysUser.AdminId
+		topic.SysUserRealName = this.SysUser.RealName
+		topic.CreateTime = time.Now()
+		topic.ModifyTime = time.Now()
+		topic.AssistantId = assistantId
+		topic.ThreadId = threadId
+		topicId, err := aimod.AddAiChatTopic(topic)
+		if err != nil {
+			br.Msg = "获取数据失败!"
+			br.ErrMsg = "生成话题失败,Err:" + err.Error()
+			return
+		}
+		resp.AiChatTopicId = int(topicId)
+		chatItem := new(aimod.AiChat)
+		chatItem.AiChatTopicId = resp.AiChatTopicId
+		chatItem.Ask = req.Ask
+		chatItem.AskUuid = utils.MD5(req.Ask)
+		chatItem.Answer = answer
+		chatItem.Model = "gpt-4-1106-preview"
+		chatItem.SysUserId = this.SysUser.AdminId
+		chatItem.SysUserRealName = this.SysUser.RealName
+		chatItem.CreateTime = time.Now()
+		chatItem.ModifyTime = time.Now()
+		_, err = aimod.AddAiChat(chatItem)
+		if err != nil {
+			br.Msg = "获取数据失败!"
+			br.ErrMsg = "生成话题记录失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		resp.AiChatTopicId = req.AiChatTopicId
+		chatItem := new(aimod.AiChat)
+		chatItem.AiChatTopicId = resp.AiChatTopicId
+		chatItem.Ask = req.Ask
+		chatItem.AskUuid = utils.MD5(req.Ask)
+		chatItem.Answer = answer
+		chatItem.Model = "gpt-4-1106-preview"
+		chatItem.SysUserId = this.SysUser.AdminId
+		chatItem.SysUserRealName = this.SysUser.RealName
+		chatItem.CreateTime = time.Now()
+		chatItem.ModifyTime = time.Now()
+		_, err = aimod.AddAiChat(chatItem)
+		if err != nil {
+			br.Msg = "获取数据失败!"
+			br.ErrMsg = "生成话题记录失败,Err:" + err.Error()
+			return
+		}
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+	return
+}

+ 23 - 8
models/aimod/ai.go

@@ -10,6 +10,8 @@ type AiChatTopic struct {
 	TopicName       string
 	SysUserId       int
 	SysUserRealName string
+	AssistantId     string
+	ThreadId        string
 	CreateTime      time.Time
 	ModifyTime      time.Time
 }
@@ -22,7 +24,9 @@ type AiChat struct {
 	Answer          string
 	Model           string
 	SysUserId       int
-	SysUserRealName string
+	SysUserRealName string `description:"提问人名称"`
+	OpenaiFileId    string `description:"openai返回的文件id"`
+	OpenaiFileName  string `description:"文件名称"`
 	CreateTime      time.Time
 	ModifyTime      time.Time
 }
@@ -65,6 +69,8 @@ type AiChatTopicView struct {
 	TopicName     string `description:"主题名称"`
 	CreateTime    string `description:"创建时间"`
 	ModifyTime    string `description:"修改时间"`
+	AssistantId   string
+	ThreadId      string
 }
 
 func GetAiChatTopicList(sysUserId int) (item []*AiChatTopicView, err error) {
@@ -79,13 +85,15 @@ type AiChatTopicListResp struct {
 }
 
 type AiChatView struct {
-	AiChatId      int    `description:"记录id"`
-	AiChatTopicId int    `description:"主题id"`
-	Ask           string `description:"提问"`
-	Answer        string `description:"答案"`
-	Model         string
-	CreateTime    string `description:"创建时间"`
-	ModifyTime    string `description:"修改时间"`
+	AiChatId       int    `description:"记录id"`
+	AiChatTopicId  int    `description:"主题id"`
+	Ask            string `description:"提问"`
+	Answer         string `description:"答案"`
+	Model          string
+	OpenaiFileId   string `description:"文件ID"`
+	OpenaiFileName string `description:"文件名称"`
+	CreateTime     string `description:"创建时间"`
+	ModifyTime     string `description:"修改时间"`
 }
 
 func GetAiChatList(aiChatTopicId int) (item []*AiChatView, err error) {
@@ -145,6 +153,13 @@ func EditTopic(topicId int, topicName string) (err error) {
 	return err
 }
 
+func (obj *AiChatTopic) GetAiChatTopicById() (item *AiChatTopicView, err error) {
+	o := orm.NewOrmUsingDB("ai")
+	sql := ` SELECT * FROM ai_chat_topic WHERE ai_chat_topic_id=? `
+	err = o.Raw(sql, obj.AiChatTopicId).QueryRow(&item)
+	return
+}
+
 type HistoryChat struct {
 	Ask    string
 	Answer string

+ 8 - 0
models/aimod/ai_file.go

@@ -0,0 +1,8 @@
+package aimod
+
+type FileRetrieveReq struct {
+	AiChatTopicId int      `description:"主题id"`
+	Ask           string   `description:"提问问题,如果是上传文件,则填入文件名称"`
+	FileName      string   `description:"文件名称"`
+	OpenaiFileId  []string `description:"openai返回的文件id"`
+}

+ 29 - 0
models/aimod/file_upload_record.go

@@ -0,0 +1,29 @@
+package aimod
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type FileUploadRecord struct {
+	FileUploadRecordId  int       `orm:"column(file_upload_record_id);pk"`
+	AdminId             int       `description:"用户id"`
+	FileUrl             string    `description:"文件地址"`
+	FileName            string    `description:"文件名称"`
+	OpenaiFileId        string    `description:"openai返回的文件id"`
+	OpenaiFileName      string    `description:"openai返回的文件名称"`
+	OpenaiObject        string    `description:"openai返回的文件对象"`
+	OpenaiStatus        string    `description:"openai返回的文件状态"`
+	OpenaiPurpose       string    `description:"openai返回的提示词"`
+	OpenaiStatusDetails string    `description:"openai返回的文件状态详情"`
+	OpenaiCreatedAt     int64     `description:"openai返回的创建时间"`
+	CreateTime          time.Time `description:"创建时间"`
+	ModifyTime          time.Time `description:"修改时间"`
+}
+
+// AddAiChatTopic 新增上传文件记录
+func (obj *FileUploadRecord) AddFileUploadRecord() (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("ai")
+	lastId, err = o.Insert(obj)
+	return
+}

+ 1 - 0
models/db.go

@@ -512,6 +512,7 @@ func initAi() {
 	orm.RegisterModel(
 		new(aimod.AiChatTopic),
 		new(aimod.AiChat),
+		new(aimod.FileUploadRecord),
 	)
 }
 

+ 1 - 0
models/resource.go

@@ -19,6 +19,7 @@ type ResourceResp struct {
 	Source       string
 	CacheKey     string
 	ResourceName string `description:"资源名称"`
+	OpenaiFileId string `description:"openai返回的文件id"`
 }
 
 func AddResource(item *Resource) (newId int64, err error) {

+ 1 - 0
routers/router.go

@@ -321,6 +321,7 @@ func init() {
 		web.NSNamespace("/ai",
 			web.NSInclude(
 				&ai.AiController{},
+				&ai.AiFileController{},
 			),
 		),
 		web.NSNamespace("/cross_variety", //跨品种分析

+ 131 - 0
services/aiser/ai.go

@@ -66,8 +66,139 @@ func ChatAutoMsg(prompt string, historyChatList []aimod.HistoryChat) (result str
 	return result, nil
 }
 
+func OpenAiFileUpload(fileUrl, fileName string) (result *OpenAiFileUploadResp, err error) {
+	chatUrl := utils.EtaAiUrl + `chat/file/upload`
+
+	param := make(map[string]interface{})
+	param["FileUrl"] = fileUrl
+	param["FileName"] = fileName
+
+	postData, err := json.Marshal(param)
+	if err != nil {
+		return result, err
+	}
+
+	client := &http.Client{}
+	//提交请求
+	reqest, err := http.NewRequest("POST", chatUrl, strings.NewReader(string(postData)))
+	businessCode := utils.BusinessCode
+	if businessCode == "" {
+		return nil, errors.New("未获取到商户号")
+	}
+	nonce := utils.GetRandStringNoSpecialChar(16)
+	timestamp := time.Now().Format(utils.FormatDateTimeUnSpace)
+	signature := utils.GetSign(nonce, timestamp, utils.EtaAppid, utils.EtaSecret)
+	//增加header选项
+	reqest.Header.Add("BusinessCode", businessCode)
+	reqest.Header.Add("Nonce", nonce)
+	reqest.Header.Add("Timestamp", timestamp)
+	reqest.Header.Add("Appid", utils.EtaAppid)
+	reqest.Header.Add("Signature", signature)
+	reqest.Header.Set("Content-Type", "application/json")
+
+	utils.FileLog.Info("postData:" + string(postData))
+
+	response, err := client.Do(reqest)
+	if err != nil {
+		return
+	}
+	defer response.Body.Close()
+
+	body, err := ioutil.ReadAll(response.Body)
+
+	utils.FileLog.Info("result:" + string(body))
+
+	resp := new(OpenAiFileUploadResp)
+	err = json.Unmarshal(body, &resp)
+	if err != nil {
+		return result, err
+	}
+	return resp, nil
+}
+
+func FileRetrieve(assistantId, threadId string, historyChatList []aimod.HistoryChat, OpenaiFileId []string) (result *OpenAiFileRetrieveResp, err error) {
+	chatUrl := utils.EtaAiUrl + `chat/file/retrieve`
+
+	param := make(map[string]interface{})
+	param["FileRetrieveList"] = historyChatList
+	param["OpenaiFileId"] = OpenaiFileId
+	param["AssistantId"] = assistantId
+	param["ThreadId"] = threadId
+
+	postData, err := json.Marshal(param)
+	if err != nil {
+		return result, err
+	}
+
+	client := &http.Client{}
+	//提交请求
+	reqest, err := http.NewRequest("POST", chatUrl, strings.NewReader(string(postData)))
+	businessCode := utils.BusinessCode
+	if businessCode == "" {
+		return nil, errors.New("未获取到商户号")
+	}
+	nonce := utils.GetRandStringNoSpecialChar(16)
+	timestamp := time.Now().Format(utils.FormatDateTimeUnSpace)
+	signature := utils.GetSign(nonce, timestamp, utils.EtaAppid, utils.EtaSecret)
+	//增加header选项
+	reqest.Header.Add("BusinessCode", businessCode)
+	reqest.Header.Add("Nonce", nonce)
+	reqest.Header.Add("Timestamp", timestamp)
+	reqest.Header.Add("Appid", utils.EtaAppid)
+	reqest.Header.Add("Signature", signature)
+	reqest.Header.Set("Content-Type", "application/json")
+
+	utils.FileLog.Info("postData:" + string(postData))
+
+	response, err := client.Do(reqest)
+	if err != nil {
+		return
+	}
+	defer response.Body.Close()
+
+	body, err := ioutil.ReadAll(response.Body)
+
+	utils.FileLog.Info("result:" + string(body))
+
+	resp := new(OpenAiFileRetrieveResp)
+	err = json.Unmarshal(body, &resp)
+	if err != nil {
+		return result, err
+	}
+	return resp, nil
+}
+
 type ChatAutoMsgResp struct {
 	Ret  int
 	Data string
 	Msg  string
 }
+
+type OpenAiFileUploadResp struct {
+	Ret  int
+	Data *OpenAiFile
+	Msg  string
+}
+
+type OpenAiFile struct {
+	Bytes         int    `json:"bytes"`
+	CreatedAt     int64  `json:"created_at"`
+	ID            string `json:"id"`
+	FileName      string `json:"filename"`
+	Object        string `json:"object"`
+	Status        string `json:"status"`
+	Purpose       string `json:"purpose"`
+	StatusDetails string `json:"status_details"`
+}
+
+type OpenAiFileRetrieveResp struct {
+	Ret  int
+	Data *OpenAiFileRetrieve
+	Msg  string
+}
+
+type OpenAiFileRetrieve struct {
+	AssistantId string `description:"助手ID"`
+	ThreadId    string `description:"进程id"`
+	Answer      string
+}

+ 1 - 1
utils/constants.go

@@ -319,7 +319,7 @@ var FrequencyDaysMap = map[string]int{
 }
 
 const (
-	AiChatLimit = 50
+	AiChatLimit = 500
 )
 
 // 系统来源