112 lines
2.8 KiB
Go
112 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"log"
|
|
"os"
|
|
|
|
"ai-agent-scaffold-go/internal/config"
|
|
"ai-agent-scaffold-go/internal/handler"
|
|
"ai-agent-scaffold-go/internal/model"
|
|
"ai-agent-scaffold-go/internal/service"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/joho/godotenv"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func main() {
|
|
envPath := flag.String("env", ".env", "path to dotenv file (empty to skip)")
|
|
configPath := flag.String("config", "configs/application.yaml", "path to application.yaml")
|
|
flag.Parse()
|
|
|
|
// 1. 加载 .env 文件
|
|
loadDotenv(*envPath)
|
|
|
|
// 2. 加载应用配置
|
|
appCfg, err := config.LoadApplication(*configPath)
|
|
if err != nil {
|
|
log.Fatalf("load config: %v", err)
|
|
}
|
|
|
|
// 3. 初始化日志
|
|
logger, _ := zap.NewProduction()
|
|
if appCfg.App.Env == "local" || appCfg.App.Env == "dev" {
|
|
logger, _ = zap.NewDevelopment()
|
|
}
|
|
defer logger.Sync()
|
|
|
|
// 4. 组装 Agent
|
|
registry := model.NewInMemoryAgentRegistry()
|
|
sessions := model.NewInMemorySessionStore()
|
|
|
|
timeout, _ := appCfg.LLM.RequestTimeoutDuration()
|
|
agents, err := service.LoadAndAssemble(context.Background(), appCfg.Agent.ConfigPaths, timeout)
|
|
if err != nil {
|
|
logger.Fatal("assemble agents failed", zap.Error(err))
|
|
}
|
|
for _, agent := range agents {
|
|
if err := registry.Register(agent); err != nil {
|
|
logger.Fatal("register agent failed", zap.Error(err))
|
|
}
|
|
}
|
|
|
|
chatService := service.NewChatService(registry, sessions)
|
|
|
|
// 5. 配置 HTTP 路由
|
|
router := gin.Default()
|
|
router.Use(corsMiddleware())
|
|
router.GET("/healthz", func(c *gin.Context) {
|
|
c.JSON(200, gin.H{"status": "ok"})
|
|
})
|
|
handler.RegisterRoutes(router, chatService)
|
|
|
|
// 6. 启动服务
|
|
addr := appCfg.Server.Addr
|
|
logger.Info("server starting", zap.String("addr", addr), zap.Int("agents", len(agents)))
|
|
if err := router.Run(addr); err != nil {
|
|
logger.Fatal("server stopped", zap.Error(err))
|
|
}
|
|
}
|
|
|
|
// corsMiddleware 跨域中间件
|
|
func corsMiddleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
origin := c.GetHeader("Origin")
|
|
if origin != "" {
|
|
c.Header("Access-Control-Allow-Origin", origin)
|
|
c.Header("Vary", "Origin")
|
|
} else {
|
|
c.Header("Access-Control-Allow-Origin", "*")
|
|
}
|
|
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
|
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
|
c.Header("Access-Control-Allow-Credentials", "true")
|
|
if c.Request.Method == "OPTIONS" {
|
|
c.AbortWithStatus(204)
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// loadDotenv 加载 dotenv 文件,已有环境变量不会被覆盖
|
|
func loadDotenv(path string) {
|
|
if path == "" {
|
|
return
|
|
}
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
return
|
|
}
|
|
pairs, err := godotenv.Read(path)
|
|
if err != nil {
|
|
log.Fatalf("read dotenv: %v", err)
|
|
}
|
|
for key, value := range pairs {
|
|
if _, exists := os.LookupEnv(key); !exists {
|
|
os.Setenv(key, value)
|
|
}
|
|
}
|
|
}
|