Exemple #1
0
    def do_intents_to_graph(intents, slot_names, targets):
        sentences, replacements = ini_jsgf.split_rules(intents)

        # Load slot values
        for slot_name in slot_names:
            slot_path = slots_dir / slot_name
            assert slot_path.is_file(), f"Missing slot file at {slot_path}"

            # Parse each non-empty line as a JSGF sentence
            slot_values = []
            with open(slot_path, "r") as slot_file:
                for line in slot_file:
                    line = line.strip()
                    if line:
                        sentence = jsgf.Sentence.parse(line)
                        slot_values.append(sentence)

            # Replace $slot with sentences
            replacements[f"${slot_name}"] = slot_values

        if profile.get("intent.replace_numbers", True):
            # Replace numbers in parsed sentences
            for intent_sentences in sentences.values():
                for sentence in intent_sentences:
                    jsgf.walk_expression(sentence, number_transform,
                                         replacements)

        # Convert to directed graph
        graph = intents_to_graph(intents, replacements)

        # Write graph to JSON file
        json_graph = graph_to_json(graph)
        with open(targets[0], "w") as graph_file:
            json.dump(json_graph, graph_file)
Exemple #2
0
    def task_ini_graph():
        """sentences.ini -> intent.json"""
        sentences, replacements = ini_jsgf.split_rules(intents)

        if profile.get("intent.replace_numbers", True):
            # Replace number ranges with rhasspy/number
            for intent_sentences in sentences.values():
                for sentence in intent_sentences:
                    jsgf.walk_expression(sentence, number_range_transform,
                                         replacements)

        # Gather used slot names
        slot_names = set()
        for intent_name in intents:
            for item in intents[intent_name]:
                for slot_name in get_slot_names(item):
                    slot_names.add(slot_name)

        # Load slot values
        has_slot_program = False
        for slot_key in slot_names:
            slot_info = find_slot(slot_key)

            if isinstance(slot_info, StaticSlotInfo):
                # Parse each non-empty line as a JSGF sentence
                slot_values = []
                with open(slot_info.path, "r") as slot_file:
                    for line in slot_file:
                        line = line.strip()
                        if line:
                            sentence = jsgf.Sentence.parse(line)
                            if word_transform:
                                jsgf.walk_expression(sentence, fix_word_case)

                            slot_values.append(sentence)
            elif isinstance(slot_info, SlotProgramInfo):
                # Program that will generate values
                has_slot_program = True
                slot_values = SlotProgram(slot_info.path,
                                          command_args=slot_info.args)

            # Replace $slot with sentences
            replacements[f"${slot_key}"] = slot_values

        # Add slot files as dependencies
        deps = [find_slot(slot_key).path for slot_key in slot_names]

        # Add profile itself as a dependency
        profile_json_path = profile_dir / "profile.json"
        if profile_json_path.is_file():
            deps.append(profile_json_path)

        return {
            "file_dep":
            ini_paths + deps,
            "targets": [intent_graph],
            "actions":
            [(do_intents_to_graph, [sentences, slot_names, replacements])],
            "uptodate": [False if has_slot_program else None],
        }
Exemple #3
0
    def do_intents_to_graph(sentences, slot_names, replacements, targets):
        # Replace actual numbers
        if profile.get("intent.replace_numbers", True):
            # Replace numbers in parsed sentences
            for intent_sentences in sentences.values():
                for sentence in intent_sentences:
                    jsgf.walk_expression(sentence, number_transform, replacements)

        # Determine whether word casing has to be fixed
        transform = None
        if word_casing == "upper":
            transform = str.upper
        elif word_casing == "lower":
            transform = str.lower

        if transform:

            def fix_case(word):
                if isinstance(word, jsgf.Word):
                    word.text = transform(word.text)

                return word

            # Fix casing
            for intent_sentences in sentences.values():
                for sentence in intent_sentences:
                    jsgf.walk_expression(sentence, fix_case, replacements)

        # Convert to directed graph
        graph = intents_to_graph(sentences, replacements)

        # Write graph to JSON file
        json_graph = graph_to_json(graph)
        with open(targets[0], "w") as graph_file:
            json.dump(json_graph, graph_file)
