package controllers import ( "bufio" "bytes" "encoding/json" "fmt" "net/http" "strings" "time" "github.com/gofiber/fiber/v2" "github.com/sujit-baniya/fiber-boilerplate/pkg/models" ) type DataIa struct { Prompt string `json:"prompt"` Model string `json:"model"` Stream bool `json:"stream"` } type respuestaOllama struct { Response string `json:"response"` Done bool `json:"done"` } func GeneraTextoStream(c *fiber.Ctx) error { var data DataIa if err := c.BodyParser(&data); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Datos inválidos"}) } data.Stream = true config, err := models.GetAiConfigForService("ia") if err != nil { return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{ "error": "No hay configuración de IA activa. Configura una en /app/ai-config", }) } if data.Model == "" { data.Model = config.ModelName } if data.Model == "" { data.Model = "gemma3:1b" } // Usar endpoint nativo de Ollama (/api/generate), no OpenAI compat (/v1/...) baseURL := strings.TrimSuffix(strings.TrimRight(config.BaseURL, "/"), "/v1") endpoint := baseURL + "/api/generate" jsonData, _ := json.Marshal(data) req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(jsonData)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") // Sin auth para red interna; api_key como token para URL pública if config.ApiKey != "" && config.ApiKey != "ollama" { req.SetBasicAuth("ollama", config.ApiKey) } client := &http.Client{Timeout: 120 * time.Second} resp, err := client.Do(req) if err != nil { return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": err.Error()}) } c.Set("Content-Type", "text/plain; charset=utf-8") c.Set("Cache-Control", "no-cache") c.Set("Connection", "keep-alive") c.Context().SetBodyStreamWriter(func(w *bufio.Writer) { defer resp.Body.Close() scanner := bufio.NewScanner(resp.Body) for scanner.Scan() { line := scanner.Bytes() if len(line) == 0 { continue } var msg respuestaOllama if err := json.Unmarshal(line, &msg); err != nil { continue } if msg.Response != "" { fmt.Fprint(w, msg.Response) w.Flush() } if msg.Done { break } } }) return nil }