feat: resume fixes, OpenRouter templates, and project context
Delegate expected output filenames to modules so resume works for rembg and composite; normalize G'MIC multi-frame output; add OpenRouter style reference support with tests. Add Crusaders, orange, and team gallery pipelines plus SOUL/AGENTS context files. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+98
-2
@@ -6,11 +6,16 @@ from pathlib import Path
|
||||
import pytest
|
||||
|
||||
from imagepipeline.core.pipeline import Pipeline
|
||||
from imagepipeline.core.resume import materialize_external_outputs, step_outputs_complete
|
||||
from imagepipeline.core.resume import (
|
||||
expected_output_filenames,
|
||||
materialize_external_outputs,
|
||||
step_outputs_complete,
|
||||
)
|
||||
from imagepipeline.core.step import StepDefinition
|
||||
from imagepipeline.modules.imagemagick_grayscale import ImageMagickGrayscale
|
||||
from imagepipeline.modules.registry import get_module
|
||||
from imagepipeline.utils.gmic import split_gmic_command
|
||||
from imagepipeline.modules.rembg import RembgModule
|
||||
from imagepipeline.utils.gmic import finalize_gmic_output, split_gmic_command
|
||||
from tests.conftest import make_png
|
||||
|
||||
|
||||
@@ -26,6 +31,97 @@ class TestGmicCommandSplit:
|
||||
assert parts == ["-fx_custom_gradient", "0,0,0,,1,0"]
|
||||
|
||||
|
||||
class TestFinalizeGmicOutput:
|
||||
def test_keeps_frame_000001_and_removes_000000(self, tmp_path: Path) -> None:
|
||||
output_dir = tmp_path / "out"
|
||||
output_dir.mkdir()
|
||||
intended = output_dir / "photo.png"
|
||||
frame_000000 = output_dir / "photo_000000.png"
|
||||
frame_000001 = output_dir / "photo_000001.png"
|
||||
frame_000000.write_bytes(b"discard")
|
||||
frame_000001.write_bytes(b"keep")
|
||||
|
||||
result = finalize_gmic_output(output_dir, intended)
|
||||
|
||||
assert result == intended
|
||||
assert intended.read_bytes() == b"keep"
|
||||
assert not frame_000000.exists()
|
||||
assert not frame_000001.exists()
|
||||
|
||||
def test_leaves_single_output_unchanged(self, tmp_path: Path) -> None:
|
||||
output_dir = tmp_path / "out"
|
||||
output_dir.mkdir()
|
||||
intended = output_dir / "photo.png"
|
||||
intended.write_bytes(b"single")
|
||||
|
||||
result = finalize_gmic_output(output_dir, intended)
|
||||
|
||||
assert result == intended
|
||||
assert intended.read_bytes() == b"single"
|
||||
|
||||
def test_renames_only_000000_when_000001_missing(self, tmp_path: Path) -> None:
|
||||
output_dir = tmp_path / "out"
|
||||
output_dir.mkdir()
|
||||
intended = output_dir / "photo.png"
|
||||
frame_000000 = output_dir / "photo_000000.png"
|
||||
frame_000000.write_bytes(b"only")
|
||||
|
||||
result = finalize_gmic_output(output_dir, intended)
|
||||
|
||||
assert result == intended
|
||||
assert intended.read_bytes() == b"only"
|
||||
assert not frame_000000.exists()
|
||||
|
||||
|
||||
class TestExpectedOutputFilenames:
|
||||
def test_rembg_maps_jpg_inputs_to_png_outputs(self, tmp_path: Path) -> None:
|
||||
jpg = tmp_path / "photo.jpg"
|
||||
jpg.write_bytes(b"jpeg")
|
||||
step = StepDefinition(
|
||||
step_id="rembg_01",
|
||||
module_name="rembg",
|
||||
module=RembgModule,
|
||||
input_refs=["input"],
|
||||
params={},
|
||||
output_dir_name="rembg_01",
|
||||
)
|
||||
|
||||
names = expected_output_filenames(
|
||||
step,
|
||||
matched_groups=[[jpg]],
|
||||
input_paths=[jpg],
|
||||
params=RembgModule.validate_module_params({}),
|
||||
)
|
||||
|
||||
assert names == ["photo.png"]
|
||||
|
||||
def test_rembg_resume_detects_existing_png_outputs(self, tmp_path: Path) -> None:
|
||||
output_dir = tmp_path / "rembg_01"
|
||||
output_dir.mkdir()
|
||||
png = output_dir / "photo.png"
|
||||
make_png(png)
|
||||
jpg = tmp_path / "input" / "photo.jpg"
|
||||
jpg.parent.mkdir()
|
||||
jpg.write_bytes(b"jpeg")
|
||||
step = StepDefinition(
|
||||
step_id="rembg_01",
|
||||
module_name="rembg",
|
||||
module=RembgModule,
|
||||
input_refs=["input"],
|
||||
params={},
|
||||
output_dir_name="rembg_01",
|
||||
)
|
||||
params = RembgModule.validate_module_params({})
|
||||
expected = expected_output_filenames(
|
||||
step,
|
||||
matched_groups=[[jpg]],
|
||||
input_paths=[jpg],
|
||||
params=params,
|
||||
)
|
||||
|
||||
assert step_outputs_complete([output_dir / name for name in expected])
|
||||
|
||||
|
||||
class TestPipelineResume:
|
||||
@pytest.mark.skipif(not shutil.which("magick"), reason="ImageMagick not installed")
|
||||
def test_continue_skips_completed_steps(self, input_dir: Path, output_base: Path, capsys) -> None:
|
||||
|
||||
Reference in New Issue
Block a user