Package: py-sc-kpm
py-sc-kpmThe python implementation of the knowledge processing machine (kpm)
forsc-machine.
Library provides tools for interacting with knowledge bases.
Communication with server is implemented in separate library
namedpy-sc-client.
This module is compatible with 0.7.0 version
ofOSTIS Technology platform.API ReferenceClassesScKeynodesScAgentScModuleScServerUtilsCommon utilsCreating utilsRetrieve utilsAction utilsUse-casesClassesThe library contains the python implementation of useful classes and functions to work with the sc-memory.ScKeynodesClass which provides the ability to cache the identifier and ScAddr of keynodes stored in the KB.fromsc_client.constantsimportsc_typesfromsc_kpmimportScKeynodes# Get the provided identifierScKeynodes["identifier_of_keynode"]# Returns an ScAddr of the given identifier# Get the unprovided identifierScKeynodes["not_stored_in_kb"]# Raises InvalidValueError if an identifier doesn't exist in the KBScKeynodes.get("not_stored_in_kb")# Returns an invalid ScAddr(0) in the same situation# Resolve identifierScKeynodes.resolve("my_class_node",sc_types.NODE_CONST_CLASS)# Returns the element if it exists, otherwise createsScKeynodes.resolve("some_node",None)# Returns the element if it exists, otherwise returns an invalid ScAddr(0)# Delete identifierScKeynodes.delete("identifier_to_delete")# Delete keynode from kb and ScKeynodes cache# Get rrel nodeScKeynodes.rrel_index(1)# Returns valid ScAddr of 'rrel_1'ScKeynodes.rrel_index(11)# Raises KeyError if index more than 10ScKeynodes.rrel_index("some_str")# Raises TypeError if index is not intScAgent and ScAgentClassicA classes for handling a single ScEvent. Define your agents like this:fromsc_client.modelsimportScAddrfromsc_kpmimportScAgent,ScAgentClassic,ScResultclassScAgentTest(ScAgent):defon_event(self,class_node:ScAddr,edge:ScAddr,action_node:ScAddr)->ScResult:...returnScResult.OKclassScAgentClassicTest(ScAgentClassic):defon_event(self,class_node:ScAddr,edge:ScAddr,action_node:ScAddr)->ScResult:# ScAgentClassic automatically checks its action...returnScResult.OKFor the ScAgent initialization you should define the sc-element and the type of the ScEvent.For the ScAgentClassic initialization
you should define the identifier of the action class node and arguments of the ScAgent.event_classis set to thequestion_initiatedkeynode by default.event_typeis set to theScEventType.ADD_OUTGOING_EDGEtype by default.ClassicScAgent checks its action element automatically and doesn't runon_eventmethod if checking fails.fromsc_client.constantsimportsc_typesfromsc_client.constants.commonimportScEventTypefromsc_kpmimportScKeynodesaction_class=ScKeynodes.resolve("test_class",sc_types.NODE_CONST_CLASS)agent=ScAgentTest(action_class,ScEventType.ADD_OUTGOING_EDGE)classic_agent=ScAgentClassicTest("classic_test_class")classic_agent_ingoing=ScAgentClassicTest("classic_test_class",ScEventType.ADD_INGOING_EDGE)ScModuleA class for handling multiple ScAgent objects.
Define your modules like this:fromsc_kpmimportScModulemodule=ScModule(agent1,agent2,)...module.add_agent(agent3)...module.remove_agent(agent3)Note: you don't need remove agents in the end of program.ScServerA class for serving, register ScModule objects.Firstly you need connect to server. You can use connect/disconnect methods:fromsc_kpmimportScServerSC_SERVER_URL="ws://localhost:8090/ws_json"server=ScServer(SC_SERVER_URL)server.connect()...server.disconnect()Or with-statement. We recommend it because it easier to use, and it's safe:fromsc_kpmimportScServerSC_SERVER_URL="ws://localhost:8090/ws_json"server=ScServer(SC_SERVER_URL)withserver.connect():...After connection, you can add and remove your modules. Manage your modules like this:...withserver.connect():module=ScModule(...)server.add_modules(module)...server.remove_modules(module)But the modules are still not registered. For this use register_modules/unregister_modules methods:...withserver.connect():...server.register_modules()...server.unregister_modules()Or one mode with-statement.
We also recommend to use so because it guarantees a safe agents unregistration if errors occur:...withserver.connect():...withserver.register_modules():...If you needn't separate connecting and registration, you can do it all using one command:withserver.start():...# orserver.start()...server.stop()There is also method for stopping program until a SIGINT signal (or ^C, or terminate in IDE) is received.
So you can leave agents registered for a long time:...withserver.connect():# Creating some agentswithserver.register_modules():# Registration some agentsserver.serve()# Agents will be active until ^CScSetsSc-set is a construction that presents main node calledset_nodeand linked elements.
There is no limit in types for sc-set elements:ScSetsc_kpm.sc_sets.ScSetClass for handling simple sc-sets.
It is the parent class forScOrientedSetandScNumberedSetMethods and properties:ScSet(*elements: ScAddr, set_node: ScAddr = None, set_node_type: ScType = None) -> NoneConstructor of sc-set receives all elements to add, optionalset_nodeand optionalset_node_type.
If you don't specifyset_node, it will be created withset_node_type(default NODE_CONST).ScSet.add(*elements: ScAddr) -> NoneAdd elements to the end of sc-set construction.ScSet.set_node-> ScAddrProperty to give themain nodeof sc-set.ScSet==ScSet-> boolCheck sc-sets have the same set_nodes.ScSet.elements_set-> Set[ScAddr]Property to give all elements from sc-set as a set.
Use it if you don't need order in ordered sets.iter(ScSet) -> Iterator[ScAddr]Dunder method for iterating by sc-set.
Lazy algorithm (ScOrientedSet and ScNumberedSet).len(ScSet) -> intFast dunder method to givecount of elements(power of sc-set).bool(ScSet) -> boolDunder method for if-statement: True if there are elements in sc-set.ScSet.is_empty() -> boolTrue if there areno elementsin sc-set.ScAddrinScSet-> boolDunder method: True if sc-set contains element.ScSet.clear() -> boolRemove all elements from sc-set.ScSet.remove(*elements: ScAddr) -> NoneRemove elements from sc-set.WARNING: method isn't optimized in ScOrientedSet and ScNumberedSetfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScAddrfromsc_kpm.sc_setsimportScSetfromsc_kpm.utilsimportcreate_node,create_nodes# Example elements to addexample_set_node:ScAddr=create_node(sc_types.NODE_CONST)elements:list[ScAddr]=create_nodes(*[sc_types.NODE_CONST]*5)# Init sc-set and add elementsempty_set=ScSet()set_with_elements=ScSet(elements[0],elements[1])set_with_elements.add(elements[2],elements[3])set_with_elements.add(elements[4])set_with_elements_and_set_node=ScSet(elements[2],set_node=example_set_node)empty_set_with_specific_set_node_type=ScSet(set_node_type=sc_types.NODE_VAR)# Get set nodeset_node=empty_set.set_nodeassertset_with_elements_and_set_node.set_node==example_set_node# Get elements: list and setassertset_with_elements.elements_set==set(elements)# set view faster and safe# Iterate by elementsforelementinset_with_elements:print(element)# Length, bool, is_empty, inassertlen(set_with_elements)==len(elements)assertbool(set_with_elements)assertnotset_with_elements.is_empty()assertempty_set.is_empty()assertelements[2]inset_with_elements# Clear and removeset_with_elements.remove(elements[4])assertlen(set_with_elements),len(elements)-1set_with_elements.clear()assertset_with_elements.is_empty()ScStructureIfset_nodehas typesc_types.NODE_CONST_STRUCTconstruction is called sc-structure
and looks like a loop in SCg:sc_kpm.ScStructureClass for handling structure construction in the kb.
The same logic as inScSet, butset_node_typeif set NODE_CONST_STRUCT.
There are checks that set node has struct sc-type:fromsc_kpm.sc_setsimportScStructuresc_struct=ScStructure(...,set_node=...,set_node_type=...)sc_struct=ScStructure(...,set_node=create_node(sc_types.NODE_CONST))# InvalidTypeError - not struct typesc_struct=ScStructure(...,set_node_type=sc_types.NODE_CONST)# InvalidTypeError - not struct typeOrdered sc-setsScOrientedSet and ScNumberedSet are ordered sc-constructions.
Child classes of ScSet, have the order in iteration and get list elements:Sc{ordered}Set.elements_set-> List[ScAddr]Get the list of all elements with orderScOrientedSetsc_kpm.ScOrientedSetClass for handling sc-oriented-set construction.
Has marked edges between edges from set_node to elements.
Easy lazy iterating.
No access by index.fromsc_client.constantsimportsc_typesfromsc_kpm.sc_setsimportScOrientedSetfromsc_kpm.utilsimportcreate_nodeselements=create_nodes(*[sc_types.NODE_CONST]*5)numbered_set=ScOrientedSet(*elements)assertnumbered_set.elements_list==elementsScNumberedSetsc_kpm.ScNumberedSetClass for handling sc-numbered-set construction.
Set-node is edged with numerating each element with rrel node.
Easy access to elements by index (index i is marked with rrel(i + 1))fromsc_client.constantsimportsc_typesfromsc_kpm.sc_setsimportScNumberedSetfromsc_kpm.utilsimportcreate_nodeselements=create_nodes(*[sc_types.NODE_CONST]*5)numbered_set=ScNumberedSet(*elements)assertnumbered_set.elements_list==elementsassertnumbered_set[2]==elements[2]numbered_set[5]# raise KeyErrorUtilsThere are some functions for working with nodes, edges, links: create them, search, get content, delete, etc.
There is also possibility to wrap in set or oriented set.Common utilsThere are utils to work with basic elementsYou can import these utils fromsc_kpm.utilsNodes creatingIf you want to create one or more nodes use these functions with type setting argument:defcreate_node(node_type:ScType,sys_idtf:str=None)->ScAddr:...defcreate_nodes(*node_types:ScType)->List[ScAddr]:...sys_idtfis optional name of keynode if you want to add it there.fromsc_client.constantsimportsc_typesfromsc_kpmimportScKeynodesfromsc_kpm.utils.common_utilsimportcreate_node,create_nodeslang=create_node(sc_types.NODE_CONST_CLASS)# ScAddr(...)lang_en=create_node(sc_types.NODE_CONST_CLASS)# ScAddr(...)assertlang.is_valid()andlang_en.is_valid()elements=create_nodes(sc_types.NODE_CONST,sc_types.NODE_VAR)# [ScAddr(...), ScAddr(...)]assertlen(elements)==2assertall(element.is_valid()forelementinelements)Edges creatingFor creating edge betweensrcandtrgwith setting its type usecreate_edgefunction:defcreate_edge(edge_type:ScType,src:ScAddr,trg:ScAddr)->ScAddr:...defcreate_edges(edge_type:ScType,src:ScAddr,*targets:ScAddr)->List[ScAddr]:...fromsc_client.constantsimportsc_typesfromsc_kpm.utilsimportcreate_nodesfromsc_kpm.utilsimportcreate_edge,create_edgessrc,trg,trg2,trg3=create_nodes(*[sc_types.NODE_CONST]*4)edge=create_edge(sc_types.EDGE_ACCESS_CONST_POS_PERM,src,trg)# ScAddr(...)edges=create_edges(sc_types.EDGE_ACCESS_CONST_POS_PERM,src,trg2,trg3)# [ScAddr(...), ScAddr(...)]assertedge.is_valid()assertall(edges)Functionis_valid()is used for validation addresses of nodes or edges.Links creatingFor creating links with string type content (by default) use these functions:defcreate_link(content:Union[str,int],content_type:ScLinkContentType=ScLinkContentType.STRING,link_type:ScType=sc_types.LINK_CONST)->ScAddr:...defcreate_links(*contents:Union[str,int],content_type:ScLinkContentType=ScLinkContentType.STRING,link_type:ScType=sc_types.LINK_CONST,)->List[ScAddr]:...You may useScLinkContentType.STRINGandScLinkContentType.INTtypes for content of created links.fromsc_client.constantsimportsc_typesfromsc_client.modelsimportScLinkContentTypefromsc_kpm.utilsimportcreate_link,create_linksmsg=create_link("hello")# ScAddr(...)four=create_link(4,ScLinkContentType.INT)# ScAddr(...)water=create_link("water",link_type=sc_types.LINK_VAR)# ScAddr(...)names=create_links("Sam","Pit")# [ScAddr(...), ScAddr(...)]Relations creatingCreate different binary relations with these functions:defcreate_binary_relation(edge_type:ScType,src:ScAddr,trg:ScAddr,*relations:ScAddr)->ScAddr:...defcreate_role_relation(src:ScAddr,trg:ScAddr,*rrel_nodes:ScAddr)->ScAddr:...defcreate_norole_relation(src:ScAddr,trg:ScAddr,*nrel_nodes:ScAddr)->ScAddr:...fromsc_client.constantsimportsc_typesfromsc_kpmimportScKeynodesfromsc_kpm.utilsimportcreate_node,create_nodesfromsc_kpm.utilsimportcreate_binary_relation,create_role_relation,create_norole_relationsrc,trg=create_nodes(*[sc_types.NODE_CONST]*2)increase_relation=create_node(sc_types.NODE_CONST_CLASS)brel=create_binary_relation(sc_types.EDGE_ACCESS_CONST_POS_PERM,src,trg,increase_relation)# ScAddr(...)rrel=create_role_relation(src,trg,ScKeynodes.rrel_index(1))# ScAddr(...)nrel=create_norole_relation(src,trg,create_node(sc_types.NODE_CONST_NOROLE))# ScAddr(...)Deleting utilsIf you want to remove all edges between two nodes, which define by their type usedefdelete_edges(source:ScAddr,target:ScAddr,*edge_types:ScType)->bool:...It returnTrueif operations was successful andFalseotherwise.fromsc_client.constantsimportsc_typesfromsc_kpm.utilsimportcreate_nodes,create_edgefromsc_kpm.utilsimportdelete_edgessrc,trg=create_nodes(*[sc_types.NODE_CONST]*2)edge=create_edge(sc_types.EDGE_ACCESS_CONST_POS_PERM,src,trg)delete_edges(src,trg,sc_types.EDGE_ACCESS_CONST_POS_PERM)# TrueGetting edgesFor getting edge or edges between two nodes use:defget_edge(source:ScAddr,target:ScAddr,edge_type:ScType)->ScAddr:...defget_edges(source:ScAddr,target:ScAddr,*edge_types:ScType)->List[ScAddr]:...NOTE: Use VAR type instead of CONST in getting utilsfromsc_client.constantsimportsc_typesfromsc_kpm.utilsimportcreate_nodes,create_edgefromsc_kpm.utilsimportget_edge,get_edgessrc,trg=create_nodes(*[sc_types.NODE_CONST]*2)edge1=create_edge(sc_types.EDGE_ACCESS_CONST_POS_PERM,src,trg)edge2=create_edge(sc_types.EDGE_D_COMMON_CONST,src,trg)class_edge=get_edge(src,trg,sc_types.EDGE_ACCESS_VAR_POS_PERM)# ScAddr(...)assertclass_edge==edge1edges=get_edges(src,trg,sc_types.EDGE_ACCESS_VAR_POS_PERM,sc_types.EDGE_D_COMMON_VAR)# [ScAddr(...), ScAddr(...)]assertedges==[edge1,edge2]Getting elements by relationGet target element by source element and relation:defget_element_by_role_relation(src:ScAddr,rrel_node:ScAddr)->ScAddr:...defget_element_by_norole_relation(src:ScAddr,nrel_node:ScAddr)->ScAddr:...fromsc_client.constantsimportsc_typesfromsc_kpmimportScKeynodesfromsc_kpm.identifiersimportCommonIdentifiersfromsc_kpm.utilsimportcreate_nodes,create_role_relation,create_norole_relationfromsc_kpm.utilsimportget_element_by_role_relation,get_element_by_norole_relationsrc,trg_rrel,trg_nrel=create_nodes(*[sc_types.NODE_CONST]*3)rrel=create_role_relation(src,trg_rrel,ScKeynodes.rrel_index(1))# ScAddr(...)nrel=create_norole_relation(src,trg_nrel,ScKeynodes[CommonIdentifiers.NREL_SYSTEM_IDENTIFIER])# ScAddr(...)result_rrel=get_element_by_role_relation(src,ScKeynodes.rrel_index(1))# ScAddr(...)assertresult_rrel==trg_rrelresult_nrel=get_element_by_norole_relation(src,ScKeynodes[CommonIdentifiers.NREL_SYSTEM_IDENTIFIER])# ScAddr(...)assertresult_nrel==trg_nrelGetting link contentFor existed links you may get their content by address with this function:defget_link_content(link:ScAddr)->Union[str,int]:...fromsc_kpm.utilsimportcreate_linkfromsc_kpm.utilsimportget_link_content_datawater=create_link("water")content=get_link_content_data(water)# "water"Getting system identifierFor getting system identifier of keynode use:defget_system_idtf(addr:ScAddr)->str:...fromsc_client.constantsimportsc_typesfromsc_kpmimportScKeynodesfromsc_kpm.utilsimportcreate_nodefromsc_kpm.utilsimportget_system_idtflang_en=create_node(sc_types.NODE_CONST_CLASS)# ScAddr(...)idtf=get_system_idtf(lang_en)# "lang_en"assertScKeynodes[idtf]==lang_enAction utilsUtils to work with actions, events and agentsCheck action classdefcheck_action_class(action_class:Union[ScAddr,Idtf],action_node:ScAddr)->bool:...This function returnsTrueif action class has connection to action node.
You can use identifier of action class instead of ScAddr.
This function should not be used in the ScAgentClassic.fromsc_client.constantsimportsc_typesfromsc_kpmimportScKeynodesfromsc_kpm.identifiersimportCommonIdentifiersfromsc_kpm.utilsimportcreate_node,create_edgefromsc_kpm.utils.action_utilsimportcheck_action_classaction_node=create_node(sc_types.NODE_CONST)create_edge(sc_types.EDGE_ACCESS_CONST_POS_PERM,ScKeynodes[CommonIdentifiers.QUESTION],action_node)action_class=create_node(sc_types.NODE_CONST_CLASS)create_edge(sc_types.EDGE_ACCESS_CONST_POS_PERM,action_class,action_node)assertcheck_action_class(action_class,action_node)# orassertcheck_action_class("some_classification",action_node)Get action argumentsFor getting list of action arguments concatenated byrrel_[1 -> count]use:defget_action_arguments(action_class:Union[ScAddr,Idtf],count:int)->List[ScAddr]:...fromsc_client.constantsimportsc_typesfromsc_kpmimportScKeynodesfromsc_kpm.identifiersimportCommonIdentifiersfromsc_kpm.utilsimportcreate_node,create_edge,create_role_relationfromsc_kpm.utils.action_utilsimportget_action_argumentsaction_node=create_node(sc_types.NODE_CONST)# Static argumentargument1=create_node(sc_types.NODE_CONST)create_role_relation(action_node,argument1,ScKeynodes.rrel_index(1))# Dynamic argumentdynamic_node=create_node(sc_types.NODE_CONST)rrel_dynamic_arg=ScKeynodes[CommonIdentifiers.RREL_DYNAMIC_ARGUMENT]create_role_relation(action_node,dynamic_node,rrel_dynamic_arg,ScKeynodes.rrel_index(2))argument2=create_node(sc_types.NODE_CONST)create_edge(sc_types.EDGE_ACCESS_CONST_POS_TEMP,dynamic_node,argument2)arguments=get_action_arguments(action_node,2)assertarguments==[argument1,dynamic_node]Create and get action answerdefcreate_action_answer(action_node:ScAddr,*elements:ScAddr)->None:...defget_action_answer(action_node:ScAddr)->ScAddr:...Create and get structure with output of actionfromsc_client.constantsimportsc_typesfromsc_kpm.utilsimportcreate_nodefromsc_kpm.utils.action_utilsimportcreate_action_answer,get_action_answerfromsc_kpm.sc_setsimportScStructureaction_node=create_node(sc_types.NODE_CONST_STRUCT)answer_element=create_node(sc_types.NODE_CONST_STRUCT)create_action_answer(action_node,answer_element)result=get_action_answer(action_node)result_elements=ScStructure(result).elements_setassertresult_elements=={answer_element}Call, execute and wait agentAgent call function: createsaction nodewith some arguments, concepts and connects it to the node with initiation identifier.
Returnsquestion nodedefcall_agent(arguments:Dict[ScAddr,IsDynamic],concepts:List[Idtf],initiation:Idtf=QuestionStatus.QUESTION_INITIATED,)->ScAddr:...Agent wait function: Waits for creation of edge to reaction node for some seconds.
Default reaction_node isquestion_finished.defwait_agent(seconds:float,question_node:ScAddr,reaction_node:ScAddr=None)->None:...Agent execute function: combines two previous functions -- calls, waits and returns question node andTrueif successdefexecute_agent(arguments:Dict[ScAddr,IsDynamic],concepts:List[Idtf],initiation:Idtf=QuestionStatus.QUESTION_INITIATED,reaction:Idtf=QuestionStatus.QUESTION_FINISHED_SUCCESSFULLY,wait_time:int=COMMON_WAIT_TIME,# 5)->Tuple[ScAddr,bool]:...fromsc_client.modelsimportScLinkContentTypefromsc_kpmimportScKeynodesfromsc_kpm.identifiersimportCommonIdentifiers,QuestionStatusfromsc_kpm.utilsimportcreate_linkfromsc_kpm.utils.action_utilsimportexecute_agent,call_agent,wait_agentarg1=create_link(2,ScLinkContentType.INT)arg2=create_link(3,ScLinkContentType.INT)kwargs=dict(arguments={arg1:False,arg2:False},concepts=[CommonIdentifiers.QUESTION,"some_class_name"],)question=call_agent(**kwargs)# ScAddr(...)wait_agent(3,question,ScKeynodes[QuestionStatus.QUESTION_FINISHED])# orquestion,is_successfully=execute_agent(**kwargs,wait_time=3)# ScAddr(...), boolCreate, call and execute actionFunctions that allow to create action and add arguments and call later.Function that creates action with concepts and return its ScAddr.defcreate_action(*concepts:Idtf)->ScAddr:...Function that creates arguments of actiondefadd_action_arguments(action_node:ScAddr,arguments:Dict[ScAddr,IsDynamic])->None:...Now you can call or execute action.
Action call functions don't return action_node because it's parameter to them.defcall_action(action_node:ScAddr,initiation:Idtf=QuestionStatus.QUESTION_INITIATED)->None:...defexecute_action(action_node:ScAddr,initiation:Idtf=QuestionStatus.QUESTION_INITIATED,reaction:Idtf=QuestionStatus.QUESTION_FINISHED_SUCCESSFULLY,wait_time:float=COMMON_WAIT_TIME,)->bool:...Example:fromsc_client.modelsimportScLinkContentTypefromsc_kpmimportScKeynodesfromsc_kpm.identifiersimportCommonIdentifiers,QuestionStatusfromsc_kpm.utilsimportcreate_linkfromsc_kpm.utils.action_utilsimportadd_action_arguments,call_action,create_action,execute_action,wait_agentarg1=create_link(2,ScLinkContentType.INT)arg2=create_link(3,ScLinkContentType.INT)action_node=create_action(CommonIdentifiers.QUESTION,"some_class_name")# ScAddr(...)# Do something herearguments={arg1:False,arg2:False}add_action_arguments(action_node,arguments)call_action(action_node)wait_agent(3,action_node,ScKeynodes[QuestionStatus.QUESTION_FINISHED])# oris_successful=execute_action(action_node,wait_time=3)# boolFinish actionFunctionfinish_actionconnects status class to action node:deffinish_action(action_node:ScAddr,status:Idtf=QuestionStatus.QUESTION_FINISHED)->ScAddr:...Functionfinish_action_with_statusconnectsquestion_finishedandquestion_finished_(un)successfullystatuses to it:deffinish_action_with_status(action_node:ScAddr,is_success:bool=True)->None:...fromsc_client.constantsimportsc_typesfromsc_kpmimportScKeynodesfromsc_kpm.identifiersimportQuestionStatusfromsc_kpm.utilsimportcreate_node,check_edgefromsc_kpm.utils.action_utilsimportfinish_action,finish_action_with_statusaction_node=create_node(sc_types.NODE_CONST)finish_action_with_status(action_node)# orfinish_action_with_status(action_node,True)# orfinish_action(action_node,QuestionStatus.QUESTION_FINISHED)finish_action(action_node,QuestionStatus.QUESTION_FINISHED_SUCCESSFULLY)question_finished=ScKeynodes[QuestionStatus.QUESTION_FINISHED]question_finished_successfully=ScKeynodes[QuestionStatus.QUESTION_FINISHED_SUCCESSFULLY]assertcheck_edge(sc_types.EDGE_ACCESS_VAR_POS_PERM,question_finished,action_node)assertcheck_edge(sc_types.EDGE_ACCESS_VAR_POS_PERM,question_finished_successfully,action_node)Use-casesScript for creating and registration agent until user press ^C:based on ScAgentClassicScripts for creating agent to calculate sum of two arguments and confirm result:based on ScAgentbased on ScAgentClassicLogging examples:pretty project logging