package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/explorer/virtual-banker/backend/api" "github.com/explorer/virtual-banker/backend/asr" "github.com/explorer/virtual-banker/backend/llm" "github.com/explorer/virtual-banker/backend/orchestrator" "github.com/explorer/virtual-banker/backend/rag" "github.com/explorer/virtual-banker/backend/realtime" "github.com/explorer/virtual-banker/backend/session" "github.com/explorer/virtual-banker/backend/tools" "github.com/explorer/virtual-banker/backend/tools/banking" "github.com/explorer/virtual-banker/backend/tts" "github.com/jackc/pgx/v5/pgxpool" "github.com/redis/go-redis/v9" ) func main() { // Load configuration from environment dbURL := getEnv("DATABASE_URL", "postgres://explorer:changeme@localhost:5432/explorer?sslmode=disable") redisURL := getEnv("REDIS_URL", "redis://localhost:6379") port := getEnv("PORT", "8081") // Initialize database connection db, err := pgxpool.New(context.Background(), dbURL) if err != nil { log.Fatalf("Failed to connect to database: %v", err) } defer db.Close() // Initialize Redis connection opt, err := redis.ParseURL(redisURL) if err != nil { log.Fatalf("Failed to parse Redis URL: %v", err) } redisClient := redis.NewClient(opt) defer redisClient.Close() // Test connections if err := db.Ping(context.Background()); err != nil { log.Fatalf("Database ping failed: %v", err) } if err := redisClient.Ping(context.Background()).Err(); err != nil { log.Fatalf("Redis ping failed: %v", err) } // Initialize services sessionManager := session.NewManager(db, redisClient) // Initialize ASR/TTS (using mocks for now) asrService := asr.NewMockASRService() ttsService := tts.NewMockTTSService() // Initialize LLM (using mock for now) llmGateway := llm.NewMockLLMGateway() // Initialize RAG ragService := rag.NewRAGService(db) // Initialize tools toolRegistry := tools.NewRegistry() toolRegistry.Register(banking.NewAccountStatusTool()) toolRegistry.Register(banking.NewCreateTicketTool()) toolRegistry.Register(banking.NewScheduleAppointmentTool()) toolRegistry.Register(banking.NewSubmitPaymentTool()) auditLogger := &tools.MockAuditLogger{} toolExecutor := tools.NewExecutor(toolRegistry, auditLogger) // Initialize orchestrator convOrchestrator := orchestrator.NewOrchestrator( asrService, ttsService, llmGateway, ragService, toolExecutor, ) // Initialize realtime gateway realtimeGateway := realtime.NewGateway() // Initialize API server apiServer := api.NewServer(sessionManager, realtimeGateway) // Store orchestrator reference (would be used by handlers) _ = convOrchestrator // Create HTTP server srv := &http.Server{ Addr: ":" + port, Handler: apiServer, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } // Start server in goroutine go func() { log.Printf("Virtual Banker API server starting on port %s", port) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Server failed to start: %v", err) } }() // Wait for interrupt signal quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down server...") // Graceful shutdown ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatalf("Server forced to shutdown: %v", err) } log.Println("Server exited") } func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue }