mvc
This commit is contained in:
parent
3f2789b4da
commit
5651b89a6b
|
|
@ -1,4 +1,5 @@
|
||||||
reasons.bleve
|
reasons.bleve
|
||||||
|
visits.bleve/**
|
||||||
visits.bleve
|
visits.bleve
|
||||||
# JetBrains / IntelliJ / GoLand project files
|
# JetBrains / IntelliJ / GoLand project files
|
||||||
.idea/
|
.idea/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,203 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
195
main.go
195
main.go
|
|
@ -2,14 +2,24 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TemplateWrapper adapts html/template.Template to TemplateExecutor
|
||||||
|
// (implements Execute(http.ResponseWriter, interface{}) error)
|
||||||
|
type TemplateWrapper struct {
|
||||||
|
Tmpl *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tw *TemplateWrapper) Execute(wr http.ResponseWriter, data interface{}) error {
|
||||||
|
return tw.Tmpl.Execute(wr, data)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
|
logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
|
||||||
logrus.SetLevel(logrus.InfoLevel)
|
logrus.SetLevel(logrus.InfoLevel)
|
||||||
|
|
@ -23,6 +33,12 @@ func main() {
|
||||||
if err := loadUITemplate("ui.html"); err != nil {
|
if err := loadUITemplate("ui.html"); err != nil {
|
||||||
logrus.Fatalf("Failed to load ui.html: %v", err)
|
logrus.Fatalf("Failed to load ui.html: %v", err)
|
||||||
}
|
}
|
||||||
|
if err := loadDBEditTemplate("ui_dbedit.html"); err != nil {
|
||||||
|
logrus.Fatalf("Failed to load ui_dbedit.html: %v", err)
|
||||||
|
}
|
||||||
|
if err := loadAdminChatsTemplate("ui_admin_chats.html"); err != nil {
|
||||||
|
logrus.Fatalf("Failed to load ui_admin_chats.html: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize PostgreSQL repository first
|
// Initialize PostgreSQL repository first
|
||||||
dsn := buildDefaultDSN()
|
dsn := buildDefaultDSN()
|
||||||
|
|
@ -44,158 +60,39 @@ func main() {
|
||||||
)
|
)
|
||||||
var llm LLMClientAPI = llmClient
|
var llm LLMClientAPI = llmClient
|
||||||
|
|
||||||
chatService := NewChatService(llm, &visitDB, repo)
|
// Wrap templates for controller
|
||||||
|
uiTmpl := &TemplateWrapper{Tmpl: uiTemplate}
|
||||||
|
uiDBEditTmpl := &TemplateWrapper{Tmpl: uiDBEditTemplate}
|
||||||
|
uiAdminChatsTmpl := &TemplateWrapper{Tmpl: uiAdminChatsTemplate}
|
||||||
|
|
||||||
|
// Create controller
|
||||||
|
ctrl := NewController(
|
||||||
|
repo,
|
||||||
|
llm,
|
||||||
|
&visitDB,
|
||||||
|
uiTmpl,
|
||||||
|
uiDBEditTmpl,
|
||||||
|
uiAdminChatsTmpl,
|
||||||
|
)
|
||||||
|
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
// Routes
|
// Routes
|
||||||
r.GET("/", func(c *gin.Context) {
|
r.GET("/", ctrl.RootUI)
|
||||||
c.Status(200)
|
r.GET("/health", ctrl.Health)
|
||||||
if err := uiTemplate.Execute(c.Writer, nil); err != nil {
|
r.POST("/chat", ctrl.HandleChat)
|
||||||
logrus.Errorf("Failed to execute ui.html template: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
r.GET("/health", func(c *gin.Context) {
|
|
||||||
c.Status(200)
|
|
||||||
c.JSON(200, gin.H{"status": "ok"})
|
|
||||||
})
|
|
||||||
r.POST("/chat", chatService.HandleChat)
|
|
||||||
|
|
||||||
if err := loadDBEditTemplate("ui_dbedit.html"); err != nil {
|
r.GET("/admin", ctrl.AdminUI)
|
||||||
logrus.Fatalf("Failed to load ui_dbedit.html: %v", err)
|
r.GET("/db.yaml", ctrl.DownloadDB)
|
||||||
}
|
|
||||||
if err := loadAdminChatsTemplate("ui_admin_chats.html"); err != nil {
|
|
||||||
logrus.Fatalf("Failed to load ui_admin_chats.html: %v", err)
|
|
||||||
}
|
|
||||||
r.GET("/admin", func(c *gin.Context) {
|
|
||||||
c.Status(200)
|
|
||||||
if err := uiDBEditTemplate.Execute(c.Writer, nil); err != nil {
|
|
||||||
logrus.Errorf("Failed to execute ui_dbedit.html template: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
r.GET("/db.yaml", func(c *gin.Context) { c.File("db.yaml") })
|
|
||||||
|
|
||||||
// Add knowledgeModel save endpoint (replaces snapshot)
|
// KnowledgeModel endpoints (replaces snapshot)
|
||||||
r.POST("/admin/chats/snapshot", func(c *gin.Context) {
|
r.POST("/admin/chats/snapshot", ctrl.SaveKnowledgeModel)
|
||||||
if repo == nil {
|
r.GET("/admin/chats/snapshots", ctrl.ListKnowledgeModels)
|
||||||
c.JSON(500, gin.H{"error": "repository not configured"})
|
r.GET("/admin/chats/snapshot/:id", ctrl.GetKnowledgeModel)
|
||||||
return
|
|
||||||
}
|
|
||||||
var req struct {
|
|
||||||
Text string `json:"text"`
|
|
||||||
}
|
|
||||||
if err := c.BindJSON(&req); err != nil || req.Text == "" {
|
|
||||||
c.JSON(400, gin.H{"error": "invalid request"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := repo.SaveKnowledgeModel(c.Request.Context(), req.Text); err != nil {
|
|
||||||
c.JSON(500, gin.H{"error": "failed to save snapshot"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(200, gin.H{"status": "ok"})
|
|
||||||
})
|
|
||||||
|
|
||||||
// JSON: list chat interactions
|
// Chat interactions and events
|
||||||
r.GET("/admin/chats", func(c *gin.Context) {
|
r.GET("/admin/chats", ctrl.ListChatInteractions)
|
||||||
if repo == nil {
|
r.GET("/admin/chats/events", ctrl.ListLLMEvents)
|
||||||
c.JSON(200, gin.H{"items": []ChatInteraction{}, "pagination": gin.H{"limit": 0, "offset": 0, "count": 0}, "warning": "repository not configured"})
|
r.GET("/admin/chats/ui", ctrl.AdminChatsUI)
|
||||||
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 := repo.ListChatInteractions(c.Request.Context(), limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{"error": "failed to list interactions"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(200, gin.H{"items": items, "pagination": gin.H{"limit": limit, "offset": offset, "count": len(items)}})
|
|
||||||
})
|
|
||||||
// JSON: list raw LLM events for a correlation id OR serve UI when no correlation_id provided
|
|
||||||
r.GET("/admin/chats/events", func(c *gin.Context) {
|
|
||||||
corr := c.Query("correlation_id")
|
|
||||||
if corr == "" {
|
|
||||||
if strings.Contains(c.GetHeader("Accept"), "application/json") {
|
|
||||||
c.JSON(400, gin.H{"error": "missing correlation_id"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Status(200)
|
|
||||||
if err := uiAdminChatsTemplate.Execute(c.Writer, nil); err != nil {
|
|
||||||
logrus.Errorf("Failed to execute ui_admin_chats.html template: %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if repo == nil { // repository not configured, return empty JSON set for events
|
|
||||||
c.JSON(200, 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 := repo.ListLLMRawEvents(c.Request.Context(), corr, limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{"error": "failed to list events"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(200, gin.H{"items": events, "pagination": gin.H{"limit": limit, "offset": offset, "count": len(events)}})
|
|
||||||
})
|
|
||||||
// HTML UI for chats & events
|
|
||||||
r.GET("/admin/chats/ui", func(c *gin.Context) {
|
|
||||||
c.Status(200)
|
|
||||||
if err := uiAdminChatsTemplate.Execute(c.Writer, nil); err != nil {
|
|
||||||
logrus.Errorf("Failed to execute ui_admin_chats.html template: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// List knowledgeModel entries (replaces snapshots)
|
|
||||||
r.GET("/admin/chats/snapshots", func(c *gin.Context) {
|
|
||||||
if repo == nil {
|
|
||||||
c.JSON(200, gin.H{"items": []interface{}{}})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
models, err := repo.ListKnowledgeModels(c.Request.Context(), 100, 0)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(500, gin.H{"error": "failed to list snapshots"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(200, gin.H{"items": models})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get a specific knowledgeModel entry (replaces snapshot)
|
|
||||||
r.GET("/admin/chats/snapshot/:id", func(c *gin.Context) {
|
|
||||||
if repo == nil {
|
|
||||||
c.JSON(404, gin.H{"error": "repository not configured"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
idStr := c.Param("id")
|
|
||||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(400, gin.H{"error": "invalid id"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
text, err := repo.GetKnowledgeModelText(c.Request.Context(), id)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(404, gin.H{"error": "snapshot not found"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Data(200, "text/yaml; charset=utf-8", []byte(text))
|
|
||||||
})
|
|
||||||
|
|
||||||
r.Run(":8080")
|
r.Run(":8080")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,10 @@ func (r *PGChatRepository) SaveChatInteraction(ctx context.Context, rec ChatInte
|
||||||
if r == nil || r.pool == nil {
|
if r == nil || r.pool == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// Ensure keywords is not nil to satisfy NOT NULL constraint
|
||||||
|
if rec.Keywords == nil {
|
||||||
|
rec.Keywords = []string{}
|
||||||
|
}
|
||||||
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
_, err := r.pool.Exec(ctx, `INSERT INTO chat_interactions
|
_, err := r.pool.Exec(ctx, `INSERT INTO chat_interactions
|
||||||
|
|
@ -224,56 +228,62 @@ func (r *PGChatRepository) ListLLMRawEvents(ctx context.Context, correlationID s
|
||||||
return out, rows.Err()
|
return out, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveKnowledgeModel inserts a new knowledgeModel entry
|
// SaveKnowledgeModel saves a knowledge model snapshot
|
||||||
func (r *PGChatRepository) SaveKnowledgeModel(ctx context.Context, text string) error {
|
func (p *PGChatRepository) SaveKnowledgeModel(ctx context.Context, text string) error {
|
||||||
if r == nil || r.pool == nil {
|
if p == nil || p.pool == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
_, err := r.pool.Exec(ctx, `INSERT INTO knowledgeModel (knowledge_text) VALUES ($1)`, text)
|
_, err := p.pool.Exec(ctx, `INSERT INTO knowledgeModel (knowledge_text) VALUES ($1)`, text)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Warn("failed to persist knowledge model")
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListKnowledgeModels returns a list of knowledgeModel metadata (id, created_at)
|
// ListKnowledgeModels lists knowledge model metadata
|
||||||
func (r *PGChatRepository) ListKnowledgeModels(ctx context.Context, limit, offset int) ([]knowledgeModelMeta, error) {
|
func (p *PGChatRepository) ListKnowledgeModels(ctx context.Context, limit, offset int) ([]knowledgeModelMeta, error) {
|
||||||
if r == nil || r.pool == nil {
|
if p == nil || p.pool == nil {
|
||||||
return nil, nil
|
return []knowledgeModelMeta{}, nil
|
||||||
}
|
}
|
||||||
if limit <= 0 || limit > 1000 {
|
if limit <= 0 || limit > 500 {
|
||||||
limit = 100
|
limit = 100
|
||||||
}
|
}
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
offset = 0
|
offset = 0
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
rows, err := r.pool.Query(ctx, `SELECT id, created_at FROM knowledgeModel ORDER BY created_at DESC LIMIT $1 OFFSET $2`, limit, offset)
|
rows, err := p.pool.Query(ctx, `SELECT id, created_at FROM knowledgeModel ORDER BY created_at DESC LIMIT $1 OFFSET $2`, limit, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
var out []knowledgeModelMeta
|
var out []knowledgeModelMeta
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var s knowledgeModelMeta
|
var meta knowledgeModelMeta
|
||||||
if err := rows.Scan(&s.ID, &s.CreatedAt); err != nil {
|
if err := rows.Scan(&meta.ID, &meta.CreatedAt); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out = append(out, s)
|
out = append(out, meta)
|
||||||
}
|
}
|
||||||
return out, rows.Err()
|
return out, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKnowledgeModelText returns the knowledge_text for a given id
|
// GetKnowledgeModelText retrieves the text of a knowledge model by ID
|
||||||
func (r *PGChatRepository) GetKnowledgeModelText(ctx context.Context, id int64) (string, error) {
|
func (p *PGChatRepository) GetKnowledgeModelText(ctx context.Context, id int64) (string, error) {
|
||||||
if r == nil || r.pool == nil {
|
if p == nil || p.pool == nil {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
var text string
|
var text string
|
||||||
err := r.pool.QueryRow(ctx, `SELECT knowledge_text FROM knowledgeModel WHERE id=$1`, id).Scan(&text)
|
err := p.pool.QueryRow(ctx, `SELECT knowledge_text FROM knowledgeModel WHERE id = $1`, id).Scan(&text)
|
||||||
return text, err
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return text, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close releases pool resources
|
// Close releases pool resources
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue