diff options
| -rw-r--r-- | doc/Design.md | 6 | ||||
| -rw-r--r-- | doc/Specifications.md | 3 | ||||
| -rw-r--r-- | doc/config.md | 24 | ||||
| -rw-r--r-- | src/mcp_sd15/config.py | 4 | ||||
| -rw-r--r-- | src/mcp_sd15/pipeline_factory.py | 5 | ||||
| -rw-r--r-- | src/mcp_sd15/service.py | 1 | ||||
| -rw-r--r-- | tests/test_config.py | 11 | ||||
| -rw-r--r-- | tests/test_pipeline_factory.py | 1 |
8 files changed, 52 insertions, 3 deletions
diff --git a/doc/Design.md b/doc/Design.md index b5e433b..9224158 100644 --- a/doc/Design.md +++ b/doc/Design.md @@ -197,11 +197,17 @@ Schedulerの文字列変換は`PipelineFactory`へ集約し、Tool層とServiceå | `dpmpp_2m_sde_karras` | `DPMSolverMultistepScheduler` | `algorithm_type="sde-dpmsolver++"`, `solver_order=2`, `use_karras_sigmas=True` | | `euler_a` | `EulerAncestralDiscreteScheduler` | pipeline scheduler configを継承 | | `ddim` | `DDIMScheduler` | pipeline scheduler configを継承 | +| `ddim_trailing` | `DDIMScheduler` | `timestep_spacing="trailing"` | | `lcm` | `LCMScheduler` | pipeline scheduler configを継承 | LCM-LoRAも通常のLoRAと同じ`[[model.lora]]`でロードする。`lcm`選択時にstepsやCFGを 自動調整せず、TOMLの`generation.steps`と`generation.cfg_scale`をそのままpipelineへ渡す。 +`ddim_trailing`は`ddim`と同じ`DDIMScheduler`を使用し、`timestep_spacing="trailing"`だけを +明示的に上書きする。Hyper-SD等の固定step LoRAと組み合わせることを想定しているが、 +LoRAファイル名による自動判定やsteps・CFGの自動補正は行わない。生成結果の再現性を保つため、 +実際に使用したscheduler名は生成メタデータへそのまま記録する。 + --- ## 9. ControlNet設計 diff --git a/doc/Specifications.md b/doc/Specifications.md index cc178bb..c038abf 100644 --- a/doc/Specifications.md +++ b/doc/Specifications.md @@ -198,7 +198,7 @@ negative_file = "negative.txt" ### 7.1 Scheduler -`generation.scheduler`は次の5種類のみを許可し、既定値は`dpmpp_2m_karras`とする。 +`generation.scheduler`は次の6種類のみを許可し、既定値は`dpmpp_2m_karras`とする。 | 値 | 動作 | |---|---| @@ -206,6 +206,7 @@ negative_file = "negative.txt" | `dpmpp_2m_sde_karras` | DPM++ 2M SDE + Karras sigmas | | `euler_a` | Euler Ancestral | | `ddim` | DDIM | +| `ddim_trailing` | DDIM。`timestep_spacing="trailing"`を強制 | | `lcm` | Latent Consistency Model scheduler | `lcm`選択時も`steps`と`cfg_scale`は設定値をそのまま使用する。LoRAファイル名から diff --git a/doc/config.md b/doc/config.md index 69dd3d6..5d7db2a 100644 --- a/doc/config.md +++ b/doc/config.md @@ -162,12 +162,15 @@ pad_last_block = false | `dpmpp_2m_karras` | `DPMSolverMultistepScheduler`。DPM++ 2M、Karras sigmas | | `dpmpp_2m_sde_karras` | `DPMSolverMultistepScheduler`。DPM++ 2M SDE、Karras sigmas | | `ddim` | DDIM | +| `ddim_trailing` | DDIM。`timestep_spacing="trailing"`を強制 | | `euler_a` | Euler Ancestral Discrete | | `lcm` | Latent Consistency Model scheduler | `dpmpp_2m_karras`は`algorithm_type="dpmsolver++"`、`solver_order=2`、 `use_karras_sigmas=true`で構築する。`dpmpp_2m_sde_karras`は`algorithm_type`だけを -`sde-dpmsolver++`へ変更する。他のschedulerはpipeline scheduler configを引き継ぐ。 +`sde-dpmsolver++`へ変更する。`ddim_trailing`は`ddim`と同じ`DDIMScheduler`を使用し、 +`timestep_spacing="trailing"`だけを明示的に上書きする。他のschedulerはpipeline scheduler +configを引き継ぐ。 ### LCM-LoRA設定例 @@ -188,6 +191,25 @@ default_size = "square_512" `lcm`選択時も`steps`と`cfg_scale`はTOML値をそのまま使用する。LCM-LoRAのファイル名を 自動判定して設定を書き換えることはない。 +### Hyper-SD設定例 + +Hyper-SD15固定step LoRAも通常のLoRAと同じ方法で指定する。`ddim_trailing`は +Hyper-SD向けの推奨schedulerであり、`timestep_spacing="trailing"`を強制する。 +stepsや`cfg_scale`はTOML値をそのまま使用し、LoRAファイル名による自動判定や自動補正は +行わない。 + +```toml +[[model.lora]] +file = "Hyper-SD15-4steps-lora.safetensors" +weight = 1.0 + +[generation] +steps = 4 +cfg_scale = 0.0 +scheduler = "ddim_trailing" +default_size = "square_512" +``` + ### `default_size` MCPツールで`size`を省略した場合に使用するサイズIDである。 diff --git a/src/mcp_sd15/config.py b/src/mcp_sd15/config.py index 39f31c9..c82cb6e 100644 --- a/src/mcp_sd15/config.py +++ b/src/mcp_sd15/config.py @@ -11,7 +11,9 @@ from typing import Any from .errors import ConfigError VALID_SIZES = frozenset({"square_512", "landscape_512", "portrait_512"}) -VALID_SCHEDULERS = frozenset({"dpmpp_2m_karras", "dpmpp_2m_sde_karras", "euler_a", "ddim", "lcm"}) +VALID_SCHEDULERS = frozenset( + {"dpmpp_2m_karras", "dpmpp_2m_sde_karras", "euler_a", "ddim", "ddim_trailing", "lcm"} +) VALID_TRANSPORTS = frozenset({"stdio", "streamable-http"}) diff --git a/src/mcp_sd15/pipeline_factory.py b/src/mcp_sd15/pipeline_factory.py index 02fe882..e865555 100644 --- a/src/mcp_sd15/pipeline_factory.py +++ b/src/mcp_sd15/pipeline_factory.py @@ -151,6 +151,11 @@ class PipelineFactory: solver_order=2, use_karras_sigmas=True, ) + if name == "ddim_trailing": + return diffusers.DDIMScheduler.from_config( + current.config, + timestep_spacing="trailing", + ) classes = { "euler_a": diffusers.EulerAncestralDiscreteScheduler, "ddim": diffusers.DDIMScheduler, diff --git a/src/mcp_sd15/service.py b/src/mcp_sd15/service.py index 65b62e7..c1feade 100644 --- a/src/mcp_sd15/service.py +++ b/src/mcp_sd15/service.py @@ -96,6 +96,7 @@ class ImageGenerationService: "seed": seed, "size": str(request.size), "mode": method, + "scheduler": self.config.generation.scheduler, "timestamp": datetime.now(UTC).isoformat(), "elapsed_seconds": round(time.monotonic() - started, 3), } diff --git a/tests/test_config.py b/tests/test_config.py index 5d339f8..0e34086 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -282,6 +282,17 @@ def test_generation_defaults_clip_skip_and_pad_last_block(tmp_path: Path) -> Non assert config.generation.pad_last_block is False +def test_generation_accepts_ddim_trailing_scheduler(tmp_path: Path) -> None: + path = tmp_path / "config.toml" + path.write_text( + f'[model]\nroot = "{tmp_path.as_posix()}"\ncheckpoint = "base.safetensors"\n' + '[generation]\nscheduler = "ddim_trailing"\n', + encoding="utf-8", + ) + config = load_config(path, validate_paths=False) + assert config.generation.scheduler == "ddim_trailing" + + def test_generation_reads_clip_skip_and_pad_last_block(tmp_path: Path) -> None: path = tmp_path / "config.toml" path.write_text( diff --git a/tests/test_pipeline_factory.py b/tests/test_pipeline_factory.py index 14b771b..e7904ab 100644 --- a/tests/test_pipeline_factory.py +++ b/tests/test_pipeline_factory.py @@ -142,6 +142,7 @@ def test_checkpoint_without_vae_fails_pipeline_initialization(app_config, monkey ), ("euler_a", "FakeEulerAncestralDiscreteScheduler", {}), ("ddim", "FakeDDIMScheduler", {}), + ("ddim_trailing", "FakeDDIMScheduler", {"timestep_spacing": "trailing"}), ("lcm", "FakeLCMScheduler", {}), ], ) |
