Implemented a first and basic version of a Rete rule engine
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.builtin_helpers import expect_one
|
||||
from core.global_symbols import EVENT_RULE_CREATED, EVENT_RULE_DELETED, EVENT_RULE_ID_DELETED
|
||||
from core.sheerka.services.sheerka_service import BaseService
|
||||
from evaluators.ConceptEvaluator import ConceptEvaluator
|
||||
from sheerkarete.network import ReteNetwork
|
||||
|
||||
DISABLED_RULES = "#disabled#"
|
||||
LOW_PRIORITY_RULES = "#low_priority#"
|
||||
@@ -11,12 +13,18 @@ class SheerkaEvaluateRules(BaseService):
|
||||
NAME = "EvaluateRules"
|
||||
|
||||
def __init__(self, sheerka):
|
||||
super().__init__(sheerka)
|
||||
# order must be before RuleManager because of event subscription
|
||||
super().__init__(sheerka, 4)
|
||||
self.evaluators_by_name = None
|
||||
self.network = ReteNetwork()
|
||||
|
||||
def initialize(self):
|
||||
self.sheerka.bind_service_method(self.evaluate_format_rules, False)
|
||||
self.sheerka.bind_service_method(self.evaluate_format_rules, False, visible=False)
|
||||
self.sheerka.bind_service_method(self.evaluate_exec_rules, False, visible=False)
|
||||
self.reset_evaluators()
|
||||
self.sheerka.subscribe(EVENT_RULE_CREATED, self.on_rule_created)
|
||||
self.sheerka.subscribe(EVENT_RULE_DELETED, self.on_rule_deleted)
|
||||
self.sheerka.subscribe(EVENT_RULE_ID_DELETED, self.on_rule_deleted)
|
||||
|
||||
def reset_evaluators(self):
|
||||
# instantiate evaluators, once for all, only keep when it's enabled
|
||||
@@ -24,6 +32,20 @@ class SheerkaEvaluateRules(BaseService):
|
||||
evaluators = [e for e in evaluators if e.enabled]
|
||||
self.evaluators_by_name = {e.short_name: e for e in evaluators}
|
||||
|
||||
def evaluate_exec_rules(self, context, return_values):
|
||||
# self.network.add_obj("__rets", return_values)
|
||||
for ret in return_values:
|
||||
self.network.add_obj("__ret", ret)
|
||||
|
||||
results = [] # list of return values, for activated rules
|
||||
for match in self.network.matches:
|
||||
for rule in match.pnode.rules:
|
||||
body = context.sheerka.new(BuiltinConcepts.RULE_EVALUATION_RESULT, rule=rule)
|
||||
return_value = context.sheerka.ret(self.NAME, True, body)
|
||||
results.append(return_value)
|
||||
|
||||
return results
|
||||
|
||||
def evaluate_format_rules(self, context, bag, disabled):
|
||||
return self.evaluate_rules(context, self.sheerka.get_format_rules(), bag, disabled)
|
||||
|
||||
@@ -37,7 +59,7 @@ class SheerkaEvaluateRules(BaseService):
|
||||
:param disabled: disabled rules (because they have already been fired or whatever)
|
||||
:return: { True : list of success, False :list of failed, '#disabled"': list of disabled...}
|
||||
"""
|
||||
with context.push(BuiltinConcepts.EVALUATING_RULES, bag, desc="Evaluating rules...") as sub_context:
|
||||
with context.push(BuiltinConcepts.RULES_EVALUATION, bag, desc="Evaluating rules...") as sub_context:
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_WHERE_REQUESTED)
|
||||
sub_context.protected_hints.add(BuiltinConcepts.EVAL_UNTIL_SUCCESS_REQUESTED)
|
||||
@@ -81,7 +103,7 @@ class SheerkaEvaluateRules(BaseService):
|
||||
"""
|
||||
|
||||
results = []
|
||||
for rule_predicate in rule.compiled_predicate:
|
||||
for rule_predicate in rule.compiled_predicates:
|
||||
|
||||
if rule_predicate.source in bag:
|
||||
# simple case where the rule is an item of the bag. No need of complicate evaluation
|
||||
@@ -94,15 +116,44 @@ class SheerkaEvaluateRules(BaseService):
|
||||
rule_predicate.concept.get_metadata().is_evaluated = False
|
||||
|
||||
evaluator = self.evaluators_by_name[rule_predicate.evaluator]
|
||||
results.append(evaluator.eval(context, rule_predicate.predicate))
|
||||
res = evaluator.eval(context, rule_predicate.predicate)
|
||||
if res.status and isinstance(res.body, bool) and res.body:
|
||||
# one successful value found. No need to look any further
|
||||
results = [res]
|
||||
break
|
||||
else:
|
||||
results.append(res)
|
||||
|
||||
debugger = context.get_debugger(SheerkaEvaluateRules.NAME, "evaluate_rule")
|
||||
debugger = context.get_debugger(SheerkaEvaluateRules.NAME, "evaluate_rule", new_debug_id=False)
|
||||
debugger.debug_rule(rule, results)
|
||||
# if context.sheerka.debug_rule_activated(rule_id, context.id):
|
||||
# context.debug(SheerkaEvaluateRules.NAME, "evaluate_rules", f"result(#{rule_id})", results)
|
||||
|
||||
return expect_one(context, results)
|
||||
|
||||
def remove_from_rete_memory(self, lst):
|
||||
if lst is None:
|
||||
return
|
||||
|
||||
for obj in lst:
|
||||
self.network.remove_obj(obj)
|
||||
|
||||
def on_rule_created(self, context, rule):
|
||||
"""
|
||||
When a new rule is added to the system, update the network
|
||||
"""
|
||||
if rule.metadata.is_enabled and rule.rete_disjunctions:
|
||||
self.network.add_rule(rule)
|
||||
|
||||
def on_rule_deleted(self, context, rule):
|
||||
"""
|
||||
When a rule is deleted from the system, remove it from the network
|
||||
"""
|
||||
if isinstance(rule, str):
|
||||
rule = self.sheerka.get_rule_by_id(rule)
|
||||
if not self.sheerka.is_known(rule):
|
||||
return
|
||||
|
||||
self.network.remove_rule(rule)
|
||||
|
||||
@staticmethod
|
||||
def get_debug_format(result):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user