Skip to content

Commit

Permalink
1.7.0 (#482)
Browse files Browse the repository at this point in the history
  • Loading branch information
NeonDaniel authored Oct 26, 2023
2 parents 6ad317f + df235bd commit 630064f
Show file tree
Hide file tree
Showing 12 changed files with 606 additions and 92 deletions.
1 change: 1 addition & 0 deletions .github/workflows/license_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ jobs:
uses: neongeckocom/.github/.github/workflows/license_tests.yml@master
with:
package-extras: audio,configuration,networking
packages-exclude: '^(precise-runner|fann2|tqdm|bs4|ovos-phal-plugin|ovos-skill|neon-core|nvidia|neon-phal-plugin|bitstruct|audioread).*'
42 changes: 33 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,52 @@
# Changelog

## [1.6.2a3](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.2a3) (2023-08-22)
## [1.7.0](https://github.com/NeonGeckoCom/neon-utils/tree/1.7.0) (2023-10-26)

[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.2a2...1.6.2a3)
[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.3a5...1.7.0)

**Fixed bugs:**

- \[BUG\] Got some error/warning when the skill initialize [\#463](https://github.com/NeonGeckoCom/neon-utils/issues/463)

## [1.6.3a5](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.3a5) (2023-10-25)

[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.3a4...1.6.3a5)

**Merged pull requests:**

- Troubleshooting Skill Class Init [\#481](https://github.com/NeonGeckoCom/neon-utils/pull/481) ([NeonDaniel](https://github.com/NeonDaniel))

## [1.6.3a4](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.3a4) (2023-10-24)

[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.3a3...1.6.3a4)

**Merged pull requests:**

- Update Skill base classes [\#480](https://github.com/NeonGeckoCom/neon-utils/pull/480) ([NeonDaniel](https://github.com/NeonDaniel))

## [1.6.3a3](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.3a3) (2023-10-10)

[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.3a2...1.6.3a3)

**Merged pull requests:**

- Update logging for better readability [\#475](https://github.com/NeonGeckoCom/neon-utils/pull/475) ([NeonDaniel](https://github.com/NeonDaniel))
- Better exception handling around signal method patching [\#479](https://github.com/NeonGeckoCom/neon-utils/pull/479) ([NeonDaniel](https://github.com/NeonDaniel))

## [1.6.2a2](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.2a2) (2023-08-16)
## [1.6.3a2](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.3a2) (2023-10-10)

[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.2a1...1.6.2a2)
[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.3a1...1.6.3a2)

**Merged pull requests:**

- Troubleshooting spoken responses in a non-default language [\#474](https://github.com/NeonGeckoCom/neon-utils/pull/474) ([NeonDaniel](https://github.com/NeonDaniel))
- Skill Compatibility [\#478](https://github.com/NeonGeckoCom/neon-utils/pull/478) ([NeonDaniel](https://github.com/NeonDaniel))

## [1.6.2a1](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.2a1) (2023-08-11)
## [1.6.3a1](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.3a1) (2023-10-10)

[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.1...1.6.2a1)
[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.2...1.6.3a1)

**Merged pull requests:**

- Supported Languages bugfix and unit test [\#473](https://github.com/NeonGeckoCom/neon-utils/pull/473) ([NeonDaniel](https://github.com/NeonDaniel))
- Emit handler complete event for CommonQuery skills [\#477](https://github.com/NeonGeckoCom/neon-utils/pull/477) ([NeonDaniel](https://github.com/NeonDaniel))



Expand Down
4 changes: 2 additions & 2 deletions neon_utils/signal_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ def init_signal_handlers():
mycroft.util.signal.create_signal = _create_signal
mycroft.util.signal.check_for_signal = _check_for_signal
LOG.info(f"Overrode mycroft.util.signal methods")
except ImportError:
pass
except (ImportError, AttributeError) as e:
LOG.debug(e)
except TypeError as e:
# This comes from tests overriding MessageBusClient()
LOG.error(e)
Expand Down
5 changes: 5 additions & 0 deletions neon_utils/skills/common_play_skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
from enum import Enum, IntEnum
from abc import ABC, abstractmethod
from ovos_bus_client import Message
from ovos_utils.log import log_deprecation

from neon_utils.skills.neon_skill import NeonSkill

from ovos_utils.skills.audioservice import AudioServiceInterface as AudioService
Expand Down Expand Up @@ -85,6 +87,9 @@ class CommonPlaySkill(NeonSkill, ABC):
is needed.
"""
def __init__(self, *args, **kwargs):
log_deprecation("This base class is deprecated. Implement "
"`ovos_workshop.skills.common_play."
"OVOSCommonPlaybackSkill`", "2.0.0")
NeonSkill.__init__(self, *args, **kwargs)
self.audioservice = None
self.play_service_string = None
Expand Down
125 changes: 72 additions & 53 deletions neon_utils/skills/common_query_skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,34 +40,32 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from enum import IntEnum
from abc import ABC, abstractmethod
from abc import abstractmethod
from os.path import dirname

from ovos_workshop.decorators.layers import IntentLayers
from ovos_workshop.skills.common_query_skill import CQSMatchLevel, CQSVisualMatchLevel
from ovos_workshop.skills.common_query_skill import CommonQuerySkill as _CQS
from ovos_utils.file_utils import resolve_resource_file
from ovos_utils.log import log_deprecation
from neon_utils.skills.neon_skill import NeonSkill


class CQSMatchLevel(IntEnum):
EXACT = 1 # Skill could find a specific answer for the question
CATEGORY = 2 # Skill could find an answer from a category in the query
GENERAL = 3 # The query could be processed as a general query


# Copy of CQSMatchLevel to use if the skill returns visual media
CQSVisualMatchLevel = IntEnum('CQSVisualMatchLevel',
[e.name for e in CQSMatchLevel])


def is_CQSVisualMatchLevel(match_level):
log_deprecation("This method is deprecated", "2.0.0")
return isinstance(match_level, type(CQSVisualMatchLevel.EXACT))


VISUAL_DEVICES = ['mycroft_mark_2']


def handles_visuals(platform):
log_deprecation("This method is deprecated", "2.0.0")
return platform in VISUAL_DEVICES


class CommonQuerySkill(NeonSkill, ABC):
# TODO: Consider deprecation and implementing ovos_workshop directly
class CommonQuerySkill(NeonSkill, _CQS):
"""Question answering skills should be based on this class.
The skill author needs to implement `CQS_match_query_phrase` returning an
Expand All @@ -78,47 +76,36 @@ class CommonQuerySkill(NeonSkill, ABC):
answers from several skills presenting the best one available.
"""
def __init__(self, *args, **kwargs):
NeonSkill.__init__(self, *args, **kwargs)
# these should probably be configurable
self.level_confidence = {
CQSMatchLevel.EXACT: 0.9,
CQSMatchLevel.CATEGORY: 0.6,
CQSMatchLevel.GENERAL: 0.5
}

# Manual init of OVOSSkill
self.private_settings = None
self._threads = []
self._original_converse = self.converse
self.intent_layers = IntentLayers()
self.audio_service = None

def bind(self, bus):
"""Overrides the default bind method of MycroftSkill.
This registers messagebus handlers for the skill during startup
but is nothing the skill author needs to consider.
"""
if bus:
super().bind(bus)
self.add_event('question:query', self.__handle_question_query)
self.add_event('question:action', self.__handle_query_action)

def __handle_question_query(self, message):
search_phrase = message.data["phrase"]
NeonSkill.__init__(self, *args, **kwargs)

# First, notify the requestor that we are attempting to handle
# (this extends a timeout while this skill looks for a match)
self.bus.emit(message.response({"phrase": search_phrase,
"skill_id": self.skill_id,
"searching": True}))

# Now invoke the CQS handler to let the skill perform its search
result = self.CQS_match_query_phrase(search_phrase, message)

if result:
match = result[0]
level = result[1]
answer = result[2]
callback = result[3] if len(result) > 3 else None
confidence = self.__calc_confidence(match, search_phrase, level)
self.bus.emit(message.response({"phrase": search_phrase,
"skill_id": self.skill_id,
"answer": answer,
"callback_data": callback,
"conf": confidence}))
else:
# Signal we are done (can't handle it)
self.bus.emit(message.response({"phrase": search_phrase,
"skill_id": self.skill_id,
"searching": False}))
noise_words_filepath = f"text/{self.lang}/noise_words.list"
default_res = f"{dirname(dirname(__file__))}/res/text/{self.lang}" \
f"/noise_words.list"
noise_words_filename = \
resolve_resource_file(noise_words_filepath,
config=self.config_core) or \
resolve_resource_file(default_res, config=self.config_core)

self._translated_noise_words = {}
if noise_words_filename:
with open(noise_words_filename) as f:
translated_noise_words = f.read().strip()
self._translated_noise_words[self.lang] = \
translated_noise_words.split()

def __calc_confidence(self, match, phrase, level):
# Assume the more of the words that get consumed, the better the match
Expand Down Expand Up @@ -156,6 +143,38 @@ def __handle_query_action(self, message):
data = message.data.get("callback_data")
# Invoke derived class to provide playback data
self.CQS_action(phrase, data)
self.bus.emit(message.forward("mycroft.skill.handler.complete",
{"handler": "common_query"}))

def __handle_question_query(self, message):
# Override ovos-workshop implementation that doesn't pass `message`
search_phrase = message.data["phrase"]

# First, notify the requestor that we are attempting to handle
# (this extends a timeout while this skill looks for a match)
self.bus.emit(message.response({"phrase": search_phrase,
"skill_id": self.skill_id,
"searching": True}))

# Now invoke the CQS handler to let the skill perform its search
result = self.CQS_match_query_phrase(search_phrase, message)

if result:
match = result[0]
level = result[1]
answer = result[2]
callback = result[3] if len(result) > 3 else None
confidence = self.__calc_confidence(match, search_phrase, level)
self.bus.emit(message.response({"phrase": search_phrase,
"skill_id": self.skill_id,
"answer": answer,
"callback_data": callback,
"conf": confidence}))
else:
# Signal we are done (can't handle it)
self.bus.emit(message.response({"phrase": search_phrase,
"skill_id": self.skill_id,
"searching": False}))

@abstractmethod
def CQS_match_query_phrase(self, phrase, message):
Expand Down
16 changes: 14 additions & 2 deletions neon_utils/skills/mycroft_skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from typing import Optional
from json_database import JsonStorage
from ovos_bus_client.message import Message
from ovos_utils.log import log_deprecation
from ovos_workshop.skills.mycroft_skill import MycroftSkill
from ovos_utils.skills.settings import get_local_settings

Expand All @@ -49,11 +50,21 @@
class PatchedMycroftSkill(MycroftSkill):
def __init__(self, name=None, bus=None, *args, **kwargs):
MycroftSkill.__init__(self, name, bus, *args, **kwargs)
log_deprecation("This base class is deprecated. Implement either"
"`NeonSkill` or `OVOSSkill`", "2.0.0")
# TODO: Should below defaults be global config?
# allow skills to specify timeout overrides per-skill
self._speak_timeout = 30
self._get_response_timeout = 15 # 10 for listener, 5 for STT, then timeout

@property
def settings_path(self):
# TODO: Deprecate backwards-compat. wrapper after ovos-workshop 0.0.13
try:
return super().settings_path
except AttributeError:
return super()._settings_path

@property
def location(self):
"""
Expand All @@ -67,19 +78,20 @@ def _init_settings(self):
Extends the default method to handle settingsmeta defaults locally
"""
super()._init_settings()
skill_settings = get_local_settings(self._settings_path)
skill_settings = get_local_settings(self.settings_path)
settings_from_disk = dict(skill_settings)
self.settings = dict_update_keys(skill_settings,
self._read_default_settings())
if self.settings != settings_from_disk:
if isinstance(self.settings, JsonStorage):
self.settings.store()
else:
with open(self._settings_path, "w+") as f:
with open(self.settings_path, "w+") as f:
json.dump(self.settings, f, indent=4)
self._initial_settings = dict(self.settings)

def _init_settings_manager(self):
# TODO: Same as upstream implementation?
from ovos_workshop.settings import SkillSettingsManager
self.settings_manager = SkillSettingsManager(self)

Expand Down
34 changes: 30 additions & 4 deletions neon_utils/skills/neon_fallback_skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from ovos_utils import LOG

from neon_utils.skills.neon_skill import NeonSkill
from ovos_workshop.skills.ovos import OVOSSkill
from ovos_utils.intents import IntentLayers
from ovos_workshop.decorators.layers import IntentLayers
from ovos_workshop.skills.fallback import FallbackSkillV1 as FallbackSkill
from ovos_workshop.skills.fallback import FallbackSkillV1


class NeonFallbackSkill(FallbackSkill, NeonSkill, OVOSSkill):
# TODO: Consider deprecation and implementing ovos_workshop directly
class NeonFallbackSkill(FallbackSkillV1, NeonSkill):
"""
Class that extends the NeonSkill and FallbackSkill classes to provide
NeonSkill functionality to any Fallback skill subclassing this class.
Expand All @@ -50,8 +51,33 @@ def __init__(self, *args, **kwargs):
# list of fallback handlers registered by this instance
self.instance_fallback_handlers = []
NeonSkill.__init__(self, *args, **kwargs)
LOG.debug(f"instance_handlers={self.instance_fallback_handlers}")
LOG.debug(f"class_handlers={FallbackSkillV1.fallback_handlers}")

@property
def fallback_config(self):
# "skill_id": priority (int) overrides
return self.config_core["skills"].get("fallbacks", {})
return self.config_core["skills"].get("fallbacks", {})

@classmethod
def _register_fallback(cls, *args, **kwargs):
LOG.debug(f"register fallback")
FallbackSkillV1._register_fallback(*args, **kwargs)

def _register_decorated(self):
# Explicitly overridden to ensure the correct super call is made
LOG.debug(f"Registering decorated methods for {self.skill_id}")
try:
FallbackSkillV1._register_decorated(self)
except Exception as e:
LOG.error(e)
NeonSkill._register_decorated(self)
from ovos_utils.skills import get_non_properties
for attr_name in get_non_properties(self):
method = getattr(self, attr_name)
if hasattr(method, 'fallback_priority'):
self.register_fallback(method, method.fallback_priority)

def register_fallback(self, *args, **kwargs):
LOG.debug(f"Registering fallback handler for {self.skill_id}")
FallbackSkillV1.register_fallback(self, *args, **kwargs)
Loading

0 comments on commit 630064f

Please sign in to comment.