You must now use 'eval' to get the body of a concept
This commit is contained in:
@@ -44,8 +44,10 @@ class BuiltinConcepts(Enum):
|
||||
CONCEPT_EVAL_ERROR = "concept evaluation error" # cannot evaluate a property or metadata of a concept
|
||||
ENUMERATION = "enum" # represents a list or a set
|
||||
LIST = "list" # represents a list
|
||||
CANNOT_RESOLVE_VALUE_ERROR = "value cannot be resolved" # don't know how to find concept value
|
||||
CONCEPT_ALREADY_IN_SET = "concept already in set"
|
||||
EVALUATOR_PRE_PROCESS = "evaluator pre process" # used modify / tweak behaviour of evaluators
|
||||
CONCEPT_EVAL_REQUESTED = "concept eval requested"
|
||||
REDUCE_REQUESTED = "reduce requested" # remove meaningless error when possible
|
||||
|
||||
NODE = "node"
|
||||
GENERIC_NODE = "generic node"
|
||||
@@ -68,7 +70,6 @@ BuiltinErrors = [str(e) for e in {
|
||||
BuiltinConcepts.INVALID_RETURN_VALUE,
|
||||
BuiltinConcepts.CONCEPT_ALREADY_DEFINED,
|
||||
BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||
BuiltinConcepts.CANNOT_RESOLVE_VALUE_ERROR,
|
||||
BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
|
||||
}]
|
||||
|
||||
@@ -267,6 +268,15 @@ class AfterEvaluationConcept(Concept):
|
||||
super().__init__(BuiltinConcepts.AFTER_EVALUATION, True, True, BuiltinConcepts.AFTER_EVALUATION)
|
||||
|
||||
|
||||
class ConceptEvalRequested(Concept):
|
||||
def __init__(self):
|
||||
super().__init__(BuiltinConcepts.CONCEPT_EVAL_REQUESTED, True, True, BuiltinConcepts.CONCEPT_EVAL_REQUESTED)
|
||||
|
||||
|
||||
class ReduceRequested(Concept):
|
||||
def __init__(self):
|
||||
super().__init__(BuiltinConcepts.REDUCE_REQUESTED, True, True, BuiltinConcepts.REDUCE_REQUESTED)
|
||||
|
||||
class ConceptEvalError(Concept):
|
||||
def __init__(self, error=None, concept=None, property_name=None):
|
||||
super().__init__(BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||
|
||||
@@ -12,6 +12,7 @@ PROPERTIES_FOR_DIGEST = ("name", "key",
|
||||
"where", "pre", "post", "body",
|
||||
"desc")
|
||||
PROPERTIES_TO_SERIALIZE = PROPERTIES_FOR_DIGEST + tuple(["id"])
|
||||
PROPERTIES_FOR_NEW = ("where", "pre", "post", "body", "desc")
|
||||
VARIABLE_PREFIX = "__var__"
|
||||
|
||||
|
||||
|
||||
+110
-40
@@ -1,13 +1,13 @@
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept, BuiltinErrors
|
||||
from core.concept import Concept, ConceptParts, PROPERTIES_FOR_DIGEST
|
||||
from core.concept import Concept, ConceptParts, PROPERTIES_FOR_NEW
|
||||
from parsers.BaseParser import BaseParser
|
||||
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderDuplicateKeyError
|
||||
import core.utils
|
||||
import core.builtin_helpers
|
||||
|
||||
from core.sheerka_logger import console_handler, get_logger
|
||||
from core.sheerka_logger import console_handler
|
||||
|
||||
import logging
|
||||
|
||||
@@ -85,8 +85,9 @@ class Sheerka(Concept):
|
||||
if self.sdp.first_time:
|
||||
self.sdp.set_key(self.USER_CONCEPTS_KEYS, 1000)
|
||||
|
||||
evt_digest = self.sdp.save_event(Event("Initializing Sheerka."))
|
||||
exec_context = ExecutionContext(self.key, evt_digest, self)
|
||||
event = Event("Initializing Sheerka.")
|
||||
self.sdp.save_event(event)
|
||||
exec_context = ExecutionContext(self.key, event, self)
|
||||
|
||||
self.initialize_builtin_concepts()
|
||||
self.initialize_builtin_parsers()
|
||||
@@ -181,19 +182,24 @@ class Sheerka(Concept):
|
||||
:return:
|
||||
"""
|
||||
self.log.debug(f"Processing user input '{text}', {user_name=}.")
|
||||
evt_digest = self.sdp.save_event(Event(text, user_name))
|
||||
event = Event(text, user_name)
|
||||
evt_digest = self.sdp.save_event(event)
|
||||
self.log.debug(f"{evt_digest=}")
|
||||
execution_context = ExecutionContext(self.key, evt_digest, self)
|
||||
execution_context = ExecutionContext(self.key, event, self)
|
||||
|
||||
user_input = self.ret(self.name, True, self.new(BuiltinConcepts.USER_INPUT, body=text, user_name=user_name))
|
||||
reduce_requested = self.ret(self.name, True, self.new(BuiltinConcepts.REDUCE_REQUESTED))
|
||||
|
||||
steps = [
|
||||
BuiltinConcepts.BEFORE_PARSING,
|
||||
BuiltinConcepts.PARSING,
|
||||
BuiltinConcepts.AFTER_PARSING,
|
||||
BuiltinConcepts.BEFORE_EVALUATION,
|
||||
BuiltinConcepts.EVALUATION,
|
||||
BuiltinConcepts.AFTER_EVALUATION
|
||||
]
|
||||
|
||||
return self.execute(execution_context, user_input, steps)
|
||||
return self.execute(execution_context, [user_input, reduce_requested], steps)
|
||||
|
||||
def _call_parsers(self, execution_context, return_values, logger=None):
|
||||
|
||||
@@ -205,6 +211,7 @@ class Sheerka(Concept):
|
||||
for return_value in return_values:
|
||||
# make sure we only parse user input
|
||||
if not return_value.status or not self.isinstance(return_value.body, BuiltinConcepts.USER_INPUT):
|
||||
result.append(return_value)
|
||||
continue
|
||||
|
||||
to_parse = self.value(return_value)
|
||||
@@ -232,6 +239,26 @@ class Sheerka(Concept):
|
||||
|
||||
def _call_evaluators(self, execution_context, return_values, process_step, evaluation_context=None, logger=None):
|
||||
|
||||
def _preprocess_evaluators(context, evaluators):
|
||||
if not context.preprocess:
|
||||
return evaluators
|
||||
|
||||
if not hasattr(evaluators, "__iter__"):
|
||||
single_one = True
|
||||
evaluators = [evaluators]
|
||||
else:
|
||||
single_one = False
|
||||
|
||||
for preprocess in context.preprocess:
|
||||
for e in evaluators:
|
||||
if preprocess.props["name"].value == e.name:
|
||||
for prop, value in preprocess.props.items():
|
||||
if prop == "name":
|
||||
continue
|
||||
if hasattr(e, prop):
|
||||
setattr(e, prop, value.value)
|
||||
return evaluators[0] if single_one else evaluators
|
||||
|
||||
# return_values must be a list
|
||||
if not isinstance(return_values, list):
|
||||
return_values = [return_values]
|
||||
@@ -255,6 +282,10 @@ class Sheerka(Concept):
|
||||
# The first one to be applied will be the one with the highest priority
|
||||
grouped_evaluators = {}
|
||||
instantiated_evaluators = [e_class() for e_class in self.evaluators]
|
||||
|
||||
# pre-process evaluators if needed
|
||||
instantiated_evaluators = _preprocess_evaluators(execution_context, instantiated_evaluators)
|
||||
|
||||
for evaluator in [e for e in instantiated_evaluators if e.enabled and process_step in e.steps]:
|
||||
if logger:
|
||||
evaluator.log = logger
|
||||
@@ -273,6 +304,7 @@ class Sheerka(Concept):
|
||||
evaluated_items = []
|
||||
to_delete = []
|
||||
for evaluator in grouped_evaluators[priority]:
|
||||
evaluator = _preprocess_evaluators(execution_context, evaluator.__class__()) # fresh copy
|
||||
|
||||
# process evaluators that work on return value
|
||||
from evaluators.BaseEvaluator import OneReturnValueEvaluator
|
||||
@@ -334,12 +366,16 @@ class Sheerka(Concept):
|
||||
for step in execution_steps:
|
||||
sub_context = execution_context.push(step=step)
|
||||
sub_context.log(logger or self.log, f"{step=}, context='{sub_context}'")
|
||||
|
||||
copy = return_values[:] if hasattr(return_values, "__iter__") else return_values
|
||||
|
||||
if step == BuiltinConcepts.PARSING:
|
||||
return_values = self._call_parsers(sub_context, return_values, logger)
|
||||
else:
|
||||
return_values = self._call_evaluators(sub_context, return_values, step, None, logger)
|
||||
|
||||
sub_context.log_result(logger or self.log, return_values)
|
||||
if copy != return_values:
|
||||
sub_context.log_result(logger or self.log, return_values)
|
||||
|
||||
return return_values
|
||||
|
||||
@@ -374,7 +410,11 @@ class Sheerka(Concept):
|
||||
# TODO checks if it exists in cache first
|
||||
if self.sdp.exists(self.CONCEPTS_ENTRY, concept.key, concept.get_digest()):
|
||||
error = SheerkaDataProviderDuplicateKeyError(self.CONCEPTS_ENTRY + "." + concept.key, concept)
|
||||
return self.ret(self.create_new_concept.__name__, False, ErrorConcept(error), error.args[0])
|
||||
return self.ret(
|
||||
self.create_new_concept.__name__,
|
||||
False,
|
||||
self.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=concept),
|
||||
error.args[0])
|
||||
|
||||
# set id before saving in db
|
||||
self.set_id_if_needed(concept, False)
|
||||
@@ -394,12 +434,18 @@ class Sheerka(Concept):
|
||||
|
||||
# save the new context in sdp
|
||||
try:
|
||||
self.sdp.add(context.event_digest, self.CONCEPTS_ENTRY, concept, use_ref=True)
|
||||
self.sdp.add(context.event.get_digest(), self.CONCEPTS_ENTRY, concept, use_ref=True)
|
||||
if concepts_definitions is not None:
|
||||
self.sdp.set(context.event_digest, self.CONCEPTS_DEFINITIONS_ENTRY, concepts_definitions, use_ref=True)
|
||||
self.sdp.set(context.event.get_digest(),
|
||||
self.CONCEPTS_DEFINITIONS_ENTRY,
|
||||
concepts_definitions, use_ref=True)
|
||||
except SheerkaDataProviderDuplicateKeyError as error:
|
||||
context.log_error(logger, "Failed to create a new concept.", who=self.create_new_concept.__name__)
|
||||
return self.ret(self.create_new_concept.__name__, False, ErrorConcept(error), error.args[0])
|
||||
return self.ret(
|
||||
self.create_new_concept.__name__,
|
||||
False,
|
||||
self.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=concept),
|
||||
error.args[0])
|
||||
|
||||
# Updates the caches
|
||||
self.concepts_cache[concept.key] = self.sdp.get_safe(self.CONCEPTS_ENTRY, concept.key)
|
||||
@@ -427,8 +473,8 @@ class Sheerka(Concept):
|
||||
assert concept_set.id
|
||||
|
||||
try:
|
||||
ret = self.sdp.add_unique(context.event_digest, "All_" + str(concept_set.id), concept.id)
|
||||
if ret == (None, None): # concept already in set
|
||||
ret = self.sdp.add_unique(context.event.get_digest(), "All_" + str(concept_set.id), concept.id)
|
||||
if ret == (None, None): # concept already in set
|
||||
return self.ret(
|
||||
self.add_concept_to_set.__name__,
|
||||
False,
|
||||
@@ -506,12 +552,13 @@ class Sheerka(Concept):
|
||||
|
||||
# to make sure of the order, it don't use ConceptParts.get_parts()
|
||||
# props must be evaluated first
|
||||
properties_to_eval = ["props", "where", "pre", "post", "body"]
|
||||
all_metadata_to_eval = ["props", "where", "pre", "post", "body"]
|
||||
|
||||
for prop_to_eval in properties_to_eval:
|
||||
if prop_to_eval == "props":
|
||||
for metadata_to_eval in all_metadata_to_eval:
|
||||
if metadata_to_eval == "props":
|
||||
for prop_name in (p for p in concept.props if p in concept.cached_asts):
|
||||
sub_context = context.push(desc=f"Evaluating property '{prop_name}'")
|
||||
sub_context.add_preprocess(self.get_evaluator_name("Concept"), return_body=True)
|
||||
res = _resolve(sub_context, concept.cached_asts[prop_name])
|
||||
if res.status:
|
||||
concept.set_prop(prop_name, res.value)
|
||||
@@ -521,12 +568,14 @@ class Sheerka(Concept):
|
||||
concept=concept,
|
||||
property_name=prop_name)
|
||||
else:
|
||||
part_key = ConceptParts(prop_to_eval)
|
||||
part_key = ConceptParts(metadata_to_eval)
|
||||
|
||||
if part_key in concept.cached_asts and concept.cached_asts[part_key] is not None:
|
||||
sub_context = context.push(desc=f"Evaluating '{part_key}'", obj=concept)
|
||||
sub_context.add_preprocess(self.get_evaluator_name("Concept"), return_body=True)
|
||||
res = _resolve(sub_context, concept.cached_asts[part_key])
|
||||
if res.status:
|
||||
setattr(concept.metadata, prop_to_eval, res.value)
|
||||
setattr(concept.metadata, metadata_to_eval, res.value)
|
||||
else:
|
||||
return self.new(BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||
body=res.value,
|
||||
@@ -611,7 +660,7 @@ class Sheerka(Concept):
|
||||
for k, v in kwargs_.items():
|
||||
if k in concept.props:
|
||||
concept.set_prop(k, v)
|
||||
elif k in PROPERTIES_FOR_DIGEST:
|
||||
elif k in PROPERTIES_FOR_NEW:
|
||||
setattr(concept.metadata, k, v)
|
||||
elif hasattr(concept, k):
|
||||
setattr(concept, k, v)
|
||||
@@ -651,28 +700,25 @@ class Sheerka(Concept):
|
||||
message=message,
|
||||
parents=parents)
|
||||
|
||||
def value(self, obj, allow_none_body=False):
|
||||
def value(self, obj, reduce_simple_list=False):
|
||||
if obj is None:
|
||||
return None
|
||||
|
||||
if self.isinstance(obj, BuiltinConcepts.RETURN_VALUE) and \
|
||||
obj.status and \
|
||||
self.isinstance(obj.value, BuiltinConcepts.USER_INPUT):
|
||||
return obj.value.body
|
||||
|
||||
if not isinstance(obj, Concept):
|
||||
return obj
|
||||
|
||||
if hasattr(obj, "get_value"):
|
||||
return obj.get_value()
|
||||
|
||||
if obj.body is not None:
|
||||
if (isinstance(obj.body, list) or isinstance(obj.body, set)) and len(obj.body) == 1:
|
||||
return obj.body[0]
|
||||
else:
|
||||
return obj.body
|
||||
if not isinstance(obj, Concept):
|
||||
return obj
|
||||
|
||||
return obj if allow_none_body else self.new(BuiltinConcepts.CANNOT_RESOLVE_VALUE_ERROR, body=obj)
|
||||
if obj.body is None:
|
||||
return obj
|
||||
|
||||
if reduce_simple_list and (isinstance(obj.body, list) or isinstance(obj.body, set)) and len(obj.body) == 1:
|
||||
body_to_use = obj.body[0]
|
||||
else:
|
||||
body_to_use = obj.body
|
||||
|
||||
return self.value(body_to_use)
|
||||
|
||||
def values(self, objs):
|
||||
if not (isinstance(objs, list) or
|
||||
@@ -786,6 +832,17 @@ class Sheerka(Concept):
|
||||
defs = self.sdp.get(self.CONCEPTS_DEFINITIONS_ENTRY)
|
||||
self.log.info(defs)
|
||||
|
||||
def dump_desc(self, concept_name):
|
||||
c = self.get(concept_name)
|
||||
if self.isinstance(c, BuiltinConcepts.UNKNOWN_CONCEPT):
|
||||
self.log.error("Concept unknown")
|
||||
return False
|
||||
|
||||
self.log.info(f"name : {c.name}")
|
||||
self.log.info(f"bnf : {c.metadata.definition}")
|
||||
self.log.info(f"key : {c.key}")
|
||||
self.log.info(f"body : {c.body}")
|
||||
|
||||
@staticmethod
|
||||
def get_builtins_classes_as_dict():
|
||||
res = {}
|
||||
@@ -817,7 +874,7 @@ class ExecutionContext:
|
||||
|
||||
def __init__(self,
|
||||
who,
|
||||
event_digest: str,
|
||||
event: Event,
|
||||
sheerka: Sheerka,
|
||||
/,
|
||||
desc: str = None,
|
||||
@@ -827,20 +884,32 @@ class ExecutionContext:
|
||||
concepts: dict = None):
|
||||
|
||||
self.who = who # who is asking
|
||||
self.event_digest = event_digest # what was the (original) trigger
|
||||
self.event = event # what was the (original) trigger
|
||||
self.sheerka = sheerka # sheerka
|
||||
|
||||
self.step = step
|
||||
self.iteration = iteration
|
||||
self.preprocess = None
|
||||
|
||||
self.desc = desc # human description of what is going on
|
||||
self.obj = obj # what is the subject of the execution context (if known)
|
||||
|
||||
self.concepts = concepts or {}
|
||||
self.concepts = concepts or {} # cache for concepts that are specific to this execution
|
||||
|
||||
self._id = ExecutionContextIdManager.get_id(event_digest)
|
||||
self._id = ExecutionContextIdManager.get_id(event.get_digest())
|
||||
self._tab = ""
|
||||
|
||||
def add_preprocess(self, name, **kwargs):
|
||||
preprocess = self.sheerka.new(BuiltinConcepts.EVALUATOR_PRE_PROCESS)
|
||||
preprocess.set_prop("name", name)
|
||||
for k, v in kwargs.items():
|
||||
preprocess.set_prop(k, v)
|
||||
|
||||
if not self.preprocess:
|
||||
self.preprocess = set()
|
||||
self.preprocess.add(preprocess)
|
||||
return self
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
@@ -854,7 +923,7 @@ class ExecutionContext:
|
||||
iteration = kwargs.get("iteration", self.iteration)
|
||||
new = ExecutionContext(
|
||||
who,
|
||||
self.event_digest,
|
||||
self.event,
|
||||
self.sheerka,
|
||||
desc=desc,
|
||||
obj=obj,
|
||||
@@ -863,6 +932,7 @@ class ExecutionContext:
|
||||
iteration=iteration,
|
||||
)
|
||||
new._tab = self._tab + " " * DEBUG_TAB_SIZE
|
||||
new.preprocess = self.preprocess
|
||||
return new
|
||||
|
||||
def log_new(self, logger):
|
||||
|
||||
Reference in New Issue
Block a user