diff --git a/CHANGELOG.md b/CHANGELOG.md index eca3d3be..6166dbac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-or-later # Changelog +# 4.46.2 + +* Fixes crash when styles included comfyUI-style {randomized|strings} +* Allows styles to avoid falling back to default params such as width/height. When a style doesn't have a width/height specified, and the user provided values for them, they will be used for the style instead of the horde defaults. + + # 4.46.1 Fixes crash when trying to set messages to foreign workers diff --git a/horde/apis/models/stable_v2.py b/horde/apis/models/stable_v2.py index 979c3891..255d03c6 100644 --- a/horde/apis/models/stable_v2.py +++ b/horde/apis/models/stable_v2.py @@ -2,6 +2,8 @@ # # SPDX-License-Identifier: AGPL-3.0-or-later +import copy + from flask_restx import fields from horde.apis.models import v2 @@ -390,6 +392,13 @@ def __init__(self, api): ), }, ) + # We have to do this to remove the defaults for styles + temp_model_copy = copy.deepcopy(self.root_model_generation_payload_style_stable) + v2.remove_all_model_default_params(temp_model_copy) + self.root_model_generation_payload_style_stable_nodefaults = api.inherit( + "ModelStyleInputParamsStableNoDefaults", + temp_model_copy, + ) self.root_model_generation_payload_stable = api.inherit( "ModelPayloadRootStable", self.root_model_generation_payload_style_stable, @@ -992,9 +1001,9 @@ def __init__(self, api): # Styles self.input_model_style_params = api.inherit( "ModelStyleInputParamsStable", - self.root_model_generation_payload_style_stable, + self.root_model_generation_payload_style_stable_nodefaults, { - "steps": fields.Integer(default=30, required=False, min=1, max=500), + "steps": fields.Integer(required=False, min=1, max=500), }, ) self.input_model_style = api.model( @@ -1169,6 +1178,6 @@ def __init__(self, api): "use_count": fields.Integer(description="The amount of times this style has been used in generations."), "creator": fields.String(description="The alias of the user to whom this style belongs to.", example="db0#1"), "examples": fields.List(fields.Nested(self.response_model_style_example, skip_none=True)), - "shared_key": fields.Nested(self.response_model_sharedkey_details), + "shared_key": fields.Nested(self.response_model_sharedkey_details, skip_none=True), }, ) diff --git a/horde/apis/models/v2.py b/horde/apis/models/v2.py index 40d13bae..40a5a186 100644 --- a/horde/apis/models/v2.py +++ b/horde/apis/models/v2.py @@ -9,6 +9,20 @@ from horde.vars import horde_noun, horde_title +def get_default_keys_of_model(model): + def_keys = set() + for key in model.keys(): + if model[key].default is not None: + def_keys.add(key) + return def_keys + + +def remove_all_model_default_params(model): + def_keys = get_default_keys_of_model(model) + for key in def_keys: + model[key].default = None + + class Parsers: def __init__(self): # A Basic parser which only expects a Client-Agent diff --git a/horde/apis/v2/stable.py b/horde/apis/v2/stable.py index 13122468..383bd0a3 100644 --- a/horde/apis/v2/stable.py +++ b/horde/apis/v2/stable.py @@ -9,6 +9,7 @@ import requests from flask import request from flask_restx import Resource, reqparse +from loguru import logger import horde.apis.limiter_api as lim import horde.classes.base.stats as stats @@ -37,7 +38,6 @@ from horde.flask import HORDE, cache, db from horde.image import calculate_image_tiles, ensure_source_image_uploaded from horde.limiter import limiter -from horde.logger import logger from horde.model_reference import model_reference from horde.patreon import patrons from horde.utils import does_extra_text_reference_exist, hash_dictionary @@ -381,10 +381,25 @@ def apply_style(self): self.negprompt = "###" + self.negprompt # We need to use defaultdict to avoid getting keyerrors in case the style author added # Erroneous keys in the string - self.prompt = self.existing_style.prompt.format_map(defaultdict(str, p=self.prompt, np=self.negprompt)) + # We have to do this sort of replacement to prevent randomized comfyUI strings from getting confused + # With the {p}/{np} prompt replacement areas + self.prompt = ( + self.existing_style.prompt.replace("{", "{{") + .replace("}", "}}") + .replace("{{p}}", "{p}") + .replace("{{np}}", "{np}") + .format_map(defaultdict(str, p=self.prompt, np=self.negprompt)) + ) + # self.prompt = self.existing_style.prompt.format_map(defaultdict(str, p=self.prompt, np=self.negprompt)) requested_n = self.params.get("n", 1) + user_params = self.params self.params = self.existing_style.params self.params["n"] = requested_n + # This allows a style without specified width/height to receive these variables from the user. + default_params = {"width", "height"} + for default_param in default_params: + if default_param not in self.params and default_param in user_params: + self.params[default_param] = user_params[default_param] self.nsfw = self.existing_style.nsfw self.existing_style.use_count += 1 # We don't reward kudos to ourselves diff --git a/horde/consts.py b/horde/consts.py index b403c1b6..52a1927c 100644 --- a/horde/consts.py +++ b/horde/consts.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: AGPL-3.0-or-later -HORDE_VERSION = "4.46.0" +HORDE_VERSION = "4.46.2" HORDE_API_VERSION = "2.5" WHITELISTED_SERVICE_IPS = {