vetrag/controllers.go

204 lines
6.1 KiB
Go

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
}