aiChatControllers.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. package controllers
  2. import (
  3. "bufio"
  4. "encoding/json"
  5. "fmt"
  6. "fwh-toolbox-server/middleware"
  7. "fwh-toolbox-server/services"
  8. "github.com/gofiber/fiber/v2"
  9. )
  10. var acpManager *services.ACPManager
  11. // InitAIChat 初始化 AI Chat 模块
  12. func InitAIChat(serviceDir string) {
  13. acpManager = services.NewACPManager(serviceDir, []string{"HOME=/home/jerry-remote"})
  14. }
  15. // AiChat 发送消息(SSE 流式响应)
  16. // POST /ai/chat
  17. func AiChat(c *fiber.Ctx) error {
  18. var req struct {
  19. Message string `json:"message"`
  20. SessionId string `json:"sessionId"`
  21. }
  22. if err := c.BodyParser(&req); err != nil {
  23. return Fail(c, "Invalid request body")
  24. }
  25. if req.Message == "" {
  26. return Fail(c, "message 不能为空")
  27. }
  28. userId := middleware.AuthGetUserName(c)
  29. sess, err := acpManager.GetOrCreateSession(userId)
  30. if err != nil {
  31. return Error(c, fmt.Sprintf("AI 会话创建失败: %v", err))
  32. }
  33. // SSE 流式响应
  34. c.Set("Content-Type", "text/event-stream")
  35. c.Set("Cache-Control", "no-cache")
  36. c.Set("Connection", "keep-alive")
  37. c.Set("X-Accel-Buffering", "no")
  38. c.Context().SetBodyStreamWriter(func(w *bufio.Writer) {
  39. // 如果有欢迎消息且是新会话,先发送 welcome 事件
  40. if sess.WelcomeMessage != "" {
  41. welcomeEvent, _ := json.Marshal(services.AIChatEvent{Type: "welcome", Content: sess.WelcomeMessage})
  42. fmt.Fprintf(w, "event: welcome\ndata: %s\n\n", string(welcomeEvent))
  43. w.Flush()
  44. }
  45. eventCh := make(chan services.AIChatEvent, 50)
  46. go acpManager.Prompt(sess, req.Message, eventCh)
  47. for event := range eventCh {
  48. data, _ := json.Marshal(event)
  49. fmt.Fprintf(w, "event: %s\ndata: %s\n\n", event.Type, string(data))
  50. w.Flush()
  51. }
  52. })
  53. return nil
  54. }
  55. // GetAiSessions 获取当前用户的会话信息
  56. // GET /ai/sessions
  57. func GetAiSessions(c *fiber.Ctx) error {
  58. userId := middleware.AuthGetUserName(c)
  59. sess, err := acpManager.GetOrCreateSession(userId)
  60. if err != nil {
  61. return Error(c, fmt.Sprintf("AI 会话创建失败: %v", err))
  62. }
  63. if sess == nil {
  64. return Success(c, fiber.Map{
  65. "active": false,
  66. "sessionId": "",
  67. })
  68. }
  69. return Success(c, fiber.Map{
  70. "active": true,
  71. "sessionId": sess.SessionId,
  72. "title": sess.Title,
  73. "welcomeMessage": sess.WelcomeMessage,
  74. "lastActive": sess.LastActive,
  75. })
  76. }
  77. // GetAiSessionList 获取用户的会话历史列表
  78. // GET /ai/sessions/list
  79. func GetAiSessionList(c *fiber.Ctx) error {
  80. userId := middleware.AuthGetUserName(c)
  81. list := acpManager.GetSessionList(userId)
  82. return Success(c, list)
  83. }
  84. // CreateAiSession 创建新的 AI 会话(保留旧会话到历史)
  85. // POST /ai/sessions/new
  86. func CreateAiSession(c *fiber.Ctx) error {
  87. userId := middleware.AuthGetUserName(c)
  88. // 创建新会话(旧会话自动归档到历史)
  89. sess, err := acpManager.CreateNewSession(userId)
  90. if err != nil {
  91. return Error(c, fmt.Sprintf("创建会话失败: %v", err))
  92. }
  93. return Success(c, fiber.Map{
  94. "sessionId": sess.SessionId,
  95. "welcomeMessage": sess.WelcomeMessage,
  96. })
  97. }
  98. // LoadAiSession 加载历史会话(恢复 AI 上下文)
  99. // POST /ai/sessions/:id/load
  100. func LoadAiSession(c *fiber.Ctx) error {
  101. sessionId := c.Params("id")
  102. if sessionId == "" {
  103. return Fail(c, "sessionId 不能为空")
  104. }
  105. userId := middleware.AuthGetUserName(c)
  106. sess, err := acpManager.LoadSession(userId, sessionId)
  107. if err != nil {
  108. return Error(c, fmt.Sprintf("加载会话失败: %v", err))
  109. }
  110. return Success(c, fiber.Map{
  111. "sessionId": sess.SessionId,
  112. "title": sess.Title,
  113. "replayMessages": sess.ReplayMessages,
  114. })
  115. }
  116. // UpdateAiSessionTitle 更新会话标题
  117. // PATCH /ai/sessions/:id
  118. func UpdateAiSessionTitle(c *fiber.Ctx) error {
  119. sessionId := c.Params("id")
  120. if sessionId == "" {
  121. return Fail(c, "sessionId 不能为空")
  122. }
  123. var req struct {
  124. Title string `json:"title"`
  125. }
  126. if err := c.BodyParser(&req); err != nil {
  127. return Fail(c, "Invalid request body")
  128. }
  129. userId := middleware.AuthGetUserName(c)
  130. acpManager.UpdateSessionTitle(userId, sessionId, req.Title)
  131. return Success[any](c, nil, "标题已更新")
  132. }
  133. // DeleteAiSession 删除 AI 会话
  134. // DELETE /ai/sessions/:id
  135. func DeleteAiSession(c *fiber.Ctx) error {
  136. sessionId := c.Params("id")
  137. userId := middleware.AuthGetUserName(c)
  138. if sessionId == "" {
  139. // 无 id 参数,删除当前活跃会话
  140. acpManager.DeleteSession(userId)
  141. } else {
  142. // 删除指定会话
  143. acpManager.DeleteSessionFromHistory(userId, sessionId)
  144. }
  145. return Success[any](c, nil, "会话已删除")
  146. }
  147. // CancelAiChat 取消当前 AI 操作
  148. // POST /ai/cancel
  149. func CancelAiChat(c *fiber.Ctx) error {
  150. userId := middleware.AuthGetUserName(c)
  151. sess := acpManager.GetSessionInfo(userId)
  152. if sess == nil {
  153. return Fail(c, "没有活跃的会话")
  154. }
  155. acpManager.Cancel(sess)
  156. return Success[any](c, nil, "已取消")
  157. }
  158. // ResolvePermission 前端回传写权限选择
  159. // POST /ai/permission
  160. func ResolvePermission(c *fiber.Ctx) error {
  161. var req struct {
  162. OptionId string `json:"optionId"`
  163. }
  164. if err := c.BodyParser(&req); err != nil {
  165. return Fail(c, "Invalid request body")
  166. }
  167. if req.OptionId == "" {
  168. return Fail(c, "optionId 不能为空")
  169. }
  170. userId := middleware.AuthGetUserName(c)
  171. if err := acpManager.ResolvePermission(userId, req.OptionId); err != nil {
  172. return Fail(c, err.Error())
  173. }
  174. return Success[any](c, nil, "已回复")
  175. }
  176. // DownloadAiFile 下载 AI 生成的文件
  177. // GET /ai/files/download?key=xxx
  178. func DownloadAiFile(c *fiber.Ctx) error {
  179. key := c.Query("key")
  180. if key == "" {
  181. return Fail(c, "key 不能为空")
  182. }
  183. userId := middleware.AuthGetUserName(c)
  184. sess := acpManager.GetSessionInfo(userId)
  185. if sess == nil {
  186. return Fail(c, "没有活跃的会话")
  187. }
  188. // 从 FileStore 查找文件记录
  189. record := sess.Client.FileStore.GetFileByKey(key)
  190. if record == nil {
  191. return Fail(c, "文件不存在")
  192. }
  193. // 从 MinIO 读取文件内容
  194. content, err := sess.Client.FileStore.ReadFile(record.Path)
  195. if err != nil {
  196. return Error(c, err.Error())
  197. }
  198. // 设置下载头(文件名为原始名,不含 uuid 前缀)
  199. c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, record.FileName))
  200. c.Set("Content-Type", "application/octet-stream")
  201. return c.SendString(content)
  202. }