feat(ml): auto-commit con 14 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
"""Tests para el backend sdcpp_python: sdcpp_python_load y sdcpp_python_generate."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
# Ajustar path para importar desde python/functions/ml/
|
||||
_ML_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
sys.path.insert(0, _ML_PATH)
|
||||
|
||||
# Importacion lazy — salta todos los tests si el package no esta instalado.
|
||||
pytest.importorskip(
|
||||
"stable_diffusion_cpp",
|
||||
reason="stable_diffusion_cpp no instalado — skip tests sdcpp_python backend",
|
||||
)
|
||||
|
||||
# El paquete usa modulos hermanos sin prefijo (model_ref, generation_config...).
|
||||
# Para evitar el double-import problem, mapeamos los aliases antes de importar
|
||||
# las funciones bajo test.
|
||||
import ml.model_ref as _mref_module
|
||||
import ml.generation_config as _gcfg_module
|
||||
import ml.image_gen_result as _igr_module
|
||||
|
||||
for _alias, _mod in [
|
||||
("model_ref", _mref_module),
|
||||
("generation_config", _gcfg_module),
|
||||
("image_gen_result", _igr_module),
|
||||
]:
|
||||
if _alias not in sys.modules:
|
||||
sys.modules[_alias] = _mod # type: ignore[assignment]
|
||||
|
||||
from ml.model_ref import ModelRef
|
||||
from ml.generation_config import GenerationConfig
|
||||
from ml.image_gen_result import ImageGenResult
|
||||
from ml.sdcpp_python_load import sdcpp_python_load, _clear_sd_cache
|
||||
from ml.sdcpp_python_generate import sdcpp_python_generate
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Constantes
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SD_TURBO_SAFETENSORS = (
|
||||
"/home/lucas/vaults/imagegen_models/diffusers/sd-turbo/sd_turbo.safetensors"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def sd_turbo_model() -> ModelRef:
|
||||
"""ModelRef apuntando al safetensors de SD Turbo en local."""
|
||||
if not os.path.isfile(SD_TURBO_SAFETENSORS):
|
||||
pytest.skip(
|
||||
f"Modelo SD Turbo no encontrado en {SD_TURBO_SAFETENSORS}"
|
||||
)
|
||||
return ModelRef(
|
||||
name="sd-turbo",
|
||||
model_type="sd15",
|
||||
quantization="fp16",
|
||||
path=SD_TURBO_SAFETENSORS,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def loaded_sd(sd_turbo_model: ModelRef):
|
||||
"""StableDiffusion cargado una sola vez para toda la sesion de tests."""
|
||||
_clear_sd_cache()
|
||||
sd = sdcpp_python_load(sd_turbo_model, n_threads=-1, wtype="default")
|
||||
yield sd
|
||||
_clear_sd_cache()
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def sd_turbo_cfg(sd_turbo_model: ModelRef) -> GenerationConfig:
|
||||
"""GenerationConfig para SD Turbo: 512x512, 4 steps, euler_a, seed=42."""
|
||||
return GenerationConfig(
|
||||
prompt="a simple red apple on a white table",
|
||||
negative_prompt=None,
|
||||
seed=42,
|
||||
steps=4,
|
||||
cfg_scale=1.0,
|
||||
sampler="euler_a",
|
||||
width=512,
|
||||
height=512,
|
||||
model=sd_turbo_model,
|
||||
loras=[],
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_load_retorna_objeto(loaded_sd) -> None:
|
||||
"""load retorna objeto StableDiffusion"""
|
||||
from stable_diffusion_cpp import StableDiffusion
|
||||
|
||||
assert isinstance(loaded_sd, StableDiffusion), (
|
||||
f"Se esperaba StableDiffusion, se obtuvo {type(loaded_sd)}"
|
||||
)
|
||||
|
||||
|
||||
def test_load_caches(sd_turbo_model: ModelRef, loaded_sd) -> None:
|
||||
"""segunda llamada retorna instancia cacheada"""
|
||||
import time
|
||||
|
||||
t0 = time.perf_counter()
|
||||
sd2 = sdcpp_python_load(sd_turbo_model, n_threads=-1, wtype="default")
|
||||
elapsed = time.perf_counter() - t0
|
||||
|
||||
assert sd2 is loaded_sd, "Segunda llamada debe retornar la misma instancia cacheada"
|
||||
assert elapsed < 0.5, (
|
||||
f"Segunda llamada tardo {elapsed:.3f}s — deberia ser inmediata (cache hit)"
|
||||
)
|
||||
|
||||
|
||||
def test_generate_retorna_image_gen_result(
|
||||
loaded_sd, sd_turbo_cfg: GenerationConfig
|
||||
) -> None:
|
||||
"""generate retorna ImageGenResult valido"""
|
||||
result = sdcpp_python_generate(loaded_sd, sd_turbo_cfg)
|
||||
|
||||
assert isinstance(result, ImageGenResult), (
|
||||
f"Se esperaba ImageGenResult, se obtuvo {type(result)}"
|
||||
)
|
||||
assert result.image is not None, "result.image no debe ser None"
|
||||
|
||||
|
||||
def test_duration_ms_mayor_que_cero(
|
||||
loaded_sd, sd_turbo_cfg: GenerationConfig
|
||||
) -> None:
|
||||
"""duration_ms mayor que cero"""
|
||||
result = sdcpp_python_generate(loaded_sd, sd_turbo_cfg)
|
||||
assert result.duration_ms > 0, (
|
||||
f"duration_ms debe ser > 0, se obtuvo {result.duration_ms}"
|
||||
)
|
||||
|
||||
|
||||
def test_meta_backend_es_sdcpp_python(
|
||||
loaded_sd, sd_turbo_cfg: GenerationConfig
|
||||
) -> None:
|
||||
"""meta backend es sdcpp_python"""
|
||||
result = sdcpp_python_generate(loaded_sd, sd_turbo_cfg)
|
||||
assert result.meta.get("backend") == "sdcpp_python", (
|
||||
f"meta['backend'] debe ser 'sdcpp_python', se obtuvo {result.meta.get('backend')!r}"
|
||||
)
|
||||
assert result.meta.get("model") == sd_turbo_cfg.model.name
|
||||
assert result.meta.get("sampler") == sd_turbo_cfg.sampler
|
||||
assert result.meta.get("seed") == sd_turbo_cfg.seed
|
||||
|
||||
|
||||
def test_vram_peak_mb_es_none(
|
||||
loaded_sd, sd_turbo_cfg: GenerationConfig
|
||||
) -> None:
|
||||
"""vram_peak_mb es None — sdcpp no expone medicion de VRAM"""
|
||||
result = sdcpp_python_generate(loaded_sd, sd_turbo_cfg)
|
||||
assert result.vram_peak_mb is None, (
|
||||
f"vram_peak_mb debe ser None, se obtuvo {result.vram_peak_mb}"
|
||||
)
|
||||
Reference in New Issue
Block a user