adieyal / sd-dynamic-prompts

A custom script for AUTOMATIC1111/stable-diffusion-webui to implement a tiny template language for random prompt generation
MIT License
2.02k stars 261 forks source link

Parse exception when using % #750

Open jonthansen opened 6 months ago

jonthansen commented 6 months ago

Describe the bug:

Using a percent sign (%) anywhere in the prompt produces a parse exception. It appears related to a (new?) wrap command. I cannot find any documentation for the wrap command and this exception now prevents any other extensions or scripts from using the percent sign without breaking Dynamic Prompts. This has always been the case for the dollar sign ($).

** Error running process: X:\Automatic1111\webui\extensions\sd-dynamic-prompts\scripts\dynamic_prompting.py Traceback (most recent call last): File "X:\Automatic1111\webui\modules\scripts.py", line 784, in process script.process(p, script_args) File "X:\Automatic1111\webui\extensions\sd-dynamic-prompts\sd_dynamic_prompts\dynamic_prompting.py", line 481, in process all_prompts, all_negative_prompts = generate_prompts( File "X:\Automatic1111\webui\extensions\sd-dynamic-prompts\sd_dynamic_prompts\helpers.py", line 93, in generate_prompts all_prompts = prompt_generator.generate(prompt, num_prompts, seeds=seeds) or [""] File "X:\Automatic1111\webui\venv\lib\site-packages\dynamicprompts\generators\randomprompt.py", line 67, in generate gen = self._context.sample_prompts(template, num_images) File "X:\Automatic1111\webui\venv\lib\site-packages\dynamicprompts\sampling_context.py", line 119, in sample_prompts command = parse(prompt, parser_config=self.parser_config) File "X:\Automatic1111\webui\venv\lib\site-packages\dynamicprompts\parser\parse.py", line 557, in parse tokens = get_cached_parser(parser_config).parse_string( File "X:\Automatic1111\webui\venv\lib\site-packages\pyparsing\core.py", line 1197, in parse_string raise exc.with_traceback(None) pyparsing.exceptions.ParseException: Expected end of text, found '%' (at char 0), (line:1, col:1)

Local Install, Version 2.17.1 of the WebUI extension, dynamicprompts library Version 0.31.0

Expected behavior:

Parsing of percent and dollar signs not conforming to Dynamic Prompts syntax should be handled gracefully, leaving them in place, and allowing Dynamic Prompts to continue without the exception

akx commented 5 months ago

Please show the prompt you're using.

You're right the wrap command is new; it's documented here.

jonthansen commented 5 months ago

Some prompts where the percent sign or dollar sign produce an error:

# A percent sign as part of prompt text
a sign with text 100% FREE

# A dollar sign as part of prompt text
a sign with text You pay $0

# A line from a wildcard file using Umi-AI syntax that snuck into my wildercards folder
{20%::blue|30%::green|red} car

# A Prompt Fusion extension variable
$haircolor=ash-brown
a man with $haircolor hair

# A Useless Useful Variables extension variable
%eyecolor=amber%
a woman with %eyecolor% eyes

# A percent sign as a modulo operator in an Unprompted extensions expression
[if "var % 2 == 0"]I'm even![/if]

What I am hoping for is that these symbols can be ignored by Dynamic Prompts if they are not part of the Dynamic Pormpts syntax. For my own use I got around the problem by adding a few lines before and after the parsing of the prompt:

import re
# BEFORE PARSE
newprompt = ''
offset = 0
# regex finds these four possible parts:
# 1: Matches variation Combinations syntax: {#-#$$,$$
# 2: Matches everything inside %{} when $$ exists (TODO: learn wrap syntax then update to handle properly)
# 3: Matches ${
# 4: Matches %{
# Even index contains substring NOT matching the regex, replace $ and % with placeholders
for index, match in enumerate(re.split(r"({[\d-]+\$\$(?:.+\$\$)?|%{.*\$\$(?:.*\$\$)*}|\${|%{)", prompt)):
  if index%2 == 0:
    newprompt = newprompt + match.replace('$', '<dp_dollar_sign>').replace('%', '<dp_percent_sign>')
  else:
    newprompt = newprompt + match
  offset += len(match)

prompt = newprompt

#AFTER PARSE
prompt = prompt.replace('<dp_dollar_sign>', '$').replace('<dp_percent_sign>', '%')