package ml import ( "regexp" "strconv" ) // SdcliProgress contiene el estado de progreso parseado de una linea de stderr de sd-cli. type SdcliProgress struct { Step int `json:"step"` TotalSteps int `json:"total_steps"` ItPerSec float64 `json:"it_per_sec"` Percent float64 `json:"percent"` } // reProgress1 parsea el formato compacto: " 3/30 | 0.84it/s | 10%" var reProgress1 = regexp.MustCompile(`\s*(\d+)\s*/\s*(\d+)\s*\|[^|]*?([\d.]+)\s*it/s[^|]*?\|\s*([\d.]+)\s*%`) // reProgress2 parsea el formato verbose: "sampling: step 3 of 30 (0.84 it/s)" var reProgress2 = regexp.MustCompile(`step\s+(\d+)\s+of\s+(\d+)\s*\(\s*([\d.]+)\s*it/s\)`) // reProgress3 parsea el formato minimal: "step 3/30" o "progress: 3/30" var reProgress3 = regexp.MustCompile(`(?:progress[:\s]+)?(\d+)\s*/\s*(\d+)`) // SdcliParseProgress parsea una linea de stderr de stable-diffusion.cpp / sd-cli // y extrae el estado de progreso. Retorna (SdcliProgress, true) si la linea // contiene informacion de progreso reconocible; (zero, false) en caso contrario. // Funcion pura: sin I/O, sin estado mutable, determinista. func SdcliParseProgress(line string) (SdcliProgress, bool) { // Formato 1: " 3/30 | 0.84it/s | 10%" if m := reProgress1.FindStringSubmatch(line); m != nil { step, err1 := strconv.Atoi(m[1]) total, err2 := strconv.Atoi(m[2]) itPerSec, err3 := strconv.ParseFloat(m[3], 64) pct, err4 := strconv.ParseFloat(m[4], 64) if err1 == nil && err2 == nil && err3 == nil && err4 == nil { return SdcliProgress{ Step: step, TotalSteps: total, ItPerSec: itPerSec, Percent: pct, }, true } } // Formato 2: "sampling: step 3 of 30 (0.84 it/s)" if m := reProgress2.FindStringSubmatch(line); m != nil { step, err1 := strconv.Atoi(m[1]) total, err2 := strconv.Atoi(m[2]) itPerSec, err3 := strconv.ParseFloat(m[3], 64) if err1 == nil && err2 == nil && err3 == nil && total > 0 { pct := 100.0 * float64(step) / float64(total) return SdcliProgress{ Step: step, TotalSteps: total, ItPerSec: itPerSec, Percent: pct, }, true } } // Formato 3: "step 3/30" o "progress: 3/30" sin velocidad if m := reProgress3.FindStringSubmatch(line); m != nil { step, err1 := strconv.Atoi(m[1]) total, err2 := strconv.Atoi(m[2]) if err1 == nil && err2 == nil && total > 0 { pct := 100.0 * float64(step) / float64(total) return SdcliProgress{ Step: step, TotalSteps: total, ItPerSec: 0, Percent: pct, }, true } } return SdcliProgress{}, false }