package main import ( "bufio" "context" "fmt" "log" "os" "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gooseek/backend/internal/llm" "github.com/gooseek/backend/internal/medicine" "github.com/gooseek/backend/pkg/middleware" ) func main() { llmClient := createLLMClient() if llmClient == nil { log.Println("medicine-svc: no LLM configured, fallback mode enabled") } svc := medicine.NewService(medicine.ServiceConfig{ LLM: llmClient, SearXNGURL: getEnv("SEARXNG_URL", "http://searxng:8080"), Timeout: 20 * time.Second, }) app := fiber.New(fiber.Config{ ReadTimeout: 60 * time.Second, WriteTimeout: 120 * time.Second, }) app.Use(logger.New()) app.Use(cors.New()) app.Get("/health", func(c *fiber.Ctx) error { return c.JSON(fiber.Map{"status": "ok", "service": "medicine-svc"}) }) jwtOptional := middleware.JWTConfig{ Secret: os.Getenv("JWT_SECRET"), AuthSvcURL: getEnv("AUTH_SVC_URL", "http://auth-svc:3050"), AllowGuest: true, } api := app.Group("/api/v1/medicine") api.Post("/consult", middleware.JWT(jwtOptional), func(c *fiber.Ctx) error { var req medicine.ConsultRequest if err := c.BodyParser(&req); err != nil { return c.Status(400).JSON(fiber.Map{"error": "Invalid request body"}) } if req.Symptoms == "" { return c.Status(400).JSON(fiber.Map{"error": "symptoms is required"}) } c.Set("Content-Type", "application/x-ndjson") c.Set("Cache-Control", "no-cache") c.Set("Transfer-Encoding", "chunked") c.Context().SetBodyStreamWriter(func(w *bufio.Writer) { ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) defer cancel() if err := svc.StreamConsult(ctx, req, w); err != nil { log.Printf("medicine consult error: %v", err) } }) return nil }) port := getEnvInt("PORT", 3037) log.Printf("medicine-svc listening on :%d", port) log.Fatal(app.Listen(fmt.Sprintf(":%d", port))) } func createLLMClient() llm.Client { cfg := llm.ProviderConfig{ ProviderID: getEnv("LLM_PROVIDER", "timeweb"), ModelKey: getEnv("LLM_MODEL", "gpt-4o-mini"), BaseURL: os.Getenv("TIMEWEB_API_BASE_URL"), APIKey: os.Getenv("TIMEWEB_API_KEY"), AgentAccessID: os.Getenv("TIMEWEB_AGENT_ACCESS_ID"), } client, err := llm.NewClient(cfg) if err == nil { return client } if os.Getenv("OPENAI_API_KEY") != "" { openAIClient, openAIErr := llm.NewClient(llm.ProviderConfig{ ProviderID: "openai", ModelKey: "gpt-4o-mini", APIKey: os.Getenv("OPENAI_API_KEY"), }) if openAIErr == nil { return openAIClient } } return nil } func getEnv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func getEnvInt(key string, fallback int) int { v := os.Getenv(key) if v == "" { return fallback } var out int if _, err := fmt.Sscanf(v, "%d", &out); err != nil { return fallback } return out }