diff --git a/README.md b/README.md index 1c605c2..1d4bfd9 100644 --- a/README.md +++ b/README.md @@ -329,8 +329,8 @@ All fields except `prompt` are optional. Omitted fields use the loaded model's d |-------|------|---------|-------------| | `prompt` | `string` | *(required)* | Text prompt | | `negative_prompt` | `string` | `""` | Negative prompt | -| `width` | `int` | model default | Output width in pixels | -| `height` | `int` | model default | Output height in pixels | +| `width` | `int` | model default | Output width in pixels (0..4096) | +| `height` | `int` | model default | Output height in pixels (0..4096) | | `steps` | `int` | model default | Number of denoising steps | | `guidance_scale` | `float` | model default | CFG scale (≤ 1.0 disables CFG) | | `seed` | `int` | random | Random seed (`-1` for random) | diff --git a/tests/test_api.py b/tests/test_api.py index fff3ba0..22c63e3 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -56,12 +56,12 @@ def test_upscalers_available_without_model(): def test_text2image_passes_pixel_upscaler(tmp_path): runtime = _fake_runtime(tmp_path) app = create_app(runtime) - req = Text2ImageRequest(prompt="a fox", pixel_upscaler="RealESRGAN_x4") + req = Text2ImageRequest(prompt="a fox", width=512, height=512, pixel_upscaler="RealESRGAN_x4") res = _endpoint(app, "/text2image")(req) assert res.status_code == 200 assert runtime._pipeline.last_request.pixel_upscaler == "RealESRGAN_x4" def test_request_field_defaults_none(): - req = Text2ImageRequest(prompt="x") + req = Text2ImageRequest(prompt="x", width=512, height=512) assert req.pixel_upscaler is None diff --git a/thenoise/api.py b/thenoise/api.py index bf3b634..5d47698 100644 --- a/thenoise/api.py +++ b/thenoise/api.py @@ -30,6 +30,7 @@ class Text2ImageRequest(BaseModel): negative_prompt: str = "" width: Optional[int] = None height: Optional[int] = None + steps: Optional[int] = None guidance_scale: Optional[float] = None seed: Optional[int] = None diff --git a/thenoise/generate.py b/thenoise/generate.py index 972cac6..bf45050 100644 --- a/thenoise/generate.py +++ b/thenoise/generate.py @@ -9,12 +9,21 @@ import logging import os import random +import sys logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def run_generate(args) -> None: + _MAX_DIM = 4096 + if args.width is not None and (args.width < 0 or args.width > _MAX_DIM): + print(f"error: width must be between 0 and {_MAX_DIM} (got {args.width}).", file=sys.stderr) + sys.exit(1) + if args.height is not None and (args.height < 0 or args.height > _MAX_DIM): + print(f"error: height must be between 0 and {_MAX_DIM} (got {args.height}).", file=sys.stderr) + sys.exit(1) + from .models.config import GenerateRequest from .runtime import Settings, ModelPaths, Runtime settings = Settings(device=args.device) diff --git a/thenoise/ui/index.html b/thenoise/ui/index.html index c7d147e..c3abb6c 100644 --- a/thenoise/ui/index.html +++ b/thenoise/ui/index.html @@ -242,11 +242,13 @@

TheNoise

+ Max 4096
+ Max 4096
@@ -744,6 +746,18 @@

TheNoise

const samplerVal = $('sampler').value; if (samplerVal) body.sampler = samplerVal; + const MAX_DIM = 4096; + for (const f of ['width', 'height']) { + const v = $(f).value === '' ? null : parseInt($(f).value, 10); + if (v !== null && (v < 0 || v > MAX_DIM)) { + btn.disabled = false; + clearInterval(timer); + overlay.classList.add('hidden'); + alert(`error: ${f} must be between 0 and ${MAX_DIM} (got ${v}).`); + return; + } + } + try { const res = await fetch('/text2image', { method: 'POST',