def test_signal_and(mocker): sig = s("mysig") conjunct = s("sig1") & s("sig2") disjunct = s("sig1") | s("sig2") with mocker.patch('ravestate.constraint.Conjunct.__init__', return_value=None): _ = sig & sig Conjunct.__init__.assert_called_once_with(sig, sig) with mocker.patch('ravestate.constraint.Conjunct.__init__', return_value=None): with mocker.patch('ravestate.constraint.Conjunct.__iter__', return_value=iter([1])): _ = sig & conjunct Conjunct.__init__.assert_called_once_with(sig, 1) with mocker.patch('ravestate.constraint.Disjunct.__init__', return_value=None): with mocker.patch('ravestate.constraint.Conjunct.__init__', return_value=None): with mocker.patch.object(disjunct, '_conjunctions', return_value={}): _ = sig & disjunct Disjunct.__init__.assert_called_with()
def test_signal(activation_fixture): sig = s("mysig") assert not sig.evaluate() assert set(sig.signals()) == {s("mysig")} sig.acquire(Spike(sig="notmysig"), activation_fixture) assert not sig.evaluate() sig.acquire(Spike(sig="mysig"), activation_fixture) assert sig.evaluate() sig_and_dis = s("sig") & (s("dis") | s("junct")) assert not sig_and_dis.evaluate() sig_and_dis.acquire(Spike(sig="sig"), activation_fixture) assert not sig_and_dis.evaluate() sig_and_dis.acquire(Spike(sig="junct"), activation_fixture) assert sig_and_dis.evaluate() expected = [(sig, sig.spike)] return_value = list(sig.dereference()) assert expected == return_value sig.spike = Spike(sig='mysig') sig.spike._age = 200 return_value = list(sig.update(activation_fixture)) assert return_value == [sig] assert str(sig) == "mysig"
def test_flag_property(context_mock): prop_base = PropertyBase(name="flag_prop", is_flag_property=True) prop_base.set_parent_path(DEFAULT_MODULE_NAME) prop_wrapper = PropertyWrapper(prop=prop_base, ctx=context_mock, allow_read=True, allow_write=True) assert (prop_base._lock.locked()) prop_wrapper.set(True) assert (prop_wrapper.get() is True) context_mock.emit.assert_any_call(s(f"{prop_wrapper.prop.id()}:changed"), parents=None, wipe=True) context_mock.emit.assert_any_call(s(f"{prop_wrapper.prop.id()}:true"), parents=None, wipe=True) context_mock.emit.reset_mock() prop_wrapper.set(False) assert (prop_wrapper.get() is False) context_mock.emit.assert_any_call(s(f"{prop_wrapper.prop.id()}:changed"), parents=None, wipe=True) context_mock.emit.assert_any_call(s(f"{prop_wrapper.prop.id()}:false"), parents=None, wipe=True) context_mock.emit.reset_mock() prop_wrapper.set(None) assert (prop_wrapper.get() is None) context_mock.emit.assert_called_once_with( s(f"{prop_wrapper.prop.id()}:changed"), parents=None, wipe=True)
def state_signal_d_fixture(mocker): @state(signal_name="d", cond=s("module:b") | s("module:c")) def state_mock_c_fn(ctx): pass state_mock_c_fn.module_name = DEFAULT_MODULE_NAME return state_mock_c_fn
def test_decorator_illegal_trigger(under_test, default_signal, default_read, default_write, default_action): with pytest.raises(ValueError): @state(signal_name=default_signal, read=default_read, write=default_write, cond=(s("rawio:in:changed") | s("facerec:face:changed")) & (s("sys:has-internet") | s("foo:poo"))) def test_state(_): return "Hello world!"
def test_disjunct_or(mocker): disjunct = (s("sig1") & s("sig2")) | s("sig3") with mocker.patch('ravestate.constraint.Disjunct.__iter__', return_value=iter([1])): with mocker.patch('ravestate.constraint.Disjunct.__init__', return_value=None): conjunct = s("sig1") & s("sig2") _ = disjunct | conjunct Disjunct.__init__.assert_called_with(1, conjunct) with mocker.patch('ravestate.constraint.Disjunct.__iter__', return_value=iter([1])): with mocker.patch('ravestate.constraint.Disjunct.__init__', return_value=None): signal = s("sig1") with mocker.patch('ravestate.constraint.Conjunct.__init__', return_value=None): _ = disjunct | signal Conjunct.__init__.assert_called_once_with(signal) Disjunct.__init__.assert_called_once() with mocker.patch('ravestate.constraint.Disjunct.__init__', return_value=None): with mocker.patch('ravestate.constraint.Disjunct.__iter__', return_value=iter([1])): disjunct2 = s("sig1") | s("sig2") _ = disjunct | disjunct2 Disjunct.__init__.assert_called_with(1)
def shutdown(self) -> None: """ Sets the shutdown flag and waits for the signal processing thread to join. """ self._shutdown_flag.set() self.emit(s(":shutdown")) self._run_task.join()
def startup(**kwargs) -> Signal: """ Obtain the startup signal, which is fired once when `Context.run()` is executed.<br> __Hint:__ All key-word arguments of #constraint.s(...) (`min_age`, `max_age`, `detached`) are supported. """ return s(":startup", **kwargs)
def shutdown(**kwargs) -> Signal: """ Obtain the shutdown signal, which is fired once when `Context.shutdown()` is called.<br> __Hint:__ All key-word arguments of #constraint.s(...) (`min_age`, `max_age`, `detached`) are supported. """ return s(":shutdown", **kwargs)
def state_signal_b_fixture(mocker): @state(signal_name="b", cond=s("module:a")) def state_mock_b_fn(ctx): pass state_mock_b_fn.module_name = DEFAULT_MODULE_NAME return state_mock_b_fn
def test_signal_or(mocker): sig = s("mysig") with mocker.patch('ravestate.constraint.Disjunct.__init__', return_value=None): conjunct = s("sig1") & s("sig2") with mocker.patch('ravestate.constraint.Conjunct.__init__', return_value=None): _ = sig | conjunct Conjunct.__init__.assert_called_once_with(sig) Disjunct.__init__.assert_called_once() with mocker.patch('ravestate.constraint.Disjunct.__init__', return_value=None): with mocker.patch('ravestate.constraint.Disjunct.__iter__', return_value=iter([1])): disjunct = s("sig1") | s("sig2") _ = sig | disjunct Disjunct.__init__.assert_called_with(sig, 1)
def popped(property_name, **kwargs) -> Signal: """ Returns the `popped` Signal for the given property. This signal is emitted, when a child property removed from it. From the perspective of a state, this can be achieved with the `ContextWrapper.pop(...)` function.<br> __Hint:__ All key-word arguments of #constraint.s(...) (`min_age`, `max_age`, `detached`) are supported. """ return s(f"{property_name}:popped", **kwargs)
def changed(property_name, **kwargs) -> Signal: """ Returns the `changed` Signal for the given property. This signal is emitted, when the Property is written to, and the new property value is different from the old one, or the propertie's `always_signal_changed` flag is True.<br> __Hint:__ All key-word arguments of #constraint.s(...) (`min_age`, `max_age`, `detached`) are supported. """ return s(f"{property_name}:changed", **kwargs)
def run(self) -> None: """ Creates a signal processing thread, starts it, and emits the :startup signal. """ if self._run_task: logger.error("Attempt to start context twice!") return self._run_task = Thread(target=self._run_loop) self._run_task.start() self.emit(s(":startup"))
def test_property_write(under_test_read_write: PropertyWrapper, default_property_base, context_mock): # Make sure that writing to writable wrapper is effective assert (default_property_base._lock.locked()) under_test_read_write.set(NEW_PROPERTY_VALUE) assert (under_test_read_write.get() == NEW_PROPERTY_VALUE) context_mock.emit.assert_called_once_with( s(f"{under_test_read_write.prop.id()}:changed"), parents=None, wipe=True)
def __init__(self, *, signal_name: Optional[str], write: Union[str, Tuple[str]], read: Union[str, Tuple[str]], cond: Constraint, action, is_receptor: bool=False, emit_detached: bool=False, weight: float=1., cooldown: float=0.): assert(callable(action)) self.name = action.__name__ self.consumable = Consumable(f"@{action.__name__}") # check to recognize states using old signal implementation if isinstance(cond, str): logger.error(f"Attempt to create state {self.name} which has a string as condition, not a constraint!") cond = None # catch the insane case if not len(read) and not cond and not is_receptor: logger.warning( f"The state `{self.name}` is not reading any properties, nor waiting for any signals. " + "It will never be activated!") # convert read/write properties to tuples if isinstance(write, str): write = (write,) if isinstance(read, str): read = (read,) # listen to default changed-signals if no signals are given. # convert triggers to disjunctive normal form. if not cond and len(read) > 0: cond = Disjunct(*list(Conjunct(s(f"{rprop_name}:changed")) for rprop_name in read)) self.signal_name = signal_name self.write_props = write self.read_props = read self.constraint = cond self.completed_constraint = cond self.action = action self.module_name = "" self.signal_object = None self.emit_detached = emit_detached self.activated = Semaphore(0) self.weight = self.current_weight = weight self.cooldown = cooldown self.lock = Lock() # add state to module in current `with Module(...)` clause module_under_construction = getattr(ravestate_thread_local, 'module_under_construction', None) if module_under_construction: module_under_construction.add(self)
def test_disjunct_and(mocker): sig = s("mysig") conjunct = s("sig1") & s("sig2") disjunct = s("sig1") | s("sig2") with mocker.patch('ravestate.constraint.Disjunct.__init__', return_value=None): with mocker.patch.object(disjunct, '_conjunctions', return_value={}): _ = disjunct & sig Disjunct.__init__.assert_called_once_with() with mocker.patch('ravestate.constraint.Disjunct.__init__', return_value=None): with mocker.patch.object(disjunct, '_conjunctions', return_value={}): _ = disjunct & conjunct Disjunct.__init__.assert_called_once_with() with pytest.raises(ValueError): with LogCapture(attributes=strip_prefix) as log_capture: _ = disjunct & disjunct log_capture.check("Can't conjunct two disjunctions.")
def test_disjunct(activation_fixture): disjunct = (s("sig1") & s("sig2")) | s("sig3") assert not disjunct.evaluate() assert set(disjunct.signals()) == {s("sig1"), s("sig2"), s("sig3")} disjunct.acquire(Spike(sig="sig1"), activation_fixture) assert not disjunct.evaluate() disjunct.acquire(Spike(sig="sig3"), activation_fixture) assert disjunct.evaluate()
def test_conjunct_or(mocker): conjunct = s("sig1") & s("sig2") & s("sig3") with mocker.patch('ravestate.constraint.Disjunct.__init__', return_value=None): conjunct2 = s("sig1") & s("sig2") _ = conjunct | conjunct2 Disjunct.__init__.assert_called_once_with(conjunct, conjunct2) with mocker.patch('ravestate.constraint.Disjunct.__init__', return_value=None): with mocker.patch('ravestate.constraint.Disjunct.__iter__', return_value=iter([1])): disjunct = s("sig1") | s("sig2") _ = conjunct | disjunct Disjunct.__init__.assert_called_with(conjunct, 1)
def test_conjunct(activation_fixture): conjunct = s("sig1") & s("sig2") & s("sig3") assert not conjunct.evaluate() assert set(conjunct.signals()) == {s("sig1"), s("sig2"), s("sig3")} conjunct.acquire(Spike(sig="sig1"), activation_fixture) assert not conjunct.evaluate() conjunct.acquire(Spike(sig="sig2"), activation_fixture) assert not conjunct.evaluate() conjunct.acquire(Spike(sig="sig2"), activation_fixture) assert not conjunct.evaluate() conjunct.acquire(Spike(sig="sig3"), activation_fixture) assert conjunct.evaluate()
ROS2_AVAILABLE = True try: import rclpy except ImportError: logger.error( "Could not import rclpy. Please make sure to have ROS2 installed.") ROS2_AVAILABLE = False NODE_NAME_CONFIG_KEY = "ros2-node-name" SPIN_FREQUENCY_CONFIG_KEY = "ros2-spin-frequency" global_prop_set = set() @state(cond=s(":startup")) def sync_ros_properties(ctx: ContextWrapper): """ State that creates a ROS2-Node, registers all Ros2SubProperties and Ros2PubProperties in ROS2 and keeps them synced """ # check for ROS2 availability if not ROS2_AVAILABLE: logger.error( "ROS2 is not available, therefore all ROS2-Properties " "will be just normal properties without connection to ROS2!") return Delete() # get config stuff node_name = ctx.conf(key=NODE_NAME_CONFIG_KEY) if not node_name: logger.error(
default_value="", always_signal_changed=True, allow_pop=False, allow_push=False), roboy = PropertyBase(name="roboy", default_value="", always_signal_changed=True, allow_pop=False, allow_push=False), yesno = PropertyBase(name="yesno", default_value="", always_signal_changed=True, allow_pop=False, allow_push=False) @state(cond=s("rawio:in:changed"), read="rawio:in", write=("nlp:tokens", "nlp:postags", "nlp:lemmas", "nlp:tags", "nlp:ner", "nlp:triples", "nlp:roboy", "nlp:yesno")) def nlp_preprocess(ctx): text = ctx["rawio:in"] if not text: return False nlp_doc = nlp(text) nlp_tokens = tuple(str(token) for token in nlp_doc) ctx["nlp:tokens"] = nlp_tokens logger.info(f"[NLP:tokens]: {nlp_tokens}") nlp_postags = tuple(str(token.pos_) for token in nlp_doc) ctx["nlp:postags"] = nlp_postags
def _run_once(self, seconds_passed=1.): with self._lock: # ---------- Update weights wrt/ cooldown for all states ----------- for state in self._activations_per_state: state.update_weight(seconds_passed) # ----------- For every state, compress it's activations ----------- for st, acts in self._activations_per_state.items(): assert len(acts) > 0 if len(acts) > 1: # We could do some fancy merge of partially fulfilled activations, # but for now let's just remove all completely unfulfilled # ones apart from one. allowed_unfulfilled: Activation = None for act in acts.copy(): if not act.spiky(): if allowed_unfulfilled: for signal in act.constraint.signals(): if signal in self._act_per_state_per_signal_age and \ act in self._act_per_state_per_signal_age[signal][0][act.state_to_activate]: self._act_per_state_per_signal_age[ signal][0][act.state_to_activate].remove(act) acts.remove(act) else: allowed_unfulfilled = act # --------- Acquire new state activations for every spike ---------- for spike in self._spikes: if spike.is_wiped(): continue for state, acts in self._act_per_state_per_signal_age[s(spike.name())][spike.age()].items(): old_acts = acts.copy() for act in old_acts: if act.acquire(spike): # Remove the Activation instance from _act_per_state_per_signal_age # for the Spike with a certain minimum age. In place of the removed # activation, if no activation with the same target state is left, # a new Activation will be created. acts.remove(act) if len(acts) == 0: self._new_state_activation(state) else: logger.error( "An activation rejected a spike it was registered to be interested in.") # ------------------ Update all state activations. ----------------- for act in self._state_activations(): if act.update(): self._activations_per_state[act.state_to_activate].discard(act) # ----------------- Forget fully unreferenced spikes --------------- old_spike_set = self._spikes.copy() for spike in old_spike_set: with spike.causal_group() as cg: if cg.stale(spike): # This should lead to the deletion of the spike self._spikes.remove(spike) spike.wipe(already_wiped_in_causal_group=True) logger.debug(f"{cg}.stale({spike})->Y") # ----------------- Increment age on active spikes ----------------- for spike in self._spikes: spike.tick() # -------------------- Force garbage collect ----------------------- gc.collect() self._update_core_properties(False)
from ravestate.constraint import ConfigurableAge from ravestate.constraint import s from ravestate.wrappers import ContextWrapper from ravestate.state import state, Emit from reggol import get_logger logger = get_logger(__name__) IMPATIENCE_THRESHOLD_CONFIG_KEY = "impatience_threshold" CONFIG = { # duration in seconds how long ":pressure" should be true before getting impatient IMPATIENCE_THRESHOLD_CONFIG_KEY: 1.0 } with Module(name="idle", config=CONFIG): @state(read=":activity", signal_name="bored") def am_i_bored(ctx: ContextWrapper): """ Emits idle:bored signal if no states are currently partially fulfilled """ if ctx[":activity"] == 0: return Emit(wipe=True) @state(cond=s(signal_name=":pressure:true", min_age=ConfigurableAge(key=IMPATIENCE_THRESHOLD_CONFIG_KEY), max_age=-1.), signal_name="impatient") def am_i_impatient(ctx: ContextWrapper): return Emit(wipe=True)
from ravestate.state import state from ravestate.module import Module from ravestate.constraint import s from roboy_parlai import wildtalk import ravestate_rawio with Module(name="wildtalk"): @state(cond=s("rawio:in:changed", max_age=-1), read="rawio:in", write="rawio:out") def wildtalk_state(ctx): text = ctx["rawio:in"] if not text: # make sure that text is not empty text = " " ctx["rawio:out"] = wildtalk(text)
def default_signal(): return s("test-signal")
from ravestate_verbaliser.verbaliser import get_phrase_list import ravestate_phrases_basic_en import ravestate_ontology from scientio.ontology.node import Node from scientio.session import Session from scientio.ontology.ontology import Ontology from reggol import get_logger logger = get_logger(__name__) DEFAULT_INTERLOC_ID = "terminal_user" with Module(name="consoleio"): @state(cond=s(":startup"), read="interloc:all") def console_input(ctx: ContextWrapper): @receptor(ctx_wrap=ctx, write="rawio:in") def write_console_input(ctx_input, value: str): ctx_input["rawio:in"] = value @receptor(ctx_wrap=ctx, write="interloc:all") def push_console_interloc(ctx: ContextWrapper, console_node: Node): if ctx.push(parentpath="interloc:all", child=PropertyBase(name=DEFAULT_INTERLOC_ID, default_value=console_node)): logger.debug(f"Pushed {console_node} to interloc:all") @receptor(ctx_wrap=ctx, write="interloc:all") def pop_console_interloc(ctx: ContextWrapper): if ctx.pop(f"interloc:all:{DEFAULT_INTERLOC_ID}"):
def default_triggers(): return s(":idle")
question = PropertyBase(name="question", default_value="", always_signal_changed=True, allow_pop=False, allow_push=False, is_flag_property=True) wrong_input = PropertyBase(name="wrong_input", default_value="", always_signal_changed=True, allow_pop=False, allow_push=False, is_flag_property=True) @state(cond=s("nlp:intent-play") | s("idle:bored"), write="rawio:out", signal_name="initiate-play", emit_detached=True) def akinator_play_ask(ctx): """ Asks if interlocutor wants to play 20 question / akinator Triggered when nlp:play property is changed by "i want to play a game" or a similar input """ ctx["rawio:out"] = "Do you want to play 20 questions?" return Emit() @state(cond=s("nlp:yes-no") & (s("akinator:initiate-play", max_age=-1) | s("akinator:initiate_play_again:changed", max_age=-1)), read="nlp:yesno",
def signal(self) -> Optional[Signal]: assert self.module_name if not self._signal and self.signal_name: self._signal = s(f"{self.module_name}:{self.signal_name}") return self._signal