Skip to content
Draft
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ dist
.pytest_cache/
/.gtm/
.env
test/unittests/skills/temp_config/
18 changes: 17 additions & 1 deletion ovos_workshop/decorators/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from functools import wraps
from typing import Optional, Callable, List
from ovos_utils.log import log_deprecation

Check failure on line 3 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:3:28: F401 `ovos_utils.log.log_deprecation` imported but unused help: Remove unused import: `ovos_utils.log.log_deprecation`
import warnings

Check failure on line 4 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:4:8: F401 `warnings` imported but unused help: Remove unused import: `warnings`
from ovos_workshop.decorators.killable import killable_intent, killable_event

Check failure on line 5 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:5:64: F401 `ovos_workshop.decorators.killable.killable_event` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `killable_event as killable_event`

Check failure on line 5 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:5:47: F401 `ovos_workshop.decorators.killable.killable_intent` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `killable_intent as killable_intent`
from ovos_workshop.decorators.layers import enables_layer, \

Check failure on line 6 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:6:45: F401 `ovos_workshop.decorators.layers.enables_layer` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `enables_layer as enables_layer`
disables_layer, layer_intent, removes_layer, resets_layers, replaces_layer

Check failure on line 7 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:7:35: F401 `ovos_workshop.decorators.layers.removes_layer` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `removes_layer as removes_layer`

Check failure on line 7 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:7:21: F401 `ovos_workshop.decorators.layers.layer_intent` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `layer_intent as layer_intent`

Check failure on line 7 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:7:5: F401 `ovos_workshop.decorators.layers.disables_layer` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `disables_layer as disables_layer`
from ovos_workshop.decorators.ocp import ocp_play, ocp_pause, ocp_resume, \
ocp_search, ocp_previous, ocp_featured_media

Expand Down Expand Up @@ -54,10 +54,20 @@
return context_removes_decorator


def intent_handler(intent_parser: object, voc_blacklist: Optional[List[str]] = None):
def intent_handler(intent_parser: object, voc_blacklist: Optional[List[str]] = None,
requires_context: Optional[List] = None,
excludes_context: Optional[List] = None):
"""
Decorator for adding a method as an intent handler.
@param intent_parser: string intent name or adapt.IntentBuilder object
@param voc_blacklist: vocabulary that must not appear in a match
@param requires_context: OVOS-CONTEXT-1 §6 positive gating declarations —
a list of bare-string keys or ``{"key", "scope"}`` mappings (default
scope ``private``); the intent only matches while every declared
context key is live in the session.
@param excludes_context: OVOS-CONTEXT-1 §6.1 negative gating declarations
(same entry form); the intent is suppressed while any declared context
key is live.
"""

def real_decorator(func):
Expand All @@ -67,8 +77,14 @@
func.intents = []
if not hasattr(func, 'voc_blacklist'):
func.voc_blacklist = []
if not hasattr(func, 'requires_context'):
func.requires_context = []
if not hasattr(func, 'excludes_context'):
func.excludes_context = []
func.intents.append(intent_parser)
func.voc_blacklist += voc_blacklist or []
func.requires_context += requires_context or []
func.excludes_context += excludes_context or []
return func

return real_decorator
Expand Down
28 changes: 22 additions & 6 deletions ovos_workshop/decorators/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,12 @@ def layer_intent(intent_parser: callable, layer_name: str):
Decorator for adding a method as an intent handler belonging to an
intent layer.

The intent is augmented to *require* the layer's context token, so it can
only match while the layer is active. For adapt intents (IntentBuilder /
Intent) the requirement is injected directly; for padatious `.intent` files
the gating is enforced at registration time via `register_intent_layer`.
The intent is augmented to *require* the layer's context token via an
OVOS-CONTEXT-1 §6 ``requires_context`` declaration, so it only matches
while the layer is active — engine-agnostically, for adapt and padatious
alike. The legacy adapt ``require(<token>)`` vocab gate is kept as a
deprecated bridge for stacks whose engine does not yet enforce
OVOS-CONTEXT-1.

@param intent_parser: intent parser method
@param layer_name: name of intent layer intent is associated with
Expand All @@ -178,10 +180,17 @@ def real_decorator(func):
func.intents = []
if not hasattr(func, 'intent_layers'):
func.intent_layers = {}
if not hasattr(func, 'requires_context'):
func.requires_context = []

token = layer_context_token(layer_name)

# gate the intent on the layer context token
# OVOS-CONTEXT-1 §6 — the engine-agnostic layer gate (private scope,
# resolves to <skill_id>:layer_<name>, set by IntentLayers.activate_layer)
if token not in func.requires_context:
func.requires_context.append(token)

# legacy adapt vocab gate — deprecated bridge, see docstring
if hasattr(intent_parser, "require"):
# IntentBuilder - require the layer context token
intent_parser = intent_parser.require(token)
Expand Down Expand Up @@ -359,11 +368,18 @@ def _set_context(self, full_layer_name: str):
if not self.skill:
return
token = layer_context_token(self._bare_name(full_layer_name))
# word is the token itself so adapt has a value to inject
# OVOS-CONTEXT-1 §5.3 — the engine-agnostic layer gate; stored at the
# private key <skill_id>:layer_<name>, matching layer_intent's
# requires_context declaration
self.skill.set_intent_context(token)
# legacy adapt vocab context — deprecated bridge (word is the token
# itself so adapt has a value to inject)
self.skill.set_context(token, token)

def _remove_context(self, full_layer_name: str):
if not self.skill:
return
token = layer_context_token(self._bare_name(full_layer_name))
self.skill.remove_intent_context(token)
# legacy adapt vocab context — deprecated bridge
self.skill.remove_context(token)
Loading
Loading