package handlers import ( "context" "encoding/json" "net/http" "strconv" "github.com/labstack/echo/v5" "trankilou.fr/lassistanoque/backend/internal/domain" "trankilou.fr/lassistanoque/backend/internal/service/auth" "trankilou.fr/lassistanoque/backend/internal/service/chat" ) func NewChatGroup(prefix string, e *echo.Group, service *chat.Service, middlewares ...echo.MiddlewareFunc) *echo.Group { chatHandler := &ChatHandler{ chatService: service, } auth := e.Group(prefix, middlewares...) auth.POST("/:space", chatHandler.NewChat) auth.POST("/:space/:id", chatHandler.NewChatMessage) auth.GET("/:space/all/:page", chatHandler.List) auth.GET("/:space/:id", chatHandler.Get) auth.GET("/:space/:id/subscribe", chatHandler.Subscribe) auth.DELETE("/:space/:id", chatHandler.Delete) return auth } type ChatHandler struct { chatService *chat.Service } type ChatRequest struct { Prompt string `json:"prompt"` AgentID string `json:"agentID"` ProviderID string `json:"providerID"` ModelID string `json:"modelID"` ReasoningLevel string `json:"reasoningLevel"` } func (h *ChatHandler) NewChat(c *echo.Context) error { userID := c.Get(auth.ContextUserIDKey).(string) teamID := c.Param("space") var request ChatRequest err := c.Bind(&request) if err != nil { c.Logger().Error("error binding request", "error", err.Error()) return echo.NewHTTPError(http.StatusBadRequest, "error binding request") } chat, err := h.chatService.NewChat( userID, teamID, request.AgentID, "", ) if err != nil { c.Logger().Error("error creating chat", "error", err.Error()) return echo.NewHTTPError(http.StatusBadRequest, "error creating chat") } return c.JSON(http.StatusOK, chat) } func (h *ChatHandler) NewChatMessage(c *echo.Context) error { ctx := context.Background() userID := c.Get(auth.ContextUserIDKey).(string) teamID := c.Param("space") chatID := c.Param("id") var request ChatRequest err := c.Bind(&request) if err != nil { c.Logger().Error("error binding request", "error", err.Error()) return echo.NewHTTPError(http.StatusBadRequest, "error binding request") } enc := json.NewEncoder(c.Response()) chunkChan := make(chan *domain.Chunk) _, err = h.chatService.AddChatMessage( ctx, userID, teamID, chatID, request.AgentID, request.Prompt, &domain.ChatParams{ OnChunk: func(chunk *domain.Chunk) { chunkChan <- chunk }, }, ) if err != nil { c.Logger().Error("error creating chat message", "error", err.Error()) return echo.NewHTTPError(http.StatusBadRequest, "error creating chat message") } enc.Encode(&domain.Chunk{ Done: false, Role: string(domain.RoleUser), Content: request.Prompt, }) http.NewResponseController(c.Response()).Flush() for { chunk := <-chunkChan enc.Encode(chunk) http.NewResponseController(c.Response()).Flush() if chunk.Done { return nil } } } func (h *ChatHandler) List(c *echo.Context) error { userID := c.Get(auth.ContextUserIDKey).(string) teamID := c.Param("space") page := c.Param("page") pageInt, _ := strconv.Atoi(page) query := c.QueryParam("q") taskID := c.QueryParam("task") chats, err := h.chatService.ListChats(userID, teamID, pageInt, query, taskID) if err != nil { c.Logger().Error("error listing chats", "error", err) return echo.NewHTTPError(http.StatusBadRequest, "error listing chats") } // évite "null" dans la réponse quand il n'y a pas de conversation if chats == nil { chats = []*domain.Chat{} } return c.JSON(http.StatusOK, chats) } func (h *ChatHandler) Get(c *echo.Context) error { userID := c.Get(auth.ContextUserIDKey).(string) teamID := c.Param("space") chatID := c.Param("id") chatWithMessages, err := h.chatService.GetChatWithMessages(userID, teamID, chatID) if err != nil { c.Logger().Error("error getting chat with messages", "error", err) return echo.NewHTTPError(http.StatusBadRequest, "error getting chat") } return c.JSON(http.StatusOK, chatWithMessages) } func (h *ChatHandler) Delete(c *echo.Context) error { userID := c.Get(auth.ContextUserIDKey).(string) teamID := c.Param("space") chatID := c.Param("id") err := h.chatService.DeleteChat(userID, teamID, chatID) if err != nil { c.Logger().Error("error deleting chat", "error", err) return echo.NewHTTPError(http.StatusBadRequest, "error deleting chat") } return c.JSON(http.StatusOK, "chat deleted") } func (h *ChatHandler) Subscribe(c *echo.Context) error { userID := c.Get(auth.ContextUserIDKey).(string) teamID := c.Param("space") chatID := c.Param("id") enc := json.NewEncoder(c.Response()) chunkChan := make(chan *domain.Chunk) err := h.chatService.Subscribe( userID, teamID, chatID, func(chunk *domain.Chunk) { chunkChan <- chunk }, ) if err != nil { c.Logger().Error("error subscribing to chat", "error", err.Error()) return echo.NewHTTPError(http.StatusBadRequest, "error creating chat") } for { chunk := <-chunkChan if chunk.Done { return nil } enc.Encode(chunk) http.NewResponseController(c.Response()).Flush() } }