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:
Frank Schwenk
2026-07-12 10:45:00 +02:00
parent 980c7f3b8b
commit ce431d7eec
22 changed files with 1616 additions and 31 deletions
+98 -2
View File
@@ -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: