| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- package controllers
- import (
- "bufio"
- "encoding/json"
- "fmt"
- "fwh-toolbox-server/middleware"
- "fwh-toolbox-server/services"
- "github.com/gofiber/fiber/v2"
- )
- var acpManager *services.ACPManager
- // InitAIChat 初始化 AI Chat 模块
- func InitAIChat(serviceDir string) {
- acpManager = services.NewACPManager(serviceDir, []string{"HOME=/home/jerry-remote"})
- }
- // AiChat 发送消息(SSE 流式响应)
- // POST /ai/chat
- func AiChat(c *fiber.Ctx) error {
- var req struct {
- Message string `json:"message"`
- SessionId string `json:"sessionId"`
- }
- if err := c.BodyParser(&req); err != nil {
- return Fail(c, "Invalid request body")
- }
- if req.Message == "" {
- return Fail(c, "message 不能为空")
- }
- userId := middleware.AuthGetUserName(c)
- sess, err := acpManager.GetOrCreateSession(userId)
- if err != nil {
- return Error(c, fmt.Sprintf("AI 会话创建失败: %v", err))
- }
- // SSE 流式响应
- c.Set("Content-Type", "text/event-stream")
- c.Set("Cache-Control", "no-cache")
- c.Set("Connection", "keep-alive")
- c.Set("X-Accel-Buffering", "no")
- c.Context().SetBodyStreamWriter(func(w *bufio.Writer) {
- // 如果有欢迎消息且是新会话,先发送 welcome 事件
- if sess.WelcomeMessage != "" {
- welcomeEvent, _ := json.Marshal(services.AIChatEvent{Type: "welcome", Content: sess.WelcomeMessage})
- fmt.Fprintf(w, "event: welcome\ndata: %s\n\n", string(welcomeEvent))
- w.Flush()
- }
- eventCh := make(chan services.AIChatEvent, 50)
- go acpManager.Prompt(sess, req.Message, eventCh)
- for event := range eventCh {
- data, _ := json.Marshal(event)
- fmt.Fprintf(w, "event: %s\ndata: %s\n\n", event.Type, string(data))
- w.Flush()
- }
- })
- return nil
- }
- // GetAiSessions 获取当前用户的会话信息
- // GET /ai/sessions
- func GetAiSessions(c *fiber.Ctx) error {
- userId := middleware.AuthGetUserName(c)
- sess, err := acpManager.GetOrCreateSession(userId)
- if err != nil {
- return Error(c, fmt.Sprintf("AI 会话创建失败: %v", err))
- }
- if sess == nil {
- return Success(c, fiber.Map{
- "active": false,
- "sessionId": "",
- })
- }
- return Success(c, fiber.Map{
- "active": true,
- "sessionId": sess.SessionId,
- "title": sess.Title,
- "welcomeMessage": sess.WelcomeMessage,
- "lastActive": sess.LastActive,
- })
- }
- // GetAiSessionList 获取用户的会话历史列表
- // GET /ai/sessions/list
- func GetAiSessionList(c *fiber.Ctx) error {
- userId := middleware.AuthGetUserName(c)
- list := acpManager.GetSessionList(userId)
- return Success(c, list)
- }
- // CreateAiSession 创建新的 AI 会话(保留旧会话到历史)
- // POST /ai/sessions/new
- func CreateAiSession(c *fiber.Ctx) error {
- userId := middleware.AuthGetUserName(c)
- // 创建新会话(旧会话自动归档到历史)
- sess, err := acpManager.CreateNewSession(userId)
- if err != nil {
- return Error(c, fmt.Sprintf("创建会话失败: %v", err))
- }
- return Success(c, fiber.Map{
- "sessionId": sess.SessionId,
- })
- }
- // LoadAiSession 加载历史会话(恢复 AI 上下文)
- // POST /ai/sessions/:id/load
- func LoadAiSession(c *fiber.Ctx) error {
- sessionId := c.Params("id")
- if sessionId == "" {
- return Fail(c, "sessionId 不能为空")
- }
- userId := middleware.AuthGetUserName(c)
- sess, err := acpManager.LoadSession(userId, sessionId)
- if err != nil {
- return Error(c, fmt.Sprintf("加载会话失败: %v", err))
- }
- return Success(c, fiber.Map{
- "sessionId": sess.SessionId,
- "title": sess.Title,
- "replayMessages": sess.ReplayMessages,
- })
- }
- // UpdateAiSessionTitle 更新会话标题
- // PATCH /ai/sessions/:id
- func UpdateAiSessionTitle(c *fiber.Ctx) error {
- sessionId := c.Params("id")
- if sessionId == "" {
- return Fail(c, "sessionId 不能为空")
- }
- var req struct {
- Title string `json:"title"`
- }
- if err := c.BodyParser(&req); err != nil {
- return Fail(c, "Invalid request body")
- }
- userId := middleware.AuthGetUserName(c)
- acpManager.UpdateSessionTitle(userId, sessionId, req.Title)
- return Success[any](c, nil, "标题已更新")
- }
- // DeleteAiSession 删除 AI 会话
- // DELETE /ai/sessions/:id
- func DeleteAiSession(c *fiber.Ctx) error {
- sessionId := c.Params("id")
- userId := middleware.AuthGetUserName(c)
- if sessionId == "" {
- // 无 id 参数,删除当前活跃会话
- acpManager.DeleteSession(userId)
- } else {
- // 删除指定会话
- acpManager.DeleteSessionFromHistory(userId, sessionId)
- }
- return Success[any](c, nil, "会话已删除")
- }
- // CancelAiChat 取消当前 AI 操作
- // POST /ai/cancel
- func CancelAiChat(c *fiber.Ctx) error {
- userId := middleware.AuthGetUserName(c)
- sess := acpManager.GetSessionInfo(userId)
- if sess == nil {
- return Fail(c, "没有活跃的会话")
- }
- acpManager.Cancel(sess)
- return Success[any](c, nil, "已取消")
- }
- // ResolvePermission 前端回传写权限选择
- // POST /ai/permission
- func ResolvePermission(c *fiber.Ctx) error {
- var req struct {
- OptionId string `json:"optionId"`
- }
- if err := c.BodyParser(&req); err != nil {
- return Fail(c, "Invalid request body")
- }
- if req.OptionId == "" {
- return Fail(c, "optionId 不能为空")
- }
- userId := middleware.AuthGetUserName(c)
- if err := acpManager.ResolvePermission(userId, req.OptionId); err != nil {
- return Fail(c, err.Error())
- }
- return Success[any](c, nil, "已回复")
- }
- // DownloadAiFile 下载 AI 生成的文件
- // GET /ai/files/download?key=xxx
- func DownloadAiFile(c *fiber.Ctx) error {
- key := c.Query("key")
- if key == "" {
- return Fail(c, "key 不能为空")
- }
- userId := middleware.AuthGetUserName(c)
- sess := acpManager.GetSessionInfo(userId)
- if sess == nil {
- return Fail(c, "没有活跃的会话")
- }
- // 从 FileStore 查找文件记录
- record := sess.Client.FileStore.GetFileByKey(key)
- if record == nil {
- return Fail(c, "文件不存在")
- }
- // 从 MinIO 读取文件内容
- content, err := sess.Client.FileStore.ReadFile(record.Path)
- if err != nil {
- return Error(c, err.Error())
- }
- // 设置下载头(文件名为原始名,不含 uuid 前缀)
- c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, record.FileName))
- c.Set("Content-Type", "application/octet-stream")
- return c.SendString(content)
- }
|