HELP PLEASE? On setting and retrieving variables in skills

Hi forum, again I’m new at writing skills and have made progress but have been stuck on this for a while.

Here is the intent and dialog file file:

$ cat salary.intent
(what is|what are|whats) my {interval} {pay_type}
$ cat salary.dialog
your {interval} {pay_type} is {amount}

Here’s the crux of my code:

    @intent_handler("salary.intent")
    def handle_salary_amount(self, msg):
        self.log.info("in function handle_salary_amount")
        salary_intent = IntentBuilder("list_salary").require('pay_type').require('interval').build()
        self.log.info('msg.data:')
        self.log.info(msg.data)
        amount = '$250'                    # TODO: call API to get real data
        self.speak_dialog('salary', data={
            'interval': msg.data['interval'],
            'pay_type': msg.data['pay_type'],
            'amount': amount
        })

Here’s a question and answer:

 what is my monthly health insurance                         
 >> your m onthly health insurance is $250  

The variable interval is getting set to ‘m’ while pay_type is ‘onthly health’. I have maximum debug on, but it gives no clue as to where the variables get set. I don’t write python regularly and decorator functions are tricky.

Any help on how to find the issue will be appreciated. Thanks.

-Mike M

Hi Mike, I am not sure if You need to use IntentBuilder, with Padatious intent I think it is not necessary.

This is how we do it in HomeAssistant skill:

Intent set.climate.intent:

 set (the|) {entity} temperature to {temp} degrees

Handler:

 @intent_handler('set.climate.intent')
    def handle_set_thermostat_intent(self, message: Message) -> None:
        """Handle set climate intent."""
        self.log.debug("Set thermostat intent on entity: %s", message.data.get("entity"))
        message.data["Entity"] = message.data.get("entity")
        message.data["Temp"] = message.data.get("temp")
        self._handle_set_thermostat(message)     
      
     def _handle_set_thermostat(self, message: Message) -> None:
        """Handler for setting thermostats."""
        entity = message.data["entity"]
        self.log.debug("Entity: %s", entity)
        self.log.debug("This is the message data: %s", message.data)
        temperature = message.data["temp"]
        self.log.debug("Temperature: %s", temperature)

Also check Remote debug skill, if you use VS Code, this can help you understood, whats going on in Your and Mycroft code.

1 Like

EDIT: see comment below and ignore most of this!

the problem is that you have 2 capture groups in a row, you need words between variables

this should likely be considered a padatious bug, i think this is a valid use case, just not well defined

keep in the mind those variables are wild cards, they match anything, you can partition the string between those variables in any random way and it would be valid,

imho this should happen at the word level not at the character level, that’s the padatious bug i am referring to. But in the end this is mainly a problem of bad intent modeling and you need to tweak your intent files

1 Like

not sure whats happening in your case, but i cloned the repos from github abd added a few unittests to padaos and padatious and they seem to be working and handling your case correctly

# added unittests in padaos

    def test_multiple_entities(self):
        self.container.add_intent('test4', ['I see {modifier} {thing}'])
        assert self.container.calc_intent('I see ugly faces') == {
            'name': 'test4', 'entities': {'modifier': 'ugly', 'thing': 'faces'}
        }

        self.container.add_intent('test5', ['(what is|what are|whats) my {interval} {pay_type}'])
        assert self.container.calc_intent('what is my monthly health insurance') == {
            'name': 'test5', 'entities': {'interval': 'monthly', 'pay_type': 'health insurance'}
        }

# added these to padatious unittests and also passing
    def test_multi_extraction(self):
        self.cont.add_intent('see', [
            'I see {modifier} {thing}'
        ])
        self.cont.add_intent('what', [
            '(what is|what are|whats) my {interval} {pay_type}'
        ])

        self.cont.train(False)

        data = self.cont.calc_intent('I see ugly faces')
        assert data.name == 'see'
        assert data.matches == {'modifier': 'ugly', 'thing': 'faces'}
        assert data.conf > 0.5

        data = self.cont.calc_intent('what is my monthly health insurance')
        assert data.name == 'what'
        assert data.matches == {'interval': 'monthly', 'pay_type': 'health insurance'}
        assert data.conf > 0.5

further debugging is needed, but seems like the issue might be in mycroft-core itself, first thing i would try is to install padatious from github in case the version in mycroft-core is outdated

1 Like

@JarbasAl @Tony763 thanks for all the help! Will try all of these today …

-Mike M
1 Like

I don’t exactly know how to do that (but I’m learning new things everyday - HA HA)

Here’s what I tried:

$ cd ~/mycroft-core/.venv/lib/python3.8/site-packages
$ mv padatious padatious.old
$ git clone https://github.com/MycroftAI/padatious
$ sudo apt-get install libfann-dev python3-dev python3-pip swig libfann-dev python3-fann2
$ pip3 install padatious
...
Successfully installed padaos-0.1.10 padatious-0.4.8 xxhash-3.0.0
$ mycroft-start debug
...
  File "/home/pi/mycroft-core/mycroft/util/format.py", line 50, in <module>
    from padatious.util import expand_parentheses
ModuleNotFoundError: No module named 'padatious.util'

Guess I’ll move back to the padatious.old I saved for now.

-Mike M

Moved back to the original code and I’m running again. :laughing:

I did a comparison of the python files with:

for i in *py; do
  diff $i ../padatious/padatious
done

The only modifed file is intent_container.py and the new code has this:

...
<         # ToDo: still padaos.compile (regex compilation) is redone when loading
---
>         # workaround: load training data for both entities and intents since
>         # padaos regex needs it for (re)compilation until TODO is cleared
78a83,98
>             if f.endswith('.entity'):
>                 entity_name = f[0:f.find('.entity')]
>                 with open(os.path.join(self.cache_dir, f), 'r') as d:
>                     entity_traindata[entity_name] = [line.strip()
>                                                      for line in d]
>
>             elif f.endswith('.intent'):
>                 intent_name = f[0:f.find('.intent')]
>                 with open(os.path.join(self.cache_dir, f), 'r') as d:
>                     intent_traindata[intent_name] = [line.strip()
>                                                      for line in d]
>
>         # TODO: padaos.compile (regex compilation) is redone when loading: find
>         # a way to persist regex, as well!
>         for f in os.listdir(self.cache_dir):
>
83c103
<                     lines=[],
---
>                     lines=entity_traindata[entity_name],
90c110
<                     lines=[],
---
>                     lines=intent_traindata[intent_name],
121d140
<                 must_train (bool): Whether to dismiss model if present and train from scratch again
146d164
<            must_train (bool): Whether to dismiss model if present and train from scratch again
173d190
<             must_train (bool): Whether to dismiss model if present and train from scratch again

So I’m thinking this change will not affect parsing and variable setting. Moving on …

-Mike M

I commented out the IntentBuilder() line and the code still works. But it did not change the variable setting issue.

Thanks for the feedback. Moving on to the last suggestion …

-Mike
1 Like