vetrag/main.go

117 lines
3.1 KiB
Go

package main
import (
"context"
"database/sql"
"html/template"
"net/http"
"os"
"github.com/gin-gonic/gin"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/pressly/goose/v3"
"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() {
logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
logrus.SetLevel(logrus.InfoLevel)
if err := loadConfig("config.yaml"); err != nil {
logrus.Fatalf("Failed to load config.yaml: %v", err)
}
logrus.Infof("Loaded config: %+v", appConfig)
visitDB := NewVisitDB()
if err := loadUITemplate("ui.html"); err != nil {
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
dsn := buildDefaultDSN()
logrus.Info("Connecting to PostgreSQL with DSN: ", dsn)
// Run goose migrations using database/sql with pgx driver before initializing repository
if dsn != "" {
sqlDB, err := sql.Open("pgx", dsn)
if err != nil {
logrus.Fatalf("Failed to open database for migration: %v", err)
}
defer sqlDB.Close()
goose.SetDialect("postgres")
if err := goose.Up(sqlDB, "migrations"); err != nil {
logrus.Fatalf("Goose migration failed: %v", err)
}
logrus.Info("Database migrations applied successfully (goose)")
}
repo, err := NewPGChatRepository(context.Background(), dsn)
if err != nil {
logrus.WithError(err).Warn("PostgreSQL repository disabled (connection failed)")
} else if repo == nil {
logrus.Info("PostgreSQL repository not configured (no DSN)")
}
// defer repo.Close() // optionally enable
// Initialize LLM client
llmClient := NewLLMClient(
os.Getenv("OPENAI_API_KEY"),
os.Getenv("OPENAI_BASE_URL"),
os.Getenv("OPENAI_MODEL"),
repo,
)
var llm LLMClientAPI = llmClient
// 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()
// Routes
r.GET("/", ctrl.RootUI)
r.GET("/health", ctrl.Health)
r.POST("/chat", ctrl.HandleChat)
r.GET("/admin", ctrl.AdminUI)
r.GET("/db.yaml", ctrl.DownloadDB)
// KnowledgeModel endpoints (replaces snapshot)
r.POST("/admin/chats/snapshot", ctrl.SaveKnowledgeModel)
r.GET("/admin/chats/snapshots", ctrl.ListKnowledgeModels)
r.GET("/admin/chats/snapshot/:id", ctrl.GetKnowledgeModel)
// Chat interactions and events
r.GET("/admin/chats", ctrl.ListChatInteractions)
r.GET("/admin/chats/events", ctrl.ListLLMEvents)
r.GET("/admin/chats/ui", ctrl.AdminChatsUI)
r.Run(":8080")
}