Exemple #4
0
    def test_walk(self):
        """Test Expression.walk with rule and slot reference."""
        ini_text = """
        [SetAlarm]
        minutes = $minute minutes
        set alarm for <minutes>
        """

        intents = parse_ini(ini_text)
        sentences, replacements = split_rules(intents)
        replacements["$minute"] = [Sentence.parse("2 | 3")]

        def num2words(word):
            if not isinstance(word, Word):
                return

            try:
                n = int(word.text)
                if n == 2:
                    word.text = "two"
                    word.substitution = "2"
                elif n == 3:
                    word.text = "three"
                    word.substitution = "3"
            except ValueError:
                pass

        for s in sentences["SetAlarm"]:
            walk_expression(s, num2words, replacements)

        # Verify minute digits were replaced
        minute = replacements["$minute"][0]
        self.assertEqual(
            minute,
            Sentence(
                text="2 | 3",
                type=SequenceType.GROUP,
                items=[
                    Sequence(
                        text="2 | 3",
                        type=SequenceType.ALTERNATIVE,
                        items=[
                            Word("two", substitution="2"),
                            Word("three", substitution="3"),
                        ],
                    )
                ],
            ),
        )
Exemple #5
0
    def test_walk(self):
        """Test walk_expression."""
        s = Sentence.parse("set alarm for (2 | 3) minutes")

        def num2words(word):
            if not isinstance(word, Word):
                return

            try:
                n = int(word.text)
                if n == 2:
                    word.text = "two"
                    word.substitution = "2"
                elif n == 3:
                    word.text = "three"
                    word.substitution = "3"
            except ValueError:
                pass

        walk_expression(s, num2words)
        self.assertEqual(
            s.items,
            [
                Word("set"),
                Word("alarm"),
                Word("for"),
                Sequence(
                    text="2 | 3",
                    type=SequenceType.GROUP,
                    items=[
                        Sequence(
                            text="2 | 3",
                            type=SequenceType.ALTERNATIVE,
                            items=[
                                Word("two", substitution="2"),
                                Word("three", substitution="3"),
                            ],
                        )
                    ],
                ),
                Word("minutes"),
            ],
        )
Exemple #6
0
    def test_rule_with_same_name(self):
        """Ensure referenced rule names don't conflict with local names."""

        ini_text = """
        [Test1]
        rule = is a test
        this <rule>

        [Test2]
        rule = <Test1.rule>
        this <rule>
        """

        _intents = parse_ini(ini_text)
        intents, replacements = split_rules(_intents)

        # Walk all sentences to make sure there's no infinite recursion
        for intent, sentences in intents.items():
            for sentence in sentences:
                walk_expression(sentence, lambda x: x, replacements)
Exemple #7
0
    def do_intents_to_graph(sentences, slot_names, replacements, targets):
        # Replace actual numbers
        if profile.get("intent.replace_numbers", True):
            # Replace numbers in parsed sentences
            for intent_sentences in sentences.values():
                for sentence in intent_sentences:
                    jsgf.walk_expression(sentence, number_transform,
                                         replacements)

        if word_transform:
            # Fix casing
            for intent_sentences in sentences.values():
                for sentence in intent_sentences:
                    jsgf.walk_expression(sentence, fix_word_case, replacements)

        # Convert to directed graph
        graph = intents_to_graph(sentences, replacements)

        # Write graph to JSON file
        json_graph = graph_to_json(graph)
        with open(targets[0], "w") as graph_file:
            json.dump(json_graph, graph_file)
Exemple #8
0
    def test_walk_multiple_words(self):
        """Test walk_expression with a multi-word replacement."""
        s = Sentence.parse("set alarm for 23 minutes")

        def num2words(word):
            if not isinstance(word, Word):
                return

            try:
                n = int(word.text)
                if n == 23:
                    return Sequence(
                        text="23",
                        type=SequenceType.GROUP,
                        items=[Word("twenty"), Word("three")],
                        substitution="23",
                    )
            except ValueError:
                pass

        walk_expression(s, num2words)
        self.assertEqual(
            s.items,
            [
                Word("set"),
                Word("alarm"),
                Word("for"),
                Sequence(
                    text="23",
                    type=SequenceType.GROUP,
                    items=[Word("twenty"), Word("three")],
                    substitution="23",
                ),
                Word("minutes"),
            ],
        )