From 46e08e9aefb45741e82fe54d2014be136a49571c Mon Sep 17 00:00:00 2001 From: db0 Date: Sun, 30 Nov 2025 17:42:25 +0100 Subject: [PATCH] feat: z-image-turbo support --- CHANGELOG.md | 4 ++++ horde/apis/v2/stable.py | 5 ++++- horde/classes/stable/processing_generation.py | 7 +++++-- horde/classes/stable/waiting_prompt.py | 4 +++- horde/consts.py | 1 + horde/model_reference.py | 11 ++++++----- horde/validation.py | 5 ++++- 7 files changed, 27 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8503206d..ee3c8b29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-or-later # Changelog +# 4.48.4 + +Z-Image-Turbo Beta + # 4.48.3 Qwen-Image Beta diff --git a/horde/apis/v2/stable.py b/horde/apis/v2/stable.py index 066e4971..b7713e8d 100644 --- a/horde/apis/v2/stable.py +++ b/horde/apis/v2/stable.py @@ -154,9 +154,12 @@ class ImageAsyncGenerate(GenerateTemplate): if any(model_reference.get_model_baseline(model_name).startswith("flux_1") for model_name in self.args.models): if "control_type" in self.params: raise e.BadRequest("ControlNet does not work with Flux currently.", rc="ControlNetMismatch") - if any(model_reference.get_model_baseline(model_name).startswith("Qwen") for model_name in self.args.models): + if any(model_reference.get_model_baseline(model_name).startswith("qwen_image") for model_name in self.args.models): if "control_type" in self.params: raise e.BadRequest("ControlNet does not work with Qwen currently.", rc="ControlNetMismatch") + if any(model_reference.get_model_baseline(model_name).startswith("z_image_turbo") for model_name in self.args.models): + if "control_type" in self.params: + raise e.BadRequest("ControlNet does not work with Z-Image currently.", rc="ControlNetMismatch") if self.params.get("transparent", False) is True: if self.args.extra_source_images and len(self.args.extra_source_images) > 0: raise e.BadRequest( diff --git a/horde/classes/stable/processing_generation.py b/horde/classes/stable/processing_generation.py index b44a90fa..7ccf9e7f 100644 --- a/horde/classes/stable/processing_generation.py +++ b/horde/classes/stable/processing_generation.py @@ -63,9 +63,12 @@ class ImageProcessingGeneration(ProcessingGeneration): if self.wp.params.get("hires_fix", False): return self.wp.kudos * 7 return self.wp.kudos * 4 - if model_reference.get_model_baseline(self.model) in ["flux_1", "qwen_image"]: + if model_reference.get_model_baseline(self.model) in ["flux_1", "z_image_turbo"]: # Flux and Qwen is double the size of SDXL and much slower, so it gives double the rewards from it. return self.wp.kudos * 8 + if model_reference.get_model_baseline(self.model) in ["qwen_image"]: + # Qwen is even larger than flux. + return self.wp.kudos * 12 return self.wp.kudos def log_aborted_generation(self): @@ -166,7 +169,7 @@ class ImageProcessingGeneration(ProcessingGeneration): if self.wp.gen_payload.get("control_type"): self.job_ttl = self.job_ttl * 2 # Flux and Qwen is way slower than Stable Diffusion - if any(model_reference.get_model_baseline(mn) in ["flux_1", "qwen_image"] for mn in self.wp.get_model_names()): + if any(model_reference.get_model_baseline(mn) in ["flux_1", "qwen_image", "z_image_turbo"] for mn in self.wp.get_model_names()): self.job_ttl = self.job_ttl * 3 if self.job_ttl < 150: self.job_ttl = 150 diff --git a/horde/classes/stable/waiting_prompt.py b/horde/classes/stable/waiting_prompt.py index ea557b57..2e2372b0 100644 --- a/horde/classes/stable/waiting_prompt.py +++ b/horde/classes/stable/waiting_prompt.py @@ -472,8 +472,10 @@ class ImageWaitingPrompt(WaitingPrompt): return (self.calculate_extra_kudos_burn(kudos) * self.n * 2) + 1 if model_reference.get_model_baseline(model_name) in ["stable_cascade"]: return (self.calculate_extra_kudos_burn(kudos) * self.n * 4) + 1 - if model_reference.get_model_baseline(model_name) in ["flux_1", "qwen_image"]: + if model_reference.get_model_baseline(model_name) in ["flux_1", "z_image_turbo"]: return (self.calculate_extra_kudos_burn(kudos) * self.n * 8) + 1 + if model_reference.get_model_baseline(model_name) in ["qwen_image"]: + return (self.calculate_extra_kudos_burn(kudos) * self.n * 12) + 1 # The +1 is the extra kudos burn per request return (self.calculate_extra_kudos_burn(kudos) * self.n) + 1 diff --git a/horde/consts.py b/horde/consts.py index 8912dfa3..280c90af 100644 --- a/horde/consts.py +++ b/horde/consts.py @@ -45,6 +45,7 @@ HEAVY_POST_PROCESSORS = { BASELINE_BATCHING_MULTIPLIERS = { "flux_1": 5, "qwen_image": 10, + "z_image_turbo": 5, } diff --git a/horde/model_reference.py b/horde/model_reference.py index 094f1ed5..088bd22f 100644 --- a/horde/model_reference.py +++ b/horde/model_reference.py @@ -33,11 +33,9 @@ class ModelReference(PrimaryTimedFunction): for _riter in range(10): try: ref_json = "https://raw.githubusercontent.com/Haidra-Org/AI-Horde-image-model-reference/main/stable_diffusion.json" - if datetime.utcnow() <= datetime(2025, 12, 30): # Qwen beta - ref_json = ( - "https://raw.githubusercontent.com/Haidra-Org/AI-Horde-image-model-reference/refs/heads/qwen/stable_diffusion.json" - ) - logger.debug("Using qwen beta model reference...") + if datetime.utcnow() <= datetime(2025, 12, 30): # Qwen and Zmodel beta + ref_json = "https://raw.githubusercontent.com/Haidra-Org/AI-Horde-image-model-reference/refs/heads/zmodel/stable_diffusion.json" + logger.debug("Using qwen and zmodel beta model reference...") self.reference = requests.get( os.getenv( "HORDE_IMAGE_COMPVIS_REFERENCE", @@ -64,6 +62,7 @@ class ModelReference(PrimaryTimedFunction): "stable_cascade", "flux_1", "qwen_image", + "z_image_turbo", }: self.stable_diffusion_names.add(model) if self.reference[model].get("nsfw"): @@ -108,6 +107,8 @@ class ModelReference(PrimaryTimedFunction): return "flux_1" if not model_details and "[Qwen]" in model_name: return "qwen_image" + if not model_details and ("[ZModel]" in model_name or "[ZImage]" in model_name): + return "z_image_turbo" return model_details.get("baseline", "stable diffusion 1") def get_all_model_baselines(self, model_names): diff --git a/horde/validation.py b/horde/validation.py index 92715d3e..4f8a9da8 100644 --- a/horde/validation.py +++ b/horde/validation.py @@ -74,9 +74,12 @@ class ParamValidator: if any(model_reference.get_model_baseline(model_name).startswith("flux_1") for model_name in self.models): if self.params.get("hires_fix", False) is True: raise e.BadRequest("HiRes Fix does not work with Flux currently.", rc="HiResMismatch") - if any(model_reference.get_model_baseline(model_name).startswith("Qwen") for model_name in self.models): + if any(model_reference.get_model_baseline(model_name).startswith("qwen_image") for model_name in self.models): if self.params.get("hires_fix", False) is True: raise e.BadRequest("HiRes Fix does not work with Qwen currently.", rc="HiResMismatch") + if any(model_reference.get_model_baseline(model_name).startswith("z_image_turbo") for model_name in self.models): + if self.params.get("hires_fix", False) is True: + raise e.BadRequest("HiRes Fix does not work with Z-Image currently.", rc="HiResMismatch") if "loras" in self.params: if len(self.params["loras"]) > 5: raise e.BadRequest("You cannot request more than 5 loras per generation.", rc="TooManyLoras")