Răsfoiți Sursa

update neuron module to handle overridden TTS setting

nico 8 ani în urmă
părinte
comite
948ee26df8
3 a modificat fișierele cu 109 adăugiri și 121 ștergeri
  1. 31 34
      Docs/neurons.md
  2. 30 41
      Tests/test_neuron_module.py
  3. 48 46
      kalliope/core/NeuronModule.py

+ 31 - 34
Docs/neurons.md

@@ -122,12 +122,12 @@ As this is multi-lines, we can put the content in a file and use a `file_templat
 
 ## Overridable parameters
 
-For each neuron, you can override some parameters to use a specific configuration of TTS instead of the default one 
-set in [settings.yml](settings.md) file.
+For each neuron, you can override some parameters to use a specific configuration instead of the default one set in [settings.yml](settings.md) file.
 
-### Cache
+### TTS
 
-You can override the default cache configuration. By default Kalliope uses a cache to save a generated audio from a TTS engine.
+You can override the default tts configuration like the TTS engine to use and its parameters like the language, api key or the cache.
+By default Kalliope uses a cache to save a generated audio from a TTS engine.
 This cache is useful to manage sentences that are not suppose to be changed very often. For example, the following sentence will not change in time, so it's more optimized to generate it once and to keep it in cash:
 ```yml
 - say:
@@ -136,43 +136,40 @@ This cache is useful to manage sentences that are not suppose to be changed very
 ```
 
 In some cases, especially when the neuron is based on a template, the generated audio will change on each new call of the neuron and so the usage of a cache is not necessary. The best example of the case like this is the `systemdate` neuron. As the time changes every minute, the generated audio will change too and so, saving the generated audio in the cache is useless. In this case, you can override the cache usage for this neuron:
+Here are my default TTS settings in my `settings.yml` file
 ```yml
-- systemdate:
-    say_template:
-      - "It's {{ hours }} hour and {{ minutes }} minute"
-    cache: False
-```
+default_text_to_speech: "pico2wave"
 
-### tts
+text_to_speech:
+  - pico2wave:
+      language: "fr-FR"
+      cache: True
+```
 
-You can override the default tts for each neurons. Just add the "tts" parameter to the neuron like in the example bellow
+And this is how I override only the cache parameter. 
 ```yml
-neurons:
-  - say:
-    message:
-      - "Hello, sir"
-  - say:
-    message:
-      - "My name is Kalliope"
-    tts: "acapela"
+- systemdate:
+    say_template:
+      - "It's {{ hours }} hour and {{ minutes }} minute"
+    tts:
+      pico2wave:
+        cache: False
 ```
 
-Here, the first neuron will use the default tts as set in the settings.yml file. The second neuron will use the tts "acapela".
-
-or with new parameters:
+You can override all parameter for each neurons like in the example bellow
 ```yml
-neurons:
-  - say:
-      message:
-        - "Me llamo kalliope"
-      tts:
-        pico2wave:
-          language: "es-ES"
+- name: "say-something-in-spanish"
+    signals:
+      - order: "Say hello in spanish"
+    neurons:
+      - say:
+          message:
+            - "Buenos dias"
+          tts:             
+            pico2wave:
+              language: "es-ES"
+              cache: False
 ```
 
-Here,  neuron will use the tts "pico2wave" with "es-ES" language.
-
-
-
->**Note:** The TTS must has been configured with its required parameters in the settings.yml file. See [TTS documentation](tts.md).
+>**Note:** The TTS must has been configured with its required parameters in the settings.yml file to be overridden. See [TTS documentation](tts.md).
 

+ 30 - 41
Tests/test_neuron_module.py

@@ -1,8 +1,10 @@
 import os
 import unittest
 import mock
+from kalliope.core.Models.Tts import Tts
 
-from kalliope.core.NeuronModule import NeuronModule, TemplateFileNotFoundException
+from kalliope import SettingLoader
+from kalliope.core.NeuronModule import NeuronModule, TemplateFileNotFoundException, TTSModuleNotFound
 
 
 class TestNeuronModule(unittest.TestCase):
@@ -20,6 +22,8 @@ class TestNeuronModule(unittest.TestCase):
         }
         self.neuron_module_test = NeuronModule()
 
+        self.settings = SettingLoader(file_path="settings_test.yml").settings
+
     def tearDown(self):
         del self.neuron_module_test
 
@@ -29,7 +33,7 @@ class TestNeuronModule(unittest.TestCase):
         """
 
         with mock.patch("kalliope.core.OrderListener.start") as mock_orderListener_start:
-            with mock.patch("kalliope.core.OrderListener.join") as mock_orderListener_join:
+            with mock.patch("kalliope.core.OrderListener.join"):
                 def callback():
                     pass
 
@@ -37,44 +41,28 @@ class TestNeuronModule(unittest.TestCase):
                 mock_orderListener_start.assert_called_once_with()
                 mock_orderListener_start.reset_mock()
 
-    def test_update_params(self):
-        """
-        Test Update the value of the cache in the provided arg list
-        """
-
-        # True -> False
-        args_dict = {
-            "cache": True
-        }
-        expected_dict = {
-            "cache": False
-        }
-        self.assertEquals(NeuronModule._update_params({'cache': False}, args_dict=args_dict),
-                          expected_dict,
-                          "Fail to update the cache value from True to False")
-        self.assertFalse(args_dict["cache"])
-
-        # False -> True
-        args_dict = {
-            "cache": False
-        }
-        expected_dict = {
-            "cache": True
-        }
-        self.assertEquals(NeuronModule._update_params({'cache': True}, args_dict=args_dict),
-                          expected_dict,
-                          "Fail to update the cache value from False to True")
-
-        self.assertTrue(args_dict["cache"])
-
-        # {} -> {'language': 'en-US'}
-        args_dict = {}
-        expected_dict = {
-            "language": "en-US"
-        }
-        self.assertEquals(NeuronModule._update_params({'language': 'en-US'}, args_dict=args_dict),
-                          expected_dict,
-                          "Fail to create new parameter: language key and value")
+    def test_get_tts_object(self):
+
+        # no TTS name provided. should return the default tts
+        expected_tts = Tts(name="pico2wave", parameters={"language": "fr-FR", "cache": True})
+        self.assertEqual(NeuronModule._get_tts_object(settings=self.settings), expected_tts)
+
+        # TTS provided, only cache parameter updated
+        expected_tts = Tts(name="pico2wave", parameters={"language": "fr-FR", "cache": False})
+        self.assertEqual(NeuronModule._get_tts_object(tts_name="pico2wave",
+                                                      override_parameter={"cache": False},
+                                                      settings=self.settings), expected_tts)
+
+        # TTS provided, all parameters updated
+        expected_tts = Tts(name="pico2wave", parameters={"language": "es-ES", "cache": False})
+        self.assertEqual(NeuronModule._get_tts_object(tts_name="pico2wave",
+                                                      override_parameter={"language": "es-ES", "cache": False},
+                                                      settings=self.settings), expected_tts)
+        # TTS not existing in settings
+        with self.assertRaises(TTSModuleNotFound):
+            NeuronModule._get_tts_object(tts_name="no_existing_tts",
+                                         override_parameter={"cache": False},
+                                         settings=self.settings)
 
     def test_get_message_from_dict(self):
 
@@ -135,4 +123,5 @@ class TestNeuronModule(unittest.TestCase):
         self.assertEqual(expected_result, neuron_module.serialize())
 
 
-
+if __name__ == '__main__':
+    unittest.main()

+ 48 - 46
kalliope/core/NeuronModule.py

@@ -7,12 +7,8 @@ from jinja2 import Template
 
 from kalliope.core import OrderListener
 from kalliope.core.ConfigurationManager import SettingLoader, BrainLoader
-from kalliope.core.Models import Order
 from kalliope.core.Models.MatchedSynapse import MatchedSynapse
-from kalliope.core.NeuronLauncher import NeuronLauncher
-from kalliope.core.NeuronParameterLoader import NeuronParameterLoader
 from kalliope.core.OrderAnalyser import OrderAnalyser
-from kalliope.core.SynapseLauncher import SynapseLauncher
 from kalliope.core.Utils.Utils import Utils
 
 logging.basicConfig()
@@ -74,23 +70,22 @@ class NeuronModule(object):
         brain_loader = BrainLoader()
         self.brain = brain_loader.brain
 
-        self.override_params = dict()
-        # get if the cache settings is present
-        override_cache = kwargs.get('cache', None)
-        if override_cache is not None:
-            self.override_params['cache'] = override_cache
-
-        # check if the user has overrider the TTS
-        tts = kwargs.get('tts', None)
-        if tts is None:
-            # No tts provided,  we load the default one
-            self.tts = self.settings.default_tts_name
-        elif type(tts) is dict:
-            for key, value in tts.iteritems():
-                self.tts = key
-                self.override_params = value
+        # a dict of overridden TTS parameters if provided by the user
+        self.override_tts_parameters = kwargs.get('tts', None)
+
+        # create the TTS instance
+        self.tts = None
+        if self.override_tts_parameters is None or not isinstance(self.override_tts_parameters, dict):
+            # we get the default TTS
+            self.tts = self._get_tts_object(settings=self.settings)
         else:
-            self.tts = tts
+            for key, value in self.override_tts_parameters.iteritems():
+                tts_name = key
+                tts_parameters = value
+                print tts_parameters
+                self.tts = self._get_tts_object(tts_name=tts_name,
+                                                override_parameter=tts_parameters,
+                                                settings=self.settings)
 
         # get templates if provided
         # Check if there is a template associate to the output message
@@ -155,23 +150,13 @@ class NeuronModule(object):
             self.tts_message = tts_message
             Utils.print_success(tts_message)
 
-            # create a tts object from the tts the user want to use
-            tts_object = next((x for x in self.settings.ttss if x.name == self.tts), None)
-            if tts_object is None:
-                raise TTSModuleNotFound("The tts module name %s does not exist in settings file" % self.tts)
-                   
-            if self.override_params is not None:
-                tts_object.parameters = self._update_params(self.override_params, tts_object.parameters)
-
-            logger.debug("NeuroneModule: TTS args: %s" % tts_object)
-
             # get the instance of the TTS module
             tts_folder = None
             if self.settings.resources:
                 tts_folder = self.settings.resources.tts_folder
             tts_module_instance = Utils.get_dynamic_class_instantiation(package_name="tts",
-                                                                        module_name=tts_object.name,
-                                                                        parameters=tts_object.parameters,
+                                                                        module_name=self.tts.name,
+                                                                        parameters=self.tts.parameters,
                                                                         resources_dir=tts_folder)
             # generate the audio file and play it
             tts_module_instance.say(tts_message)
@@ -249,20 +234,6 @@ class NeuronModule(object):
         with open(real_file_template_path, 'r') as content_file:
             return content_file.read()
 
-    @staticmethod
-    def _update_params(new_override_params, args_dict):
-        """
-        update the value for the keys in the dict args_list
-        :param new_override_cache: cache boolean to set in place of the current one in args_list
-        :param args_dict: arg list that contain "cache" to update
-        :return: args_dict updated
-        """
-        logger.debug("args for TTS plugin before update: %s" % str(args_dict))
-        for key, value in new_override_params.iteritems():
-            args_dict[key] = value
-        logger.debug("args for TTS plugin after update: %s" % str(args_dict))
-        return args_dict
-
     @staticmethod
     def get_audio_from_stt(callback):
         """
