Files
virtual-banker/backend/main.go
defiQUG 9839401d1d
Some checks failed
CI / build (push) Has been cancelled
TTS: configurable auth, Health check, Phoenix options; .env.example; Gitea CI workflow
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 16:54:10 -08:00

159 lines
4.7 KiB
Go

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
asrService := asr.NewMockASRService()
ttsService := newTTSService()
// 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")
}
// newTTSService returns a TTS service from env: use real API when TTS_API_KEY (or
// ELEVENLABS_API_KEY) and TTS_VOICE_ID are set. Optional: TTS_BASE_URL (Phoenix),
// TTS_AUTH_HEADER_NAME / TTS_AUTH_HEADER_VALUE (e.g. Authorization: Bearer),
// USE_PHOENIX_TTS=true to require TTS_BASE_URL.
func newTTSService() tts.Service {
apiKey := getEnv("TTS_API_KEY", os.Getenv("ELEVENLABS_API_KEY"))
voiceID := getEnv("TTS_VOICE_ID", os.Getenv("ELEVENLABS_VOICE_ID"))
baseURL := getEnv("TTS_BASE_URL", "")
authName := getEnv("TTS_AUTH_HEADER_NAME", "")
authValue := getEnv("TTS_AUTH_HEADER_VALUE", "")
usePhoenix := getEnv("USE_PHOENIX_TTS", "") == "true" || getEnv("USE_PHOENIX_TTS", "") == "1"
if usePhoenix && baseURL == "" {
baseURL = getEnv("PHOENIX_TTS_BASE_URL", "https://phoenix.example.com/tts/v1")
}
hasAuth := apiKey != "" || authValue != ""
if hasAuth && voiceID != "" {
opts := tts.TTSOptions{BaseURL: baseURL, AuthHeaderName: authName, AuthHeaderValue: authValue}
return tts.NewElevenLabsTTSServiceWithOptionsFull(apiKey, voiceID, opts)
}
return tts.NewMockTTSService()
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}