def register_dummy_known_person_to_db(): onto: Ontology = mem.get_ontology() sess: Session = mem.get_session() person_node = Node(metatype=onto.get_type("Person")) person_node.set_properties({'name': 'visionio_test_person'}) person_node = sess.create(person_node) return person_node
def update(self, request: Node) -> Optional[Node]: if self._ontology.__contains__(request.get_type( )) and request.get_id() >= 0: # The class of Node is in ontology return self.update_node(request) print(f"No such type in ontology: the {request.get_type()} is missing" ) # Error return None
def delete_dummy_people(): onto: Ontology = mem.get_ontology() sess: Session = mem.get_session() person_node = Node(metatype=onto.get_type("Person")) person_node.set_properties({'name': 'visionio_test_person'}) # TODO: Delete method is not working! sess.delete(person_node)
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}"): logger.debug(f"Popped interloc:all:{DEFAULT_INTERLOC_ID}") while not ctx.shutting_down(): input_value = input("> ") write_console_input(input_value) console_interloc_exists = f"interloc:all:{DEFAULT_INTERLOC_ID}" in ctx.enum( "interloc:all") # push Node if you got a greeting if input_value.strip() in get_phrase_list( "greeting") and not console_interloc_exists: # set up scientio sess: Session = ravestate_ontology.get_session() onto: Ontology = ravestate_ontology.get_ontology() # create scientio Node of type Person query = Node(metatype=onto.get_type("Person")) query.set_name("x") console_node_list = sess.retrieve(query) if not console_node_list: console_node = sess.create(query) logger.info( f"Created new Node in scientio session: {console_node}" ) elif len(console_node_list) == 1: console_node = console_node_list[0] else: logger.error( f'Found multiple Persons with name {DEFAULT_INTERLOC_ID} in scientio session. Cannot push node to interloc:all!' ) continue # push interloc-Node push_console_interloc(console_node) # pop Node if you got a farewell elif input_value.strip() in get_phrase_list( "farewells") and console_interloc_exists: pop_console_interloc()
def push_interlocutor(ctx): interloc_exists = ANON_INTERLOC_PATH in ctx.enum(prop_all) if not interloc_exists: mem.initialized.wait() onto: Ontology = mem.get_ontology() new_interloc = Node(metatype=onto.get_type("Person")) new_interloc.set_name(ANON_INTERLOC_ID) if ctx.push(parent_property_or_path=prop_all, child=rs.Property(name=ANON_INTERLOC_ID, default_value=new_interloc)): logger.debug(f"Pushed {new_interloc} to interloc:all")
def get_node(self, req: Node) -> Optional[List[Node]]: builder = QueryBuilder() if req.get_entity() is not None: builder.add(f"MATCH (n:{req.get_entity()}") if req.get_meta() is not None and len(req.get_meta()) > 0: builder.add_meta(req.get_meta()) else: builder.add(f"MATCH (n") if req.get_properties() is not None: properties = dict( (x, y) for x, y in req.get_properties().items() if y != "") if len(properties) > 0: builder.add_parameters(properties) builder.add(")") if req.get_relationships() is not None: counter: int = 0 for key, value in req.get_relationships().items(): if len(value) > 0: builder.add( f"MATCH (n)-[r{counter}:{key}]-(m{counter}) WHERE ID(m{counter}) IN {str(value)}" ) counter += 1 builder.add("RETURN COLLECT(ID(n)) AS ids") records = self._exec_query(builder.get())[0] result_nodes: List[Node] = [] # nodes from result if len(records) > 0: for record in records: result_node = self.get_node_by_id(int(record)) result_nodes.append(result_node[0]) return result_nodes
def update_node(self, req: Node) -> Optional[Node]: if req.get_properties() is not None: properties = dict( (x, y) for x, y in req.get_properties().items() if y != "") if len(properties) > 0: builder = QueryBuilder() builder.match_by_id(req.get_id(), "n").set_values(req.get_properties(), "n").add("RETURN n") _ = self._exec_query(builder.get()) if req.get_relationships() is not None: relationships = dict((x, y) for x, y in req.get_relationships().items() if len(y) > 0) if len(relationships) > 0: builder = QueryBuilder() builder.match_by_id(req.get_id(), "n") counter: int = 0 for _, value in relationships.items(): builder.add( f"MATCH (m{counter}) WHERE ID(m{counter}) IN [{','.join((str(rel) for rel in value))}] " ) counter += 1 counter = 0 for key, _ in relationships.items(): builder.add(f"MERGE (n)-[r{counter}:{key}]-(m{counter})") counter += 1 builder.add("RETURN n") _ = self._exec_query(builder.get()) result_node = self.get_node_by_id(req.get_id())[0] # node from result return result_node
def compose_node(self, otype: OType, node_id: int, properties: Dict[str, str], relationships: Dict[str, Set[int]]): new_node: Node = Node(metatype=otype) new_node.set_id(node_id) new_node.set_properties(properties) new_node.set_relationships(relationships) return new_node
def make_sure_effective_user_exists(update: Update): """ Retrieves scientio Node of User if it exists, otherwise creates it in the scientio session Calls the push_telegram_interloc receptor to push the scientio node into interloc:all Adds the User to the set of active_users and the chat to the set of active_chats """ active_chats[update.effective_chat.id] = (Timestamp(), None) if update.effective_user.id in active_users: active_users[update.effective_user.id].add( update.effective_chat.id) else: # set up scientio if ontology.initialized.wait(): sess: Session = ontology.get_session() onto: Ontology = ontology.get_ontology() # create scientio Node of type TelegramPerson query = Node(metatype=onto.get_type("TelegramPerson")) prop_dict = {'telegram_id': update.effective_user.id} if update.effective_user.username: prop_dict['name'] = update.effective_user.username if update.effective_user.full_name: prop_dict['full_name'] = update.effective_user.full_name query.set_properties(prop_dict) node_list = sess.retrieve(query) if not node_list: telegram_node = sess.create(query) logger.info( f"Created new Node in scientio session: {telegram_node}" ) elif len(node_list) == 1: telegram_node = node_list[0] else: logger.error( f'Found multiple TelegramPersons that matched query: {update.message.chat_id} ' f'in scientio session. Cannot push node to interloc:all!' ) return # push chat-Node push_telegram_interloc(telegram_node, update.effective_chat.id) active_users[update.effective_user.id] = { update.effective_chat.id }
def create_node(self, req: Node) -> Optional[Node]: builder = QueryBuilder() builder.add("CREATE (a:" + req.get_entity()) if req.get_meta() is not None and len(req.get_meta()) > 0: builder.add_meta(req.get_meta()) builder.add_parameters(req.get_properties()).add(") RETURN a") record: Neo4jNode = self._exec_query(builder.get())[0] req.set_id(int(record.id)) return req
def retrieve(self, request: Node = None, node_id: int = None) -> Optional[List[Node]]: if node_id is not None and node_id >= 0: return self.get_node_by_id(node_id) else: if request is not None and self._ontology.__contains__( request.get_type()): # The class of Node is in ontology return self.get_node(request) print(f"No such type in ontology: the {request.get_type()} is missing" ) # Error return None
def store_face_and_name(ctx: rs.ContextWrapper): tokens = ctx[nlp.prop_tokens] triples = ctx[nlp.prop_triples] if len(tokens) == 1: name = tokens[0] elif triples[0].get_object().text and triples[0].match_either_lemma( pred={"be"}): name = triples[0].get_object().text else: ctx["rawio:out"] = "Sorry, what was the name?" return rs.Emit() ctx["rawio:out"] = f"Got it, I'm sure I'll remember {name} next time I see that face!" # Create memory entry sess: Session = ravestate_ontology.get_session() onto: Ontology = ravestate_ontology.get_ontology() query = Node(metatype=onto.get_type("Person")) query.set_properties({"name": name}) node_list = sess.retrieve(query) if not node_list: node = sess.create(query) logger.info(f"Created new Node in scientio session: {node}") elif len(node_list) == 1: node = node_list[0] else: logger.error( f'Failed to create or retrieve Scientio Node for {name}!') return logger.info(f"Node ID for {name} in picture is {node.get_id()}!") # Store face vector with node id in redis try: redis_conn = redis.Redis(host=ctx.conf(key=REDIS_HOST_CONF), port=ctx.conf(key=REDIS_PORT_CONF), password=ctx.conf(key=REDIS_PASS_CONF)) redis_conn.set(node.get_id(), ctx["sendpics:face_vec"]) except redis.exceptions.ConnectionError as e: err_msg = "Looks like the redis connection is unavailable :-(" logger.error(err_msg) ctx[rawio.prop_out] = err_msg
def react(ctx: rs.ContextWrapper): """ Retrieves memory node with the name, or creates a new one outputs a polite response. """ onto: Ontology = mem.get_ontology() sess: Session = mem.get_session() inferred_answer = ctx[prop_answer] pred = ctx[prop_predicate] subject_path: str = ctx[prop_subject] if not subject_path: return rs.Resign() subject_node: Node = ctx[subject_path] assert inferred_answer if pred == "NAME": # If name was asked, it must be because the node is not yet persistent assert subject_node.get_id() < 0 subject_node.set_name(inferred_answer) persistent_subject_node = retrieve_or_create_node(subject_node) # TODO: Workaround - see #83 - if this state would write to interloc:all, # it would promise an interloc:all:pushed signal. This would be # picked up by persqa:new_interloc. subject_node.set_node(persistent_subject_node) sess.update(subject_node) ctx[interloc.prop_persisted] = subject_node elif pred in PREDICATE_SET: relationship_type = onto.get_type(ONTOLOGY_TYPE_FOR_PRED[pred]) relationship_node = Node(metatype=relationship_type) relationship_node.set_name(inferred_answer) relationship_node = retrieve_or_create_node(relationship_node) if relationship_node is not None: subject_node.add_relationships( {pred: {relationship_node.get_id()}}) sess.update(subject_node) ctx[rawio.prop_out] = verbaliser.get_random_successful_answer( pred).format(name=subject_node.get_name(), obj=inferred_answer) ctx[prop_predicate] = None
def handle_input(bot: Bot, update: Update): """ Handler for incoming messages Adds the chat/user of the incoming message to the set of active_chats/active_users Calls the telegram_callback receptor to process the incoming message Retrieves scientio Node of User if it exists, otherwise creates it in the scientio session Calls the push_telegram_interloc receptor to push the scientio node into interloc:all """ telegram_callback(update.effective_message.text) active_chats.add(update.effective_chat.id) if update.effective_user not in active_users: # set up scientio sess: Session = ravestate_ontology.get_session() onto: Ontology = ravestate_ontology.get_ontology() # create scientio Node of type TelegramPerson query = Node(metatype=onto.get_type("TelegramPerson")) prop_dict = {'telegram_id': update.effective_user.id} if update.effective_user.username: prop_dict['name'] = update.effective_user.username if update.effective_user.full_name: prop_dict['full_name'] = update.effective_user.full_name query.set_properties(prop_dict) node_list = sess.retrieve(query) if not node_list: telegram_node = sess.create(query) logger.info(f"Created new Node in scientio session: {telegram_node}") elif len(node_list) == 1: telegram_node = node_list[0] else: logger.error(f'Found multiple TelegramPersons that matched query: {update.message.chat_id} ' f'in scientio session. Cannot push node to interloc:all!') return # push chat-Node push_telegram_interloc(telegram_node, update.effective_chat.id) active_users.add(update.effective_user)
def test_init(self): """ Integration test with a default neo4j instance. Should be executed by Travis after setting up neo4j in docker. """ # Default values are neo4j defaults, EXCEPT the password. address = os.environ.get('NEO4J_ADDRESS', 'bolt://localhost:7687') user = os.environ.get('NEO4J_USERNAME', 'neo4j') passw = os.environ.get('NEO4J_PASSWORD', 'neo42j') ontpath = "scientio/examples/example_ontology.yaml" o = Ontology(path_to_yaml=str(ontpath)) s = Session(ontology=o, neo4j_address=address, neo4j_username=user, neo4j_password=passw) n = Node(metatype=o.get_type('Person')) n.set_name('Test') n_response = s.create(n) # TODO make smarter assert (n_response is not None)
def delete_properties_relationships(self, req: Node) -> bool: response = False if req.get_properties() is not None: properties = dict( (x, y) for x, y in req.get_properties().items() if y != "") if len(properties) > 0: builder = QueryBuilder() builder.match_by_id(req.get_id(), "n") for key, _ in properties.items(): if key != "name": builder.add(f"REMOVE n.{key}") builder.add("RETURN n") result = self._exec_query(builder.get()) if result: response = True if req.get_relationships() is not None: relationships = dict((x, y) for x, y in req.get_relationships().items() if len(y) > 0) if len(relationships) > 0: builder = QueryBuilder() builder.match_by_id(req.get_id(), "n") counter: int = 0 for key, value in relationships.items(): builder.add( f"MATCH (n)-[r{counter}:{key}]-(m{counter}) WHERE ID(m{counter}) IN {value}" ) counter += 1 counter -= 1 builder.add(f"DELETE r{counter}") while counter > 0: counter -= 1 builder.add(f",r{counter}") builder.add("RETURN n") result = self._exec_query(builder.get()) if result: response = True return response
def delete(self, request: Node) -> bool: if request.get_id() >= 0: return self.delete_properties_relationships(request) return False
def handle_single_interlocutor_input(ctx: ContextWrapper, input_value: str, id="anonymous_interlocutor") -> None: """ Forwards input to `rawio:in` and manages creation/deletion of a singleton interlocutor. A new interlocutor node is pushed, when the input is a greeting, and there is no interlocutor present. The interlocutor is popped, if the input is a farewell, and there is an interlocutor present. * `ctx`: Context Wrapper of the calling state. Must have read permissions for `interloc:all`. * `input_value`: The string input value that should be written to `rawio:in`. * `id`: Name of the interlocutor context property, and initial name for the interlocutor's Neo4j node (until a proper name is set by persqa). """ @receptor(ctx_wrap=ctx, write="rawio:in") def write_input(ctx_input, value: str): ctx_input["rawio:in"] = value @receptor(ctx_wrap=ctx, write="interloc:all") def push_interloc(ctx: ContextWrapper, interlocutor_node: Node): if ctx.push(parentpath="interloc:all", child=PropertyBase(name=id, default_value=interlocutor_node)): logger.debug(f"Pushed {interlocutor_node} to interloc:all") @receptor(ctx_wrap=ctx, write="interloc:all") def pop_interloc(ctx: ContextWrapper): if ctx.pop(f"interloc:all:{id}"): logger.debug(f"Popped interloc:all:{id}") write_input(input_value) interloc_exists = f"interloc:all:{id}" in ctx.enum("interloc:all") # push Node if you got a greeting if input_value.strip() in get_phrase_list( "greeting") and not interloc_exists: # set up scientio sess: Session = ravestate_ontology.get_session() onto: Ontology = ravestate_ontology.get_ontology() # create scientio Node of type Person query = Node(metatype=onto.get_type("Person")) query.set_name(id) interlocutor_node_list = sess.retrieve(query) if not interlocutor_node_list: interlocutor_node = sess.create(query) logger.info( f"Created new Node in scientio session: {interlocutor_node}") else: interlocutor_node = interlocutor_node_list[0] # push interloc-node push_interloc(interlocutor_node) # pop Node if you got a farewell elif input_value.strip() in get_phrase_list( "farewells") and interloc_exists: pop_interloc()
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 list of questions that can be answered: - who are you? - what is your name? - how old are you? - what is your age? - what is your hobby? - what are your hobbies? - what do you like? - where are you from? - where do you live? - who is your father/dad? - who is your brother/sibling? - who is your friend? - what do you want to become? - what are you a member of? - what can you do? - what are your skills? - what have you learned? - what are your abilities? """ sess: Session = ravestate_ontology.get_session() onto: Ontology = ravestate_ontology.get_ontology() roboy = Node(metatype=onto.get_type("Robot")) roboy.set_properties(ctx.conf(key=ROBOY_NODE_PROP_CONF_KEY)) node_list = sess.retrieve(request=roboy) if node_list: roboy = node_list[0] else: create_default_nodes() node_list = sess.retrieve(request=roboy) if node_list: roboy = node_list[0] else: logger.error( f"Seems like you do not have my memory running, or no node with properties" f"{ctx.conf(key=ROBOY_NODE_PROP_CONF_KEY)} exists!") return rs.Resign() triple = ctx[nlp.prop_triples][0] category = None memory_info = None # question word: What? if triple.is_question(nlp.QuestionWord.OBJECT): if triple.match_either_lemma(pred={"like"}, subj={"hobby"}): category = "HAS_HOBBY" elif triple.match_either_lemma(pred={"learn"}, subj={"skill"}): category = "skills" elif triple.match_either_lemma(pred={"can"}, subj={"ability"}): category = "abilities" elif triple.match_either_lemma(subj={"age"}): category = "age" memory_info = roboy_age(roboy.get_properties(key="birthdate")) elif triple.match_either_lemma(subj={"name"}): category = "full_name" memory_info = roboy.get_properties(key=category) elif triple.match_either_lemma(pred={"become"}): category = "future" # question word: Where? elif triple.is_question(nlp.QuestionWord.PLACE): if triple.match_either_lemma(pred={"be"}): category = "FROM" elif triple.match_either_lemma(pred={"live"}): category = "LIVE_IN" # question word: Who? elif triple.is_question(nlp.QuestionWord.PERSON): if triple.match_either_lemma(obj={"father", "dad"}): category = "CHILD_OF" elif triple.match_either_lemma(obj={"brother", "sibling"}): category = "SIBLING_OF" elif triple.match_either_lemma(obj={"friend", "girlfriend"}): category = "FRIEND_OF" else: category = "full_name" memory_info = roboy.get_properties(key=category) elif triple.match_either_lemma(obj={"part", "member"}): category = "MEMBER_OF" # question word: How? elif triple.is_question(nlp.QuestionWord.FORM): if triple.match_either_lemma(pred={"old"}): category = "age" memory_info = roboy_age(roboy.get_properties(key="birthdate")) elif triple.match_either_lemma(pred={"be"}): category = "well_being" elif triple.match_either_lemma(obj={"skill"}): category = "skills" elif triple.match_either_lemma(obj={"ability"}): category = "abilities" if category and category.isupper() and not isinstance( roboy.get_relationships(key=category), dict): node_id = random.sample(roboy.get_relationships(key=category), 1)[0] memory_info = sess.retrieve(node_id=int(node_id))[0].get_name() elif category and category.islower() and not isinstance( roboy.get_properties(key=category), dict): property_list = [ x.strip() for x in roboy.get_properties(key=category).split(',') ] memory_info = random.sample(property_list, 1)[0] if memory_info: ctx[rawio.prop_out] = verbaliser.get_random_successful_answer( "roboy_" + category) % memory_info elif category == "well_being": ctx[rawio.prop_out] = verbaliser.get_random_successful_answer( "roboy_" + category) else: return rs.Resign()
def create_default_nodes(): onto: Ontology = ravestate_ontology.get_ontology() sess = ravestate_ontology.get_session() organization_type = onto.get_type("Organization") city_type = onto.get_type("City") person_type = onto.get_type("Person") robot_type = onto.get_type("Robot") hobby_type = onto.get_type("Hobby") student_team_node = Node(metatype=organization_type) student_team_node.set_properties({"name": "roboy student team"}) munich_node = Node(metatype=city_type) munich_node.set_properties({"name": "munich"}) roboy_junior_node = Node(metatype=robot_type) roboy_junior_node.set_properties({ "name": "roboy", "abilities": "talking to people,reading wikipedia,reading reddit", "skills": "tell jokes,tell fun facts about famous people and places,recognize you next time we meet," "remember your name and our conversation", "speed_kmh": 0, "full_name": "roboy junior", "birthdate": "08.03.2013", "conversation_id": "#bitch_boy", "sex": "male", "age": 5 }) raf_node = Node(metatype=person_type) raf_node.set_properties({ "name": "rafael", "full_name": "rafael hostettler" }) lucy_node = Node(metatype=person_type) lucy_node.set_properties({"name": "lucy", "age": 6, "sex": "female"}) reddit_node = Node(metatype=hobby_type) reddit_node.set_properties({"name": "reading reddit"}) roboy_node = Node(metatype=robot_type) roboy_node.set_properties({ "name": "roboy two", "skills": "telling jokes,telling fun facts,telling you about the famous people and places,doing the math", "abilities": "talk to people,recognize objects,show emotions,move my body,shake a hand," "party like there is no tomorrow,surf the internet,answer your questions", "speed_kmh": 0, "birthdate": "12.04.2018", "full_name": "roboy 2.0", "conversation_id": "#bitch_boy", "future": "become a robot rights advocate,help people and robots become friends," "find the answer to the question of life the universe and everything," "visit mars and other planets,become a music star,become a michelin star chef," "get a robo pet,become as good as my father", "sex": "male", "age": "0" }) tricycle_node = Node(metatype=hobby_type) tricycle_node.set_properties({"name": "riding a tricycle"}) nodes = (student_team_node, munich_node, roboy_junior_node, raf_node, lucy_node, reddit_node, roboy_node, tricycle_node) for node in nodes: sess.create(node) roboy_junior_node.add_relationships({"HAS_HOBBY": {reddit_node.get_id()}}) roboy_junior_node.add_relationships( {"MEMBER_OF": {student_team_node.get_id()}}) roboy_junior_node.add_relationships({"CHILD_OF": {raf_node.get_id()}}) roboy_junior_node.add_relationships({"LIVE_IN": {munich_node.get_id()}}) raf_node.add_relationships( {"FRIEND_OF": {roboy_node.get_id(), roboy_junior_node.get_id()}}) raf_node.add_relationships({"WORK_FOR": {student_team_node.get_id()}}) lucy_node.add_relationships( {"FRIEND_OF": {roboy_node.get_id(), roboy_junior_node.get_id()}}) roboy_node.add_relationships({"FROM": {munich_node.get_id()}}) roboy_node.add_relationships({"SIBLING_OF": {roboy_junior_node.get_id()}}) roboy_node.add_relationships( {"HAS_HOBBY": {tricycle_node.get_id(), reddit_node.get_id()}}) roboy_node.add_relationships({"MEMBER_OF": {student_team_node.get_id()}}) roboy_node.add_relationships({"CHILD_OF": {raf_node.get_id()}}) roboy_node.add_relationships({"LIVE_IN": {munich_node.get_id()}}) for node in nodes: sess.update(node)
def recognize_faces(ctx: rs.ContextWrapper): """ Activates with each incoming face data served by face oracle. Responsible for synchronizing the node of person in vision with the anonymous interlocutor node. Uses face oracle filter to organize the incoming data and find out the right person. """ face_filter: FaceOracleFilter = ctx[prop_face_filter] faces: Faces = ctx[prop_subscribe_faces] # Push faces to face filter best_guess_changed = face_filter.push_message(faces) if best_guess_changed: current_best_guess: Person = face_filter.current_best_guess onto: Ontology = mem.get_ontology() sess: Session = mem.get_session() person_node = Node(metatype=onto.get_type("Person")) best_guess_id = current_best_guess.id face_vector = current_best_guess.face_vector if current_best_guess.is_known: person_node_query = sess.retrieve(node_id=best_guess_id) if person_node_query: person_node = person_node_query[0] else: err_msg = "Person with id %s is not found in memory." % best_guess_id logger.error(err_msg) return else: person_node.set_properties({ 'face_vector': face_vector, 'name': interloc.ANON_INTERLOC_ID }) push = False # Check if there is any interlocutor. If necessary and pop the current node and push person node # instead. if any(ctx.enum(interloc.prop_all)): interloc_node: Node = ctx[ f'interloc:all:{interloc.ANON_INTERLOC_ID}'] # If interloc and the person nodes are not same pop and push person node. if not (interloc_node.get_id() == person_node.get_id() ) or interloc_node.get_id() < 0: # Remove the current interloc logger.info('Popping current interlocutor') popped_node = ctx.pop( f'interloc:all:{interloc.ANON_INTERLOC_ID}') assert popped_node == True push = True else: # Update the face vector of the person already familiar with save_face(ctx, interloc_node.get_id(), current_best_guess.face_vector) else: push = True if push: # Push the new interlocutor ctx.push(parent_property_or_path=interloc.prop_all, child=rs.Property(name=interloc.ANON_INTERLOC_ID, default_value=person_node)) logger.info( f"Pushed node with id {person_node.id} to interloc:all" )