@@ -283,3 +254,34 @@ class NeuronModule(object):
         :return:
         """
         return self.neuron_name
+
+    @staticmethod
+    def _get_tts_object(tts_name=None, override_parameter=None, settings=None):
+        """
+        Return a TTS model object
+        If no tts name provided, return the default TTS defined in the settings
+        If the TTS name is provided, get the default configuration for this TTS in settings and override each parameters
+        with parameters provided in override_parameter
+        :param tts_name: name of the TTS to load
+        :param override_parameter: dict of parameter to override the default configuration of the TTS
+        :param settings: current settings
+        :return: Tts model object
+        """
+
+        # if the tts_name is not provided, we get the default tts from settings
+        if tts_name is None:
+            tts_name = settings.default_tts_name
+
+        # create a tts object from the tts the user want to use
+        tts_object = next((x for x in settings.ttss if x.name == tts_name), None)
+        if tts_object is None:
+            raise TTSModuleNotFound("The tts module name %s does not exist in settings file" % tts_name)
+
+        if override_parameter is not None:  # the user want to override the default TTS configuration
+            logger.debug("args for TTS plugin before update: %s" % str(tts_object.parameters))
+            for key, value in override_parameter.iteritems():
+                tts_object.parameters[key] = value
+            logger.debug("args for TTS plugin after update: %s" % str(tts_object.parameters))
+
+        logger.debug("NeuroneModule: TTS args: %s" % tts_object)
+        return tts_object