Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.7.0 #482

Merged
merged 17 commits into from
Oct 26, 2023
Merged

1.7.0 #482

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading