chatai/internal/server/routes.go
洛天依 644e1339fb
All checks were successful
Build / Build (push) Successful in 1m22s
feat: home page
2025-01-21 19:11:48 +00:00

142 lines
3.5 KiB
Go

package server
import (
"text/template"
"net/http"
"os"
"devops.lty.name/luo/chatai/internal/agent"
"devops.lty.name/luo/chatai/internal/log"
"devops.lty.name/luo/chatai/web"
"devops.lty.name/luo/chatai/web/assets"
"github.com/gin-gonic/gin"
)
type Server struct {
agents map[string]*agent.AgentConf
logPath string
}
func Setup(e *gin.Engine, agentsDir string, msgLogPath string) {
log.T("server").Dbgf("Loading agents from %s", agentsDir)
agents, err := agent.LoadAllAgentsFromDir(agentsDir)
if err != nil {
log.T("server").Errf("Failed to load agents: %v", err)
os.Exit(1)
}
log.T("server").Inff("Loaded %d agents:", len(agents))
for _, agent := range agents {
log.T("server").Inff(" - %s: %s", agent.AgentID, agent.AgentName)
}
s := &Server{
agents: agents,
logPath: msgLogPath,
}
e.StaticFS("/assets", http.FS(assets.AssetsFS))
e.GET("", s.handleHomepageWithRendenedTemplate)
e.GET("/:agentID", s.handleWithRendenedTemplate)
e.GET("/:agentID/avatar.webp", s.handleAvatar)
e.POST("/:agentID/chat", s.handleChat)
}
func (s *Server) handleHomepageWithRendenedTemplate(c *gin.Context) {
tmpl, err := template.New("home").Parse(web.HomeLayout)
if err != nil {
log.T("server/tmpl").Errf("Failed to parse template: %v", err)
c.String(http.StatusInternalServerError, "server error")
return
}
err = tmpl.Execute(c.Writer, s.agents)
if err != nil {
log.T("server/tmpl").Errf("Failed to render template: %v", err)
c.String(http.StatusInternalServerError, "server error")
return
}
c.Status(http.StatusOK)
c.Header("Content-Type", "text/html; charset=utf-8")
c.Writer.Flush()
c.Abort()
}
func (s *Server) handleWithRendenedTemplate(c *gin.Context) {
agentID := c.Param("agentID")
agentConf, ok := s.agents[agentID]
if !ok || agentConf == nil {
c.String(http.StatusNotFound, "agent not found")
return
}
tmpl, err := template.New("agent").Parse(web.AgentLayout)
if err != nil {
log.T("server/tmpl").Errf("Failed to parse template: %v", err)
c.String(http.StatusInternalServerError, "server error")
return
}
err = tmpl.Execute(c.Writer, agentConf)
if err != nil {
log.T("server/tmpl").Errf("Failed to render template: %v", err)
c.String(http.StatusInternalServerError, "server error")
return
}
c.Status(http.StatusOK)
c.Header("Content-Type", "text/html; charset=utf-8")
c.Writer.Flush()
c.Abort()
}
func (s *Server) handleAvatar(c *gin.Context) {
agentID := c.Param("agentID")
agentConf, ok := s.agents[agentID]
if !ok || agentConf == nil {
c.Data(http.StatusNotFound, "image/webp", assets.DefaultAgentAvater)
return
}
agent := agentConf.ReadAgent()
c.Data(http.StatusOK, "image/webp", agent.Avater)
}
func (s *Server) handleChat(c *gin.Context) {
agentID := c.Param("agentID")
agentConf, ok := s.agents[agentID]
if !ok || agentConf == nil {
c.JSON(http.StatusNotFound, gin.H{
"error": "agent not found",
})
return
}
msgs := []*OpenAIMessage{}
err := c.ShouldBind(&msgs)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "invalid request",
})
return
}
agent := agentConf.ReadAgent()
req := &ChatRequest{
Agent: agent,
ClientIP: c.ClientIP(),
ClientUserAgent: c.GetHeader("User-Agent"),
ResWriter: c.Writer,
LogPath: s.logPath,
OpenAIMessages: msgs,
}
c.Header("Content-Type", "text/event-stream")
err = req.Chat()
if err != nil {
log.T("server/chat").Errf("Failed to chat: %v", err)
c.AbortWithStatus(http.StatusInternalServerError)
}
}