summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-09-12 20:12:17 +0900
committertanaka takashi <isazaka@gmail.com>2026-09-12 20:12:17 +0900
commitfd987cb31fd20483080f8d80ce35b53f101f243f (patch)
tree0e1d04c1f233e57d05f91006c8fd04f4caf89446 /tests
parent23f6c0850385d873d2e55926a34855c479cd3def (diff)
feat: SD 1.x向けScheduler対応を5種類に整理
generation.schedulerの正式対応値をdpmpp_2m_karras、dpmpp_2m_sde_karras、euler_a、ddim、lcmに限定し、既定値をdpmpp_2m_karrasへ変更する。旧dpmpp_2m、eulerおよび未知値は起動時の設定validationで拒否する。 Scheduler生成処理をPipelineFactoryへ集約する。DPM++ 2M系はDPMSolverMultistepSchedulerを共用し、algorithm_type、solver_order=2、use_karras_sigmas=Trueで通常版とSDE版を切り替える。Euler Ancestral、DDIM、LCMは現在のpipeline scheduler configを引き継いで構築する。 LCM-LoRAは既存のmodel.lora設定から通常のLoRAとして読み込み、LCM選択時にもstepsとcfg_scaleを自動調整せずTOML値をそのまま使用する。MCP Tool schemaにはscheduler入力を追加しない。 Specifications.md、Design.md、config.mdおよびconfig.example.tomlのenum、既定値、対応表、設定例を新仕様へ統一する。5 schedulerの変換、DPM++ constructor option、旧値と未知値の拒否、省略時の既定値、MCP schema非公開をテストで検証する。 検証: ruff check、ruff format --check、pyright、pytest(39 passed)、インストール済みDiffusersで5 schedulerの実インスタンス構築を確認。
Diffstat (limited to 'tests')
-rw-r--r--tests/test_config.py3
-rw-r--r--tests/test_pipeline_factory.py68
-rw-r--r--tests/test_server.py1
3 files changed, 66 insertions, 6 deletions
diff --git a/tests/test_config.py b/tests/test_config.py
index 5e4341a..c24064b 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -76,6 +76,8 @@ def test_loads_and_resolves_all_model_paths(tmp_path: Path) -> None:
[
('checkpoint = "../base.safetensors"', "file name"),
("steps = 0", "steps"),
+ ('scheduler = "dpmpp_2m"', "scheduler"),
+ ('scheduler = "euler"', "scheduler"),
('scheduler = "unknown"', "scheduler"),
('default_size = "1024"', "default_size"),
('preprocessor = "zoe"', "preprocessor"),
@@ -120,3 +122,4 @@ def test_can_skip_path_checks_for_schema_only_use(tmp_path: Path) -> None:
)
config = load_config(path, validate_paths=False)
assert config.model.root == (tmp_path / "missing").resolve()
+ assert config.generation.scheduler == "dpmpp_2m_karras"
diff --git a/tests/test_pipeline_factory.py b/tests/test_pipeline_factory.py
index 454a5b1..408133b 100644
--- a/tests/test_pipeline_factory.py
+++ b/tests/test_pipeline_factory.py
@@ -34,8 +34,24 @@ class FakePipeline:
class FakeScheduler:
@classmethod
- def from_config(cls, config: dict[str, object]) -> object:
- return {"scheduler": cls.__name__, **config}
+ def from_config(cls, config: dict[str, object], **kwargs: object) -> object:
+ return SimpleNamespace(implementation=cls.__name__, config=config, options=kwargs)
+
+
+class FakeDPMSolverMultistepScheduler(FakeScheduler):
+ pass
+
+
+class FakeDDIMScheduler(FakeScheduler):
+ pass
+
+
+class FakeEulerAncestralDiscreteScheduler(FakeScheduler):
+ pass
+
+
+class FakeLCMScheduler(FakeScheduler):
+ pass
def fake_diffusers(pipeline: FakePipeline, external_vae: object) -> SimpleNamespace:
@@ -54,10 +70,10 @@ def fake_diffusers(pipeline: FakePipeline, external_vae: object) -> SimpleNamesp
return SimpleNamespace(
StableDiffusionPipeline=StableDiffusionPipeline,
AutoencoderKL=AutoencoderKL,
- DPMSolverMultistepScheduler=FakeScheduler,
- DDIMScheduler=FakeScheduler,
- EulerDiscreteScheduler=FakeScheduler,
- EulerAncestralDiscreteScheduler=FakeScheduler,
+ DPMSolverMultistepScheduler=FakeDPMSolverMultistepScheduler,
+ DDIMScheduler=FakeDDIMScheduler,
+ EulerAncestralDiscreteScheduler=FakeEulerAncestralDiscreteScheduler,
+ LCMScheduler=FakeLCMScheduler,
)
@@ -97,3 +113,43 @@ def test_checkpoint_without_vae_fails_pipeline_initialization(app_config, monkey
with pytest.raises(ConfigError, match="does not provide a VAE"):
factory.create_base()
+
+
+@pytest.mark.parametrize(
+ ("name", "implementation", "options"),
+ [
+ (
+ "dpmpp_2m_karras",
+ "FakeDPMSolverMultistepScheduler",
+ {
+ "algorithm_type": "dpmsolver++",
+ "solver_order": 2,
+ "use_karras_sigmas": True,
+ },
+ ),
+ (
+ "dpmpp_2m_sde_karras",
+ "FakeDPMSolverMultistepScheduler",
+ {
+ "algorithm_type": "sde-dpmsolver++",
+ "solver_order": 2,
+ "use_karras_sigmas": True,
+ },
+ ),
+ ("euler_a", "FakeEulerAncestralDiscreteScheduler", {}),
+ ("ddim", "FakeDDIMScheduler", {}),
+ ("lcm", "FakeLCMScheduler", {}),
+ ],
+)
+def test_scheduler_mapping(app_config, name, implementation, options) -> None:
+ generation = replace(app_config.generation, scheduler=name)
+ factory = PipelineFactory(replace(app_config, generation=generation))
+ current_config = {"beta_start": 0.00085, "beta_end": 0.012}
+ current = SimpleNamespace(config=current_config)
+ diffusers = fake_diffusers(FakePipeline(), object())
+
+ result = factory._create_scheduler(current, diffusers)
+
+ assert result.implementation == implementation
+ assert result.config is current_config
+ assert result.options == options
diff --git a/tests/test_server.py b/tests/test_server.py
index 4590049..a6c6af5 100644
--- a/tests/test_server.py
+++ b/tests/test_server.py
@@ -25,6 +25,7 @@ async def test_exposes_exactly_three_tools_with_closed_enums(app_config) -> None
"openpose_full",
]
assert "width" not in guided.input_schema["properties"]
+ assert all("scheduler" not in tool.input_schema["properties"] for tool in tools)
async def test_mcp_call_returns_png_image_content(app_config) -> None: