aiChatControllers.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. })
  96. }
  97. // LoadAiSession 加载历史会话(恢复 AI 上下文)
  98. // POST /ai/sessions/:id/load
  99. func LoadAiSession(c *fiber.Ctx) error {
  100. sessionId := c.Params("id")
  101. if sessionId == "" {
  102. return Fail(c, "sessionId 不能为空")
  103. }
  104. userId := middleware.AuthGetUserName(c)
  105. sess, err := acpManager.LoadSession(userId, sessionId)
  106. if err != nil {
  107. return Error(c, fmt.Sprintf("加载会话失败: %v", err))
  108. }
  109. return Success(c, fiber.Map{
  110. "sessionId": sess.SessionId,
  111. "title": sess.Title,
  112. "replayMessages": sess.ReplayMessages,
  113. })
  114. }
  115. // UpdateAiSessionTitle 更新会话标题
  116. // PATCH /ai/sessions/:id
  117. func UpdateAiSessionTitle(c *fiber.Ctx) error {
  118. sessionId := c.Params("id")
  119. if sessionId == "" {
  120. return Fail(c, "sessionId 不能为空")
  121. }
  122. var req struct {
  123. Title string `json:"title"`
  124. }
  125. if err := c.BodyParser(&req); err != nil {
  126. return Fail(c, "Invalid request body")
  127. }
  128. userId := middleware.AuthGetUserName(c)
  129. acpManager.UpdateSessionTitle(userId, sessionId, req.Title)
  130. return Success[any](c, nil, "标题已更新")
  131. }
  132. // DeleteAiSession 删除 AI 会话
  133. // DELETE /ai/sessions/:id
  134. func DeleteAiSession(c *fiber.Ctx) error {
  135. sessionId := c.Params("id")
  136. userId := middleware.AuthGetUserName(c)
  137. if sessionId == "" {
  138. // 无 id 参数,删除当前活跃会话
  139. acpManager.DeleteSession(userId)
  140. } else {
  141. // 删除指定会话
  142. acpManager.DeleteSessionFromHistory(userId, sessionId)
  143. }
  144. return Success[any](c, nil, "会话已删除")
  145. }
  146. // CancelAiChat 取消当前 AI 操作
  147. // POST /ai/cancel
  148. func CancelAiChat(c *fiber.Ctx) error {
  149. userId := middleware.AuthGetUserName(c)
  150. sess := acpManager.GetSessionInfo(userId)
  151. if sess == nil {
  152. return Fail(c, "没有活跃的会话")
  153. }
  154. acpManager.Cancel(sess)
  155. return Success[any](c, nil, "已取消")
  156. }
  157. // ResolvePermission 前端回传写权限选择
  158. // POST /ai/permission
  159. func ResolvePermission(c *fiber.Ctx) error {
  160. var req struct {
  161. OptionId string `json:"optionId"`
  162. }
  163. if err := c.BodyParser(&req); err != nil {
  164. return Fail(c, "Invalid request body")
  165. }
  166. if req.OptionId == "" {
  167. return Fail(c, "optionId 不能为空")
  168. }
  169. userId := middleware.AuthGetUserName(c)
  170. if err := acpManager.ResolvePermission(userId, req.OptionId); err != nil {
  171. return Fail(c, err.Error())
  172. }
  173. return Success[any](c, nil, "已回复")
  174. }
  175. // DownloadAiFile 下载 AI 生成的文件
  176. // GET /ai/files/download?key=xxx
  177. func DownloadAiFile(c *fiber.Ctx) error {
  178. key := c.Query("key")
  179. if key == "" {
  180. return Fail(c, "key 不能为空")
  181. }
  182. userId := middleware.AuthGetUserName(c)
  183. sess := acpManager.GetSessionInfo(userId)
  184. if sess == nil {
  185. return Fail(c, "没有活跃的会话")
  186. }
  187. // 从 FileStore 查找文件记录
  188. record := sess.Client.FileStore.GetFileByKey(key)
  189. if record == nil {
  190. return Fail(c, "文件不存在")
  191. }
  192. // 从 MinIO 读取文件内容
  193. content, err := sess.Client.FileStore.ReadFile(record.Path)
  194. if err != nil {
  195. return Error(c, err.Error())
  196. }
  197. // 设置下载头(文件名为原始名,不含 uuid 前缀)
  198. c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, record.FileName))
  199. c.Set("Content-Type", "application/octet-stream")
  200. return c.SendString(content)
  201. }