Esempio n. 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)
Esempio n. 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],
        }
Esempio n. 3
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"),
                        ],
                    )
                ],
            ),
        )
Esempio n. 4
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)