package v1 import ( "net/http" "strconv" "github.com/gin-gonic/gin" "github.com/gochat/gochat/internal/service" applogger "github.com/gochat/gochat/pkg/logger" "github.com/gochat/gochat/pkg/response" ) // CaptainConversationHandler handles Captain conversation-level AI auto-response. // Reference: M12 PRD §Captain AI — Conversation Auto-Response (handoff mode) // // This handler exposes the CaptainConversationService, which generates AI // responses for conversations bound to a Captain Assistant. When the AI // determines a handoff is needed, the conversation is transferred to a human // agent instead of sending an AI-generated reply. type CaptainConversationHandler struct { svc *service.CaptainConversationService } // NewCaptainConversationHandler creates a new CaptainConversationHandler. func NewCaptainConversationHandler(svc *service.CaptainConversationService) *CaptainConversationHandler { return &CaptainConversationHandler{svc: svc} } // BuildResponse triggers AI response generation for a conversation. // The assistant must be bound to the conversation's inbox via CaptainInbox. // // POST /api/v1/accounts/:account_id/captain/conversations/:conversation_id/respond func (h *CaptainConversationHandler) BuildResponse(c *gin.Context) { accountID, err := strconv.ParseUint(c.Param("account_id"), 10, 64) if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, "invalid_account_id", "Invalid account ID") return } conversationID, err := strconv.ParseUint(c.Param("conversation_id"), 10, 64) if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, "invalid_conversation_id", "Invalid conversation ID") return } var req struct { AssistantID uint `json:"assistant_id" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, "invalid_request", err.Error()) return } msg, err := h.svc.BuildConversationResponseByAccount(c.Request.Context(), uint(accountID), uint(conversationID), req.AssistantID) if err != nil { applogger.L().Errorf("CaptainConversationHandler.BuildResponse: %v", err) response.AbortWithStatusError(c, http.StatusInternalServerError, "response_failed", "Failed to generate AI response") return } if msg == nil { response.OK(c, map[string]any{"status": "skipped", "message": "Conversation not eligible for AI response"}) return } response.OK(c, map[string]any{"status": "sent", "message_id": msg.ID}) }