121 lines
2.8 KiB
Go
121 lines
2.8 KiB
Go
package server
|
|
|
|
import (
|
|
"html/template"
|
|
|
|
"net/http"
|
|
"os"
|
|
|
|
"codelab/ai-agent/internal/agent"
|
|
"codelab/ai-agent/internal/log"
|
|
"codelab/ai-agent/web"
|
|
"codelab/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)
|
|
}
|
|
}
|