Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: extend dialog/intent templates #317

Merged
merged 4 commits into from
Dec 6, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 92 additions & 1 deletion ovos_utils/bracket_expansion.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,80 @@
import itertools
import re
from typing import List
from typing import List, Dict

from ovos_utils.log import deprecated


def expand_template(template: str) -> List[str]:
def expand_optional(text):
"""Replace [optional] with two options: one with and one without."""
return re.sub(r"\[([^\[\]]+)\]", lambda m: f"({m.group(1)}|)", text)

def expand_alternatives(text):
"""Expand (alternative|choices) into a list of choices."""
parts = []
for segment in re.split(r"(\([^\(\)]+\))", text):
if segment.startswith("(") and segment.endswith(")"):
options = segment[1:-1].split("|")
parts.append(options)
else:
parts.append([segment])
return itertools.product(*parts)
JarbasAl marked this conversation as resolved.
Show resolved Hide resolved

def fully_expand(texts):
"""Iteratively expand alternatives until all possibilities are covered."""
result = set(texts)
while True:
expanded = set()
for text in result:
options = list(expand_alternatives(text))
expanded.update(["".join(option).strip() for option in options])
if expanded == result: # No new expansions found
break
result = expanded
return sorted(result) # Return a sorted list for consistency

# Expand optional items first
template = expand_optional(template)

# Fully expand all combinations of alternatives
return fully_expand([template])


def expand_slots(template: str, slots: Dict[str, List[str]]) -> List[str]:
"""Expand a template by first expanding alternatives and optional components,
then substituting slot placeholders with their corresponding options.

Args:
template (str): The input string template to expand.
slots (dict): A dictionary where keys are slot names and values are lists of possible replacements.

Returns:
list[str]: A list of all expanded combinations.
"""
# Expand alternatives and optional components
base_expansions = expand_template(template)

# Process slots
all_sentences = []
for sentence in base_expansions:
matches = re.findall(r"\{([^\{\}]+)\}", sentence)
if matches:
# Create all combinations for slots in the sentence
slot_options = [slots.get(match, [f"{{{match}}}"]) for match in matches]
for combination in itertools.product(*slot_options):
filled_sentence = sentence
for slot, replacement in zip(matches, combination):
filled_sentence = filled_sentence.replace(f"{{{slot}}}", replacement)
all_sentences.append(filled_sentence)
else:
# No slots to expand
all_sentences.append(sentence)

return all_sentences


@deprecated("use 'expand_template' directly instead", "1.0.0")
def expand_parentheses(sent: List[str]) -> List[str]:
"""
['1', '(', '2', '|', '3, ')'] -> [['1', '2'], ['1', '3']]
Expand All @@ -22,6 +95,7 @@ def expand_parentheses(sent: List[str]) -> List[str]:
return SentenceTreeParser(sent).expand_parentheses()


@deprecated("use 'expand_template' directly instead", "1.0.0")
def expand_options(parentheses_line: str) -> list:
"""
Convert 'test (a|b)' -> ['test a', 'test b']
Expand All @@ -38,6 +112,7 @@ def expand_options(parentheses_line: str) -> list:
class Fragment:
"""(Abstract) empty sentence fragment"""

@deprecated("use 'expand_template' function directly instead", "1.0.0")
def __init__(self, tree):
"""
Construct a sentence tree fragment which is merely a wrapper for
Expand Down Expand Up @@ -73,6 +148,11 @@ class Word(Fragment):
Construct with a string as argument.
"""

@deprecated("use 'expand_template' function directly instead", "1.0.0")
def __init__(self, tree):
"""DEPRECATED"""
super().__init__(tree)

def expand(self):
"""
Creates one sentence that contains exactly that word.
Expand All @@ -89,6 +169,11 @@ class Sentence(Fragment):
Construct with a List<Fragment> as argument.
"""

@deprecated("use 'expand_template' function directly instead", "1.0.0")
def __init__(self, tree):
"""DEPRECATED"""
super().__init__(tree)

def expand(self):
"""
Creates a combination of all sub-sentences.
Expand All @@ -114,6 +199,11 @@ class Options(Fragment):
Construct with List<Fragment> as argument.
"""

@deprecated("use 'expand_template' function directly instead", "1.0.0")
def __init__(self, tree):
"""DEPRECATED"""
super().__init__(tree)

def expand(self):
"""
Returns all of its options as seperated sub-sentences.
Expand All @@ -133,6 +223,7 @@ class SentenceTreeParser:
['1', '(', '2', '|', '3, ')'] -> [['1', '2'], ['1', '3']]
"""

@deprecated("use 'expand_template' function directly instead", "1.0.0")
def __init__(self, tokens):
self.tokens = tokens

Expand Down
Loading