* Added option to have a default choice when no other choices are available after conditions (in choices and wildcards).
This commit is contained in:
@@ -53,6 +53,7 @@ The choice options are as follows:
|
||||
* `'identifiers'`: comma separated labels for the choice (optional, quotes can be single or double). Only makes sense inside a wildcard definition. Can be used when specifying the wildcard to select this specific choice. It's case insensitive.
|
||||
* `n`: weight of the choice (optional, default 1).
|
||||
* `if condition`: filters out the choice if the condition is false (optional; this is an extension to the *Dynamic Prompts* syntax). Same conditions as in the `if` command.
|
||||
* `else`: flag to indicate this is the choice to use if no other choice is available after conditions. It won't be considered if other choices are available. Also optional.
|
||||
* `::`: end of choice options (not optional if any options)
|
||||
|
||||
Whitespace is allowed between parameters/options.
|
||||
@@ -142,6 +143,7 @@ The choices of the wildcard follow the same format as in the choices construct,
|
||||
If using the object format for a choice you can use the following in addition to the standard `weight` and `text`/`content`:
|
||||
|
||||
* `if`: the condition (a string)
|
||||
* `else`: flag (boolean) to indicate this is the choice to use if no other choice is available after conditions. It won't be considered if other choices are available.
|
||||
* `labels`: list of labels (an array of strings)
|
||||
* `command`: indicates the content is a command (a boolean)
|
||||
|
||||
|
||||
+5
-4
@@ -165,27 +165,28 @@ extranetworktag: "<" /(?!ppp:)\w+:/ encontent ">"
|
||||
choicesoptions_sep: "$$" plain
|
||||
|
||||
// choice options
|
||||
choice: [ [ _WHITESPACE? choiceiscmd ] [ _WHITESPACE? choicelabels ] [ _WHITESPACE? choiceweight ] [ _WHITESPACE? choiceif ] _WHITESPACE? "::" ] choicevalue
|
||||
choice: [ [ _WHITESPACE? choiceiscmd ] [ _WHITESPACE? choicelabels ] [ _WHITESPACE? choiceweight ] [ _WHITESPACE? ( choiceif | choiceelse ) ] _WHITESPACE? "::" ] choicevalue
|
||||
choiceiscmd: /%/ // the option text is a special command
|
||||
choicelabels: ( /"/ IDENTIFIER ( _WHITESPACE? "," _WHITESPACE? IDENTIFIER )* /"/ )
|
||||
| ( /'/ IDENTIFIER ( _WHITESPACE? "," _WHITESPACE? IDENTIFIER )* /'/ )
|
||||
choiceweight: NUMBER
|
||||
choiceif: "if" _WHITESPACE condition
|
||||
choiceelse: "else"
|
||||
choicevalue: content_choice
|
||||
//#endif
|
||||
|
||||
//#if ALLOW_CHOICES
|
||||
// choices construct
|
||||
choices: "{" [ choicesoptions_sampler | ( choicesoptions _WHITESPACE? "$$" ) ] choice ( "|" choice )* "}"
|
||||
choices: "{" [ choicesoptions_sampler | ( choicesoptions "$$" ) ] choice ( "|" choice )* "}"
|
||||
//#endif
|
||||
|
||||
//#if ALLOW_WILDCARDS
|
||||
// wildcard definition options
|
||||
wcdefoptions: [ choicesoptions_sampler ] [ _WHITESPACE? choicesoptions_flags ] [ _WHITESPACE? choicesoptions_range ] [ _WHITESPACE? wcdescription ] [ _WHITESPACE? choicesoptions_sep ]
|
||||
wcdefoptions: [ choicesoptions_sampler ] [ _WHITESPACE? choicesoptions_flags ] [ _WHITESPACE? choicesoptions_range ] [ _WHITESPACE? wcdescription ] [ _WHITESPACE? choicesoptions_sep ] "$$"
|
||||
wcdescription: STRING
|
||||
|
||||
// wildcards construct
|
||||
wildcard: "__" [ choicesoptions_sampler | ( choicesoptions _WHITESPACE? "$$" ) ] wildcard_name [ wc_filter ] [ wildcardvar ] "__"
|
||||
wildcard: "__" [ choicesoptions_sampler | ( choicesoptions "$$" ) ] wildcard_name [ wc_filter ] [ wildcardvar ] "__"
|
||||
wc_filter_nums: IDENTIFIER | INDEX | (INDEX /-/ INDEX)
|
||||
//#if ALLOW_COMMVARS
|
||||
wildcard_name.2: ( WC_NAME_PLAIN_START | variableuse | commandecho ) ( WC_NAME_PLAIN | variableuse | commandecho )*
|
||||
|
||||
+15
-2
@@ -1891,8 +1891,12 @@ class TreeProcessor(lark.visitors.Interpreter):
|
||||
included_choices = 0
|
||||
excluded_choices = 0
|
||||
excluded_weights_sum = 0
|
||||
else_choice = None
|
||||
for i, c in enumerate(expanded_choice_values):
|
||||
c["choice_index"] = i # we index them to later sort the results
|
||||
if c.get("else", False):
|
||||
else_choice = c
|
||||
continue
|
||||
weight = float(c.get("weight", 1.0))
|
||||
condition = c.get("if", None)
|
||||
if weight > 0 and (condition is None or self.__eval_condition(condition)):
|
||||
@@ -1903,6 +1907,10 @@ class TreeProcessor(lark.visitors.Interpreter):
|
||||
weights.append(-1)
|
||||
excluded_choices += 1
|
||||
excluded_weights_sum += weight
|
||||
if not available_choices and else_choice is not None:
|
||||
available_choices = [else_choice]
|
||||
weights = [float(else_choice.get("weight", 1.0))]
|
||||
included_choices = 1
|
||||
if excluded_choices > 0: # we need to redistribute the excluded weights
|
||||
weights = [weight + excluded_weights_sum / included_choices for weight in weights if weight >= 0]
|
||||
weights = np.array(weights)
|
||||
@@ -2114,7 +2122,12 @@ class TreeProcessor(lark.visitors.Interpreter):
|
||||
c_label_obj = choice.children[1]
|
||||
choice_dict["labels"] = [str(x).lower() for x in c_label_obj.children[1:-1]] if c_label_obj is not None else []
|
||||
choice_dict["weight"] = float(choice.children[2].children[0]) if choice.children[2] is not None else 1.0
|
||||
choice_dict["if"] = choice.children[3].children[0] if choice.children[3] is not None else None
|
||||
ifelse = choice.children[3]
|
||||
if ifelse is not None:
|
||||
if len(ifelse.children) and ifelse.children[0] is not None:
|
||||
choice_dict["if"] = ifelse.children[0]
|
||||
else:
|
||||
choice_dict["else"] = True
|
||||
choice_dict["content"] = choice.children[-1]
|
||||
return choice_dict
|
||||
|
||||
@@ -2242,7 +2255,7 @@ class TreeProcessor(lark.visitors.Interpreter):
|
||||
parse_prompt(
|
||||
self.state,
|
||||
"as wildcard options",
|
||||
wildcard.unprocessed_choices[0][:-2].strip(),
|
||||
wildcard.unprocessed_choices[0],
|
||||
self.state.parsers["wcdefoptions"],
|
||||
True,
|
||||
),
|
||||
|
||||
+1
-1
@@ -268,7 +268,7 @@ class PPPWildcards:
|
||||
Returns:
|
||||
bool: Whether the dictionary is a valid choice options dictionary or not.
|
||||
"""
|
||||
return all(k in ["command", "labels", "weight", "if", "content", "text"] for k in d.keys())
|
||||
return all(k in ["command", "labels", "weight", "if", "else", "content", "text"] for k in d.keys())
|
||||
|
||||
def __get_choices(self, obj: object, full_path: Path | None, key_parts: list[str]) -> list:
|
||||
"""
|
||||
|
||||
@@ -103,6 +103,13 @@ class TestChoices(TestPromptPostProcessorBase):
|
||||
ppp="nocup",
|
||||
)
|
||||
|
||||
def test_ch_choices_if_default(self): # choices with if and a default
|
||||
self.process(
|
||||
InputTuple("the choice is: {if false::choice1|if _is_sd1::choice2|else::choice3}", ""),
|
||||
OutputTuple("the choice is: choice3", ""),
|
||||
ppp="nocup",
|
||||
)
|
||||
|
||||
def test_ch_choices_set_if_multiple(self): # choices with if user variable and multiple selection
|
||||
self.process(
|
||||
InputTuple("${var=test}the choices are: {2$$, $$3::choice1|2 if not var eq 'test'::choice2|choice3}", ""),
|
||||
|
||||
@@ -337,6 +337,13 @@ class TestWildcards(TestPromptPostProcessorBase):
|
||||
ppp="nocup",
|
||||
)
|
||||
|
||||
def test_wc_wildcard_emptydefault(self): # empty wildcard with default
|
||||
self.process(
|
||||
InputTuple("the choices are: __yaml/empty_default_wildcard__", ""),
|
||||
OutputTuple("the choices are: 6", ""),
|
||||
ppp="nocup",
|
||||
)
|
||||
|
||||
def test_wc_wildcard4_yaml(self): # simple yaml wildcard with one option
|
||||
self.process(
|
||||
InputTuple("the choices are: __yaml/wildcard4__", ""),
|
||||
|
||||
@@ -111,6 +111,14 @@ yaml:
|
||||
- if _sd in ("test1", "test2")::4
|
||||
- if (false or false)::5
|
||||
|
||||
empty_default_wildcard:
|
||||
- if false::1
|
||||
- if false::2
|
||||
- if false::3
|
||||
- if _sd in ("test1", "test2")::4
|
||||
- if (false or false)::5
|
||||
- else::6
|
||||
|
||||
circular1:
|
||||
- 5::__yaml/circular2__
|
||||
- choice1
|
||||
|
||||
Reference in New Issue
Block a user