Example #1
0
 def in_started(self, message: Any, sender: RhasspyActor) -> None:
     """Handle messages in started state."""
     if isinstance(message, RecognizeIntent):
         intent = empty_intent()
         intent["text"] = message.text
         intent["speech_confidence"] = message.confidence
         self.send(message.receiver or sender, IntentRecognized(intent))
Example #2
0
    def in_started(self, message: Any, sender: RhasspyActor) -> None:
        """Handle messages in started state."""
        if isinstance(message, RecognizeIntent):
            try:
                self._logger.debug(self.command)

                # Text -> STDIN -> STDOUT -> JSON
                output = subprocess.run(
                    self.command,
                    check=True,
                    input=message.text.encode(),
                    stdout=subprocess.PIPE,
                ).stdout.decode()

                intent = json.loads(output)

            except Exception:
                self._logger.exception("in_started")
                intent = empty_intent()
                intent["text"] = message.text
                intent["raw_text"] = message.text

            intent["speech_confidence"] = message.confidence
            self.send(
                message.receiver or sender,
                IntentRecognized(intent, handle=message.handle),
            )
Example #3
0
    def in_loaded(self, message: Any, sender: RhasspyActor) -> None:
        """Handle messages in loaded state."""
        if isinstance(message, RecognizeIntent):
            try:
                self.load_graph()

                # Assume lower case, white-space separated tokens
                text = message.text
                tokens = re.split(r"\s+", text)

                if self.profile.get("intent.fsticuffs.ignore_unknown_words", True):
                    # Filter tokens
                    tokens = [w for w in tokens if w in self.words]

                recognitions = recognize(
                    tokens, self.graph, fuzzy=self.fuzzy, stop_words=self.stop_words
                )
                assert recognitions, "No intent recognized"

                # Use first intent
                recognition = recognitions[0]

                # Convert to JSON
                intent = recognition.asdict()
            except Exception:
                self._logger.exception("in_loaded")
                intent = empty_intent()

            intent["speech_confidence"] = message.confidence
            self.send(
                message.receiver or sender,
                IntentRecognized(intent, handle=message.handle),
            )
Example #4
0
    def in_started(self, message: Any, sender: RhasspyActor) -> None:
        """Handle messages in started state."""
        if isinstance(message, RecognizeIntent):
            post_url = urljoin(self.hass_config["url"],
                               "api/conversation/process")

            # Send to Home Assistant
            kwargs = hass_request_kwargs(self.hass_config, self.pem_file)
            kwargs["json"] = {"text": message.text}

            if self.pem_file is not None:
                kwargs["verify"] = self.pem_file

            # POST to /api/conversation/process
            response = requests.post(post_url, **kwargs)
            response.raise_for_status()

            response_json = response.json()

            # Extract speech
            if self.handle_speech:
                speech = pydash.get(response_json, "speech.plain.speech", "")
                if speech:
                    # Forward to TTS system
                    self._logger.debug("Handling speech")
                    self.send(sender, SpeakSentence(speech))

            # Return empty intent since conversation doesn't give it to us
            intent = empty_intent()
            intent["text"] = message.text
            intent["raw_text"] = message.text
            intent["speech_confidence"] = message.confidence
            self.send(message.receiver or sender, IntentRecognized(intent))
Example #5
0
    def recognize(self, text: str) -> Dict[str, Any]:
        """Use Adapt engine to recognize intent."""
        # Get all intents
        assert self.engine is not None, "Adapt engine not loaded"
        intents = [
            intent for intent in self.engine.determine_intent(text) if intent
        ]

        if len(intents) > 0:
            # Return the best intent only
            intent = max(intents, key=lambda x: x.get("confidence", 0))
            intent_type = intent["intent_type"]
            entity_prefix = "{0}.".format(intent_type)

            slots = {}
            for key, value in intent.items():
                if key.startswith(entity_prefix):
                    key = key[len(entity_prefix):]
                    slots[key] = value

            # Try to match Rasa NLU format for future compatibility
            return {
                "text":
                text,
                "intent": {
                    "name": intent_type,
                    "confidence": intent.get("confidence", 0),
                },
                "entities": [{
                    "entity": name,
                    "value": value
                } for name, value in slots.items()],
            }

        return empty_intent()
Example #6
0
    def recognize(self, text: str) -> Dict[str, Any]:
        """Find sentence with lowest string-edit distance."""
        confidence = 0
        if len(text) > 0:
            assert self.examples is not None, "No examples JSON"

            choices: Dict[str, Tuple[str, Dict[str, Any]]] = {}
            with concurrent.futures.ProcessPoolExecutor() as executor:
                future_to_name = {}
                for intent_name, intent_examples in self.examples.items():
                    sentences = []
                    for example in intent_examples:
                        example_text = example.get("raw_text", example["text"])
                        logging.debug(example_text)
                        choices[example_text] = (example_text, example)
                        sentences.append(example_text)

                    future = executor.submit(_get_best_fuzzy, text, sentences)
                    future_to_name[future] = intent_name

            # Process them as they complete
            best_text = ""
            best_score = None
            for future in concurrent.futures.as_completed(future_to_name):
                intent_name = future_to_name[future]
                text, score = future.result()
                if (best_score is None) or (score > best_score):
                    best_text = text
                    best_score = score

            if best_text in choices:
                confidence = (best_score / 100) if best_score else 1
                if confidence >= self.min_confidence:
                    # (text, intent, slots)
                    best_text, best_intent = choices[best_text]

                    # Update confidence and return example intent
                    best_intent["intent"]["confidence"] = confidence
                    return best_intent

                self._logger.warning(
                    "Intent did not meet confidence threshold: %s < %s",
                    confidence,
                    self.min_confidence,
                )

        # Empty intent
        intent = empty_intent()
        intent["text"] = text
        intent["intent"]["confidence"] = confidence

        return intent
