package server import ( "html/template" "net/http" "os" "github.com/ltylab/ai-agent/internal/agent" "github.com/ltylab/ai-agent/internal/log" "github.com/ltylab/ai-agent/web" "github.com/ltylab/ai-agent/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("/:agentID", s.handleWithRendenedTemplate) e.GET("/:agentID/avatar.webp", s.handleAvatar) e.POST("/:agentID/chat", s.handleChat) } 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("tmpl.html").Parse(web.Template) 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) } }