aec5d82011
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
89 lines
2.3 KiB
Go
89 lines
2.3 KiB
Go
package ml
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// SdcliBinary describe el binario sd resuelto: su path absoluto, version detectada
|
|
// y como fue localizado.
|
|
type SdcliBinary struct {
|
|
Path string `json:"path"`
|
|
Version string `json:"version,omitempty"`
|
|
Source string `json:"source"` // "config" | "path"
|
|
}
|
|
|
|
// SdcliResolveBinary localiza el binario sd / sd-cli de stable-diffusion.cpp.
|
|
//
|
|
// Orden de busqueda:
|
|
// 1. Si hint != "" y el archivo existe y es ejecutable: usar como "config".
|
|
// 2. exec.LookPath("sd"): primer candidato en PATH.
|
|
// 3. exec.LookPath("sd-cli"): segundo candidato en PATH.
|
|
// 4. Error descriptivo si no se encuentra ninguno.
|
|
//
|
|
// Tras localizar el binario, intenta obtener la version ejecutando
|
|
// `<bin> --version` con timeout de 3 segundos. Si el comando falla
|
|
// o no produce output, Version queda vacia (no es error fatal).
|
|
func SdcliResolveBinary(hint string) (SdcliBinary, error) {
|
|
var binPath string
|
|
var source string
|
|
|
|
if hint != "" {
|
|
info, err := os.Stat(hint)
|
|
if err != nil {
|
|
return SdcliBinary{}, fmt.Errorf("sdcli hint %q: %w", hint, err)
|
|
}
|
|
if info.Mode()&0o111 == 0 {
|
|
return SdcliBinary{}, fmt.Errorf("sdcli hint %q: file exists but is not executable", hint)
|
|
}
|
|
binPath = hint
|
|
source = "config"
|
|
}
|
|
|
|
if binPath == "" {
|
|
if p, err := exec.LookPath("sd"); err == nil {
|
|
binPath = p
|
|
source = "path"
|
|
}
|
|
}
|
|
|
|
if binPath == "" {
|
|
if p, err := exec.LookPath("sd-cli"); err == nil {
|
|
binPath = p
|
|
source = "path"
|
|
}
|
|
}
|
|
|
|
if binPath == "" {
|
|
return SdcliBinary{}, fmt.Errorf(
|
|
"sd binary not found in PATH (hint: install from leejet/stable-diffusion.cpp)",
|
|
)
|
|
}
|
|
|
|
version := sdcliProbeVersion(binPath)
|
|
return SdcliBinary{
|
|
Path: binPath,
|
|
Version: version,
|
|
Source: source,
|
|
}, nil
|
|
}
|
|
|
|
// sdcliProbeVersion ejecuta `<bin> --version` con timeout 3s y retorna
|
|
// la primera linea de la salida. Retorna "" si el comando falla o no
|
|
// produce output; no propaga el error (version es best-effort).
|
|
func sdcliProbeVersion(binPath string) string {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
defer cancel()
|
|
|
|
out, err := exec.CommandContext(ctx, binPath, "--version").Output()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
line := strings.TrimSpace(strings.SplitN(string(out), "\n", 2)[0])
|
|
return line
|
|
}
|