Implemented a first and basic version of a Rete rule engine
This commit is contained in:
+80
-28
@@ -1,6 +1,7 @@
|
||||
import inspect
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from operator import attrgetter
|
||||
|
||||
import core.builtin_helpers
|
||||
import core.utils
|
||||
@@ -10,8 +11,7 @@ from cache.IncCache import IncCache
|
||||
from core.builtin_concepts import ErrorConcept, ReturnValueConcept, UnknownConcept
|
||||
from core.builtin_concepts_ids import BuiltinErrors, BuiltinConcepts
|
||||
from core.concept import Concept, ConceptParts, get_concept_attrs
|
||||
from core.error import ErrorObj
|
||||
from core.global_symbols import EVENT_USER_INPUT_EVALUATED, NotInit, NotFound
|
||||
from core.global_symbols import EVENT_USER_INPUT_EVALUATED, NotInit, NotFound, ErrorObj, EVENT_ONTOLOGY_CREATED
|
||||
from core.profiling import profile
|
||||
from core.sheerka.ExecutionContext import ExecutionContext
|
||||
from core.sheerka.SheerkaOntologyManager import SheerkaOntologyManager, OntologyAlreadyExists
|
||||
@@ -31,6 +31,23 @@ EXECUTE_STEPS = [
|
||||
BuiltinConcepts.AFTER_EVALUATION
|
||||
]
|
||||
|
||||
RULES_EVALUATE_STEPS = [
|
||||
BuiltinConcepts.BEFORE_RULES_EVALUATION,
|
||||
BuiltinConcepts.RULES_EVALUATION,
|
||||
BuiltinConcepts.AFTER_RULES_EVALUATION,
|
||||
]
|
||||
|
||||
RULES_EXECUTE_STEPS = [
|
||||
BuiltinConcepts.BEFORE_EVALUATION,
|
||||
BuiltinConcepts.EVALUATION,
|
||||
BuiltinConcepts.AFTER_EVALUATION
|
||||
]
|
||||
|
||||
# when a concept is instantiated via resolve or false_resolve
|
||||
# It indicate which parameter was used to recognize the concept
|
||||
RECOGNIZED_BY_ID = "by_id"
|
||||
RECOGNIZED_BY_NAME = "by_name"
|
||||
|
||||
|
||||
@dataclass
|
||||
class SheerkaMethod:
|
||||
@@ -92,17 +109,25 @@ class Sheerka(Concept):
|
||||
|
||||
self.save_execution_context = True
|
||||
self.enable_process_return_values = True
|
||||
self.enable_process_rules = True
|
||||
|
||||
self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods
|
||||
self.sheerka_methods = {
|
||||
"test": SheerkaMethod(self.test, False),
|
||||
"test_using_context": SheerkaMethod(self.test_using_context, False),
|
||||
"test_dict": SheerkaMethod(self.test_dict, False)
|
||||
"test_dict": SheerkaMethod(self.test_dict, False),
|
||||
"test_error": SheerkaMethod(self.test_error, False),
|
||||
}
|
||||
|
||||
self.locals = {}
|
||||
self.concepts_ids = None
|
||||
|
||||
def __copy__(self):
|
||||
return self
|
||||
|
||||
def __deepcopy__(self, memodict={}):
|
||||
return self
|
||||
|
||||
@property
|
||||
def concepts_grammars(self):
|
||||
"""
|
||||
@@ -138,7 +163,7 @@ class Sheerka(Concept):
|
||||
|
||||
setattr(self, bound_method.__name__, bound_method)
|
||||
|
||||
def initialize(self, root_folder: str = None, save_execution_context=None, enable_process_return_values=None):
|
||||
def initialize(self, root_folder: str = None, **kwargs):
|
||||
"""
|
||||
Starting Sheerka
|
||||
Loads the current configuration
|
||||
@@ -149,11 +174,10 @@ class Sheerka(Concept):
|
||||
:return: ReturnValue(Success or Error)
|
||||
"""
|
||||
|
||||
if save_execution_context is not None:
|
||||
self.save_execution_context = save_execution_context
|
||||
|
||||
if enable_process_return_values is not None:
|
||||
self.enable_process_return_values = enable_process_return_values
|
||||
self.save_execution_context = kwargs.get("save_execution_context", self.save_execution_context)
|
||||
self.enable_process_return_values = kwargs.get("enable_process_return_values",
|
||||
self.enable_process_return_values)
|
||||
self.enable_process_rules = kwargs.get("enable_process_rules", self.enable_process_rules)
|
||||
|
||||
try:
|
||||
self.during_initialisation = True
|
||||
@@ -168,6 +192,7 @@ class Sheerka(Concept):
|
||||
self.get_builtin_evaluators()
|
||||
self.initialize_services()
|
||||
self.initialize_builtin_evaluators()
|
||||
self.om.init_subscribers()
|
||||
|
||||
event = Event("Initializing Sheerka.", user_id=self.name)
|
||||
self.om.save_event(event)
|
||||
@@ -234,11 +259,12 @@ class Sheerka(Concept):
|
||||
|
||||
core.utils.import_module_and_sub_module('core.sheerka.services')
|
||||
base_class = "core.sheerka.services.sheerka_service.BaseService"
|
||||
for service in core.utils.get_sub_classes("core.sheerka.services", base_class):
|
||||
instance = service(self)
|
||||
if hasattr(instance, "initialize"):
|
||||
instance.initialize()
|
||||
self.services[service.NAME] = instance
|
||||
services = [service(self) for service in core.utils.get_sub_classes("core.sheerka.services", base_class)]
|
||||
services.sort(key=attrgetter("order"))
|
||||
for service in services:
|
||||
if hasattr(service, "initialize"):
|
||||
service.initialize()
|
||||
self.services[service.NAME] = service
|
||||
|
||||
def initialize_services_deferred(self, context, is_first_time):
|
||||
"""
|
||||
@@ -325,7 +351,6 @@ class Sheerka(Concept):
|
||||
ontologies = self.om.current_sdp().load_ontologies()
|
||||
if not ontologies:
|
||||
return
|
||||
|
||||
for ontology_name in list(reversed(ontologies))[1:]:
|
||||
self.om.push_ontology(ontology_name, False)
|
||||
self.initialize_services_deferred(context, False)
|
||||
@@ -360,6 +385,10 @@ class Sheerka(Concept):
|
||||
ret = self.execute(execution_context, [user_input, reduce_requested], EXECUTE_STEPS)
|
||||
execution_context.add_values(return_values=ret)
|
||||
|
||||
# rule management
|
||||
if self.enable_process_rules:
|
||||
ret = self.execute_rules(execution_context, ret, RULES_EVALUATE_STEPS, RULES_EXECUTE_STEPS)
|
||||
|
||||
if self.om.is_dirty:
|
||||
self.om.commit(execution_context)
|
||||
|
||||
@@ -388,10 +417,14 @@ class Sheerka(Concept):
|
||||
:return:
|
||||
"""
|
||||
|
||||
def new_instances(concepts):
|
||||
def add_recognized_by(c, _recognized_by):
|
||||
c.set_hint(BuiltinConcepts.RECOGNIZED_BY, _recognized_by)
|
||||
return c
|
||||
|
||||
def new_instances(concepts, _recognized_by):
|
||||
if hasattr(concepts, "__iter__"):
|
||||
return [self.new_from_template(c, c.key) for c in concepts]
|
||||
return self.new_from_template(concepts, concepts.key)
|
||||
return [add_recognized_by(self.new_from_template(c, c.key), _recognized_by) for c in concepts]
|
||||
return add_recognized_by(self.new_from_template(concepts, concepts.key), _recognized_by)
|
||||
|
||||
if concept is None:
|
||||
return None
|
||||
@@ -421,10 +454,11 @@ class Sheerka(Concept):
|
||||
if self.is_known(found := self.get_by_id(concept[1])):
|
||||
instance = self.new_from_template(found, found.key)
|
||||
instance._metadata.is_evaluated = True
|
||||
instance.set_hint(BuiltinConcepts.RECOGNIZED_BY, RECOGNIZED_BY_ID)
|
||||
return instance
|
||||
elif concept[0]:
|
||||
if self.is_known(found := self.get_by_name(concept[0])):
|
||||
instances = new_instances(found)
|
||||
instances = new_instances(found, RECOGNIZED_BY_NAME)
|
||||
core.builtin_helpers.set_is_evaluated(instances)
|
||||
return instances
|
||||
else:
|
||||
@@ -433,17 +467,22 @@ class Sheerka(Concept):
|
||||
# otherwise search in db
|
||||
if isinstance(concept, str):
|
||||
if self.is_known(found := self.get_by_name(concept)):
|
||||
instances = new_instances(found)
|
||||
instances = new_instances(found, RECOGNIZED_BY_NAME)
|
||||
core.builtin_helpers.set_is_evaluated(instances, check_nb_variables=True)
|
||||
return instances
|
||||
|
||||
return None
|
||||
|
||||
def fast_resolve(self, key, return_new=True):
|
||||
def new_instances(concepts):
|
||||
def add_recognized_by(c, _recognized_by):
|
||||
c.set_hint(BuiltinConcepts.RECOGNIZED_BY, _recognized_by)
|
||||
return c
|
||||
|
||||
def new_instances(concepts, _recognized_by):
|
||||
if hasattr(concepts, "__iter__"):
|
||||
return [self.new_from_template(c, c.key) for c in concepts]
|
||||
return self.new_from_template(concepts, concepts.key)
|
||||
return [add_recognized_by(self.new_from_template(c, c.key), _recognized_by) for c in concepts]
|
||||
|
||||
return add_recognized_by(self.new_from_template(concepts, concepts.key), _recognized_by)
|
||||
|
||||
if isinstance(key, Token):
|
||||
if key.type == TokenKind.RULE: # do not recognize rules !!!
|
||||
@@ -459,14 +498,17 @@ class Sheerka(Concept):
|
||||
|
||||
if key[1]:
|
||||
concept = self.om.get(self.CONCEPTS_BY_ID_ENTRY, key[1])
|
||||
recognized_by = RECOGNIZED_BY_ID
|
||||
else:
|
||||
concept = self.om.get(self.CONCEPTS_BY_NAME_ENTRY, key[0])
|
||||
recognized_by = RECOGNIZED_BY_NAME
|
||||
else:
|
||||
concept = self.om.get(self.CONCEPTS_BY_NAME_ENTRY, key)
|
||||
recognized_by = RECOGNIZED_BY_NAME
|
||||
|
||||
if concept is NotFound:
|
||||
return None
|
||||
return new_instances(concept) if return_new else concept
|
||||
return new_instances(concept, recognized_by) if return_new else concept
|
||||
|
||||
def new(self, concept_key, **kwargs):
|
||||
"""
|
||||
@@ -478,6 +520,8 @@ class Sheerka(Concept):
|
||||
"""
|
||||
if isinstance(concept_key, tuple):
|
||||
concept_key, concept_id = concept_key[0], concept_key[1]
|
||||
elif isinstance(concept_key, Concept):
|
||||
concept_key, concept_id = concept_key.key, concept_key.id
|
||||
else:
|
||||
concept_id = None
|
||||
|
||||
@@ -547,12 +591,13 @@ class Sheerka(Concept):
|
||||
if name in self.om.current_sdp().load_ontologies():
|
||||
self.initialize_services_deferred(context, False)
|
||||
|
||||
self.om.save_ontologies()
|
||||
self.om.save_ontologies_names()
|
||||
self.publish(context, EVENT_ONTOLOGY_CREATED, name)
|
||||
|
||||
return self.ret(self.name, True, self.new(BuiltinConcepts.SUCCESS))
|
||||
|
||||
def pop_ontology(self):
|
||||
ontology = self.om.pop_ontology()
|
||||
def pop_ontology(self, context):
|
||||
ontology = self.om.pop_ontology(context)
|
||||
|
||||
self.om.reset_sheerka_state()
|
||||
for service in self.services.values():
|
||||
@@ -561,7 +606,7 @@ class Sheerka(Concept):
|
||||
if hasattr(service, "reset_state"):
|
||||
service.reset_state()
|
||||
|
||||
self.om.save_ontologies()
|
||||
self.om.save_ontologies_names()
|
||||
return self.ret(self.name, True, self.new(BuiltinConcepts.ONTOLOGY_REMOVED, body=ontology))
|
||||
|
||||
def get_ontology(self, context):
|
||||
@@ -699,6 +744,13 @@ class Sheerka(Concept):
|
||||
|
||||
return bool(obj)
|
||||
|
||||
@staticmethod
|
||||
def is_error(obj):
|
||||
"""
|
||||
opposite of is_success
|
||||
"""
|
||||
return not Sheerka.is_success(obj)
|
||||
|
||||
@staticmethod
|
||||
def is_known(obj):
|
||||
if not isinstance(obj, Concept):
|
||||
|
||||
Reference in New Issue
Block a user