def test_illegal_nested_modules(): with LogCapture(attributes=strip_prefix) as log_capture: with Module(name="a"): with pytest.raises(RuntimeError) as exc: with Module(name="b"): pass assert str( exc.value ) == "Nested `with Module(...)` calls are not supported!`"
def register(*, name: str = "", props=(), states=(), config={}): """ May be called to register a named set of states, properties and config entries, which form a coherent bundle. :param name: The name of the module. Will be prefixed to property and signal names like <modulename>:<proeprty/signal-name>. :param props: The properties that should be registered. :param states: The states that should be registered. :param config: A dictionary of config entries and their default values, which should be read from the default/user config files. :return: """ global _registered_modules global _registration_callback if name in _registered_modules: logging.error(f"Attempt to add module {name} twice!") return _registered_modules[name] = Module(name=name, props=props, states=states, config=config) if _registration_callback: _registration_callback(_registered_modules[name])
def test_add_state_configurable_age(context_with_property_fixture: Context): my_cond = s(signal_name=DEFAULT_PROPERTY_CHANGED, min_age=ConfigurableAge(key="min_age_key"), max_age=ConfigurableAge(key="max_age_key")) @state(cond=my_cond) def conf_st(ctx): pass conf_st.module_name = DEFAULT_MODULE_NAME context_with_property_fixture._config.add_conf(mod=Module(name=DEFAULT_MODULE_NAME, config={"min_age_key": 2.5, "max_age_key": 4.5})) context_with_property_fixture.add_state(st=conf_st) assert my_cond.min_age == 2.5 assert my_cond.max_age == 4.5
def __init__(self, *arguments, runtime_overrides: List[Tuple[str, str, Any]] = None): """ Construct a context from command line arguments. * `arguments`: A series of command line arguments which can be parsed by the ravestate command line parser (see argparse.py). * `runtime_overrides`: A list of config overrides in the form of (modulename, key, value). Can be used to set config entries to values other than strings or lists like in command line arguments. An example use-case is a module that starts a new context (in separate process) and can set config entries to Connection Objects to enable communication between old and new context. """ modules, overrides, config_files = argparse.handle_args(*arguments) self._config = Configuration(config_files) self._core_config = { self.import_modules_config: [], self.tick_rate_config: 20 } self._config.add_conf(Module(name=self.core_module_name, config=self._core_config)) self._lock = RLock() self._shutdown_flag = Event() self._properties = dict() self._spikes = set() self._act_per_state_per_signal_age = dict() self._signal_causes = dict() self._activations_per_state = dict() self._run_task = None # Register default signals for signal in self._default_signals: self._add_sig(signal) # Register default properties for prop in self._default_properties: self.add_prop(prop=prop) # Load required modules for module_name in self._core_config[self.import_modules_config] + modules: self.add_module(module_name) # Set required config overrides for module_name, key, value in overrides: self._config.set(module_name, key, value) # Set additional config runtime overrides if runtime_overrides: for module_name, key, value in runtime_overrides: self._config.set(module_name, key, value) if self._core_config[self.tick_rate_config] < 1: logger.error("Attempt to set core config `tickrate` to a value less-than 1!") self._core_config[self.tick_rate_config] = 1
def test_unknown_module(under_test: Configuration): with LogCapture(attributes=strip_prefix) as log_capture: under_test.get_conf(DEFAULT_MODULE_NAME) log_capture.check( f"Bad request for unknown module config {DEFAULT_MODULE_NAME}!") under_test.set(module_name=DEFAULT_MODULE_NAME, key="a", value=True) log_capture.check_present( f"Attempt to run set() for unknown modname {DEFAULT_MODULE_NAME}!") under_test.add_conf(Module(name=DEFAULT_MODULE_NAME, config={})) under_test.set(module_name=DEFAULT_MODULE_NAME, key="a", value=True) log_capture.check_present( f"Not setting unknown conf key a for module {DEFAULT_MODULE_NAME}." )
def test_config_write(mocker, under_test: Configuration): under_test.add_conf( Module(name=DEFAULT_MODULE_NAME, config={"a": "value_A"})) m = mocker.mock_open() mocker.patch('builtins.open', m) under_test.write(path="config_mock.yml") m.assert_called_once_with('config_mock.yml', mode='w') handle = m() expected_calls = [ 'config', ':', '\n', ' ', 'a', ':', ' ', 'value_A', '\n', 'module', ':', ' ', 'module', '\n' ] handle.write.assert_has_calls( [mocker.call(exp_call) for exp_call in expected_calls])
def test_apply_config(under_test: Configuration, config_file_paths): under_test.read(config_file_paths[0]) under_test.read(config_file_paths[1]) with LogCapture(attributes=strip_prefix) as log_capture: under_test.add_conf( Module(name="foo", config={ "a": "hehe", "b": "hoho", "c": "lol" })) log_capture.check( f"Config entry for foo.a has conflicting type bool (should be str)." ) assert under_test.get("foo", "a") == False assert under_test.get("foo", "b") == "haha" assert under_test.get("foo", "c") == "lol"
def __init__(self, *arguments): """ Construct a context from command line arguments. * `arguments`: A series of command line arguments which can be parsed by the ravestate command line parser (see argparse.py). """ modules, overrides, config_files = argparse.handle_args(*arguments) self._config = Configuration(config_files) self._core_config = { self._import_modules_config: [], self._tick_rate_config: 20 } self._config.add_conf(Module(name=self._core_module_name, config=self._core_config)) self._lock = Lock() self._shutdown_flag = Event() self._properties = dict() self._spikes = set() self._act_per_state_per_signal_age = dict() self._signal_causes = dict() self._activations_per_state = dict() self._run_task = None # Register default signals for signal in self._default_signals: self._add_sig(signal) # Register default properties for prop in self._default_properties: self.add_prop(prop=prop) # Set required config overrides for module_name, key, value in overrides: self._config.set(module_name, key, value) if self._core_config[self._tick_rate_config] < 1: logger.error("Attempt to set core config `tickrate` to a value less-than 1!") self._core_config[self._tick_rate_config] = 1 # Load required modules for module_name in self._core_config[self._import_modules_config]+modules: self.add_module(module_name)
def test_list_convert(under_test: Configuration, config_file_paths): under_test.read(config_file_paths[0]) under_test.read(config_file_paths[1]) under_test.add_conf( Module(name="foo", config={ "a": "hehe", "b": "hoho", "c": "lol", "d": [], "e": [1] })) under_test.set("foo", "d", "value") value = under_test.get("foo", "d") assert isinstance(value, list) and value[0] == "value" under_test.set("foo", "e", "30") value = under_test.get("foo", "e") assert isinstance(value, list) and value[0] == 30 under_test.set("foo", "e", [True]) value = under_test.get("foo", "e") assert isinstance(value, list) and value[0] == True
def test_run_with_pressure(): with Module(name=DEFAULT_MODULE_NAME): prop = Property(name=DEFAULT_PROPERTY_NAME) a = Signal("a") b = Signal("b") @state(cond=sig_startup, signal=a) def signal_a(ctx): return Emit() @state(cond=a, signal=b) def signal_b(ctx): return Emit() @state(cond=a, write=prop) def pressuring_state(ctx): pass @state(cond=a & b, write=prop) def specific_state(ctx): pass ctx = Context(DEFAULT_MODULE_NAME) ctx.emit(sig_startup) ctx.run_once() assert signal_a.wait() ctx.run_once() assert signal_b.wait() # make sure that pressuring_state is pressuring specific_state acts = ctx._state_activations(st=specific_state) assert any(act.is_pressured() for act in acts) ctx.run_once() assert specific_state.wait()
from ravestate.property import PropertyBase from ravestate.module import Module with Module(name="interloc"): # TODO: Make interloc:all a special property type, that only accepts ScientioNodeProperty as children all = PropertyBase(name="all", allow_read=True, allow_write=False, allow_push=True, allow_pop=True)
from ravestate.module import Module from ravestate_telegramio import telegram_bot import ravestate_rawio as rawio import ravestate_interloc as interloc import ravestate_ontology as ontology CONFIG = { # Token for the Telegram Bot telegram_bot.TOKEN_CONFIG_KEY: "", # Not to be set in a config file or via the command line. Will be set by master process as a runtime_override. telegram_bot.CHILD_CONN_CONFIG_KEY: None, # The config-paths listed here will be used for each new telegram chat (if they don't share the same context). # Currently only absolute paths are supported telegram_bot.CHILD_FILES_CONFIG_KEY: [], # Whether all telegram chats should share the same context or not telegram_bot.ALL_IN_ONE_CONTEXT_CONFIG_KEY: False, # The timespan in minutes in which a chat will be kept active after the last message telegram_bot.CHAT_LIFETIME: 5 } with Module( name=telegram_bot.MODULE_NAME, config=CONFIG, depends=(rawio.mod, interloc.mod, ontology.mod)) as mod: mod.add(telegram_bot.telegram_run) mod.add(telegram_bot.telegram_output)
from ravestate.module import Module from ravestate.property import PropertyBase with Module(name="rawio"): input = PropertyBase(name="in", default_value="", allow_pop=False, allow_push=False, always_signal_changed=True) output = PropertyBase(name="out", default_value="", allow_pop=False, allow_push=False, always_signal_changed=True)
from roboy_cognition_msgs.msg import RecognizedSpeech PYROBOY_AVAILABLE = True except ImportError as e: logger.error(f""" -------- An exception occured during imports: {e} Please make sure to have the following items installed & sourced: 1. ROS2 bouncy 2. roboy_communication 3. pyroboy -------- """) if PYROBOY_AVAILABLE: with Module(name="roboyio"): recognized_speech = Ros2SubProperty( name="recognized_speech", topic="/roboy/cognition/speech/recognition", msg_type=RecognizedSpeech, always_signal_changed=True) @state(read=recognized_speech.id()) def roboy_input(ctx: ContextWrapper): handle_single_interlocutor_input(ctx, ctx[recognized_speech.id()].text) @state(read="rawio:out") def roboy_output(ctx): ret = say(ctx["rawio:out:changed"])
from ravestate.module import Module from ravestate.property import PropertyBase from ravestate.state import state from ravestate_nlp.question_word import QuestionWord from ravestate_nlp.triple import Triple from ravestate_nlp.extract_triples import extract_triples from ravestate_nlp.triple import Triple from ravestate.state import Emit from ravestate.constraint import s from ravestate_nlp.yes_no import yes_no from reggol import get_logger logger = get_logger(__name__) with Module(name="nlp"): tokens = PropertyBase(name="tokens", default_value="", always_signal_changed=True, allow_pop=False, allow_push=False), postags = PropertyBase(name="postags", default_value="", always_signal_changed=True, allow_pop=False, allow_push=False), lemmas = PropertyBase(name="lemmas", default_value="", always_signal_changed=True, allow_pop=False,
import ravestate_rawio import ravestate_interloc 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")
from ravestate_sendpics.face_recognition import recognize_face_from_image_file from reggol import get_logger logger = get_logger(__name__) REDIS_HOST_CONF = "redis_host" REDIS_PORT_CONF = "redis_port" REDIS_PASS_CONF = "redis_pass" CONFIG = { REDIS_HOST_CONF: "localhost", REDIS_PORT_CONF: 6379, REDIS_PASS_CONF: None } with Module(name="sendpics", config=CONFIG): face_vec = PropertyBase(name="face_vec", always_signal_changed=True) @state(cond=s("idle:bored"), write="rawio:out", weight=1.2, cooldown=30.) def prompt_send(ctx): ctx["rawio:out"] = "Why don't you send me a picture? I'm really good at recognizing faces!" @state( read="rawio:pic_in", write=("rawio:out", "sendpics:face_vec"), emit_detached=True) def prompt_name(ctx): # Get face ancoding face = recognize_face_from_image_file(ctx["rawio:pic_in"]) # Prompt name
from ravestate.module import Module 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)
def test_add_list_of_props(state_signal_a_fixture, state_signal_b_fixture): with Module(name=DEFAULT_MODULE_NAME) as mod: mod.add([state_signal_a_fixture, state_signal_b_fixture]) assert len(mod.states) == 2 assert state_signal_a_fixture in mod.states assert state_signal_b_fixture in mod.states
import rclpy from ravestate.module import Module from ravestate.constraint import s from ravestate.state import state from ravestate.receptor import receptor from ravestate.property import PropertyBase from std_msgs.msg import String rclpy.init() node = rclpy.create_node("vision_node") with Module(name="facerec"): face = PropertyBase(name="face", default_value="") @state(cond=s(":startup")) def facerec_run(ctx): @receptor(ctx_wrap=ctx, write="facerec:face") def face_recognition_callback(ctx, msg): ctx["facerec:face"] = msg node.create_subscription(String, "/roboy/vision/recognized_faces", face_recognition_callback) rclpy.spin(node) @state(cond=s(":shutdown")) def facerec_shutdown(): node.destroy_node()
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)
from ravestate.property import Property from ravestate.activation import Activation from ravestate import argparser from ravestate.config import Configuration from ravestate.constraint import * from ravestate.spike import Spike from reggol import get_logger logger = get_logger(__name__) CORE_MODULE_NAME = "core" IMPORT_MODULES_CONFIG_KEY = "import" TICK_RATE_CONFIG_KEY = "tickrate" CORE_MODULE_CONFIG = {IMPORT_MODULES_CONFIG_KEY: [], TICK_RATE_CONFIG_KEY: 20} with Module(name="core", config=CORE_MODULE_CONFIG) as core_module: """ The startup signal, which is fired once when `Context.run()` is executed. """ sig_startup = Signal("startup") """ The shutdown signal, which is fired once when `Context.shutdown()` is called. """ sig_shutdown = Signal("shutdown") prop_pressure = Property(name="pressure", allow_read=True, allow_write=True, allow_push=False, allow_pop=False, default_value=False,
from ravestate.state import state from ravestate.module import Module from ravestate.constraint import s from ravestate.wrappers import ContextWrapper import ravestate_verbaliser import ravestate_phrases_basic_en import ravestate_interloc with Module(name="hibye"): @state(cond=s("interloc:all:pushed") & s("rawio:in:changed"), write="verbaliser:intent") def react_to_pushed_interloc(ctx: ContextWrapper): ctx["verbaliser:intent"] = "greeting" @state(cond=s("interloc:all:popped") & s("rawio:in:changed"), write="verbaliser:intent") def react_to_popped_interloc(ctx: ContextWrapper): ctx["verbaliser:intent"] = "farewells"
from ravestate_verbaliser import verbaliser from os.path import realpath, dirname, join import random import datetime import ravestate_idle from reggol import get_logger logger = get_logger(__name__) verbaliser.add_folder(join(dirname(realpath(__file__)), "answering_phrases")) ROBOY_NODE_CONF_KEY = "roboy_node_id" with Module(name="roboyqa", config={ROBOY_NODE_CONF_KEY: 356}): @state(cond=s("idle:bored"), write="rawio:out") def hello_world_roboyqa(ctx): ctx["rawio:out"] = "Ask me something about myself!" @state(cond=s("nlp:contains-roboy") & s("nlp:is-question"), read="nlp:triples", write="rawio:out") def roboyqa(ctx): """ answers question regarding roboy by retrieving the information out of the neo4j roboy memory graph state gets triggered when nlp extracts a new triple: subject, predicate, object by analysing the triple the content of the question can be ascertained the necessary information is gathered using the neo4j memory session if the triple combination is known and the information could be retrieved an answer will be given
import logging from ravestate.module import Module from ravestate.property import PropertyBase from ravestate.state import state from ravestate_verbaliser import verbaliser with Module(name="verbaliser"): intent = PropertyBase(name="intent", default_value="", allow_push=False, allow_pop=False, always_signal_changed=True) @state(read="verbaliser:intent", write="rawio:out") def react_to_intent(ctx): """ Looks for intents written to the verbaliser:intent property and writes a random phrase for that intent to rawio:out """ intent = ctx["verbaliser:intent"] phrase = verbaliser.get_random_phrase(intent) if phrase: ctx["rawio:out"] = phrase else: logging.error('No phrase for intent ' + intent + ' found.')
from ravestate.module import Module from ravestate.wrappers import ContextWrapper from ravestate.constraint import s from ravestate.state import state import ravestate_idle import ravestate_verbaliser import ravestate_phrases_basic_en with Module(name="fillers"): @state(cond=s("idle:impatient"), write=("verbaliser:intent",)) def impatient_fillers(ctx: ContextWrapper): ctx["verbaliser:intent"] = "fillers"
from ravestate.module import Module from ravestate_telegramio import telegram_bot CONFIG = { # Token for the Telegram Bot telegram_bot.TOKEN_CONFIG_KEY: "", # Not to be set in a config file or via the command line. Will be set by master process as a runtime_override. telegram_bot.CHILD_CONN_CONFIG_KEY: None, # The config-paths listed here will be used for each new telegram chat (if they don't share the same context). # Currently only absolute paths are supported telegram_bot.CHILD_FILES_CONFIG_KEY: [], # Whether all telegram chats should share the same context or not telegram_bot.ALL_IN_ONE_CONTEXT_CONFIG_KEY: False, # The timespan in minutes in which a chat will be kept active after the last message telegram_bot.CHAT_LIFETIME: 5 } with Module(name=telegram_bot.MODULE_NAME, config=CONFIG) as mod: mod.add(telegram_bot.telegram_run) mod.add(telegram_bot.telegram_output)
An exception occurred during `from roboy_cognition_msgs.msg import RecognizedFaces`: {e} Roboy will not comment on recognized faces! -------- """) if ROBOY_COGNITION_AVAILABLE: ROS2_FACE_TOPIC_CONFIG = "ros2-node" FACE_CONFIDENCE_THRESHOLD = "min-confidence" CONFIG = { ROS2_FACE_TOPIC_CONFIG: "/roboy/cognition/vision/visible_face_names", FACE_CONFIDENCE_THRESHOLD: 0.85 } with Module(name="stalker", config=CONFIG) as mod: # Create a dummy parent, under which we can push the actual recognized faces topic, # once a context with a configuration is available. subscriber_parent = PropertyBase(name="face_names_parent") @state(cond=startup(), write=subscriber_parent.id()) def create_subscriber(ctx: ContextWrapper): face_names = Ros2SubProperty( "face_names", topic=ctx.conf(key=ROS2_FACE_TOPIC_CONFIG), msg_type=RecognizedFaces, always_signal_changed=False) rec_faces = PropertyBase(name="rec_faces",
from ravestate.property import PropertyBase from ravestate.state import state, Resign, Emit, Delete from ravestate.constraint import s from ravestate_akinator.api import Api from reggol import get_logger logger = get_logger(__name__) import ravestate_idle import ravestate_nlp import ravestate_rawio akinator_api: Api CERTAINTY = "certainty_percentage" with Module(name="akinator", config={CERTAINTY: 90}): initiate_play_again = PropertyBase(name="initiate_play_again", default_value="", always_signal_changed=True, allow_pop=False, allow_push=False, is_flag_property=True) is_it = PropertyBase(name="is_it", default_value="", always_signal_changed=True, allow_pop=False, allow_push=False, is_flag_property=True)
logger = get_logger(__name__) onto = None sess = None initialized: Event = Event() NEO4J_ADDRESS_KEY: str = "neo4j_address" NEO4J_USERNAME_KEY: str = "neo4j_username" NEO4J_PASSWORD_KEY: str = "neo4j_pw" CONFIG = { NEO4J_ADDRESS_KEY: "bolt://*****:*****@state(cond=s(":startup")) def hello_world_ontology(ctx): """ Creates a scientio session with neo4j backend. (neo4j is a knowledge graph) an ontology is loaded from a yaml file - it can be accessed with a getter (the ontology description is a collection of named entity types, their properties and relationships) then the session is created - it can be accessed with a getter Using ravestate_ontology.initialized.wait() it can be made sure that the session was created before accessing it """ # TODO: Make sess and onto context properties? global onto, sess, initialized onto = Ontology(path_to_yaml=join(dirname(realpath(__file__)),