Example #7
0
    def in_started(self, message: Any, sender: RhasspyActor) -> None:
        """Handle messages in started state."""
        if isinstance(message, RecognizeIntent):
            try:
                intent = self.recognize(message.text)
            except Exception:
                self._logger.exception("in_started")
                intent = empty_intent()
                intent["text"] = message.text

            intent["speech_confidence"] = message.confidence
            self.send(
                message.receiver or sender,
                IntentRecognized(intent, handle=message.handle),
            )
Example #8
0
    def recognize_fuzzy(self, text: str, eps: str = "<eps>") -> Dict[str, Any]:
        """Do fuzzy breadth-first search on FST as graph."""
        from rhasspy.train.jsgf2fst import symbols2intent

        # Assume lower case, white-space separated tokens
        tokens = re.split(r"\s+", text)

        if self.profile.get("intent.fsticuffs.ignore_unknown_words", True):
            # Filter tokens
            tokens = [w for w in tokens if w in self.words]

        # Only run search if there are any tokens
        intents = []
        if len(tokens) > 0:
            intent_symbols_and_costs = FsticuffsRecognizer._get_symbols_and_costs(
                self.graph, tokens, stop_words=self.stop_words, eps=eps
            )
            for symbols, cost in intent_symbols_and_costs.values():
                intent = symbols2intent(symbols, eps=eps)
                intent["intent"]["confidence"] = (len(tokens) - cost) / len(tokens)
                intents.append(intent)

            intents = sorted(
                intents, key=lambda i: i["intent"]["confidence"], reverse=True
            )

        self._logger.debug("Recognized %s intent(s)", len(intents))

        # Use first intent
        if len(intents) > 0:
            intent = intents[0]

            # Add slots
            intent["slots"] = {}
            for ev in intent["entities"]:
                intent["slots"][ev["entity"]] = ev["value"]

            # Add alternative intents
            intent["intents"] = []
            for other_intent in intents[1:]:
                intent["intents"].append(other_intent)

            self._logger.debug(intents)
        else:
            intent = empty_intent()
            intent["text"] = text

        return intent
Example #9
0
    def in_started(self, message: Any, sender: RhasspyActor) -> None:
        """Handle messages in started state."""
        if isinstance(message, RecognizeIntent):
            try:
                intent = self.recognize(message.text)
                intent["intent"]["name"] = intent["intent"]["name"] or ""
                logging.debug(repr(intent))
            except Exception:
                self._logger.exception("in_started")
                intent = empty_intent()
                intent["text"] = message.text

            intent["raw_text"] = message.text
            self.send(
                message.receiver or sender,
                IntentRecognized(intent, handle=message.handle),
            )
Example #10
0
    def recognize(self, text: str) -> Dict[str, Any]:
        """Run intent classifier and then named-entity recognizer."""
        # pylint: disable=E0401
        from flair.data import Sentence

        intent = empty_intent()
        sentence = Sentence(text)

        assert self.intent_map is not None
        if self.class_model is not None:
            self.class_model.predict(sentence)
            assert len(sentence.labels) > 0, "No intent predicted"

            label = sentence.labels[0]
            intent_id = label.value
            intent["intent"]["confidence"] = label.score
        else:
            # Assume first intent
            intent_id = next(iter(self.intent_map.keys()))
            intent["intent"]["confidence"] = 1

        intent["intent"]["name"] = self.intent_map[intent_id]

        assert self.ner_models is not None
        if intent_id in self.ner_models:
            # Predict entities
            self.ner_models[intent_id].predict(sentence)
            ner_dict = sentence.to_dict(tag_type="ner")
            for named_entity in ner_dict["entities"]:
                intent["entities"].append({
                    "entity":
                    named_entity["type"],
                    "value":
                    named_entity["text"],
                    "start":
                    named_entity["start_pos"],
                    "end":
                    named_entity["end_pos"],
                    "confidence":
                    named_entity["confidence"],
                })

        return intent
Example #11
0
    def in_loaded(self, message: Any, sender: RhasspyActor) -> None:
        """Handle messages in loaded state."""
        if isinstance(message, RecognizeIntent):
            try:
                self.load_fst()

                if self.fuzzy:
                    # Fuzzy search
                    intent = self.recognize_fuzzy(message.text)
                else:
                    # Strict search
                    intent = self.recognize(message.text)
            except Exception:
                self._logger.exception("in_loaded")
                intent = empty_intent()

            intent["speech_confidence"] = message.confidence
            self.send(
                message.receiver or sender,
                IntentRecognized(intent, handle=message.handle),
            )