package main import ( "net/http" "strconv" "strings" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" ) // Controller struct to hold dependencies // Add more dependencies as needed // e.g. templates, services, etc. type Controller struct { repo ChatRepositoryAPI llm LLMClientAPI visitDB *VisitDB template TemplateExecutor uiDBEditTemplate TemplateExecutor uiAdminChatsTemplate TemplateExecutor } // NewController creates a new Controller instance func NewController(repo ChatRepositoryAPI, llm LLMClientAPI, visitDB *VisitDB, template, uiDBEditTemplate, uiAdminChatsTemplate TemplateExecutor) *Controller { return &Controller{ repo: repo, llm: llm, visitDB: visitDB, template: template, uiDBEditTemplate: uiDBEditTemplate, uiAdminChatsTemplate: uiAdminChatsTemplate, } } // Handler for root UI func (ctrl *Controller) RootUI(c *gin.Context) { c.Status(http.StatusOK) if err := ctrl.template.Execute(c.Writer, nil); err != nil { logrus.Errorf("Failed to execute ui.html template: %v", err) } } // Handler for health check func (ctrl *Controller) Health(c *gin.Context) { c.Status(http.StatusOK) c.JSON(http.StatusOK, gin.H{"status": "ok"}) } // Handler for chat func (ctrl *Controller) HandleChat(c *gin.Context) { // Use your existing chatService logic here // You may want to move NewChatService to a service file for full MVC chatService := NewChatService(ctrl.llm, ctrl.visitDB, ctrl.repo) chatService.HandleChat(c) } // Handler for admin UI func (ctrl *Controller) AdminUI(c *gin.Context) { c.Status(http.StatusOK) if err := ctrl.uiDBEditTemplate.Execute(c.Writer, nil); err != nil { logrus.Errorf("Failed to execute ui_dbedit.html template: %v", err) } } // Handler for download db.yaml func (ctrl *Controller) DownloadDB(c *gin.Context) { c.File("db.yaml") } // Handler for saving knowledgeModel (snapshot) func (ctrl *Controller) SaveKnowledgeModel(c *gin.Context) { if ctrl.repo == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "repository not configured"}) return } var req struct { Text string `json:"text"` } if err := c.BindJSON(&req); err != nil || req.Text == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) return } if err := ctrl.repo.SaveKnowledgeModel(c.Request.Context(), req.Text); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save snapshot"}) return } c.JSON(http.StatusOK, gin.H{"status": "ok"}) } // Handler for listing chat interactions func (ctrl *Controller) ListChatInteractions(c *gin.Context) { if ctrl.repo == nil { c.JSON(http.StatusOK, gin.H{"items": []ChatInteraction{}, "pagination": gin.H{"limit": 0, "offset": 0, "count": 0}, "warning": "repository not configured"}) return } limit := 50 if ls := c.Query("limit"); ls != "" { if v, err := strconv.Atoi(ls); err == nil { limit = v } } offset := 0 if osf := c.Query("offset"); osf != "" { if v, err := strconv.Atoi(osf); err == nil { offset = v } } items, err := ctrl.repo.ListChatInteractions(c.Request.Context(), limit, offset) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list interactions"}) return } c.JSON(http.StatusOK, gin.H{"items": items, "pagination": gin.H{"limit": limit, "offset": offset, "count": len(items)}}) } // Handler for listing LLM events func (ctrl *Controller) ListLLMEvents(c *gin.Context) { corr := c.Query("correlation_id") if corr == "" { if strings.Contains(c.GetHeader("Accept"), "application/json") { c.JSON(http.StatusBadRequest, gin.H{"error": "missing correlation_id"}) return } c.Status(http.StatusOK) if err := ctrl.uiAdminChatsTemplate.Execute(c.Writer, nil); err != nil { logrus.Errorf("Failed to execute ui_admin_chats.html template: %v", err) } return } if ctrl.repo == nil { c.JSON(http.StatusOK, gin.H{"items": []RawLLMEvent{}, "pagination": gin.H{"limit": 0, "offset": 0, "count": 0}, "warning": "repository not configured"}) return } limit := 100 if ls := c.Query("limit"); ls != "" { if v, err := strconv.Atoi(ls); err == nil { limit = v } } offset := 0 if osf := c.Query("offset"); osf != "" { if v, err := strconv.Atoi(osf); err == nil { offset = v } } events, err := ctrl.repo.ListLLMRawEvents(c.Request.Context(), corr, limit, offset) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list events"}) return } c.JSON(http.StatusOK, gin.H{"items": events, "pagination": gin.H{"limit": limit, "offset": offset, "count": len(events)}}) } // Handler for admin chats UI func (ctrl *Controller) AdminChatsUI(c *gin.Context) { c.Status(http.StatusOK) if err := ctrl.uiAdminChatsTemplate.Execute(c.Writer, nil); err != nil { logrus.Errorf("Failed to execute ui_admin_chats.html template: %v", err) } } // Handler for listing knowledgeModel entries func (ctrl *Controller) ListKnowledgeModels(c *gin.Context) { if ctrl.repo == nil { c.JSON(http.StatusOK, gin.H{"items": []interface{}{}}) return } models, err := ctrl.repo.ListKnowledgeModels(c.Request.Context(), 100, 0) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list snapshots"}) return } c.JSON(http.StatusOK, gin.H{"items": models}) } // Handler for getting a specific knowledgeModel entry func (ctrl *Controller) GetKnowledgeModel(c *gin.Context) { if ctrl.repo == nil { c.JSON(http.StatusNotFound, gin.H{"error": "repository not configured"}) return } idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) return } text, err := ctrl.repo.GetKnowledgeModelText(c.Request.Context(), id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "snapshot not found"}) return } c.Data(http.StatusOK, "text/yaml; charset=utf-8", []byte(text)) } // TemplateExecutor is an interface for template execution // This allows for easier testing and decoupling // You can implement this interface for your templates type TemplateExecutor interface { Execute(wr http.ResponseWriter, data interface{}) error }