package controllers import ( "bufio" "bytes" "encoding/json" "fmt" "io" "log" "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"` Error string `json:"error"` } 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 hasDBConfig := true config, err := models.GetAiConfigForService("ia") if err != nil { hasDBConfig = false config = &models.AiConfig{ BaseURL: "http://72.60.24.97:8080/ollama", ApiKey: "", ModelName: "gemma3:1b", } } if data.Model == "" { data.Model = config.ModelName } if data.Model == "" { data.Model = "gemma3:1b" } baseURL := strings.TrimSuffix(strings.TrimRight(config.BaseURL, "/"), "/v1") endpoint := baseURL + "/api/generate" if !hasDBConfig { log.Printf("[IA] Usando config default: endpoint=%s model=%s", endpoint, data.Model) } else { log.Printf("[IA] Usando config BD: endpoint=%s model=%s", endpoint, data.Model) } 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") 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 { log.Printf("[IA] Error conectando a Ollama: %v", err) return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": err.Error()}) } if resp.StatusCode != http.StatusOK { bodyBytes, _ := io.ReadAll(resp.Body) resp.Body.Close() log.Printf("[IA] Ollama status %d: %s", resp.StatusCode, string(bodyBytes)) return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{ "error": fmt.Sprintf("Ollama respondió con status %d", resp.StatusCode), "details": string(bodyBytes), }) } c.Set("Content-Type", "text/plain; charset=utf-8") c.Set("Cache-Control", "no-cache") c.Set("X-Accel-Buffering", "no") c.Context().SetBodyStreamWriter(func(w *bufio.Writer) { defer resp.Body.Close() dec := json.NewDecoder(resp.Body) for { var msg respuestaOllama if err := dec.Decode(&msg); err != nil { if err != io.EOF { log.Printf("[IA] Error decodificando: %v", err) } break } if msg.Error != "" { log.Printf("[IA] Ollama error: %s", msg.Error) fmt.Fprintf(w, "[Error: %s]", msg.Error) w.Flush() break } if msg.Response != "" { fmt.Fprint(w, msg.Response) w.Flush() } if msg.Done { break } } }) return nil }