pull/223/head
ThereforeGames 2023-10-27 16:36:50 -04:00
parent aac4796fa3
commit 5148b2d02e
19 changed files with 515 additions and 27 deletions

13
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [ThereforeGames]
patreon: ThereforeGames
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -49,7 +49,8 @@
"tag_close":"/",
"tag_escape":"`",
"delimiter":"|",
"wizard_delimiter":"🡢"
"wizard_delimiter":"🡢",
"not_operator":"!"
},
"templates":
{

View File

@ -7,6 +7,6 @@ Unprompted is a powerful templating language written in Python. Unlike most temp
- 💬 [Discussion Board](https://github.com/ThereforeGames/unprompted/discussions)
- 🔧 [Issue Tracker](https://github.com/ThereforeGames/unprompted/issues)
Software created by [Therefore Games](https://therefore.games). If you like what I do, you can [support me on <span class="patreon-symbol">┃🔴</span> Patreon](https://patreon.com/thereforegames).
Software created by [Therefore Games](https://therefore.games). If you like my work, you can [sponsor the project on ☕ Github](https://github.com/sponsors/ThereforeGames) or [support me on <span class="patreon-symbol">┃🔴</span> Patreon](https://patreon.com/thereforegames). Thank you!
*Compatible with Python v3.10.6 and WebUI v1.6.0.*

View File

@ -3,7 +3,28 @@ All notable changes to this project will be documented in this file.
For more details on new features, please check the [Manual](./MANUAL.md).
<details open><summary>10.1.5 - 18 October 2023</summary>
<details open><summary>10.2.0 - 27 October 2023</summary>
### Added
- New shortcode `[tags]`: Assigns arbitrary metatags that can be used in conjunction with a filter for bypassing the content
- New shortcode `[filter_tags]`: Bypasses the content of `[tags]` blocks that do not match the given filter
- New shortcode `[logs]`: Atomic version of `[log]` that allows you print multiple messages at once
- Blue badge with the number of active Wizard scripts, similar to ControlNet
- New setting `Config.syntax.not_operator`: Allows you to change the character used for the "not" operator, defaults to `!` (currently only used for tags)
- New guide: "Using Metatags with Your `[choose]` Blocks"
### Changed
- Wizard "auto-include" string now contains name of the current script or shortcode
- Shuffle the Unprompted promo on UI load
- Minor CSS improvements
### Fixed
- `[override]`: Fixed a syntax error
- Fixed an error related to setting `batch_count` > 1 with hires fix enabled
</details>
<details><summary>10.1.5 - 18 October 2023</summary>
### Fixed
- Wizard UI: Fixed an issue related to block shortcode content

View File

@ -251,4 +251,125 @@ Add an entry called `templates` as shown below:
The asterisk wildcard represents any prompt. Restart the WebUI and you're all set!
</details>
<details><summary>Using Metatags with Your [choose] Blocks</summary>
In this guide, we will utilize the `[tags]` and `[filter_tags]` shortcodes to create a more dynamic `[choose]` block.
Let's say we want to generate a random animal. We can use the following prompt:
```
[choose]
dog
cat
lion
tiger
wolf
lizard
turtle
crocodile
newt
salamander
frog
[/choose]
```
But what if we want to generate a random animal that is also a reptile? We could create a separate `[choose]` block for reptiles, but that would be time-consuming and redundant. Instead, we can use the `[tags]` shortcode to filter our options.
We can sort our animals into three categories: `mammal`, `reptile`, and `amphibian`. Here's how we can do this:
```
[choose]
[tags mammal]
dog
cat
lion
tiger
wolf
[/tags]
[tags reptile]
lizard
turtle
crocodile
[/tags]
[tags amphibian]
newt
salamander
frog
[/tags]
[/choose]
```
Now we can simply write `[filter_tags reptile]` before our `[choose]` block to select a random reptile.
Note that the `[tags]` shortcode is not limited to use with `[choose]` blocks. You can use it anywhere in your template to create grouped content that can be filtered by `[filter_tags]`.
We can also write multiple tags in a single `[tags]` block. Both reptiles and amphibians are known for laying eggs, so we can add a `lays_eggs` tag to both of those categories and use it as our filter criteria:
```
[filter_tags lays_eggs]
[choose]
[tags mammal]
dog
cat
lion
tiger
wolf
[/tags]
[tags reptile lays_eggs]
lizard
turtle
crocodile
[/tags]
[tags amphibian lays_eggs]
newt
salamander
frog
[/tags]
[/choose]
```
Now it will only select animals that lay eggs - either a reptile or an amphibian.
For our final example, let's say we want to generate a mammal that is **not** a feline. To achieve this, we will first create `feline` and `canine` genus tags within the mammal category (yes, we can use nested `[tags]`!):
```
[choose]
[tags mammal]
[tags genus="feline"]
cat
lion
tiger
[/tags]
[tags genus="canine"]
dog
wolf
[/tags]
[/tags]
[tags reptile lays_eggs]
lizard
turtle
crocodile
[/tags]
[tags amphibian lays_eggs]
newt
salamander
frog
[/tags]
[/choose]
```
Then we update our filter to exclude the `feline` genus using the `!` operator:
```
[filter_tags mammal genus="!feline"]
```
With this setup, we will only get `dog` or `wolf` as a result.
As you can see, `[tags]` and `[filter_tags]` are very flexible - you can use them to create complex, multi-layered prompts that are easy to maintain.
For more information on these shortcodes, please consult the [Unprompted Manual](MANUAL.md).
</details>

View File

@ -987,6 +987,41 @@ Supports the macro `%BASE_DIR%` which will be substituted with an absolute path
</details>
<details><summary>[filter_tags]</summary>
Prepare a list of tags which will be evaluated against subsequent `[tags]` blocks. The content of `[tags]` is bypassed if it does not match your filters.
Supports the `_extend` parg to add to the existing list of filters instead of replacing it.
Supports the `_clear` parg to clear all filter rules after the first matching `[tags]` block.
Supports the `_once` parg to remove an individual tag from the filter after the first match.
Supports the `_must_match` kwarg that determines the behavior of the filter when using multiple tags:
- `any` (default): The `[tags]` block must contain at least one matching parg or kwarg.
- `all`: The `[tags]` block must contain all matching pargs and kwargs.
- `selective`: The `[tags]` block must contain the same pargs. It does **not** have to contain the same kwargs, but if it does, the kwarg values must match.
Supports `Config.syntax.not_operator` to exclude tags from the filter. For example, if you want to exclude all blocks with the "outdoors" tag, you can do it like this: `[filter_tags !outdoors]`.
For kwarg tags, the not operator can be used with keys or values as shown:
- `[filter_tags !location="indoors"]` will exclude all blocks that contain a kwarg with the `location` key
- `[filter_tags location="!indoors"]` will exclude all blocks that contain a `location` kwarg with a value of `indoors`
Supports the `_debug` parg to print some diagnostic information to the console.
You can clear the filter at any time by calling `[filter_tags]` without any arguments.
```
[filter_tags location="outdoors"]
[tags location="indoors"]This will not print[/tags]
[tags location="outdoors"]This will print[/tags]
```
</details>
<details><summary>[for var "test var" "update var"]</summary>
Returns the content an arbitrary number of times until the `test` condition returns false.
@ -1196,6 +1231,20 @@ By default, the message context is `DEBUG`. The first positional argument determ
</details>
<details><summary>[logs]</summary>
Prints one or more messages to the console. This is the atomic version of `[log]`.
By default, the message context is `INFO`. You can change this with the optional `_level` argument.
Each parg is a message to be printed. You should enclose your message in quotes if it contains spaces.
```
[logs "This is a message" "This is another message" _level="INFO"]
```
</details>
<details><summary>[max]</summary>
Returns the greatest value among the arguments. Supports advanced expressions.
@ -1477,6 +1526,18 @@ The optional `enable_wordnet` keyword argument determines whether WordNet will b
</details>
<details><summary>[tags]</summary>
Assigns arbitrary tags to the content. Supports both parg and kwarg-style tags.
On its own, this shortcode merely returns the content, but it can be used in conjunction with `[filter_tags]` to bypass the content if the tags don't match your filter rules. See `[filter_tags]` for more information.
```
[tags "tag_one" tag_two="value_two"]A photo of a dog.[/tags]
```
</details>
<details><summary>[while variable {_not} {_any} {_is}]</summary>
Checks whether `variable` is equal to the given value, returning the content repeatedly until the condition is false. This can create an infinite loop if you're not careful.

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,50 @@
/**
* Give a badge on Unprompted Accordion indicating total number of active
* wizard scripts.
*/
(function()
{
const unprompted_accordions = new Set();
function update_active_unit_count(accordion)
{
const span = accordion.querySelector(".label-wrap span");
let active_unit_count = 0;
accordion.querySelectorAll(".wizard-autoinclude input").forEach(function(checkbox)
{
if (checkbox.checked)
{
console.log("found checkbox");
active_unit_count++;
}
});
if (span.childNodes.length !== 1)
{
span.removeChild(span.lastChild);
}
if (active_unit_count > 0)
{
const div = document.createElement("div");
div.classList.add("unprompted-badge");
div.innerHTML = `🪄 ${active_unit_count} wizard${active_unit_count > 1 ? "s" : ""}`;
span.appendChild(div);
}
}
onUiUpdate(() =>
{
gradioApp().querySelectorAll(".unprompted-accordion").forEach(function(accordion)
{
if (unprompted_accordions.has(accordion)) return;
accordion.querySelectorAll(".wizard-autoinclude input").forEach(function(checkbox)
{
checkbox.addEventListener("change", function()
{
update_active_unit_count(accordion);
});
});
unprompted_accordions.add(accordion);
});
});
})();

View File

@ -101,7 +101,7 @@ class Unprompted:
self.log.info(f"Finished loading in {time.time()-start_time} seconds.")
def __init__(self, base_dir="."):
self.VERSION = "10.1.5"
self.VERSION = "10.2.0"
self.shortcode_modules = {}
self.shortcode_objects = {}

View File

@ -15,7 +15,7 @@ import lib_unprompted.shortcodes as shortcodes
import lib_unprompted.helpers as helpers
from pathlib import Path
from enum import IntEnum, auto
import sys, os, html
import sys, os, html, random
base_dir = scripts.basedir()
@ -288,8 +288,6 @@ class Scripts(scripts.Script):
shortcodes_region = [None] * 2
shortcodes_dropdown = [None] * 2
# templates_dropdown = [None] * 2
def title(self):
return "Unprompted"
@ -297,8 +295,9 @@ class Scripts(scripts.Script):
return scripts.AlwaysVisible
def ui(self, is_img2img):
mode_string = "img2img" if is_img2img else "txt2img"
with gr.Group():
with gr.Accordion("Unprompted", open=Unprompted.Config.ui.open):
with gr.Accordion("Unprompted", open=Unprompted.Config.ui.open, elem_classes=["unprompted-accordion",mode_string]):
with gr.Row(equal_height=True):
is_enabled = gr.Checkbox(label="Enabled", value=gradio_enabled_checkbox_workaround)
@ -308,11 +307,18 @@ class Scripts(scripts.Script):
unprompted_seed = gr.Number(label="Unprompted Seed", value=-1)
setattr(unprompted_seed, "do_not_save_to_config", True)
if (os.path.exists(f"{base_dir}/{Unprompted.Config.template_directory}/pro/beautiful_soul_v0.0.1/main{Unprompted.Config.formats.txt}")): is_open = False
if (os.path.exists(f"{base_dir}/{Unprompted.Config.template_directory}/pro/beautiful_soul_v0.1.0/main{Unprompted.Config.formats.txt}")): is_open = False
else: is_open = True
promos = []
promos.append(f'<a href="https://payhip.com/b/L1uNF" target="_blank"><img src="{get_local_file_dir()}/images/promo_box_beautiful_soul.png" class="thumbnail"></a><h1><strong>Beautiful Soul</strong>: Bring your characters to life.</h1><p>A highly expressive character generator for the A1111 WebUI. With thousands of wildcards and direct ControlNet integration, this is by far our most powerful Unprompted template to date. <strong>Available at half price until November 11th!</strong></p><a href="https://payhip.com/b/L1uNF" target=_blank><button class="gr-button gr-button-lg gr-button-secondary" title="View premium assets for Unprompted">Download Now ➜</button></a>')
promos.append(f'<a href="https://payhip.com/b/qLUX9" target="_blank"><img src="{get_local_file_dir()}/images/promo_box_demoncrawl_avatar_generator.png" class="thumbnail"></a><h1>The <strong>DemonCrawl</strong> Pixel Art Avatar Generator</h1><p>Create pixel art portraits in the style of the popular roguelite, <a href="https://demoncrawl.com" _target=blank>DemonCrawl</a>. Includes a custom Stable Diffusion model trained by the game\'s developer, as well as a custom GUI and the ability to randomize your prompts.</p><a href="https://payhip.com/b/qLUX9" target=_blank><button class="gr-button gr-button-lg gr-button-secondary" title="View premium assets for Unprompted">Download Now ➜</button></a>')
promos.append(f'<a href="https://payhip.com/b/hdgNR" target="_blank"><img src="{get_local_file_dir()}/images/promo_box_fantasy.png" class="thumbnail"></a><h1>Create beautiful art for your <strong>Fantasy Card Game</strong></h1><p>Generate a wide variety of creatures and characters in the style of a fantasy card game. Perfect for heroes, animals, monsters, and even crazy hybrids.</p><a href="https://payhip.com/b/hdgNR" target=_blank><button class="gr-button gr-button-lg gr-button-secondary" title="View premium assets for Unprompted">Download Now ➜</button></a>')
promos.append(f'<a href="https://github.com/ThereforeGames/unprompted" target="_blank"><img src="{get_local_file_dir()}/images/promo_github_star.png" class="thumbnail"></a><h1>Give Unprompted a <strong>star</strong> for visibility</h1><p>Most WebUI users have never heard of Unprompted. You can help more people discover it by giving the repo a ⭐ on Github. Thank you for your support!</p><a href="https://github.com/ThereforeGames/unprompted" target=_blank><button class="gr-button gr-button-lg gr-button-secondary" title="View the Unprompted repo">Visit Github ➜</button></a>')
promos.append(f'<a href="https://github.com/sponsors/ThereforeGames" target="_blank"><img src="{get_local_file_dir()}/images/promo_github_sponsor.png" class="thumbnail"></a><h1>Become a Sponsor</h1><p>One of the best ways to support Unprompted is by becoming our Sponsor on Github - sponsors receive access to a private repo containing all of our premium add-ons. <em>(Still setting that up... should be ready soon!)</em></p><a href="https://github.com/sponsors/ThereforeGames" target=_blank><button class="gr-button gr-button-lg gr-button-secondary" title="View the Unprompted repo">Visit Github ➜</button></a>')
with gr.Accordion("🎉 Promo", open=is_open):
plug = gr.HTML(label="plug", elem_id="promo", value=f'<a href="https://payhip.com/b/L1uNF" target="_blank"><img src="{get_local_file_dir()}/images/promo_box_beautiful_soul.png" style="float: left;width: 150px;margin-bottom:10px;"></a><h1 style="font-size: 20px;letter-spacing:0.015em;margin-top:10px;"><strong>Beautiful Soul</strong>: Bring your characters to life.</h1><p style="margin:1em 0;">A highly expressive character generator for the A1111 WebUI. With thousands of wildcards and direct ControlNet integration, this is by far our most powerful Unprompted template to date. <strong>Available at half price until November 11th!</strong></p><a href="https://payhip.com/b/L1uNF" target=_blank><button class="gr-button gr-button-lg gr-button-secondary" title="View premium assets for Unprompted">Download Now ➜</button></a>')
plug = gr.HTML(label="plug", elem_id="promo", value=random.choice(promos))
with gr.Accordion("🧙 Wizard", open=Unprompted.Config.ui.wizard_open):
if Unprompted.Config.ui.wizard_enabled:
@ -399,6 +405,7 @@ class Scripts(scripts.Script):
wizard_shortcode_parser.register(handler, "wizard", f"{Unprompted.Config.syntax.tag_close}wizard", preprocess)
with gr.Tabs():
self.filtered_templates = Unprompted.wizard_groups[WizardModes.TEMPLATES][int(is_img2img)]
self.filtered_shortcodes = Unprompted.wizard_groups[WizardModes.SHORTCODES][int(is_img2img)]
@ -413,7 +420,7 @@ class Scripts(scripts.Script):
# Render the text file's UI with special parser object
wizard_shortcode_parser.parse(file.read())
# Auto-include is always the last element
gr.Checkbox(label="🪄 Auto-include this in prompt", value=False, elem_classes=["wizard-autoinclude"])
gr.Checkbox(label=f"🪄 Auto-include {self.dropdown_item_name} in prompt", value=False, elem_classes=["wizard-autoinclude",mode_string])
# Add event listeners
wizard_prep_event_listeners(self.filtered_templates[filename])
@ -456,7 +463,7 @@ class Scripts(scripts.Script):
# Run the shortcode's UI template to populate
Unprompted.shortcode_objects[key].ui(gr)
# Auto-include is always the last element
gr.Checkbox(label="🪄 Auto-include this in prompt", value=False, elem_classes=["wizard-autoinclude"])
gr.Checkbox(label=f"🪄 Auto-include [{key}] in prompt", value=False, elem_classes=["wizard-autoinclude",mode_string])
# Add event listeners
wizard_prep_event_listeners(self.filtered_shortcodes[key])
@ -503,7 +510,7 @@ class Scripts(scripts.Script):
self.shortcodes_region[int(is_img2img)] = gr.Blocks()
wizard_populate_shortcodes(self.shortcodes_region[int(is_img2img)], True)
wizard_shortcode_btn = gr.Button(value="Generate Shortcode")
wizard_shortcode_btn = gr.Button(value="🧠 Generate Shortcode")
with gr.Tab("Capture"):
gr.Markdown(value="This assembles Unprompted code with the WebUI settings for the last image you generated. You can save the code to your `templates` folder and `[call]` it later, or send it to someone as 'preset' for foolproof image reproduction.<br><br>**⚠️ Important:** <em>When you change your inference settings, you must generate an image before Unprompted can detect the changes. This is due to a limitation in the WebUI extension framework.</em>")
@ -817,6 +824,13 @@ class Scripts(scripts.Script):
p.all_prompts[batch_real_index] = prompt_result
p.all_negative_prompts[batch_real_index] = negative_prompt_result
if Unprompted.fix_hires_prompts:
Unprompted.log.debug("Synchronizing prompt vars with hr_prompt vars")
p.hr_prompt = prompt_result
p.hr_negative_prompt = negative_prompt_result
p.all_hr_prompts = p.all_prompts
p.all_negative_prompts = p.all_negative_prompts
if (batch_count_index > 0):
try:
Unprompted.log.debug("Attempting to re-parse and re-activate extra networks...")
@ -832,17 +846,6 @@ class Scripts(scripts.Script):
if unprompted_seed != -1:
import random
random.seed()
if Unprompted.fix_hires_prompts:
Unprompted.log.debug("Synchronizing prompt vars with hr_prompt vars")
p.hr_prompt = Unprompted.shortcode_user_vars["prompt"]
p.hr_negative_prompt = Unprompted.shortcode_user_vars["negative_prompt"]
p.all_hr_prompts = p.all_prompts
p.all_hr_negative_prompts = p.all_negative_prompts
# SDXL is using hr_prompts instead of hr_all_prompts
# TODO: Check if all_hr_prompts is used anywhere else anymore
p.hr_prompts = p.all_prompts
p.hr_negative_prompts = p.all_negative_prompts
else:
Unprompted.log.debug("Proceeding to next batch_count batch")
# Increment batch index

View File

@ -75,7 +75,7 @@ class Shortcode():
del parts[part_index] # Prevent the same choice from being made again
final_string += _sep
except Exception as e:
self.log.exception("Exception while parsing the list of choices")
self.log.exception(f"Exception while parsing the list of choices. The partially assembled final string was: {final_string}")
pass
# Reset to original value

View File

@ -0,0 +1,46 @@
class Shortcode():
def __init__(self,Unprompted):
self.Unprompted = Unprompted
self.shortcode_overrides = {}
self.description = "Tags for filtering content inside of [tags] blocks."
self.parg_tags = []
self.kwarg_tags = {}
self.once = False
self.debug = False
self.clear = False
self.must_match = "any"
def run_atomic(self, pargs, kwargs, context):
_extend = self.Unprompted.parse_arg("_extend",False)
self.debug = self.Unprompted.parse_arg("_debug",False)
self.once = self.Unprompted.parse_arg("_once",False)
self.clear = self.Unprompted.parse_arg("_clear",False)
self.must_match = self.Unprompted.parse_arg("_must_match","any")
if _extend:
self.parg_tags.extend(pargs)
self.kwarg_tags.update(kwargs)
else:
self.parg_tags = pargs
self.kwarg_tags = kwargs
if self.debug:
self.log.info(f"Parg tags: {self.parg_tags}")
self.log.info(f"Kwarg tags: {self.kwarg_tags}")
return("")
def cleanup(self):
self.parg_tags = []
self.kwarg_tags = {}
self.once = False
self.clear = False
self.must_match = "any"
self.debug = False
def ui(self,gr):
gr.Textbox(label="Arbitrary tag filters 🡢 verbatim",max_lines=1,placeholder='location=outdoors nature')
gr.Checkbox(label="Clear all filters after first matching tag block 🡢 _clear",value=False)
gr.Checkbox(label="Match each tag only once 🡢 _once",value=False)
gr.Radio(label="Rules for what is considered a matching block 🡢 _must_match",choices=["any","all","selective"])
gr.Checkbox(label="Debug 🡢 _debug",value=False)

16
shortcodes/basic/logs.py Normal file
View File

@ -0,0 +1,16 @@
class Shortcode():
def __init__(self, Unprompted):
self.Unprompted = Unprompted
self.description = "Prints one or more messages to the console."
def run_atomic(self, pargs, kwargs, context):
_level = self.Unprompted.parse_arg("_level","info")
log_func = getattr(self.log,_level.lower())
for parg in pargs:
log_func(self.Unprompted.parse_advanced(parg,context))
return ("")
def ui(self, gr):
gr.Textbox(label="Arbitrary messages as pargs 🡢 verbatim",max_lines=1,placeholder='"message one" "message two goes here"')
gr.Dropdown(label="Log level 🡢 _level",choices=["debug","info","warning","error","critical"],value="info")

View File

@ -5,7 +5,7 @@ class Shortcode():
def run_block(self, pargs, kwargs, context,content):
# self.shortcode_overrides.update(kwargs)
self.Unprompted.shortcode_objects["overrides"][pargs[0]] = content
self.Unprompted.shortcode_objects["overrides"].shortcode_overrides[pargs[0]] = self.Unprompted.parse_advanced(content,context)
return("")
def ui(self,gr):

101
shortcodes/basic/tags.py Normal file
View File

@ -0,0 +1,101 @@
class Shortcode():
def __init__(self,Unprompted):
self.Unprompted = Unprompted
self.description = "Applies one or more metatags to be evaluated with [filter_tags] for bypassing the content."
def preprocess_block(self, pargs, kwargs, context):
return True
def run_block(self, pargs, kwargs, context,content):
# self.shortcode_overrides.update(kwargs)
filter_obj = self.Unprompted.shortcode_objects["filter_tags"]
failed_test = False
matched_one = False
for orig_parg_tag in filter_obj.parg_tags:
parg_tag = orig_parg_tag
if self.Unprompted.is_system_arg(parg_tag): continue
# Check if the tag begins with a hyphen for "negative" matching
elif parg_tag.startswith(self.Unprompted.Config.syntax.not_operator):
is_not = True
parg_tag = parg_tag[1:]
else: is_not = False
if parg_tag not in pargs:
if is_not:
if filter_obj.once: filter_obj.parg_tags.remove(orig_parg_tag)
matched_one = True
continue
if filter_obj.must_match == "any": continue
else:
failed_test = True
break
else:
if is_not:
if filter_obj.must_match != "any":
failed_test = True
break
else:
if filter_obj.once: filter_obj.parg_tags.remove(parg_tag)
matched_one = True
for orig_kwarg_tag in filter_obj.kwarg_tags:
kwarg_tag = orig_kwarg_tag
if self.Unprompted.is_system_arg(kwarg_tag): continue
elif kwarg_tag.startswith(self.Unprompted.Config.syntax.not_operator):
key_is_not = True
kwarg_tag = kwarg_tag[1:]
else: key_is_not = False
# Support "not" operator for kwarg value:
if isinstance(filter_obj.kwarg_tags[orig_kwarg_tag], str) and filter_obj.kwarg_tags[orig_kwarg_tag].startswith(self.Unprompted.Config.syntax.not_operator):
val_is_not = True
kwarg_val = filter_obj.kwarg_tags[orig_kwarg_tag][1:]
else:
val_is_not = False
kwarg_val = filter_obj.kwarg_tags[orig_kwarg_tag]
if kwarg_tag not in kwargs:
if key_is_not:
if filter_obj.once: filter_obj.kwarg_tags.pop(orig_kwarg_tag)
matched_one = True
continue
if filter_obj.must_match != "all": continue
else:
failed_test = True
break
if kwargs[kwarg_tag] != kwarg_val:
if val_is_not:
if filter_obj.once: filter_obj.kwarg_tags.pop(orig_kwarg_tag)
matched_one = True
continue
elif filter_obj.must_match == "any": continue
else:
failed_test = True
break
else:
if val_is_not:
if filter_obj.must_match != "any":
failed_test = True
break
else:
if filter_obj.once: filter_obj.kwarg_tags.pop(kwarg_tag)
matched_one = True
# Return content in the event of success or no filters
if (matched_one and not failed_test) or (not filter_obj.parg_tags and not filter_obj.kwarg_tags):
if filter_obj.debug: self.log.info(f"Matched tags block: {pargs} {kwargs}")
# Reset filter tags
if filter_obj.clear:
filter_obj.cleanup()
return self.Unprompted.parse_alt_tags(content, context)
if filter_obj.debug: self.log.info(f"Failed tags block: {pargs} {kwargs}")
return ""
def ui(self,gr):
gr.Textbox(label="Arbitrary tags 🡢 verbatim",max_lines=1,placeholder="nature")

View File

@ -48,6 +48,44 @@ details[open]
display: block;
}
#promo .thumbnail
{
float: left;
width: 150px;
margin-bottom: 10px;
}
#promo h1
{
font-size: 20px;
letter-spacing: 0.015em;
margin-top: 10px;
}
#promo p
{
margin: 1em 0;
}
#promo .gr-button
{
transition: 0.1s;
display: inline-block;
padding: 0.25em 0.75em;
font-weight: bold;
color: var(--background-fill-primary);
border-radius: 0.5em;
text-align: center;
vertical-align: middle;
background-color: var(--body-text-color);
}
#promo .gr-button:hover
{
background-color: var(--checkbox-background-color-selected);
color: var(--body-text-color);
}
.gradio-group div .wizard-autoinclude
{
margin-top: 20px;
@ -65,11 +103,26 @@ details[open]
top: -10px;
}
.wizard-autoinclude span
.wizard-autoinclude input[type="checkbox"]:checked+span
{
color: var(--checkbox-background-color-selected);
font-weight: 1000;
}
.unprompted-badge
{
display: inline-block;
padding: 0.25em 0.75em;
font-size: 0.75em;
font-weight: bold;
color: white;
border-radius: 0.5em;
text-align: center;
vertical-align: middle;
margin-left: var(--size-2);
background-color: var(--checkbox-background-color-selected);
}
.patreon-symbol
{
letter-spacing: -7px;

View File

@ -0,0 +1,2 @@
[# A decent starting point to upscale images using the Tile model for ControlNet.]
[sets sampler="DPM++ 3M SDE" denoising_strength=0.3 cfg_scale=2 cn_0_enabled=1 cn_0_pixel_perfect=0 cn_0_module=tile_resample cn_0_model=tile cn_0_weight=1.15 cn_0_control_mode=2]