Source code for intents.connectors._experimental.snips.connector

"""
Snips NLU is an open source Python/Rust library for Natural Language Understanding.
It provides intent classification and entity (slot) tagging, and it runs **locally**
as a standard Python library.

:class:`SnipsConnector` is a Connector that doesn't require any other service
(nor remote or local) to run. It makes predictions internally, by calling the
underlying `snips-nlu` library functions.

To use :class:`SnipsConnector` it is necessary to install its optional
dependency group:

.. code-block:: sh

    pip install intents[snips]

More details about Snips can be found at

* https://github.com/snipsco/snips-nlu
* https://snips-nlu.readthedocs.io/
"""
import os
import json
import shutil
import logging
from typing import Union, Type

import snips_nlu

from intents import Intent, Agent, LanguageCode
from intents.language import agent_supported_languages, ensure_language_code
from intents.connectors.interface import Connector, ServiceEntityMappings, FulfillmentRequest
from intents.connectors._experimental.snips.prediction import SnipsPrediction, SnipsPredictionComponent
from intents.connectors._experimental.snips import entities, prediction_format

logger = logging.getLogger(__name__)

[docs]class SnipsConnector(Connector): """ This is a :class:`~intents.connectors.interface.Connector` that runs entirely locally, without needing any resident service to operate. Predictions are made by calling the `snips-nlu` Python API. .. warning:: `SnipsConnector` is **experimental**: expect running into relevant rough edges when using it. Its main limitations include: * Intent relations are not implemented * Prompts for required parameters are not implemented: an exception will be thrown if all the required parameters are not present * :class:`Sys.Email`, :class:`Sys.PhoneNumber` and :class:`Url` entities are patched with empty placeholders * :class:`Sys.Date` is only available in English Args: agent_cls: The Agent class to train the system default_session: A default session identifier. Will be generated randomly if None default_language: Default language for predictions. English will be used if None. """ entity_mappings: ServiceEntityMappings = entities.ENTITY_MAPPINGS nlu_engine: snips_nlu.SnipsNLUEngine = None prediction_component: SnipsPredictionComponent def __init__(self, agent_cls: Type[Agent], default_session: str=None, default_language: Union[LanguageCode, str]=None ): super().__init__(agent_cls, default_session, default_language) self.nlu_engines = { lang: snips_nlu.SnipsNLUEngine() for lang in agent_supported_languages(agent_cls) } self.prediction_component = SnipsPredictionComponent(agent_cls, self.entity_mappings)
[docs] def export(self, destination: str): """ Export Agent in the given folder: .. code-block:: python from example_agent.agent import ExampleAgent from intents.connectors._experimental.snips import SnipsConnector snips = SnipsConnector(ExampleAgent) snips.export("./TMP_SNIPS") The export will generate one JSON file per language, they can be loaded into Snips as a JSON Dataset. Args: destination: A folder that will contain exported JSON files """ from intents.connectors._experimental.snips import export rendered = export.render(self) if os.path.isdir(destination): logger.warning("Removing existing export folder: %s", destination) shutil.rmtree(destination) os.makedirs(destination) for lang, data in rendered.items(): with open(os.path.join(destination, f"agent.{lang.value}.json"), "w") as f: json.dump(data, f, indent=4)
[docs] def upload(self): """ As Snips runs locally as a Python library, there is no external service to upload the model to. Instead, `upload` will train Snips local models. Currently there is no persistence for trained models. This means that `upload` should be called every time :class:`SnipsConnector` is instantiated. """ from intents.connectors._experimental.snips import export for lang, rendered in export.render(self).items(): self.nlu_engines[lang].fit(rendered)
[docs] def predict(self, message: str, session: str=None, language: Union[LanguageCode, str]=None) -> SnipsPrediction: """ Predict the given User message in the given session using the given language. When `session` or `language` are None, `predict` will use the default values that are specified in :meth:`__init__`. *predict* will return an instance of :class:`Prediction`, representing the service response. >>> from intents.connectors._experimental.snips import SnipsConnector >>> from example_agent import ExampleAgent >>> snips = SnipsConnector(ExampleAgent) >>> snips.upload() # This trains the models >>> prediction = snips.predict("Hi, my name is Guido") >>> prediction.intent UserNameGive(user_name='Guido') >>> prediction.intent.user_name "Guido" >>> prediction.fulfillment_text "Hi Guido, I'm Bot" >>> prediction.confidence 0.62 Note that the Italian version of :class:`~example_agent.ExampleAgent` won't be trained, as :class:`Sys.Date` is not available for the Italian language in Snips. Args: message: The User message to predict session: Any string identifying a conversation language: A LanguageCode object, or a ISO 639-1 string (e.g. "en") """ if not language: language = self.default_language language = ensure_language_code(language) parse_result_dict = self.nlu_engines[language].parse(message) parse_result = prediction_format.from_dict(parse_result_dict) prediction = self.prediction_component.prediction_from_parse_result(parse_result, language) return self.prediction_component.fulfill_local(prediction, language)
[docs] def trigger(self, intent: Intent, session: str=None, language: Union[LanguageCode, str]=None) -> SnipsPrediction: """ As Snips runs locally and intent relation resolution is not supported, Triggers don't do much more at the moment than returing the intent as it was passed in input. >>> from intents.connectors._experimental.snips import SnipsConnector >>> from example_agent import ExampleAgent, smalltalk >>> snips = SnipsConnector(ExampleAgent) >>> snips.upload() # This trains the models >>> prediction = snips.trigger(smalltalk.AgentNameGive(agent_name='Alice')) >>> prediction.intent AgentNameGive(agent_name='Alice') >>> prediction.fulfillment_text "Howdy Human, I'm Alice" >>> prediction.confidence 1.0 Args: intent: The Intent instance to trigger session: Any string identifying a conversation language: A LanguageCode object, or a ISO 639-1 string (e.g. "en") """ if not language: language = self.default_language language = ensure_language_code(language) prediction = self.prediction_component.prediction_from_intent(intent, language) return self.prediction_component.fulfill_local(prediction, language)
[docs] def fulfill(self, fulfillment_request: FulfillmentRequest) -> dict: """ *Not implemented* """ raise NotImplementedError()