def __init_state_machine(self): ''' Build the state machine ''' states = [ { 'name': 'transition_main' }, { 'name': 'transition_diverging' }, { 'name': 'diverging' }, { 'name': 'main' }, ] transitions = [ { 'trigger': '_set_main', 'source': 'diverging', 'dest': 'transition_main' }, { 'trigger': '_set_diverging', 'source': 'main', 'dest': 'transition_diverging' }, { 'trigger': '_motion_complete_main', 'source': 'transition_main', 'dest': 'main' }, { 'trigger': '_motion_complete_diverging', 'source': 'transition_diverging', 'dest': 'diverging' }, ] Machine.__init__(self, states=states, transitions=transitions, initial='transition_main', auto_transitions=False, after_state_change='_update_state', ignore_invalid_triggers=True)
def __init__(self): """ Initializes the Model and registers the default state machine transitions available to all models.ctor """ self.state = None self._endpoint_service = None states = [ State(n, on_enter='new_state_entered') for n in _transform_states ] self._machine = Machine(model=self, states=states, initial='started', auto_transitions=False) self._machine.add_transition(trigger='fail', source=list(_transform_states - {'terminated'}), dest='failed') self._machine.add_transition(trigger='ready', source=['started', 'transforming'], dest='ready') self._machine.add_transition(trigger='transform', source=['started', 'ready'], dest='transforming', after='_do_transform') self._machine.add_transition( trigger='terminate', source=list(_transform_states - {'terminating', 'terminated', 'failed'}), dest='terminating', after='_do_terminate') self._machine.add_transition(trigger='terminated', source='terminating', dest='terminated')
def __init__(self): running_states = [ ADCState.RUNNING, ADCState.SUSPENDED_ALLOW_NEW, ADCState.SUSPENDED_NO_NEW ] self.machine = Machine(name='ADC', model=self, states=ADC_STATES, initial=ADCState.UNKNOWN) self.machine.add_transition('init_starting', [ADCState.UNKNOWN], ADCState.STARTING) self.machine.add_transition('done_starting', [ADCState.STARTING], ADCState.RUNNING) self.machine.add_transition( 'shutdown', [ADCState.STARTING, ADCState.STOPPED] + running_states, ADCState.STOPPED) self.machine.add_transition('request_running', running_states, ADCState.RUNNING) self.machine.add_transition('request_suspended_allow_new', running_states, ADCState.SUSPENDED_ALLOW_NEW) self.machine.add_transition('request_suspended_no_new', running_states, ADCState.SUSPENDED_NO_NEW)
def __init__(self, name, APIKey, Secret, currency_pair): self.name = name self.polo = poloniex(APIKey,Secret) self.currency_pair = currency_pair self.machine = Machine(model=self, states=MM.states, initial='INIT', send_event=True, ignore_invalid_triggers=True) #parameters self.risk = float(0) self.bid_delta = 0.0000001 # the amount to repost order when you are too high or low self.bid_increments = 0.00000001 # the amount to outbid the highest bid by self.amount = 0.001 #setup values self.bid = float(0) self.ask = float(0) self.bid_waiting_at = float(0) self.bid_order = None self.bid_position = float(0) self.ask_waiting_at = float(0) self.ask_order = None self.ask_position = float(0) #self.result = {} #main trading loop self.machine.add_transition(trigger='connect', source='INIT', dest='BID_SEARCH') # bid self.machine.add_transition(trigger='tick', source='BID_SEARCH', dest='BID_SENDING',prepare='set_tick',conditions='found_bid') self.machine.on_enter_BID_SENDING('retry_place_bid') self.machine.add_transition(trigger='try_place_bid', source='BID_SENDING', dest='BID_WAIT_CONFIRM', conditions='bid_placed') self.machine.on_enter_BID_WAIT_CONFIRM('retry_bid_confirm') self.machine.add_transition(trigger='try_confirm_bid', source='BID_WAIT_CONFIRM', dest='BID_PLACED',conditions='inBidOrder') self.machine.on_enter_BID_PLACED('retry_bid_executed') self.machine.add_transition(trigger='try_bid_executed', source='BID_PLACED', dest='ASK_SEARCH', conditions='bid_executed') # ask self.machine.add_transition(trigger='tick', source='ASK_SEARCH', dest='ASK_SENDING', prepare='set_tick',conditions='found_ask') self.machine.on_enter_ASK_SENDING('retry_place_ask') self.machine.add_transition(trigger='place_ask', source='ASK_SENDING', dest='ASK_WAIT_CONFIRM', conditions='ask_placed') self.machine.on_enter_ASK_WAIT_CONFIRM('retry_ask_placed') self.machine.add_transition(trigger='ask_placed', source='ASK_WAIT_CONFIRM', dest='ASK_PLACED',conditions='isInAskOrderbook') self.machine.on_enter_ASK_PLACED('retry_ask_executed') self.machine.add_transition(trigger='ask_executed', source='ASK_PLACED', dest='BID_SEARCH', conditions='ask_executed') #reposition bids self.machine.add_transition(trigger='tick', source='BID_PLACED', dest='BID_CANCELLING', prepare='set_tick', conditions='bid_out_of_range') self.machine.on_enter_BID_CANCELLING('retry_cancel_bid') self.machine.add_transition(trigger='post_cancel_bid', source='BID_CANCELLING', dest='BID_SEARCH', conditions='cancel_bid') self.machine.add_transition(trigger='tick_abort', source='ASK_SEARCH', dest='BID_LIQUIDATING') self.machine.add_transition(trigger='order_executed', source='BID_LIQUIDATING', dest='BID_SEARCH') self.machine.add_transition(trigger='tick_abort', source='ASK_PLACED', dest='ASK_CANCELLING') self.machine.add_transition(trigger='order_executed', source='ASK_CANCELLING', dest='ASK_SEARCH')
def __init__(self): logging.debug('Initializing System...') self.player = player_module self.player.play_media() logging.debug('VLC player initialized..Setting up machine..') self.machine = Machine(model=self, states=MusicMachine.states, initial=states.MOBILE_DISCONNECTED) self.machine.add_transition(trigger=triggers.MOBILE_CONNECTED, source=states.MOBILE_DISCONNECTED, dest=states.MOBILE_CONNECTED, before='trigger_device_connection') self.machine.add_transition( trigger=triggers.MOBILE_DISCONNECTED, source=[states.MOBILE_CONNECTED, states.PLAYING], dest=states.MOBILE_DISCONNECTED, before='trigger_device_disconnect') self.machine.add_transition(trigger=triggers.MOBILE_CONNECTED, source=states.MOBILE_CONNECTED, dest=states.PLAYING) self.machine.add_transition(trigger=triggers.MOBILE_CONNECTED, source=states.PLAYING, dest=states.PLAYING) self.machine.add_transition(trigger=triggers.MOBILE_DISCONNECTED, source=states.MOBILE_DISCONNECTED, dest=states.MOBILE_DISCONNECTED) self.machine.add_transition(trigger=triggers.MOBILE_DISCONNECTED, source=states.MANUALLY_PAUSED, dest=states.MANUALLY_PAUSED) self.machine.add_transition(trigger=triggers.MOBILE_DISCONNECTED, source=states.PLAYING, dest=states.MOBILE_DISCONNECTED, before='trigger_device_disconnect') self.machine.add_transition(trigger=triggers.MANUAL_PLAY, source=MusicMachine.states, dest=states.PLAYING, before='trigger_device_connection') self.machine.add_transition(trigger=triggers.MANUAL_PAUSE, source=MusicMachine.states, dest=states.MANUALLY_PAUSED, before='trigger_device_disconnect') logging.debug('Machine Setup Completed..')
def __init__(self, bot, user=None, chat=None, text_and_qa=None): self.machine = Machine(model=self, states=BotBrain.states, initial='init') # Start part self.machine.add_transition('start', 'init', 'started', after='after_start') self.machine.add_transition('start_convai', 'init', 'started', after='after_start') # Universal states self.machine.add_transition('return_to_start', '*', 'started', after='after_start') self.machine.add_transition('return_to_wait', '*', 'waiting', after='after_wait') self.machine.add_transition('return_to_init', '*', 'init', after='clear_all') # Classify user utterance part self.machine.add_transition('classify', '*', 'classifying', after='get_class_of_user_message') # Too long wait part self.machine.add_transition('user_off', 'waiting', 'init', after='propose_conversation_ending') self._bot = bot self._user = user self._chat = chat self._text_and_qa = text_and_qa self._too_long_waiting_cntr = 0 self._last_user_message = None self._threads = [] self.reinit_text_based_skills_and_data(text_and_qa) self._init_stateless_skills() self._dialog_context = []
def __init__(self): self.machine = Machine(name='AXMon', model=self, states=AXMON_STATES, initial=AXMonState.UNKNOWN) self.machine.add_transition('detected_running', [ AXMonState.UNKNOWN, AXMonState.STOPPED, AXMonState.STARTING, AXMonState.RUNNING ], AXMonState.RUNNING) self.machine.add_transition('detected_upgrade', AXMonState.UNKNOWN, AXMonState.STARTING) self.machine.add_transition('detected_stopped', AXMonState.UNKNOWN, AXMonState.STOPPED) self.machine.add_transition('cluster_start_begin', [AXMonState.STOPPED, AXMonState.RUNNING], AXMonState.STARTING) self.machine.add_transition( 'cluster_start_end', # TODO: RUNNING is here because axmon state can transition to RUNNING # from both cluster_start and the @state property. If cluster_start is # broken into separate platform and devops pieces, RUNNING can/should # be removed from the source state. [AXMonState.STARTING, AXMonState.RUNNING], AXMonState.RUNNING) self.machine.add_transition('cluster_start_end', [AXMonState.RUNNING_SCALING], AXMonState.RUNNING_SCALING) self.machine.add_transition('cluster_stop_begin', '*', AXMonState.STOPPING) self.machine.add_transition('cluster_stop_end', AXMonState.STOPPING, AXMonState.STOPPED) self.machine.add_transition( 'cluster_upgrade_begin', [AXMonState.RUNNING, AXMonState.STOPPED, AXMonState.STARTING], AXMonState.STOPPING) # NOTE: There is no need for cluster_upgrade_end since axmon will be killed by cluster-upgrade, at which # point, the state of AXMon will be determined on boot up of the next axmon self.machine.add_transition('cluster_scaling_start', AXMonState.RUNNING, AXMonState.RUNNING_SCALING) self.machine.add_transition('cluster_scaling_end', AXMonState.RUNNING_SCALING, AXMonState.RUNNING)
def __init__(self, device_group): self.device_group = device_group self.machine = Machine(self, states=TestFSM.states, initial='r') self.machine.add_transition( trigger='switch', source='r', dest='g' ) self.machine.add_transition( trigger='switch', source='g', dest='b' ) self.machine.add_transition( trigger='switch', source='b', dest='r' )
class TestFSM(object): states = [ State('r', on_enter='set_red'), State('g', on_enter='set_green'), State('b', on_enter='set_blue') ] def __init__(self, device_group): self.device_group = device_group self.machine = Machine(self, states=TestFSM.states, initial='r') self.machine.add_transition( trigger='switch', source='r', dest='g' ) self.machine.add_transition( trigger='switch', source='g', dest='b' ) self.machine.add_transition( trigger='switch', source='b', dest='r' ) def set_red(self): self.device_group.send_message(4, 0, 3, b'\xff\x00\x00') def set_green(self): self.device_group.send_message(4, 0, 3, b'\x00\xff\x00') def set_blue(self): self.device_group.send_message(4, 0, 3, b'\x00\x00\xff') def handle_message(self, msg): if msg[0] == 4 and msg[2] == 4 and msg[3] == '\x01': self.switch()
def __init__(self, states, transitions): self.name = 'brain' self.states += states self.machine = Machine(model=self, states=ArtBrainMachine.states, initial='pre_init', auto_transitions=False, send_event=True, queued=True) # *** transitions *** self.machine.add_transition('init', 'pre_init', 'init') self.machine.add_transition('init_done', 'init', 'waiting_for_action', conditions='is_everything_calibrated') self.machine.add_transition('program_start', 'waiting_for_action', 'program_init') self.machine.add_transition('learning_start', 'waiting_for_action', 'learning_init') self.machine.add_transition('visualize_start', 'waiting_for_action', 'visualize_run') # program self.machine.add_transition('program_init_done', 'program_init', 'program_run') self.machine.add_transition('error', 'program_init', 'program_error') self.machine.add_transition('error', 'program_run', 'program_error') self.machine.add_transition('program_error_handled', 'program_error', 'program_load_instruction') self.machine.add_transition('program_error_shutdown', 'program_error', 'shutdown') self.machine.add_transition('program_error_fatal', 'program_error', 'program_finished') self.machine.add_transition('try_again', 'program_error', 'program_run') self.machine.add_transition('skip', 'program_error', 'program_load_instruction') self.machine.add_transition('fail', 'program_error', 'program_load_instruction') self.machine.add_transition('done', 'program_load_instruction', 'program_run') self.machine.add_transition('error', 'program_load_instruction', 'program_error') self.machine.add_transition('finished', 'program_load_instruction', 'program_finished') self.machine.add_transition('done', 'program_finished', 'waiting_for_action') self.machine.add_transition('finished', 'program_run', 'program_finished') self.machine.add_transition('pause', 'program_load_instruction', 'program_paused') self.machine.add_transition('resume', 'program_paused', 'program_run') # learning self.machine.add_transition('init_done', 'learning_init', 'learning_run') self.machine.add_transition('done', 'learning_step_done', 'learning_run') self.machine.add_transition('error_handled', 'learning_step_error', 'learning_run') self.machine.add_transition('error_fatal', 'learning_step_error', 'waiting_for_action') self.machine.add_transition('done', 'learning_done', 'waiting_for_action') self.machine.add_transition('learning_done', 'learning_run', 'learning_done') self.machine.add_transition('visualize_done', 'visualize_run', 'visualize_done') self.machine.add_transition('done', 'visualize_done', 'waiting_for_action') for transition in transitions: self.machine.add_transition(transition[0], transition[1], transition[2])
class ArtBrainMachine(object): states = [ State(name='pre_init', on_enter=[], on_exit=[]), State(name='init', on_enter=['state_init_ros'], on_exit=[]), State(name='shutdown', on_enter=['state_shutdown'], on_exit=[]), State(name='waiting_for_action', on_enter=['state_waiting_for_action'], on_exit=[]), State(name='program_init', on_enter=['state_program_init'], on_exit=[]), State(name='program_run', on_enter=['state_program_run'], on_exit=[]), State(name='program_paused', on_enter=['state_program_paused'], on_exit=[]), # basic instructions State(name='get_ready', on_enter=['state_get_ready'], on_exit=[]), # synchronization with the user State(name='wait_for_user', on_enter=['state_wait_for_user'], on_exit=[]), State(name='wait_until_user_finishes', on_enter=['state_wait_until_user_finishes'], on_exit=[]), # manipulation - pick State(name='pick_from_polygon', on_enter=['state_pick_from_polygon'], on_exit=[]), State(name='pick_from_feeder', on_enter=['state_pick_from_feeder'], on_exit=[]), State(name='pick_object_id', on_enter=['state_pick_object_id'], on_exit=[]), # manipulation - place State(name='place_to_pose', on_enter=['state_place_to_pose'], on_exit=[]), # manipulation State(name='path_through_points', on_enter=['state_path_through_points'], on_exit=[]), State(name='welding_points', on_enter=['state_welding_points'], on_exit=[]), State(name='welding_seam', on_enter=['state_welding_seam'], on_exit=[]), State(name='drill_points', on_enter=['state_drill_points'], on_exit=[]), State(name='program_error', on_enter=['state_program_error'], on_exit=[]), State(name='program_finished', on_enter=['state_program_finished'], on_exit=[]), State(name='program_load_instruction', on_enter=['state_program_load_instruction'], on_exit=[]), # learning State(name='learning_init', on_enter=['state_learning_init'], on_exit=[]), State(name='learning_run', on_enter=['state_learning_run'], on_exit=[]), # learning picking State(name='learning_pick_from_polygon', on_enter=['state_learning_pick_from_polygon'], on_exit=[]), State(name='learning_pick_from_feeder', on_enter=['state_learning_pick_from_feeder'], on_exit=['state_learning_pick_from_feeder_exit']), State(name='learning_pick_object_id', on_enter=['state_learning_pick_object_id'], on_exit=[]), State(name='learning_pick_from_polygon_run', on_enter=['state_learning_pick_from_polygon_run'], on_exit=[]), State(name='learning_pick_from_feeder_run', on_enter=['state_learning_pick_from_feeder_run'], on_exit=[]), State(name='learning_pick_object_id_run', on_enter=['state_learning_pick_object_id_run'], on_exit=[]), # learning placing State(name='learning_place_to_pose', on_enter=['state_learning_place_to_pose'], on_exit=[]), State(name='learning_place_to_pose_run', on_enter=['state_learning_place_to_pose_run'], on_exit=[]), State(name='learning_wait', on_enter=['state_learning_wait'], on_exit=[]), State(name='learning_step_done', on_enter=['state_learning_step_done'], on_exit=[]), State(name='learning_step_error', on_enter=['state_learning_step_error'], on_exit=[]), State(name='learning_done', on_enter=['state_learning_done'], on_exit=[]) ] def __init__(self): self.name = 'brain' self.machine = Machine(model=self, states=ArtBrainMachine.states, initial='pre_init', auto_transitions=False, send_event=True, queued=True) # *** transitions *** self.machine.add_transition('init', 'pre_init', 'init') self.machine.add_transition('init_done', 'init', 'waiting_for_action', conditions='is_everything_calibrated') self.machine.add_transition('program_start', 'waiting_for_action', 'program_init') self.machine.add_transition('learning_start', 'waiting_for_action', 'learning_init') # program self.machine.add_transition('program_init_done', 'program_init', 'program_run') self.machine.add_transition('error', 'program_init', 'program_error') self.machine.add_transition('error', 'program_run', 'program_error') self.machine.add_transition('program_error_handled', 'program_error', 'program_load_instruction') self.machine.add_transition('program_error_shutdown', 'program_error', 'shutdown') self.machine.add_transition('program_error_fatal', 'program_error', 'program_finished') self.machine.add_transition('try_again', 'program_error', 'program_run') self.machine.add_transition('skip', 'program_error', 'program_load_instruction') self.machine.add_transition('fail', 'program_error', 'program_load_instruction') self.machine.add_transition('done', 'program_load_instruction', 'program_run') self.machine.add_transition('error', 'program_load_instruction', 'program_error') self.machine.add_transition('finished', 'program_load_instruction', 'program_finished') self.machine.add_transition('done', 'program_finished', 'waiting_for_action') self.machine.add_transition('finished', 'program_run', 'program_finished') self.machine.add_transition('pause', 'program_load_instruction', 'program_paused') self.machine.add_transition('resume', 'program_paused', 'program_run') # get ready instruction self.machine.add_transition('get_ready', 'program_run', 'get_ready') self.machine.add_transition('done', 'get_ready', 'program_load_instruction') self.machine.add_transition('error', 'get_ready', 'program_error') # pick_from_polygon instruction self.machine.add_transition('pick_from_polygon', 'program_run', 'pick_from_polygon') self.machine.add_transition('done', 'pick_from_polygon', 'program_load_instruction') self.machine.add_transition('error', 'pick_from_polygon', 'program_error') # pick_from_feeder instruction self.machine.add_transition('pick_from_feeder', 'program_run', 'pick_from_feeder') self.machine.add_transition('done', 'pick_from_feeder', 'program_load_instruction') self.machine.add_transition('error', 'pick_from_feeder', 'program_error') # pick_object_id instruction self.machine.add_transition('pick_object_id', 'program_run', 'pick_object_id') self.machine.add_transition('done', 'pick_object_id', 'program_load_instruction') self.machine.add_transition('error', 'pick_object_id', 'program_error') # place_to_pose instruction self.machine.add_transition('place_to_pose', 'program_run', 'place_to_pose') self.machine.add_transition('done', 'place_to_pose', 'program_load_instruction') self.machine.add_transition('error', 'place_to_pose', 'program_error') # path through poses instruction self.machine.add_transition('path_through_points', 'program_run', 'path_through_points') self.machine.add_transition('done', 'path_through_points', 'program_load_instruction') self.machine.add_transition('error', 'path_through_points', 'program_error') # path through poses instruction self.machine.add_transition('welding_points', 'program_run', 'welding_points') self.machine.add_transition('done', 'welding_points', 'program_load_instruction') self.machine.add_transition('error', 'welding_points', 'program_error') # path through poses instruction self.machine.add_transition('welding_seam', 'program_run', 'welding_seam') self.machine.add_transition('done', 'welding_seam', 'program_load_instruction') self.machine.add_transition('error', 'welding_seam', 'program_error') # path through poses instruction self.machine.add_transition('drill_points', 'program_run', 'drill_points') self.machine.add_transition('done', 'drill_points', 'program_load_instruction') self.machine.add_transition('error', 'drill_points', 'program_error') # wait instruction self.machine.add_transition('wait_for_user', 'program_run', 'wait_for_user') self.machine.add_transition('done', 'wait_for_user', 'program_load_instruction') self.machine.add_transition('error', 'wait_for_user', 'program_error') # wait instruction self.machine.add_transition('wait_until_user_finishes', 'program_run', 'wait_until_user_finishes') self.machine.add_transition('done', 'wait_until_user_finishes', 'program_load_instruction') self.machine.add_transition('error', 'wait_until_user_finishes', 'program_error') # # learning # self.machine.add_transition('init_done', 'learning_init', 'learning_run') self.machine.add_transition('done', 'learning_step_done', 'learning_run') self.machine.add_transition('error_handled', 'learning_step_error', 'learning_run') self.machine.add_transition('error_fatal', 'learning_step_error', 'waiting_for_action') self.machine.add_transition('done', 'learning_done', 'waiting_for_action') self.machine.add_transition('learning_done', 'learning_run', 'learning_done') # learning pick_from_polygon self.machine.add_transition('pick_from_polygon', 'learning_run', 'learning_pick_from_polygon') self.machine.add_transition('done', 'learning_pick_from_polygon', 'learning_step_done') self.machine.add_transition('error', 'learning_pick_from_polygon', 'learning_step_error') self.machine.add_transition('pick_from_polygon_run', 'learning_run', 'learning_pick_from_polygon_run') self.machine.add_transition('done', 'learning_pick_from_polygon_run', 'learning_run') self.machine.add_transition('error', 'learning_pick_from_polygon_run', 'learning_step_error') # learning pick_from_feeder self.machine.add_transition('pick_from_feeder', 'learning_run', 'learning_pick_from_feeder') self.machine.add_transition('done', 'learning_pick_from_feeder', 'learning_step_done') self.machine.add_transition('error', 'learning_pick_from_feeder', 'learning_step_error') self.machine.add_transition('pick_from_feeder_run', 'learning_run', 'learning_pick_from_feeder_run') self.machine.add_transition('done', 'learning_pick_from_feeder_run', 'learning_run') self.machine.add_transition('error', 'learning_pick_from_feeder_run', 'learning_step_error') # learning pick_object_id self.machine.add_transition('pick_object_id', 'learning_run', 'learning_pick_object_id') self.machine.add_transition('done', 'learning_pick_object_id', 'learning_step_done') self.machine.add_transition('error', 'learning_pick_object_id', 'learning_step_error') self.machine.add_transition('pick_object_id_run', 'learning_run', 'learning_pick_object_id_run') self.machine.add_transition('done', 'learning_pick_object_id_run', 'learning_run') self.machine.add_transition('error', 'learning_pick_object_id', 'learning_step_error') # learning place_to_pose self.machine.add_transition('place_to_pose', 'learning_run', 'learning_place_to_pose') self.machine.add_transition('done', 'learning_place_to_pose', 'learning_step_done') self.machine.add_transition('error', 'learning_place_to_pose', 'learning_step_error') self.machine.add_transition('place_to_pose_run', 'learning_run', 'learning_place_to_pose_run') self.machine.add_transition('done', 'learning_place_to_pose_run', 'learning_run') self.machine.add_transition('error', 'learning_place_to_pose_run', 'learning_step_error') # learning wait self.machine.add_transition('wait', 'learning_run', 'learning_wait') self.machine.add_transition('done', 'learning_wait', 'learning_step_done') self.machine.add_transition('error', 'learning_wait', 'learning_step_error')
class BotBrain: """Main class that controls dialog flow""" states = ['init', 'started', 'waiting', 'classifying'] wait_messages = [ "What do you feel about the text?", "Do you like this text?", "Do you know familiar texts?", "Can you write similar text?", "Do you like to chat with me?", "Are you a scientist?", "What do you think about ConvAI competition?", "Do you like to be an assessor?", "What is your job?" ] # TODO: move to config file? CHITCHAT_URL = 'tcp://opennmtchitchat:5556' FB_CHITCHAT_URL = 'tcp://opennmtfbpost:5556' SUMMARIZER_URL = 'tcp://opennmtsummary:5556' BIGARTM_URL = 'http://bigartm:3000' ALICE_URL = 'http://alice:3000' INTENT_URL = 'http://intent_classifier:3000/get_intent' SPELLCHECKER_URL = 'http://spellchecker:3050/respond' CLASSIFY_ANSWER = 'ca' CLASSIFY_QUESTION = 'cq' CLASSIFY_REPLICA = 'cr' CLASSIFY_FB = 'cf' CLASSIFY_ASK_QUESTION = 'caq' CLASSIFY_ALICE = "calice" CLASSIFY_SUMMARY = "csummary" CLASSIFY_TOPIC = "ctopic" CLASSIFY_BYE = 'cbye' # TODO: move to config file? MESSAGE_CLASSIFIER_MODEL = "model_all_labels.ftz" ASK_QUESTION_ON_WAIT_PROB = 0.5 MAX_WAIT_TURNS = 4 def __init__(self, bot, user=None, chat=None, text_and_qa=None): self.machine = Machine(model=self, states=BotBrain.states, initial='init') # Start part self.machine.add_transition('start', 'init', 'started', after='after_start') self.machine.add_transition('start_convai', 'init', 'started', after='after_start') # Universal states self.machine.add_transition('return_to_start', '*', 'started', after='after_start') self.machine.add_transition('return_to_wait', '*', 'waiting', after='after_wait') self.machine.add_transition('return_to_init', '*', 'init', after='clear_all') # Classify user utterance part self.machine.add_transition('classify', '*', 'classifying', after='get_class_of_user_message') # Too long wait part self.machine.add_transition('user_off', 'waiting', 'init', after='propose_conversation_ending') self._bot = bot self._user = user self._chat = chat self._text_and_qa = text_and_qa self._too_long_waiting_cntr = 0 self._last_user_message = None self._threads = [] self.reinit_text_based_skills_and_data(text_and_qa) self._init_stateless_skills() self._dialog_context = [] def _init_stateless_skills(self): self._opensub_chitchat_skill = chitchat.OpenSubtitlesChitChatSkill( BotBrain.CHITCHAT_URL) self._fb_chitchat_skill = chitchat.FbChitChatSkill( BotBrain.FB_CHITCHAT_URL) self._alice_chitchat_skill = chitchat.AliceChitChatSkill( BotBrain.ALICE_URL) self._bye_skill = bye.ByeSkill() def _skill_exec_wrap(self, skill, *args): result = skill.predict(*args) if result: self._send_message(self._filter_seq2seq_output(result)) else: result = self._alice_chitchat_skill.predict( self._last_user_message, self._dialog_context) if result: self._send_message(self._filter_seq2seq_output(result)) else: self._send_message(random.sample(BotBrain.wait_messages, 1)[0]) self.return_to_wait() def reinit_text_based_skills_and_data(self, text_and_qa): self._text_and_qa = text_and_qa self._text = self._text_and_qa['text'] qa_skill = qa.QuestionAskingAndAnswerCheckingSkill( self._text_and_qa['qas'], self._user) self._question_ask_skill = qa.QuestionAskingSkill(qa_skill) self._answer_check_skill = qa.AnswerCheckingSkill(qa_skill) self._question_answerer_skill = qa.QuestionAnsweringSkill(self._text) self._summarization_skill = summary.SummarizationSkill( self.SUMMARIZER_URL, self._text) self._topic_skill = topic.TopicDetectionSkill(self.BIGARTM_URL, self._text) def after_start(self): self._cancel_timer_threads(presereve_cntr=False) def _say_about_topic_if_user_inactive(): if self.is_started(): self._skill_exec_wrap(self._topic_skill) def _ask_question_if_user_inactive(): if self.is_started(): self._skill_exec_wrap(self._question_ask_skill) if random.random() > 0.5: t = threading.Timer(config.WAIT_TIME, _ask_question_if_user_inactive) else: t = threading.Timer(config.WAIT_TIME, _say_about_topic_if_user_inactive) t.start() self._threads.append(t) def set_user_message(self, text): spellchecked = check_spelling(text) self._last_user_message = spellchecked # Debug and human evaluation function (/evaluation_start mode in Telegram) # Should be moved somewhere else def generate_suggestions(self): def process_tsv(tsv): payload = [] for line in tsv.split('\n'): _, resp, score = line.split('\t') score = float(score) payload.append((resp, score)) payload = sorted(payload, key=lambda x: x[1], reverse=True)[:3] return payload answer = self._answer_check_skill.get_answer() question = self._question_ask_skill.get_question() class_to_string = { BotBrain.CLASSIFY_ASK_QUESTION: 'Factoid question', BotBrain.CLASSIFY_ANSWER: 'Answer to Factoid question', BotBrain.CLASSIFY_QUESTION: 'Factoid question from user', BotBrain.CLASSIFY_FB: 'Facebook seq2seq', BotBrain.CLASSIFY_REPLICA: 'OpenSubtitles seq2seq', BotBrain.CLASSIFY_ALICE: 'Alice', BotBrain.CLASSIFY_SUMMARY: 'Summary' } raw_fb_response = self._fb_chitchat_skill._get_opennmt_fb_reply( self._last_user_message, self._dialog_context, self._text, False) raw_opensub_response = self._opensub_chitchat_skill._get_opennmt_chitchat_reply( self._last_user_message, self._dialog_context, False) fb_replicas = process_tsv(raw_fb_response) opensubtitle_replicas = process_tsv(raw_opensub_response) alice_replicas = [ self._alice_chitchat_skill.predict(self._last_user_message, self._dialog_context) ] summaries = self._summarization_skill._get_summaries(False) result = [(class_to_string[BotBrain.CLASSIFY_ASK_QUESTION], [question ]), (class_to_string[BotBrain.CLASSIFY_ANSWER], [answer]), (class_to_string[BotBrain.CLASSIFY_QUESTION], [None]), (class_to_string[BotBrain.CLASSIFY_FB], fb_replicas), (class_to_string[BotBrain.CLASSIFY_REPLICA], opensubtitle_replicas), (class_to_string[BotBrain.CLASSIFY_ALICE], alice_replicas), (class_to_string[BotBrain.CLASSIFY_SUMMARY], [summaries]), ('Topic Modelling', [self._topic_skill.predict()])] return result def after_wait(self): self._cancel_timer_threads(presereve_cntr=True) def _too_long_waiting_if_user_inactive(): if self.is_waiting( ) and self._too_long_waiting_cntr < BotBrain.MAX_WAIT_TURNS: if random.random() > BotBrain.ASK_QUESTION_ON_WAIT_PROB: self._skill_exec_wrap(self._question_ask_skill) else: self._send_message( random.sample(BotBrain.wait_messages, 1)[0]) self.return_to_wait() elif self.is_waiting( ) and self._too_long_waiting_cntr > BotBrain.MAX_WAIT_TURNS: self.user_off() self._too_long_waiting_cntr = 0 else: self._too_long_waiting_cntr = 0 self._too_long_waiting_cntr += 1 t = threading.Timer(config.WAIT_TOO_LONG, _too_long_waiting_if_user_inactive) t.start() self._threads.append(t) def propose_conversation_ending(self): self._cancel_timer_threads() self._send_message(("Seems you went to the real life." "Type /start to replay.")) def clear_all(self): self._cancel_timer_threads() def get_class_of_user_message(self): self._cancel_timer_threads() message_class = self._classify(self._last_user_message) self._last_classify_label = message_class self._classify_user_utterance(message_class) def _classify(self, text): text = normalize(text) cmd = "echo \"{}\" | /fasttext/fasttext predict /src/data/{} -".format( text, BotBrain.MESSAGE_CLASSIFIER_MODEL) ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = ps.communicate()[0] res = str(output, "utf-8").strip() logger.info(res) intent = self._get_intent(text) if intent is not None: return intent if res == '__label__0': return BotBrain.CLASSIFY_REPLICA elif res == '__label__1': return BotBrain.CLASSIFY_QUESTION elif res == '__label__2': return BotBrain.CLASSIFY_FB elif res == '__label__3': return BotBrain.CLASSIFY_ANSWER elif res == '__label__4': return BotBrain.CLASSIFY_ALICE def _classify_user_utterance(self, clf_type): self._cancel_timer_threads() if clf_type == BotBrain.CLASSIFY_ANSWER: self._skill_exec_wrap(self._answer_check_skill, self._last_user_message) elif clf_type == BotBrain.CLASSIFY_QUESTION: self._skill_exec_wrap(self._question_answerer_skill, self._last_user_message) elif clf_type == BotBrain.CLASSIFY_REPLICA: self._skill_exec_wrap(self._opensub_chitchat_skill, self._last_user_message, self._dialog_context) elif clf_type == BotBrain.CLASSIFY_FB: self._skill_exec_wrap(self._fb_chitchat_skill, self._last_user_message, self._dialog_context, self._text) elif clf_type == BotBrain.CLASSIFY_ASK_QUESTION: self._skill_exec_wrap(self._question_ask_skill) elif clf_type == BotBrain.CLASSIFY_ALICE: self._skill_exec_wrap(self._alice_chitchat_skill, self._last_user_message, self._dialog_context) elif clf_type == BotBrain.CLASSIFY_SUMMARY: self._skill_exec_wrap(self._summarization_skill) elif clf_type == BotBrain.CLASSIFY_TOPIC: self._skill_exec_wrap(self._topic_skill) elif clf_type == BotBrain.CLASSIFY_BYE: self._skill_exec_wrap(self._bye_skill, self._last_user_message) def _get_intent(self, text): r = requests.post(self.INTENT_URL, json={'text': text}) intent = r.json()['intent'] score = r.json()['score'] if score and score > 0.9: return intent return None def _send_message(self, text, reply_markup=None): text = text.strip() logger_bot.info("BOT[_send_message]: {}".format(text)) self._bot.send_message(chat_id=self._chat.id, text=text, reply_markup=reply_markup) if self._last_user_message is None: self._last_user_message = "" text = text.replace('"', " ").replace("`", " ").replace("'", " ") self._dialog_context.append((self._last_user_message, text)) def _cancel_timer_threads(self, presereve_cntr=False): if not presereve_cntr: self._too_long_waiting_cntr = 0 [t.cancel() for t in self._threads] def _filter_seq2seq_output(self, s): s = normalize(str(s)) s = detokenize(s) return s
def __init_state_machine(self, randomize): ''' ''' if randomize: self.discovery_timeout = random.uniform( (self.discovery_timeout * (1 - NetworkManager.DISCOVERY_TIMEOUT_RAND_FACTOR)), (self.discovery_timeout * (1 + NetworkManager.DISCOVERY_TIMEOUT_RAND_FACTOR))) self.__STATES = [ { 'name': 'initialized' }, { 'name': 'searching', 'timeout': self.discovery_timeout, 'on_timeout': '_start_server', 'on_enter': '_start_client' }, { 'name': 'connected' }, { 'name': 'disconnecting', 'on_enter': '_stop' }, { 'name': 'stopping', 'on_enter': '_stop' }, ] self.__TRANSITIONS = [{ 'trigger': 'start', 'source': 'initialized', 'dest': 'searching' }, { 'trigger': '_connected', 'source': 'searching', 'dest': 'connected' }, { 'trigger': '_disconnected', 'source': 'connected', 'dest': 'disconnecting' }, { 'trigger': 'stop', 'source': ['searching', 'connected'], 'dest': 'stopping' }, { 'trigger': '_stopped', 'source': 'stopping', 'dest': 'initialized' }, { 'trigger': '_stopped', 'source': 'disconnecting', 'dest': 'searching' }] Machine.__init__(self, states=self.__STATES, transitions=self.__TRANSITIONS, initial='initialized', auto_transitions=False, after_state_change='_update_connection_state', ignore_invalid_triggers=True)
def __init__(self): self.name = 'brain' self.machine = Machine(model=self, states=ArtBrainMachine.states, initial='pre_init', auto_transitions=False, send_event=True, queued=True) # *** transitions *** self.machine.add_transition('init', 'pre_init', 'init') self.machine.add_transition('init_done', 'init', 'waiting_for_action', conditions='is_everything_calibrated') self.machine.add_transition('program_start', 'waiting_for_action', 'program_init') self.machine.add_transition('learning_start', 'waiting_for_action', 'learning_init') # program self.machine.add_transition('program_init_done', 'program_init', 'program_run') self.machine.add_transition('error', 'program_init', 'program_error') self.machine.add_transition('error', 'program_run', 'program_error') self.machine.add_transition('program_error_handled', 'program_error', 'program_load_instruction') self.machine.add_transition('program_error_shutdown', 'program_error', 'shutdown') self.machine.add_transition('program_error_fatal', 'program_error', 'program_finished') self.machine.add_transition('try_again', 'program_error', 'program_run') self.machine.add_transition('skip', 'program_error', 'program_load_instruction') self.machine.add_transition('fail', 'program_error', 'program_load_instruction') self.machine.add_transition('done', 'program_load_instruction', 'program_run') self.machine.add_transition('error', 'program_load_instruction', 'program_error') self.machine.add_transition('finished', 'program_load_instruction', 'program_finished') self.machine.add_transition('done', 'program_finished', 'waiting_for_action') self.machine.add_transition('finished', 'program_run', 'program_finished') self.machine.add_transition('pause', 'program_load_instruction', 'program_paused') self.machine.add_transition('resume', 'program_paused', 'program_run') # get ready instruction self.machine.add_transition('get_ready', 'program_run', 'get_ready') self.machine.add_transition('done', 'get_ready', 'program_load_instruction') self.machine.add_transition('error', 'get_ready', 'program_error') # pick_from_polygon instruction self.machine.add_transition('pick_from_polygon', 'program_run', 'pick_from_polygon') self.machine.add_transition('done', 'pick_from_polygon', 'program_load_instruction') self.machine.add_transition('error', 'pick_from_polygon', 'program_error') # pick_from_feeder instruction self.machine.add_transition('pick_from_feeder', 'program_run', 'pick_from_feeder') self.machine.add_transition('done', 'pick_from_feeder', 'program_load_instruction') self.machine.add_transition('error', 'pick_from_feeder', 'program_error') # pick_object_id instruction self.machine.add_transition('pick_object_id', 'program_run', 'pick_object_id') self.machine.add_transition('done', 'pick_object_id', 'program_load_instruction') self.machine.add_transition('error', 'pick_object_id', 'program_error') # place_to_pose instruction self.machine.add_transition('place_to_pose', 'program_run', 'place_to_pose') self.machine.add_transition('done', 'place_to_pose', 'program_load_instruction') self.machine.add_transition('error', 'place_to_pose', 'program_error') # path through poses instruction self.machine.add_transition('path_through_points', 'program_run', 'path_through_points') self.machine.add_transition('done', 'path_through_points', 'program_load_instruction') self.machine.add_transition('error', 'path_through_points', 'program_error') # path through poses instruction self.machine.add_transition('welding_points', 'program_run', 'welding_points') self.machine.add_transition('done', 'welding_points', 'program_load_instruction') self.machine.add_transition('error', 'welding_points', 'program_error') # path through poses instruction self.machine.add_transition('welding_seam', 'program_run', 'welding_seam') self.machine.add_transition('done', 'welding_seam', 'program_load_instruction') self.machine.add_transition('error', 'welding_seam', 'program_error') # path through poses instruction self.machine.add_transition('drill_points', 'program_run', 'drill_points') self.machine.add_transition('done', 'drill_points', 'program_load_instruction') self.machine.add_transition('error', 'drill_points', 'program_error') # wait instruction self.machine.add_transition('wait_for_user', 'program_run', 'wait_for_user') self.machine.add_transition('done', 'wait_for_user', 'program_load_instruction') self.machine.add_transition('error', 'wait_for_user', 'program_error') # wait instruction self.machine.add_transition('wait_until_user_finishes', 'program_run', 'wait_until_user_finishes') self.machine.add_transition('done', 'wait_until_user_finishes', 'program_load_instruction') self.machine.add_transition('error', 'wait_until_user_finishes', 'program_error') # # learning # self.machine.add_transition('init_done', 'learning_init', 'learning_run') self.machine.add_transition('done', 'learning_step_done', 'learning_run') self.machine.add_transition('error_handled', 'learning_step_error', 'learning_run') self.machine.add_transition('error_fatal', 'learning_step_error', 'waiting_for_action') self.machine.add_transition('done', 'learning_done', 'waiting_for_action') self.machine.add_transition('learning_done', 'learning_run', 'learning_done') # learning pick_from_polygon self.machine.add_transition('pick_from_polygon', 'learning_run', 'learning_pick_from_polygon') self.machine.add_transition('done', 'learning_pick_from_polygon', 'learning_step_done') self.machine.add_transition('error', 'learning_pick_from_polygon', 'learning_step_error') self.machine.add_transition('pick_from_polygon_run', 'learning_run', 'learning_pick_from_polygon_run') self.machine.add_transition('done', 'learning_pick_from_polygon_run', 'learning_run') self.machine.add_transition('error', 'learning_pick_from_polygon_run', 'learning_step_error') # learning pick_from_feeder self.machine.add_transition('pick_from_feeder', 'learning_run', 'learning_pick_from_feeder') self.machine.add_transition('done', 'learning_pick_from_feeder', 'learning_step_done') self.machine.add_transition('error', 'learning_pick_from_feeder', 'learning_step_error') self.machine.add_transition('pick_from_feeder_run', 'learning_run', 'learning_pick_from_feeder_run') self.machine.add_transition('done', 'learning_pick_from_feeder_run', 'learning_run') self.machine.add_transition('error', 'learning_pick_from_feeder_run', 'learning_step_error') # learning pick_object_id self.machine.add_transition('pick_object_id', 'learning_run', 'learning_pick_object_id') self.machine.add_transition('done', 'learning_pick_object_id', 'learning_step_done') self.machine.add_transition('error', 'learning_pick_object_id', 'learning_step_error') self.machine.add_transition('pick_object_id_run', 'learning_run', 'learning_pick_object_id_run') self.machine.add_transition('done', 'learning_pick_object_id_run', 'learning_run') self.machine.add_transition('error', 'learning_pick_object_id', 'learning_step_error') # learning place_to_pose self.machine.add_transition('place_to_pose', 'learning_run', 'learning_place_to_pose') self.machine.add_transition('done', 'learning_place_to_pose', 'learning_step_done') self.machine.add_transition('error', 'learning_place_to_pose', 'learning_step_error') self.machine.add_transition('place_to_pose_run', 'learning_run', 'learning_place_to_pose_run') self.machine.add_transition('done', 'learning_place_to_pose_run', 'learning_run') self.machine.add_transition('error', 'learning_place_to_pose_run', 'learning_step_error') # learning wait self.machine.add_transition('wait', 'learning_run', 'learning_wait') self.machine.add_transition('done', 'learning_wait', 'learning_step_done') self.machine.add_transition('error', 'learning_wait', 'learning_step_error')
def __init__(self, name, APIKey, Secret, currency_pair): self.name = name self.polo = poloniex(APIKey, Secret) self.currency_pair = currency_pair self.machine = Machine(model=self, states=MM.states, initial='INIT', send_event=True, ignore_invalid_triggers=True) #parameters self.risk = float(0) self.bid_delta = 0.0000001 # the amount to repost order when you are too high or low self.bid_increments = 0.00000001 # the amount to outbid the highest bid by self.amount = 0.001 #setup values self.bid = float(0) self.ask = float(0) self.bid_waiting_at = float(0) self.bid_order = None self.bid_position = float(0) self.ask_waiting_at = float(0) self.ask_order = None self.ask_position = float(0) #self.result = {} #main trading loop self.machine.add_transition(trigger='connect', source='INIT', dest='BID_SEARCH') # bid self.machine.add_transition(trigger='tick', source='BID_SEARCH', dest='BID_SENDING', prepare='set_tick', conditions='found_bid') self.machine.on_enter_BID_SENDING('retry_place_bid') self.machine.add_transition(trigger='try_place_bid', source='BID_SENDING', dest='BID_WAIT_CONFIRM', conditions='bid_placed') self.machine.on_enter_BID_WAIT_CONFIRM('retry_bid_confirm') self.machine.add_transition(trigger='try_confirm_bid', source='BID_WAIT_CONFIRM', dest='BID_PLACED', conditions='inBidOrder') self.machine.on_enter_BID_PLACED('retry_bid_executed') self.machine.add_transition(trigger='try_bid_executed', source='BID_PLACED', dest='ASK_SEARCH', conditions='bid_executed') # ask self.machine.add_transition(trigger='tick', source='ASK_SEARCH', dest='ASK_SENDING', prepare='set_tick', conditions='found_ask') self.machine.on_enter_ASK_SENDING('retry_place_ask') self.machine.add_transition(trigger='place_ask', source='ASK_SENDING', dest='ASK_WAIT_CONFIRM', conditions='ask_placed') self.machine.on_enter_ASK_WAIT_CONFIRM('retry_ask_placed') self.machine.add_transition(trigger='ask_placed', source='ASK_WAIT_CONFIRM', dest='ASK_PLACED', conditions='isInAskOrderbook') self.machine.on_enter_ASK_PLACED('retry_ask_executed') self.machine.add_transition(trigger='ask_executed', source='ASK_PLACED', dest='BID_SEARCH', conditions='ask_executed') #reposition bids self.machine.add_transition(trigger='tick', source='BID_PLACED', dest='BID_CANCELLING', prepare='set_tick', conditions='bid_out_of_range') self.machine.on_enter_BID_CANCELLING('retry_cancel_bid') self.machine.add_transition(trigger='post_cancel_bid', source='BID_CANCELLING', dest='BID_SEARCH', conditions='cancel_bid') self.machine.add_transition(trigger='tick_abort', source='ASK_SEARCH', dest='BID_LIQUIDATING') self.machine.add_transition(trigger='order_executed', source='BID_LIQUIDATING', dest='BID_SEARCH') self.machine.add_transition(trigger='tick_abort', source='ASK_PLACED', dest='ASK_CANCELLING') self.machine.add_transition(trigger='order_executed', source='ASK_CANCELLING', dest='ASK_SEARCH')
class MM(object): states = [ 'INIT', 'BID_SEARCH', 'BID_SENDING', 'BID_WAIT_CONFIRM', 'BID_PLACED', 'BID_CANCELLING', 'BID_LIQUIDATING', 'ASK_SEARCH', 'ASK_SENDING', 'ASK_WAIT_CONFIRM', 'ASK_PLACED', 'ASK_CANCELLING' ] def __init__(self, name, APIKey, Secret, currency_pair): self.name = name self.polo = poloniex(APIKey, Secret) self.currency_pair = currency_pair self.machine = Machine(model=self, states=MM.states, initial='INIT', send_event=True, ignore_invalid_triggers=True) #parameters self.risk = float(0) self.bid_delta = 0.0000001 # the amount to repost order when you are too high or low self.bid_increments = 0.00000001 # the amount to outbid the highest bid by self.amount = 0.001 #setup values self.bid = float(0) self.ask = float(0) self.bid_waiting_at = float(0) self.bid_order = None self.bid_position = float(0) self.ask_waiting_at = float(0) self.ask_order = None self.ask_position = float(0) #self.result = {} #main trading loop self.machine.add_transition(trigger='connect', source='INIT', dest='BID_SEARCH') # bid self.machine.add_transition(trigger='tick', source='BID_SEARCH', dest='BID_SENDING', prepare='set_tick', conditions='found_bid') self.machine.on_enter_BID_SENDING('retry_place_bid') self.machine.add_transition(trigger='try_place_bid', source='BID_SENDING', dest='BID_WAIT_CONFIRM', conditions='bid_placed') self.machine.on_enter_BID_WAIT_CONFIRM('retry_bid_confirm') self.machine.add_transition(trigger='try_confirm_bid', source='BID_WAIT_CONFIRM', dest='BID_PLACED', conditions='inBidOrder') self.machine.on_enter_BID_PLACED('retry_bid_executed') self.machine.add_transition(trigger='try_bid_executed', source='BID_PLACED', dest='ASK_SEARCH', conditions='bid_executed') # ask self.machine.add_transition(trigger='tick', source='ASK_SEARCH', dest='ASK_SENDING', prepare='set_tick', conditions='found_ask') self.machine.on_enter_ASK_SENDING('retry_place_ask') self.machine.add_transition(trigger='place_ask', source='ASK_SENDING', dest='ASK_WAIT_CONFIRM', conditions='ask_placed') self.machine.on_enter_ASK_WAIT_CONFIRM('retry_ask_placed') self.machine.add_transition(trigger='ask_placed', source='ASK_WAIT_CONFIRM', dest='ASK_PLACED', conditions='isInAskOrderbook') self.machine.on_enter_ASK_PLACED('retry_ask_executed') self.machine.add_transition(trigger='ask_executed', source='ASK_PLACED', dest='BID_SEARCH', conditions='ask_executed') #reposition bids self.machine.add_transition(trigger='tick', source='BID_PLACED', dest='BID_CANCELLING', prepare='set_tick', conditions='bid_out_of_range') self.machine.on_enter_BID_CANCELLING('retry_cancel_bid') self.machine.add_transition(trigger='post_cancel_bid', source='BID_CANCELLING', dest='BID_SEARCH', conditions='cancel_bid') self.machine.add_transition(trigger='tick_abort', source='ASK_SEARCH', dest='BID_LIQUIDATING') self.machine.add_transition(trigger='order_executed', source='BID_LIQUIDATING', dest='BID_SEARCH') self.machine.add_transition(trigger='tick_abort', source='ASK_PLACED', dest='ASK_CANCELLING') self.machine.add_transition(trigger='order_executed', source='ASK_CANCELLING', dest='ASK_SEARCH') # need to figure out cleanup mechanism on shutdown #self.machine.add_transition(trigger='shutdown', source='*', dest='EXIT') #self.machine.add_transition(trigger='shutdown', source='BID_PLACED', dest='BID_CANCELLING') #self.machine.on_enter_EXIT('cleanup') def set_risk(self, risk): self.risk = float(risk) def set_tick(self, event): self.bid = float(event.kwargs.get('bid')) self.ask = float(event.kwargs.get('ask')) def found_bid(self, event): self.bid_waiting_at = self.bid + self.bid_increments return True def retry_if_true(result): return result #decorator to run job on thread (till it exits, so make sure it does) def asDaemon(worker_function): def inner_function(self, *args): print("create Thread()") thread = Thread(target=worker_function, args=(self, args)) thread.daemon = True print("thread.starting()") thread.start() return inner_function @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_place_bid(self, event): print("try place bid") self.try_place_bid() return self.is_BID_SENDING() def bid_placed(self, event): self.bid_order = self.polo.buy(currencyPair=self.currency_pair, rate=self.bid_waiting_at, amount=self.amount) print('placing bid @ ' + str(self.bid_waiting_at) + ' orderid: ' + self.bid_order['orderNumber']) return self.bid_order != None @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_bid_confirm(self, event): print("try order") self.try_confirm_bid() return self.is_BID_WAIT_CONFIRM() def inBidOrder(self, event): print('searching for order') orderbook = self.polo.returnOpenOrders(self.currency_pair) for order in orderbook: if order['orderNumber'] == self.bid_order['orderNumber']: print('order found') return True return False @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_bid_executed(self, event): print("checking if bid is executed") self.try_bid_executed() return self.is_BID_PLACED() def bid_executed(self, event): tradehistory = self.polo.returnTradeHistory(self.currency_pair) for trade in tradehistory: if trade['orderNumber'] == self.bid_order['orderNumber']: self.bid_position = float(trade['rate']) return True return False @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_cancel_bid(self, event): print("cancelling bid") self.post_cancel_bid() return self.is_BID_CANCELLING() def bid_out_of_range(self, event): delta = abs(self.bid_waiting_at - self.bid) return (delta > self.bid_delta) def cancel_bid(self, event): if self.bid_order != None: print('canceling bid @ ' + str(self.bid_position) + ' as market bid is ' + str(self.bid) + ' orderid was ' + self.bid_order['orderNumber']) result = self.polo.cancel(self.currency_pair, self.bid_order['orderNumber']) return result['success'] == 1 return True def found_ask(self, event): self.ask_waiting_at = self.ask - self.bid_increments return True @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_place_ask(self, event): print("try place ask") self.place_ask() return self.is_ASK_SENDING() def place_ask(self, event): self.ask_order = self.polo.buy(currencyPair=self.currency_pair, rate=self.ask_waiting_at, amount=self.amount) print('placing ask @ ' + str(self.ask_waiting_at) + ' orderid: ' + self.ask_order['orderNumber']) return self.ask_order != None @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_ask_placed(self, event): print("try order") self.ask_placed() return self.is_ASK_WAIT_CONFIRM() def isInBidOrderbook(self, event): orderbook = self.polo.returnOpenOrders(self.currency_pair) for order in orderbook: if order['orderNumber'] == self.ask_order['orderNumber']: return True return False @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_ask_executed(self, event): print("checking if bid is executed") self.ask_executed() return self.is_ASK_PLACED() def ask_executed(self, event): tradehistory = self.polo.returnTradeHistory(self.currency_pair) for trade in tradehistory: if trade['orderNumber'] == self.ask_order['orderNumber']: self.ask_position = trade['rate'] return True return False def set_ask_position(self, event): self.ask_position = self.ask_waiting_at print('ask placed @ ' + str(self.ask_position)) def bid_risk_exceeded(self, event): #return (self.risk > self.bid_position - self.bid) return False
class BotBrain: states = [ 'init', 'started', 'asked', 'waiting', 'classifying', 'ending', 'checking_answer', 'correct_answer', 'incorrect_answer', 'bot_answering_question', 'bot_answering_replica', 'bot_correct_answer', 'bot_incorrect_answer' ] wait_messages = [ "What do you feel about the text?", "Do you like this text?", "Do you know familiar texts?", "Can you write similar text?", "Do you like to chat with me?", "Are you a scientist?", "What do you think about ConvAI competition?", "Do you like to be an assessor?", "What is your job?" ] CHITCHAT_URL = 'tcp://opennmtchitchat:5556' FB_CHITCHAT_URL = 'tcp://opennmtfbpost:5556' SUMMARIZER_URL = 'tcp://opennmtsummary:5556' BIGARTM_URL = 'http://bigartm:3000' CLASSIFY_ANSWER = 'ca' CLASSIFY_QUESTION = 'cq' CLASSIFY_REPLICA = 'cr' CLASSIFY_FB = 'cf' CLASSIFY_ASK_QUESTION = 'caq' CLASSIFY_ALICE = "calice" CLASSIFY_SUMMARY = "csummary" CLASSIFY_TOPIC = "ctopic" MESSAGE_CLASSIFIER_MODEL = "model_all_labels.ftz" def __init__(self, bot, user=None, chat=None, text_and_qa=None): self.machine = Machine(model=self, states=BotBrain.states, initial='init') self.machine.add_transition('start', 'init', 'started', after='wait_for_user_typing') self.machine.add_transition('start_convai', 'init', 'started', after='wait_for_user_typing_convai') self.machine.add_transition('ask_question', 'started', 'asked', after='ask_question_to_user') self.machine.add_transition('classify', 'started', 'classifying', after='get_class_of_user_message') self.machine.add_transition('classify', 'asked', 'classifying', after='get_class_of_user_message') self.machine.add_transition('classify', 'waiting', 'classifying', after='get_class_of_user_message') self.machine.add_transition('classify', 'classifying', 'classifying', after='get_class_of_user_message') self.machine.add_transition('classify', 'checking_answer', 'classifying', after='get_class_of_user_message') self.machine.add_transition('check_user_answer_on_asked', 'asked', 'checking_answer', after='checking_user_answer') self.machine.add_transition('check_user_answer', 'classifying', 'checking_answer', after='checking_user_answer') self.machine.add_transition('correct_user_answer', 'checking_answer', 'correct_answer') self.machine.add_transition('incorrect_user_answer', 'checking_answer', 'incorrect_answer') self.machine.add_transition('return_to_asked', 'incorrect_answer', 'asked') self.machine.add_transition('return_to_start', '*', 'started', after='wait_for_user_typing') self.machine.add_transition('return_to_wait', '*', 'waiting', after='say_user_about_long_waiting') self.machine.add_transition('return_to_init', '*', 'init', after='clear_all') self.machine.add_transition('answer_to_user_question', 'classifying', 'bot_answering_question', after='answer_to_user_question_') self.machine.add_transition('classify', 'bot_answering_question', 'classifying', after='get_class_of_user_message') self.machine.add_transition('answer_to_user_question_correct', 'bot_answering_question', 'bot_correct_answer') self.machine.add_transition('answer_to_user_question_incorrect', 'bot_answering_question', 'bot_incorrect_answer') self.machine.add_transition('answer_to_user_replica', 'classifying', 'bot_answering_replica', after='answer_to_user_replica_') self.machine.add_transition('answer_to_user_replica_with_fb', 'classifying', 'bot_answering_replica', after='answer_to_user_replica_with_fb_') self.machine.add_transition('answer_to_user_replica_with_alice', 'classifying', 'bot_answering_replica', after='answer_to_user_replica_with_alice_') self.machine.add_transition('answer_to_user_with_summary', 'classifying', 'bot_answering_replica', after='answer_to_user_with_summary_') self.machine.add_transition('answer_to_user_with_topic', 'classifying', 'bot_answering_replica', after='answer_to_user_with_topic_') self.machine.add_transition('long_wait', 'asked', 'waiting', after='say_user_about_long_waiting') self.machine.add_transition('too_long_wait', 'waiting', 'waiting', after='say_user_about_long_waiting') self.machine.add_transition('user_off', 'waiting', 'init', after='propose_conversation_ending') self.machine.add_transition('ask_question_after_waiting', 'waiting', 'asked', after='ask_question_to_user') self.machine.add_transition('ask_question_after_classifying', 'classifying', 'asked', after='ask_question_to_user') self._bot = bot self._user = user self._chat = chat self._text_and_qa = text_and_qa self._too_long_waiting_cntr = 0 self._last_user_message = None self._threads = [] self._init_factoid_qas_and_text() self._dialog_context = [] self._is_first_incorrect = True # to prevent recursion call self._is_chitchat_replica_is_answer = False self._setup_topics_info() def _init_factoid_qas_and_text(self): # list of all questions and answers self._factoid_qas = self._text_and_qa['qas'] self._text = self._text_and_qa['text'] self._question_asked = False # last asked factoid qas self._last_factoid_qas = None def _get_topics_response(self): return random.sample(self._best_additionals, k=1)[0] def _setup_topics_info(self): def _send_additional(): response = self._get_topics_response() print(["topic additinonal", response]) self._send_message(response) if self._text: r = requests.post(BotBrain.BIGARTM_URL + '/respond', json={'text': self._text}) self._topics_info = r.json()['result'] print("Topics result: {}".format(self._topics_info)) self._best_additionals = self._topics_info[0]['responses'] self._topic_thread = threading.Timer(1, _send_additional) self._topic_thread.start() def set_text_and_qa(self, text_and_qa): self._text_and_qa = text_and_qa self._init_factoid_qas_and_text() self._setup_topics_info() def wait_for_user_typing(self): self._cancel_timer_threads(presereve_cntr=False, reset_question=False, reset_seq2seq_context=False, reset_topic=False) def _ask_question_if_user_inactive(): if self.is_started(): self.ask_question() t = threading.Timer(config.WAIT_TIME, _ask_question_if_user_inactive) t.start() self._threads.append(t) def ask_question_to_user(self): self._cancel_timer_threads(reset_question=False, presereve_cntr=True) def _too_long_waiting_if_user_inactive(): if self.is_asked(): self.long_wait() if self._get_factoid_question() is not None: self._send_message( self._filter_seq2seq_output( self._last_factoid_qas['question'])) else: self._send_message(random.sample(BotBrain.wait_messages, 1)[0]) self.return_to_wait() t = threading.Timer(config.WAIT_TOO_LONG, _too_long_waiting_if_user_inactive) t.start() self._threads.append(t) def generate_suggestions(self): # Блин, нужен колоссальный рефакторинг, сделаем после 12 ноября # ЧТобы было так: for each skill: generate # # Waiting* - BotBrain.wait_messages, ask factoid question # ------------------------------------------------------------ # greet_user - NOT possible, clf type required (BotBrain.ClassifyGreeting) # # _get_factoid_question (CLASSIFY_ASK_QUESTION, waiting) # checking_user_answer (CLASSIFY_ANSWER, is_asked) # _get_answer_to_factoid_question (answer_to_user_question_) (CLASSIFY_QUESTION) # _get_opennmt_fb_reply (answer_to_user_replica_with_fb_) (CLASSIFY_FB) # _get_opennmt_chitchat_reply (answer_to_user_replica_) (CLASSIFY_REPLICA) # _select_from_common_responses (_get_best_response) (BAD! or NOT? CLASSIFY_FB AND CLASSIFY_REPLICA) # _classify_user_response_to_bot_answer (ANSWER_CORRECT, ANSWER_INCORRECT) # ------------------------------------------------------------ # При этом надо все таки знать какой ответ был бы при том или ином выборе! def process_tsv(tsv): payload = [] for line in tsv.split('\n'): _, resp, score = line.split('\t') score = float(score) payload.append((resp, score)) payload = sorted(payload, key=lambda x: x[1], reverse=True)[:3] return payload answer = None if self._last_factoid_qas and self._last_factoid_qas.get('answer'): answer = self._last_factoid_qas.get('answer') if self._factoid_qas: qa = self._factoid_qas[0] class_to_string = { BotBrain.CLASSIFY_ASK_QUESTION: 'Factoid question', BotBrain.CLASSIFY_ANSWER: 'Answer to Factoid question', BotBrain.CLASSIFY_QUESTION: 'Factoid question from user', BotBrain.CLASSIFY_FB: 'Facebook seq2seq', BotBrain.CLASSIFY_REPLICA: 'OpenSubtitles seq2seq', BotBrain.CLASSIFY_ALICE: 'Alice', BotBrain.CLASSIFY_SUMMARY: 'Summary' } fb_replicas = process_tsv( self._get_opennmt_fb_reply(with_heuristic=False)) opensubtitle_replicas = process_tsv( self._get_opennmt_chitchat_reply(with_heuristic=False)) alice_replicas = [self._get_alice_reply()] summaries = self._get_summaries() result = [(class_to_string[BotBrain.CLASSIFY_ASK_QUESTION], [qa]), (class_to_string[BotBrain.CLASSIFY_ANSWER], [answer]), (class_to_string[BotBrain.CLASSIFY_QUESTION], [None]), (class_to_string[BotBrain.CLASSIFY_FB], fb_replicas), (class_to_string[BotBrain.CLASSIFY_REPLICA], opensubtitle_replicas), (class_to_string[BotBrain.CLASSIFY_ALICE], alice_replicas), (class_to_string[BotBrain.CLASSIFY_SUMMARY], [summaries]), ('Common Responses', [self._select_from_common_responses()]), ('Topic Modelling', self._topics_info)] return result def _get_factoid_question(self): if len(self._factoid_qas) == 0: return None # takes one question from list and removes it self._question_asked = True self._last_factoid_qas = self._factoid_qas[0] self._factoid_qas = self._factoid_qas[1:] return self._question_asked def _get_alice_reply(self): alice_url = 'http://alice:3000' user_sentences = [e[0] for e in self._dialog_context] if self._dialog_context and self._dialog_context[-1][ 0] != self._last_user_message: user_sentences += [self._last_user_message] elif not self._dialog_context: user_sentences = [self._last_user_message] print("Alice input {}".format(user_sentences)) url = alice_url + '/respond' r = requests.post(url, json={'sentences': user_sentences}) print("Alice output: {}".format(r.json())) msg = self._filter_seq2seq_output(r.json()['message']) return msg def say_user_about_long_waiting(self): self._cancel_timer_threads(reset_question=False, presereve_cntr=True, reset_seq2seq_context=False) def _too_long_waiting_if_user_inactive(): if self.is_waiting() and self._too_long_waiting_cntr < 4: if random.random() > 0.5: self.ask_question_after_waiting() else: self._send_message( random.sample(BotBrain.wait_messages, 1)[0]) self.too_long_wait() elif self.is_waiting() and self._too_long_waiting_cntr > 3: self.user_off() self._too_long_waiting_cntr = 0 else: self._too_long_waiting_cntr = 0 self._too_long_waiting_cntr += 1 t = threading.Timer(config.WAIT_TOO_LONG, _too_long_waiting_if_user_inactive) t.start() self._threads.append(t) def wait_for_user_typing_convai(self): self._cancel_timer_threads(reset_question=False, reset_seq2seq_context=False, reset_topic=False) def _ask_question_if_user_inactive(): if self.is_started(): self.ask_question() t = threading.Timer(config.CONVAI_WAIT_QUESTION, _ask_question_if_user_inactive) t.start() self._threads.append(t) def propose_conversation_ending(self): self._cancel_timer_threads() self._send_message(("Seems you went to the real life." "Type /start to replay.")) def _classify(self, text): text = normalize(text) cmd = "echo \"{}\" | /fasttext/fasttext predict /src/data/{} -".format( text, BotBrain.MESSAGE_CLASSIFIER_MODEL) ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = ps.communicate()[0] res = str(output, "utf-8").strip() logger.info(res) # TODO: make more clever classification if ('ask me' in text or 'discuss with me' in text or 'talk with me' in text \ or 'ask question' in text or 'ask a question' in text or 'next question' in text) \ and ("n't" not in text and 'not' not in text): return BotBrain.CLASSIFY_ASK_QUESTION if ('text' in text or 'paragraph' in text or 'article' in text) and ('about' in text or 'summar' in text or 'short' in text) \ and ("n't" not in text and 'not' not in text): return BotBrain.CLASSIFY_SUMMARY intent = self._get_intent(text) if intent is not None: return intent logger.info('_classify: QUESTION ASKED: {}'.format( self._question_asked)) if self._question_asked and self._is_user_answer_correct() >= 80: return BotBrain.CLASSIFY_ANSWER if self.is_asked(): return BotBrain.CLASSIFY_ANSWER if res == '__label__0': return BotBrain.CLASSIFY_REPLICA elif res == '__label__1': return BotBrain.CLASSIFY_QUESTION elif res == '__label__2': return BotBrain.CLASSIFY_FB elif res == '__label__4' or res == '__label__3': # TMP hack, because in some cases classifier returns label3 here return BotBrain.CLASSIFY_ALICE def get_class_of_user_message(self): self._cancel_timer_threads(reset_question=False, reset_seq2seq_context=False) message_class = self._classify(self._last_user_message) self._last_classify_label = message_class self._classify_user_utterance(message_class) def _classify_user_utterance(self, clf_type): self._cancel_timer_threads(reset_question=False, reset_seq2seq_context=False) self._is_chitchat_replica_is_answer = False if clf_type == BotBrain.CLASSIFY_ANSWER and self._question_asked: self._is_chitchat_replica_is_answer = True self.check_user_answer() elif clf_type == BotBrain.CLASSIFY_ANSWER and not self._question_asked: self._send_message(( "I did not ask you a question. Then why do you think" " it has the answer type? My last sentence is a rhetorical question 😋" )) self.return_to_start() elif clf_type == BotBrain.CLASSIFY_QUESTION: self.answer_to_user_question() elif clf_type == BotBrain.CLASSIFY_REPLICA: self.answer_to_user_replica() elif clf_type == BotBrain.CLASSIFY_FB: self.answer_to_user_replica_with_fb() elif clf_type == BotBrain.CLASSIFY_ASK_QUESTION: self.ask_question_after_classifying() elif clf_type == BotBrain.CLASSIFY_ALICE: self.answer_to_user_replica_with_alice() elif clf_type == BotBrain.CLASSIFY_SUMMARY: self.answer_to_user_with_summary() elif clf_type == BotBrain.CLASSIFY_TOPIC: self.answer_to_user_with_topic() def _is_not_answer(self, reply): reply = normalize(reply) cmd = "echo \"{}\" | /fasttext/fasttext predict /src/data/{} -".format( reply, BotBrain.MESSAGE_CLASSIFIER_MODEL) ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = ps.communicate()[0] res = str(output, "utf-8").strip() logger.info("Answer classification result: {}; Input: {}".format( res, reply)) if res == '__label__3': return False else: return True def _is_user_answer_correct(self): true_answer = self._last_factoid_qas['answer'] # make user answer lowercased + remove ending chars true_answer_clean = true_answer.lower().rstrip(' .,;?!') user_answer_clean = self._last_user_message.lower().rstrip(' .,;?!') sim = fuzz.ratio(true_answer_clean, user_answer_clean) return sim def checking_user_answer(self): self._cancel_timer_threads(reset_question=False) tokens_count = len(word_tokenize(self._last_user_message)) logger.info( "#Checking_user_answer:_is_chitchat_replica_is_answer {}".format( self._is_chitchat_replica_is_answer)) if self._is_not_answer( self._last_user_message ) and tokens_count > 2 and not self._is_chitchat_replica_is_answer: self.classify() return true_answer = self._last_factoid_qas['answer'] sim = self._is_user_answer_correct() if sim == 100: msg = "👍" if random.random() > 0.6: msg1 = ['It is right', 'And its right answer', 'Right'] msg2 = ['!', ':)'] msg3 = ["You're smart.", ""] msg4 = [ "Ask me something or wait for my new question", "Ask me or wait my new question" ] msg5 = ["🌈", ":)", ""] total_msg = [msg1, msg2, msg3, msg4, msg5] msg = combinate_and_return_answer(total_msg) self._send_message(msg) self._question_asked = False self.correct_user_answer() self.return_to_start() elif sim >= 80: msg1 = [ "I think you mean: {}".format(true_answer), "Did you mean {}?".format(true_answer) ] msg2 = [ "My congratulations", "If you really mean what I think then my congratulations", "Good job" ] msg3 = ["!", "."] msg4 = [ "Ask me something or wait for my new question", "Ask me or wait my new question" ] msg5 = ["🌈", ":)", ""] total_msg = [msg1, msg2, msg3, msg4, msg5] msg = combinate_and_return_answer(total_msg) self._send_message(msg) self._question_asked = False self.correct_user_answer() self.return_to_start() else: self.incorrect_user_answer() if self._is_first_incorrect is True: msg1 = [ "You can do better", "Show me your best", "It is incorrect" ] msg2 = [".", "!", ":)", '¯\_(ツ)_/¯'] if len(true_answer) > 3: msg3 = [ "Hint: first 3 letters is {}.".format(true_answer[:3]) ] else: msg3 = [ "Hint: first 2 letters is {}.".format(true_answer[:2]) ] msg4 = ["Try again", "Try again, please"] msg5 = ["", "!", "."] total_msg = [msg1, msg2, msg3, msg4, msg5] msg = combinate_and_return_answer(total_msg) self._send_message(msg) self.return_to_asked() self._is_first_incorrect = False else: msg = "😕" if random.random() > 0.5: msg1 = ['Still incorrect', 'Incorrect', 'Maybe other time'] msg2 = ['.', ':('] total_msg = [msg1, msg2] msg = combinate_and_return_answer(total_msg) self._send_message(msg) msg3 = ['I think that'] msg4 = ['correct answer', 'true answer', 'answer'] msg5 = ['is: {}'.format(true_answer)] msg6 = [":)", "", "."] total_msg = [msg3, msg4, msg5, msg6] msg = combinate_and_return_answer(total_msg) self._send_message(msg) self._question_asked = False self.return_to_wait() self._is_first_incorrect = True def answer_to_user_question_(self): self._cancel_timer_threads() answer = self._filter_seq2seq_output( self._get_answer_to_factoid_question()) msg1 = ["I think that", "It seems that", "I'd like to say that"] msg2 = ["correct answer", "answer", "true answer"] msg3 = ["is: {}".format(detokenize(normalize(answer))).lower()] total_msg = [msg1, msg2, msg3] msg = combinate_and_return_answer(total_msg) self._send_message(msg) self.return_to_wait() def _get_answer_to_factoid_question(self): out = subprocess.check_output([ "python3", "from_factoid_question_answerer/get_answer.py", "--paragraph", self._text, "--question", self._last_user_message ]) return str(out, "utf-8").strip() def answer_to_user_replica_(self): self._cancel_timer_threads(reset_question=False, reset_seq2seq_context=False) bots_answer = self._get_opennmt_chitchat_reply() self._send_message(bots_answer) self.return_to_wait() def answer_to_user_replica_with_fb_(self): self._cancel_timer_threads(reset_question=False, reset_seq2seq_context=False) bots_answer = self._get_opennmt_fb_reply() self._send_message(bots_answer) self.return_to_wait() def answer_to_user_replica_with_alice_(self): self._cancel_timer_threads(reset_question=False, reset_seq2seq_context=False) bots_answer = self._get_alice_reply() self._send_message(bots_answer) self.return_to_wait() def answer_to_user_with_summary_(self): self._cancel_timer_threads(reset_question=False, reset_seq2seq_context=False) bots_answer = self._get_summaries() self._send_message(bots_answer) self.return_to_wait() def answer_to_user_with_topic_(self): self._cancel_timer_threads(reset_question=False, reset_seq2seq_context=False) bots_answer = self._get_topics_response() self._send_message(bots_answer) self.return_to_wait() def _get_last_bot_reply(self): if len(self._dialog_context): return self._dialog_context[-1][1] return "" def _get_opennmt_chitchat_reply(self, with_heuristic=True): # feed_context = "{} {}".format(self._get_last_bot_reply(), self._last_user_message) sentence = self._last_user_message sentence_with_context = None user_sent = None if len(self._dialog_context) > 0: sentence_with_context = " _EOS_ ".join( [self._dialog_context[-1][1], self._last_user_message]) user_sent = " ".join( [self._dialog_context[-1][0], self._last_user_message]) to_echo = sentence if sentence_with_context: to_echo = "{}\n{}".format(to_echo, sentence_with_context) if user_sent: to_echo = "{}\n{}".format(to_echo, user_sent) logger.info("Send to opennmt chitchat: {}".format(to_echo)) cmd = "echo \"{}\" | python from_opennmt_chitchat/get_reply.py {}".format( to_echo, BotBrain.CHITCHAT_URL) ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = ps.communicate()[0] res = str(output, "utf-8").strip() logger.info("Got from opennmt chitchat: {}".format(res)) if with_heuristic: return self._get_best_response(res) else: return res def _get_best_response_legacy(self, tsv): # legacy # score is perplexity: it can't describe quality of answer # TODO: maybe make like in summarization? filter stopwords answers and take random best_score = -100000 best_resp = "" for line in tsv.split('\n'): _, resp, score = line.split('\t') score = float(score) if score > best_score and not self._is_bad_resp(resp): best_score = score best_resp = resp if self._is_bad_resp(best_resp): # best_resp = self._select_from_common_responses() best_resp = self._get_alice_reply() logger.info("Best response is {}".format(best_resp)) return best_resp def _get_best_response(self, tsv): candidates = [] for line in tsv.split('\n'): _, resp, score = line.split('\t') words_cnt = len(word_tokenize(resp)) print(resp, words_cnt, self._get_stopwords_count(resp), self._is_bad_resp(resp)) if words_cnt >= 1 and self._get_stopwords_count( resp) / words_cnt <= 0.75 and not self._is_bad_resp(resp): candidates.append(resp) print('candidates:', candidates) if len(candidates) > 0: return random.choice(candidates) return self._get_alice_reply() def _is_bad_resp(self, resp): if len(self._dialog_context) > 1: if (self._dialog_context[-2][1] == self._dialog_context[-1][1]): return True if (self._dialog_context[-1][1] == self._last_user_message): return True if '<unk>' in resp or re.match('\w', resp) is None or ( 'youtube' in resp and 'www' in resp and 'watch' in resp): return True else: return False def _get_stopwords_count(self, resp): return len( list( filter(lambda x: x.lower() in stopwords.words('english'), word_tokenize(resp)))) def _get_summaries(self, with_heuristic=True): text = self._text logger.info("Send to opennmt summary: {}".format(text)) cmd = "echo \"{}\" | python from_opennmt_summary/get_reply.py {}".format( text, BotBrain.SUMMARIZER_URL) ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = ps.communicate()[0] res = str(output, "utf-8").strip() logger.info("Got from opennmt summary: {}".format(res)) # now lets select one best response candidates = [] for line in res.split('\n'): _, resp, score = line.split('\t') words_cnt = len(word_tokenize(resp)) print(resp, words_cnt, self._get_stopwords_count(resp)) if words_cnt >= 2 and self._get_stopwords_count( resp) / words_cnt < 0.5 and '<unk>' not in resp: candidates.append(resp) if len(candidates) > 0: summary = random.choice(candidates) msg1 = ['I think this', 'I suppose that this', 'Maybe this'] msg2 = ['article', 'text', 'paragraph'] msg3 = [ 'can be described as:', 'can be summarized as:', 'main idea is:', 'in a nutshell is:' ] msg4 = [summary] msg5 = ['.', '...', '?', '..?'] msg = [msg1, msg2, msg3, msg4, msg5] return combinate_and_return_answer(msg) return self._get_alice_reply() def _select_from_common_responses(self): msg1 = ['Do you know what?', '', "I don't understand :(", '¯\_(ツ)_/¯'] msg2 = ["I can't answer", "Its beyond my possibilities"] msg3 = [':(', '.', '!', ';('] msg4 = [ "Let's talk about", "I would like to talk about", "I would like to discuss" ] msg5 = ["movies", "politics", "news", "you", "myself", "cats", "..."] msg6 = ['.', '', '!', ':)'] total_msg = [msg1, msg2, msg3, msg4, msg5, msg6] msg = combinate_and_return_answer(total_msg) return msg def _get_opennmt_fb_reply(self, with_heuristic=True): # feed_context = "{} {}".format(self._get_last_bot_reply(), self._last_user_message) sentence = self._last_user_message sentence_with_context = None user_sent = None if len(self._dialog_context) > 0: sentence_with_context = " ".join( [self._dialog_context[-1][1], self._last_user_message]) user_sent = " ".join( [self._dialog_context[-1][0], self._last_user_message]) text_with_sent = "{} {}".format(self._text, self._last_user_message) to_echo = "{}\n{}".format(sentence, text_with_sent) if sentence_with_context: to_echo = "{}\n{}".format(to_echo, sentence_with_context) if user_sent: to_echo = "{}\n{}".format(to_echo, user_sent) logger.info("Send to fb chitchat: {}".format(to_echo)) cmd = "echo \"{}\" | python from_opennmt_chitchat/get_reply.py {}".format( to_echo, BotBrain.FB_CHITCHAT_URL) ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = ps.communicate()[0] res = str(output, "utf-8").strip() logger.info("Got from fb chitchat: {}".format(res)) if with_heuristic: return self._get_best_response(res) else: return res def _get_intent(self, text): url = 'http://intent_classifier:3000/get_intent' r = requests.post(url, json={'text': text}) intent = r.json()['intent'] score = r.json()['score'] if score and score > 0.95: return intent return None def _send_message(self, text, reply_markup=None): text = text.strip() logger_bot.info("BOT[_send_message]: {}".format(text)) self._bot.send_message(chat_id=self._chat.id, text=text, reply_markup=reply_markup) if self._last_user_message is None: self._last_user_message = "" text = text.replace('"', " ").replace("`", " ").replace("'", " ") self._dialog_context.append((self._last_user_message, text)) def _cancel_timer_threads(self, presereve_cntr=False, reset_question=True, reset_seq2seq_context=True, reset_topic=True): if not presereve_cntr: self._too_long_waiting_cntr = 0 if reset_question: self._question_asked = False if reset_topic: self._topic_thread.cancel() [t.cancel() for t in self._threads] def _filter_seq2seq_output(self, s): s = normalize(str(s)) s = detokenize(s) return s def clear_all(self): self._cancel_timer_threads(reset_topic=False)
def __init__(self, bot, user=None, chat=None, text_and_qa=None): self.machine = Machine(model=self, states=BotBrain.states, initial='init') self.machine.add_transition('start', 'init', 'started', after='wait_for_user_typing') self.machine.add_transition('start_convai', 'init', 'started', after='wait_for_user_typing_convai') self.machine.add_transition('ask_question', 'started', 'asked', after='ask_question_to_user') self.machine.add_transition('classify', 'started', 'classifying', after='get_class_of_user_message') self.machine.add_transition('classify', 'asked', 'classifying', after='get_class_of_user_message') self.machine.add_transition('classify', 'waiting', 'classifying', after='get_class_of_user_message') self.machine.add_transition('classify', 'classifying', 'classifying', after='get_class_of_user_message') self.machine.add_transition('classify', 'checking_answer', 'classifying', after='get_class_of_user_message') self.machine.add_transition('check_user_answer_on_asked', 'asked', 'checking_answer', after='checking_user_answer') self.machine.add_transition('check_user_answer', 'classifying', 'checking_answer', after='checking_user_answer') self.machine.add_transition('correct_user_answer', 'checking_answer', 'correct_answer') self.machine.add_transition('incorrect_user_answer', 'checking_answer', 'incorrect_answer') self.machine.add_transition('return_to_asked', 'incorrect_answer', 'asked') self.machine.add_transition('return_to_start', '*', 'started', after='wait_for_user_typing') self.machine.add_transition('return_to_wait', '*', 'waiting', after='say_user_about_long_waiting') self.machine.add_transition('return_to_init', '*', 'init', after='clear_all') self.machine.add_transition('answer_to_user_question', 'classifying', 'bot_answering_question', after='answer_to_user_question_') self.machine.add_transition('classify', 'bot_answering_question', 'classifying', after='get_class_of_user_message') self.machine.add_transition('answer_to_user_question_correct', 'bot_answering_question', 'bot_correct_answer') self.machine.add_transition('answer_to_user_question_incorrect', 'bot_answering_question', 'bot_incorrect_answer') self.machine.add_transition('answer_to_user_replica', 'classifying', 'bot_answering_replica', after='answer_to_user_replica_') self.machine.add_transition('answer_to_user_replica_with_fb', 'classifying', 'bot_answering_replica', after='answer_to_user_replica_with_fb_') self.machine.add_transition('answer_to_user_replica_with_alice', 'classifying', 'bot_answering_replica', after='answer_to_user_replica_with_alice_') self.machine.add_transition('answer_to_user_with_summary', 'classifying', 'bot_answering_replica', after='answer_to_user_with_summary_') self.machine.add_transition('answer_to_user_with_topic', 'classifying', 'bot_answering_replica', after='answer_to_user_with_topic_') self.machine.add_transition('long_wait', 'asked', 'waiting', after='say_user_about_long_waiting') self.machine.add_transition('too_long_wait', 'waiting', 'waiting', after='say_user_about_long_waiting') self.machine.add_transition('user_off', 'waiting', 'init', after='propose_conversation_ending') self.machine.add_transition('ask_question_after_waiting', 'waiting', 'asked', after='ask_question_to_user') self.machine.add_transition('ask_question_after_classifying', 'classifying', 'asked', after='ask_question_to_user') self._bot = bot self._user = user self._chat = chat self._text_and_qa = text_and_qa self._too_long_waiting_cntr = 0 self._last_user_message = None self._threads = [] self._init_factoid_qas_and_text() self._dialog_context = [] self._is_first_incorrect = True # to prevent recursion call self._is_chitchat_replica_is_answer = False self._setup_topics_info()
class AbstractTransformPlugin(metaclass=ABCMeta): """ classdocs """ @abstractmethod def __init__(self): """ Initializes the Model and registers the default state machine transitions available to all models.ctor """ self.state = None self._endpoint_service = None states = [ State(n, on_enter='new_state_entered') for n in _transform_states ] self._machine = Machine(model=self, states=states, initial='started', auto_transitions=False) self._machine.add_transition(trigger='fail', source=list(_transform_states - {'terminated'}), dest='failed') self._machine.add_transition(trigger='ready', source=['started', 'transforming'], dest='ready') self._machine.add_transition(trigger='transform', source=['started', 'ready'], dest='transforming', after='_do_transform') self._machine.add_transition( trigger='terminate', source=list(_transform_states - {'terminating', 'terminated', 'failed'}), dest='terminating', after='_do_terminate') self._machine.add_transition(trigger='terminated', source='terminating', dest='terminated') def update_status(self, payload): """ Provides additional details to the current state of the model. If the model is training, then this might contain the number of records that have been trained. :param payload: Extra information regarding the status update """ self.endpoint_service.update_state(state=None, payload=payload) @property def endpoint_service(self) -> TransformPluginEndpoint: """ Returns the endpoint service for this Model """ return self._endpoint_service @endpoint_service.setter def endpoint_service(self, endpoint_service: TransformPluginEndpoint): """ Sets the endpoint service to the provided TransformPluginService :param endpoint_service: The new TransformPluginService """ self._endpoint_service = endpoint_service def new_state_entered(self, *args, **kwargs): """ Notifies the endpoint service that the current state of the state machine has been update :param args: Optional non-keyworded variable length arguments to pass in :param kwargs: Optional keyworded variable length arguments to pass in """ logger.info("New state entered - %s {args: %s, kwargs: %s}", self.state, args, kwargs) if self.state == 'failed' and len(args) > 0: self.endpoint_service.update_state(self.state, payload=args[0]) else: self.endpoint_service.update_state(self.state) def fail(self, reason): """ Triggers the model to enter the failed state. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. :param reason: The reason why the state machine is transitioned into the failure state """ pass def ready(self): """ Triggers the model to enter the ready state. It is expected that this method will only be called by the implementing model subclass. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def transform(self, inputDirs, outputDir, properties): """ Triggers the model to enter the training state. A subsequent call to do_train with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def report_failure(self, reason): """ Reports the failure of a model during its operations :param reason: The error message explaining why the model failed. """ logger.info('Plugin has failed: ' + str(reason)) def terminated(self): """ Shutdowns the transform plugin """ pass def _do_terminate(self): """ Stops all processing and releases any resources that are in use in preparation for being shut down. """ try: self.do_terminate() self.terminated() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_terminate") self.fail(str(ex)) @abstractmethod def do_terminate(self): """ Stops all processing and releases any resources that are in use in preparation for being shut down. """ pass def _do_transform(self, inputDirs, outputDir, properties): """ Executes the transform activity """ logger.debug("_do_transform started") try: logger.info("Calling do_transform method.") self.do_transform(inputDirs, outputDir, properties) self.ready() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_transform") self.fail(str(ex)) logger.debug("_do_transform complete") @abstractmethod def do_transform(self, inputDirs, outputDir, properties): # noqa: E501 """ Executes the transform activity """ pass
class AbstractEvaluationPlugin(metaclass=ABCMeta): """ classdocs """ @abstractmethod def __init__(self): """ Initializes the Metrics and registers the default state machine transitions available to all clients. """ self.state = None self._endpoint_service = None states = [ State(n, on_enter='new_state_entered') for n in _evaluation_states ] self._machine = Machine(model=self, states=states, initial='started', auto_transitions=False) self._machine.add_transition(trigger='fail', source=list(_evaluation_states - {'terminated'}), dest='failed') self._machine.add_transition(trigger='ready', source=['started', 'evaluating'], dest='ready') self._machine.add_transition(trigger='evaluate', source=['started', 'ready'], dest='evaluating', after='_do_evaluate') self._machine.add_transition( trigger='terminate', source=list(_evaluation_states - {'terminating', 'terminated', 'failed'}), dest='terminating', after='_do_terminate') self._machine.add_transition(trigger='terminated', source='terminating', dest='terminated') def update_status(self, payload): """ Provides additional details to the current state of the evaluation. :param payload: Extra information regarding the status update """ self.endpoint_service.update_state(state=None, payload=payload) @property def endpoint_service(self) -> EvaluationPluginEndpoint: """ Returns the endpoint service for this Evaluation """ return self._endpoint_service @endpoint_service.setter def endpoint_service(self, endpoint_service: EvaluationPluginEndpoint): """ Sets the endpoint service to the provided EvaluationPluginService :param endpoint_service: The new EvaluationPluginService """ self._endpoint_service = endpoint_service def new_state_entered(self, *args, **kwargs): """ Notifies the endpoint service that the current state of the state machine has been update :param args: Optional non-keyworded variable length arguments to pass in :param kwargs: Optional keyworded variable length arguments to pass in """ logger.info("New state entered - %s {args: %s, kwargs: %s}", self.state, args, kwargs) if self.state == 'failed' and len(args) > 0: self.endpoint_service.update_state(self.state, payload=args[0]) else: self.endpoint_service.update_state(self.state) def fail(self, reason): """ Triggers the model to enter the failed state. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. :param reason: The reason why the state machine is transitioned into the failure state """ pass def ready(self): """ Triggers the model to enter the ready state. It is expected that this method will only be called by the implementing model subclass. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def evaluate(self, assessment_type, metrics, input_data_path, evaluation_input_format, ground_truth_path, evaluation_path, properties): """ Triggers the model to enter the evaluation state. A subsequent call to do_evaluate with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def metrics(self): """ Metrics that can be performed by the evaluate method """ logger.debug("metrics started") try: metrics_list = self.plugin_manager.get_metrics_list() logger.debug("metrics complete") except Exception as ex: logger.exception("Error running metrics") self.fail(str(ex)) return metrics_list def assessment_types(self): """ Assessment types that are supported by the evaluate method """ types = [] try: metrics_list = self.plugin_manager.get_metrics_list() for metric in metrics_list: for assessment_types in metric.assessment_types(): if type not in assessment_types: types.append(type) except Exception as ex: logger.exception("Error running metrics") self.fail(str(ex)) return types def report_failure(self, reason): """ Reports the failure of a model during its operations :param reason: The error message explaining why the model failed. """ logger.info('Plugin has failed: ' + str(reason)) def terminated(self): """ Shutdowns the evaluation plugin """ pass def _do_terminate(self): """ Stops all processing and releases any resources that are in use in preparation for being shut down. """ try: self.do_terminate() self.terminated() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_terminate") self.fail(str(ex)) @abstractmethod def do_terminate(self): """ Stops all processing and releases any resources that are in use in preparation for being shut down. """ pass def _do_evaluate(self, assessment_type, metrics, input_data_path, evaluation_input_format, ground_truth_path, evaluation_path, properties): """ Performs metrics' evaluation using the predictions and ground truth files provided. Stored the assessment results as a JSON file in the evaluation_path :param assessment_type: The evaluation type. One of {'BinaryClassification', 'MultilabelClassification', 'MulticlassClassification', 'Regression'} :param metrics: Specific metrics to evaluate against instead of all metrics defined by assessment_type :param input_data_path: Path to input data for the evaluation :param evaluation_input_format: The format of the input data :param ground_truth_path: The directory path where the ground_truth.csv file is located :param evaluation_path: A directory path to where the evaluation.json output file will be stored :param properties: A dictionary of key value pairs for evaluation plugin arguments. """ logger.debug("_do_evaluation started") try: logger.info("Calling do_evaluation method.") self.do_evaluate(assessment_type, metrics, input_data_path, evaluation_input_format, ground_truth_path, evaluation_path, properties) self.ready() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_evaluation") self.fail(str(ex)) logger.debug("_do_evaluation complete") @abstractmethod def do_evaluate(self, assessment_type, metrics, input_data_format, evaluation_input_format, ground_truth_path, evaluation_path, properties): # noqa: E501 """ Performs metrics' evaluation using the predictions and ground truth files provided. Stored the assessment results as a JSON file in the evaluation_path :param assessment_type: The evaluation type. One of {'BinaryClassification', 'MultilabelClassification', 'MulticlassClassification', 'Regression'} :param metrics: Specific metrics to evaluate against instead of all metrics defined by assessment_type :param input_data_path: Path to input data for the evaluation :param evaluation_input_format: The format of the input data :param ground_truth_path: The directory path where the ground_truth.csv file is located :param evaluation_path: A directory path to where the evaluation.json output file will be stored :param properties: A dictionary of key value pairs for evaluation plugin arguments. """ pass
class AbstractModel(metaclass=ABCMeta): """ The definition of an abstract Model to be implemented by Model developers. """ @abstractmethod def __init__(self): """ Initializes the Model and registers the default state machine transitions available to all models. """ self.state = None self._endpoint_service = None states = [ State(n, on_enter='new_state_entered') for n in _model_states ] self._machine = Machine(model=self, states=states, initial='started', auto_transitions=False) self._machine.add_transition(trigger='fail', source=list(_model_states - {'terminated'}), dest='failed') self._machine.add_transition(trigger='initialize', source='started', dest='initializing', after='_do_initialize') self._machine.add_transition(trigger='initialized', source=['initializing', 'loading_data'], dest='initialized') self._machine.add_transition(trigger='load_data', source=['initialized', 'ready'], dest='loading_data', after='_do_load_data') self._machine.add_transition(trigger='build_model', source='initialized', dest='building_model', after='_do_build_model') self._machine.add_transition(trigger='ready', source=[ 'loading_data', 'building_model', 'training', 'predicting', 'saving_model', 'saving_predictions', 'resetting' ], dest='ready') self._machine.add_transition(trigger='train', source='ready', dest='training', after='_do_train') self._machine.add_transition(trigger='pause', source=['training', 'predicting'], dest='pausing', after='_do_pause') self._machine.add_transition(trigger='paused', source='pausing', dest='paused') self._machine.add_transition(trigger='resume_training', source='paused', dest='training', after='_do_resume_training') self._machine.add_transition(trigger='resume_predicting', source='paused', dest='predicting', after='_do_resume_predict') self._machine.add_transition(trigger='save_model', source=['ready', 'paused'], dest='saving_model', after='_do_save_model') self._machine.add_transition(trigger='predict', source='ready', dest='predicting', after='_do_predict') self._machine.add_transition(trigger='stream_predict', source='ready', dest='predicting', after='_do_stream_predict') self._machine.add_transition(trigger='save_predictions', source='ready', dest='saving_predictions', after='_do_save_predictions') self._machine.add_transition( trigger='terminate', source=list(_model_states - {'terminating', 'terminated', 'failed'}), dest='terminating', after='_do_terminate') self._machine.add_transition(trigger='terminated', source='terminating', dest='terminated') self._machine.add_transition( trigger='reset', source=list(_model_states - {'terminating', 'terminated'}), dest='resetting', after='_do_reset') self._model_built = False self._response = None def update_status(self, payload): """ Provides additional details to the current state of the model. If the model is training, then this might contain the number of records that have been trained. :param payload: Extra information regarding the status update """ self.endpoint_service.update_state(state=None, payload=payload) @property def endpoint_service(self) -> ModelInstanceEndpoint: """ Returns the endpoint service for this Model """ return self._endpoint_service @endpoint_service.setter def endpoint_service(self, endpoint_service: ModelInstanceEndpoint): """ Sets the endpoint service to the provided ModelEndpointService :param endpoint_service: The new ModelEndpointService """ self._endpoint_service = endpoint_service def new_state_entered(self, *args, **kwargs): """ Notifies the endpoint service that the current state of the state machine has been update :param args: Optional non-keyworded variable length arguments to pass in :param kwargs: Optional keyworded variable length arguments to pass in """ logger.info("New state entered - %s {args: %s, kwargs: %s}", self.state, args, kwargs) if self.state == 'failed' and len(args) > 0: self.endpoint_service.update_state(self.state, payload=args[0]) else: self.endpoint_service.update_state(self.state) def fail(self, reason): """ Triggers the model to enter the failed state. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. :param reason: The reason why the state machine is transitioned into the failure state """ pass def initialize(self, objectives, props, hparams): """ Triggers the model to enter the initializing state. A subsequent call to do_initialize with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. :param objectives: The objectives of this model. :param props: A dictionary that is parsed from a JSON string. These are settings that are passed from the ecosystem, but are not really considered hyperparameters. They are not used by the model, but rather the endpoint itself (e.g., where should heartbeats be sent and how often, etc). :param hparams: A dictionary that is parsed from a JSON string. These are the hyperparameters that are used by the model. """ logger.debug('Called abstractModel.initialize') pass def initialized(self): """ Triggers the model to enter the initialized state. It is expected that this method will only be called by the implementing model subclass. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ logger.debug('Called abstractModel.initialized') pass def load_data(self, dataset_map): """ Triggers the model to enter the loading_data state. A subsequent call to do_load_data with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. :param dataset_map: A dictionary that maps string keys {training_data, test_data} to a Dataset object that contains information on the dataset to load """ pass def build_model(self, modelPath): """ Triggers the model to enter the building_model state. A subsequent call to do_build_model with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. :param modelPath: The path to the model file """ pass def ready(self): """ Triggers the model to enter the ready state. It is expected that this method will only be called by the implementing model subclass. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def train(self): """ Triggers the model to enter the training state. A subsequent call to do_train with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def pause(self): """ Triggers the model to enter the pausing state. A subsequent call to do_pause with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def paused(self): """ Triggers the model to enter the paused state. It is expected that this method will only be called by the implementing model subclass. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def unpause(self): """ Triggers the model to enter the unpausing state. A subsequent call to do_unpause with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def save_model(self): """ Triggers the model to enter the unpausing state. A subsequent call to do_unpause with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def predict(self): """ Triggers the model to enter the predicting state. A subsequent call to do_predict with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def stream_predict(self, data_map): """ Triggers the model to enter the predicting state. A subsequent call to do_stream_predict with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. """ pass def save_predictions(self, dataPath): """ Triggers the model to enter the saving_predictions state. A subsequent call to do_save_predictions with the given parameters will be made as a result. This method should not be implemented or overwritten by subclasses. It will be created by the state machine. :param dataPath: The file path to where the model predictions will be saved. This can be the local file system or on the distributed file system """ pass def report_failure(self, reason): """ Reports the failure of a model during its operations :param reason: The error message explaining why the model failed. """ logger.info('Model has failed: ' + str(reason)) def terminated(self): """ Terminates the model """ logger.info("Shutting down") sys.exit() def reset(self): """ Resets the model container instance """ pass def _do_initialize(self, objectives: list, props: dict, hparams: dict): """ Called once the endpoint service has launched. This would typically be the first call made to the service. :param objectives: The objectives for the model. :param props: A dictionary that is parsed from a JSON string. These are settings that are passed from the ecosystem, but are not really considered hyperparameters. They are not used by the model, but rather the endpoint itself (e.g., where should heartbeats be sent and how often, etc). :param hparams: A dictionary that is parsed from a JSON string. These are the hyperparameters that are used by the model. """ try: logger.debug("_do_initialize called") self.do_initialize(objectives, props, hparams) logger.debug("Changing state to 'initialized'") self.initialized() logger.debug("_do_initialize complete") except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_initalize") self.fail(str(ex)) @abstractmethod def do_initialize(self, objectives: list, props: dict, hparams: dict): """ Called once the endpoint service has launched. This would typically be the first call made to the service. :param objective: The objectives for the model. :param props: A dictionary that is parsed from a JSON string. These are settings that are passed from the ecosystem, but are not really considered hyperparameters. They are not used by the model, but rather the endpoint itself (e.g., where should heartbeats be sent and how often, etc). :param hparams: A dictionary that is parsed from a JSON string. These are the hyperparameters that are used by the model. """ pass def _do_load_data(self, dataset_map: dict): """ Instructs the container to load data (or at least record in memory where the data is, if it’s actually to be loaded during training). :param dataset_map: A dictionary that maps string keys {training_data, test_data} to a Dataset object that contains information on the dataset to load """ try: self.do_load_data(dataset_map) if self._model_built: self.ready() else: self.initialized() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_load_data") self.fail(str(ex)) @abstractmethod def do_load_data(self, dataset_map: dict): """ Instructs the container to load data (or at least record in memory where the data is, if it’s actually to be loaded during training). :param dataset_map: A dictionary that maps string keys {training_data, test_data} to a Dataset object that contains information on the dataset to load """ pass def _do_build_model(self, path=None): """ Instructs the service to build all necessary data structures given the architecture and selected hyperparameters. :param path: The path to the model file """ try: self.do_build_model(path) self._model_built = True self.ready() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_build_model") self.fail(str(ex)) @abstractmethod def do_build_model(self, path=None): """ Instructs the service to build all necessary data structures given the architecture and selected hyperparameters. :param path: The path to the model file """ pass def _do_train(self): """ Executes/resumes the training activity """ logger.debug("_do_train started") try: logger.info("Calling do_train method.") self.do_train() self.ready() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_train") self.fail(str(ex)) logger.debug("_do_train complete") @abstractmethod def do_train(self): """ Executes/resumes the training activity """ pass def _do_save_model(self, path): """ Save the model to an agreed upon location. It will be up to some other process to make sure the saved model ends up back in the repository. This call could be a NOOP if, for instance, the model is saved periodically throughout the training process. :param path: The path to which the model should be saved. """ try: self.do_save_model(path) self.ready() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_save_model") self.fail(str(ex)) @abstractmethod def do_save_model(self, path): """ Save the model to an agreed upon location. It will be up to some other process to make sure the saved model ends up back in the repository. This call could be a NOOP if, for instance, the model is saved periodically throughout the training process. :param path: The path to which the model should be saved. """ pass def _do_pause(self): """ Pauses the current activity (training or prediction). """ try: self.do_pause() self.paused() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_pause") self.fail(str(ex)) @abstractmethod def do_pause(self): """ Pauses the current activity (training or prediction). This operation is currently not supported. """ pass def _do_resume_training(self): """ Unpauses the current activity (training or prediction). """ try: self.do_resume_training() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_resume_training") self.fail(str(ex)) @abstractmethod def do_resume_training(self): """ Unpauses the current activity (training or prediction). This operation is currently not supported. """ pass def _do_resume_predict(self): """ Executes/resumes the prediction activity """ try: self.do_resume_predict() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_resume_predict") self.fail(str(ex)) @abstractmethod def do_resume_predict(self): """ Executes/resumes the prediction activity """ pass def _do_predict(self): """ Executes/resumes the prediction activity """ try: self.do_predict() self.ready() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_predict") self.fail(str(ex)) @abstractmethod def do_predict(self): """ Executes/resumes the prediction activity This operation is currently not supported. """ pass def _do_stream_predict(self, data_map: dict): """ Executes/resumes the stream prediction activity """ try: response = self.do_stream_predict(data_map) self.endpoint_service.put_response(response) self.ready() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_stream_predict") self.fail(str(ex)) @abstractmethod def do_stream_predict(self, data_map: dict): """ Executes/resumes the prediction activity """ pass def _do_save_predictions(self, dataPath): """ Saves the current prediction to the location specified :param dataPath: The location on the local file system or distributed file system where the predictions will be saved """ try: self.do_save_predictions(dataPath) self.ready() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_save_predictions") self.fail(str(ex)) @abstractmethod def do_save_predictions(self, dataPath): """ Saves the current prediction to the location specified :param dataPath: The location on the local file system or distributed file system where the predictions will be saved """ pass def _do_terminate(self): """ Stops all processing and releases any resources that are in use in preparation for being shut down. """ try: self.do_terminate() self.terminated() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_terminate") self.fail(str(ex)) @abstractmethod def do_terminate(self): """ Stops all processing and releases any resources that are in use in preparation for being shut down. """ pass def _do_reset(self): """ Resets the model into its initial state """ try: self.do_reset() self._model_built = False self.ready() except Exception as ex: #pylint: disable=broad-except logger.exception("Error running do_reset") self.fail(str(ex)) @abstractmethod def do_reset(self): """ Resets the model into its initial state """ pass
class MM(object): states = ['INIT','BID_SEARCH','BID_SENDING','BID_WAIT_CONFIRM','BID_PLACED','BID_CANCELLING','BID_LIQUIDATING', 'ASK_SEARCH','ASK_SENDING','ASK_WAIT_CONFIRM','ASK_PLACED','ASK_CANCELLING'] def __init__(self, name, APIKey, Secret, currency_pair): self.name = name self.polo = poloniex(APIKey,Secret) self.currency_pair = currency_pair self.machine = Machine(model=self, states=MM.states, initial='INIT', send_event=True, ignore_invalid_triggers=True) #parameters self.risk = float(0) self.bid_delta = 0.0000001 # the amount to repost order when you are too high or low self.bid_increments = 0.00000001 # the amount to outbid the highest bid by self.amount = 0.001 #setup values self.bid = float(0) self.ask = float(0) self.bid_waiting_at = float(0) self.bid_order = None self.bid_position = float(0) self.ask_waiting_at = float(0) self.ask_order = None self.ask_position = float(0) #self.result = {} #main trading loop self.machine.add_transition(trigger='connect', source='INIT', dest='BID_SEARCH') # bid self.machine.add_transition(trigger='tick', source='BID_SEARCH', dest='BID_SENDING',prepare='set_tick',conditions='found_bid') self.machine.on_enter_BID_SENDING('retry_place_bid') self.machine.add_transition(trigger='try_place_bid', source='BID_SENDING', dest='BID_WAIT_CONFIRM', conditions='bid_placed') self.machine.on_enter_BID_WAIT_CONFIRM('retry_bid_confirm') self.machine.add_transition(trigger='try_confirm_bid', source='BID_WAIT_CONFIRM', dest='BID_PLACED',conditions='inBidOrder') self.machine.on_enter_BID_PLACED('retry_bid_executed') self.machine.add_transition(trigger='try_bid_executed', source='BID_PLACED', dest='ASK_SEARCH', conditions='bid_executed') # ask self.machine.add_transition(trigger='tick', source='ASK_SEARCH', dest='ASK_SENDING', prepare='set_tick',conditions='found_ask') self.machine.on_enter_ASK_SENDING('retry_place_ask') self.machine.add_transition(trigger='place_ask', source='ASK_SENDING', dest='ASK_WAIT_CONFIRM', conditions='ask_placed') self.machine.on_enter_ASK_WAIT_CONFIRM('retry_ask_placed') self.machine.add_transition(trigger='ask_placed', source='ASK_WAIT_CONFIRM', dest='ASK_PLACED',conditions='isInAskOrderbook') self.machine.on_enter_ASK_PLACED('retry_ask_executed') self.machine.add_transition(trigger='ask_executed', source='ASK_PLACED', dest='BID_SEARCH', conditions='ask_executed') #reposition bids self.machine.add_transition(trigger='tick', source='BID_PLACED', dest='BID_CANCELLING', prepare='set_tick', conditions='bid_out_of_range') self.machine.on_enter_BID_CANCELLING('retry_cancel_bid') self.machine.add_transition(trigger='post_cancel_bid', source='BID_CANCELLING', dest='BID_SEARCH', conditions='cancel_bid') self.machine.add_transition(trigger='tick_abort', source='ASK_SEARCH', dest='BID_LIQUIDATING') self.machine.add_transition(trigger='order_executed', source='BID_LIQUIDATING', dest='BID_SEARCH') self.machine.add_transition(trigger='tick_abort', source='ASK_PLACED', dest='ASK_CANCELLING') self.machine.add_transition(trigger='order_executed', source='ASK_CANCELLING', dest='ASK_SEARCH') # need to figure out cleanup mechanism on shutdown #self.machine.add_transition(trigger='shutdown', source='*', dest='EXIT') #self.machine.add_transition(trigger='shutdown', source='BID_PLACED', dest='BID_CANCELLING') #self.machine.on_enter_EXIT('cleanup') def set_risk(self, risk): self.risk = float(risk) def set_tick(self, event): self.bid = float(event.kwargs.get('bid')) self.ask = float(event.kwargs.get('ask')) def found_bid(self,event): self.bid_waiting_at = self.bid + self.bid_increments return True def retry_if_true(result): return result #decorator to run job on thread (till it exits, so make sure it does) def asDaemon(worker_function): def inner_function(self, *args): print("create Thread()") thread = Thread(target=worker_function, args=(self,args)) thread.daemon = True print("thread.starting()") thread.start() return inner_function @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_place_bid(self,event): print("try place bid") self.try_place_bid() return self.is_BID_SENDING() def bid_placed(self,event): self.bid_order = self.polo.buy(currencyPair = self.currency_pair, rate=self.bid_waiting_at, amount=self.amount) print ('placing bid @ ' + str(self.bid_waiting_at) + ' orderid: ' + self.bid_order['orderNumber']) return self.bid_order != None @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_bid_confirm(self,event): print("try order") self.try_confirm_bid() return self.is_BID_WAIT_CONFIRM() def inBidOrder(self,event): print ('searching for order') orderbook = self.polo.returnOpenOrders(self.currency_pair) for order in orderbook: if order['orderNumber'] == self.bid_order['orderNumber']: print('order found') return True return False @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_bid_executed(self,event): print("checking if bid is executed") self.try_bid_executed() return self.is_BID_PLACED() def bid_executed(self,event): tradehistory = self.polo.returnTradeHistory(self.currency_pair) for trade in tradehistory: if trade['orderNumber'] == self.bid_order['orderNumber']: self.bid_position = float(trade['rate']) return True return False @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_cancel_bid(self,event): print("cancelling bid") self.post_cancel_bid() return self.is_BID_CANCELLING() def bid_out_of_range(self,event): delta = abs ( self.bid_waiting_at - self.bid ) return ( delta > self.bid_delta ) def cancel_bid(self,event): if self.bid_order != None: print ('canceling bid @ ' + str(self.bid_position) + ' as market bid is ' + str(self.bid) + ' orderid was ' + self.bid_order['orderNumber']) result = self.polo.cancel(self.currency_pair,self.bid_order['orderNumber']) return result['success'] == 1 return True def found_ask(self,event): self.ask_waiting_at = self.ask - self.bid_increments return True @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_place_ask(self,event): print("try place ask") self.place_ask() return self.is_ASK_SENDING() def place_ask(self,event): self.ask_order = self.polo.buy(currencyPair = self.currency_pair, rate=self.ask_waiting_at, amount=self.amount) print ('placing ask @ ' + str(self.ask_waiting_at) + ' orderid: ' + self.ask_order['orderNumber']) return self.ask_order != None @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_ask_placed(self,event): print("try order") self.ask_placed() return self.is_ASK_WAIT_CONFIRM() def isInBidOrderbook(self,event): orderbook = self.polo.returnOpenOrders(self.currency_pair) for order in orderbook: if order['orderNumber'] == self.ask_order['orderNumber']: return True return False @asDaemon @retry(wait_fixed=1000, retry_on_result=retry_if_true) def retry_ask_executed(self,event): print("checking if bid is executed") self.ask_executed() return self.is_ASK_PLACED() def ask_executed(self,event): tradehistory = self.polo.returnTradeHistory(self.currency_pair) for trade in tradehistory: if trade['orderNumber'] == self.ask_order['orderNumber']: self.ask_position = trade['rate'] return True return False def set_ask_position(self,event): self.ask_position = self.ask_waiting_at print ('ask placed @ ' + str(self.ask_position)) def bid_risk_exceeded(self,event): #return (self.risk > self.bid_position - self.bid) return False
class ArtBrainMachine(object): states = [ State(name='pre_init', on_enter=[], on_exit=[]), State(name='init', on_enter=['state_init_ros'], on_exit=[]), State(name='shutdown', on_enter=['state_shutdown'], on_exit=[]), State(name='waiting_for_action', on_enter=['state_waiting_for_action'], on_exit=[]), State(name='program_init', on_enter=['state_program_init'], on_exit=[]), State(name='program_run', on_enter=['state_program_run'], on_exit=[]), State(name='program_paused', on_enter=['state_program_paused'], on_exit=[]), State(name='program_error', on_enter=['state_program_error'], on_exit=[]), State(name='program_finished', on_enter=['state_program_finished'], on_exit=[]), State(name='program_load_instruction', on_enter=['state_program_load_instruction'], on_exit=[]), # learning State(name='learning_init', on_enter=['learning_load_block_id', 'state_learning_init'], on_exit=[]), State(name='learning_run', on_enter=['learning_load_block_id', 'state_learning_run'], on_exit=[]), # learning drilling and welding State(name='learning_welding_point', on_enter=[ 'learning_load_block_id', 'state_learning_welding_point' ], on_exit=[]), State(name='learning_welding_point_run', on_enter=[ 'learning_load_block_id', 'state_learning_welding_point_run' ], on_exit=[]), State( name='learning_welding_seam', on_enter=['learning_load_block_id', 'state_learning_welding_seam'], on_exit=[]), State(name='learning_welding_seam_run', on_enter=[ 'learning_load_block_id', 'state_learning_welding_seam_run' ], on_exit=[]), # learning others State(name='learning_wait', on_enter=['learning_load_block_id', 'state_learning_wait'], on_exit=[]), State(name='learning_step_done', on_enter=['learning_load_block_id', 'state_learning_step_done'], on_exit=[]), State(name='learning_step_error', on_enter=['learning_load_block_id', 'state_learning_step_error'], on_exit=[]), State(name='learning_done', on_enter=['learning_load_block_id', 'state_learning_done'], on_exit=[]), # visualize # State(name='visualize_init', on_enter=[ # 'visualize_load_block_id', 'state_visualize_init'], on_exit=[]), State(name='visualize_run', on_enter=['visualize_load_block_id', 'state_visualize_run'], on_exit=[]), State(name='visualize_done', on_enter=['state_visualize_done'], on_exit=[]) ] def __init__(self, states, transitions): self.name = 'brain' self.states += states self.machine = Machine(model=self, states=ArtBrainMachine.states, initial='pre_init', auto_transitions=False, send_event=True, queued=True) # *** transitions *** self.machine.add_transition('init', 'pre_init', 'init') self.machine.add_transition('init_done', 'init', 'waiting_for_action', conditions='is_everything_calibrated') self.machine.add_transition('program_start', 'waiting_for_action', 'program_init') self.machine.add_transition('learning_start', 'waiting_for_action', 'learning_init') self.machine.add_transition('visualize_start', 'waiting_for_action', 'visualize_run') # program self.machine.add_transition('program_init_done', 'program_init', 'program_run') self.machine.add_transition('error', 'program_init', 'program_error') self.machine.add_transition('error', 'program_run', 'program_error') self.machine.add_transition('program_error_handled', 'program_error', 'program_load_instruction') self.machine.add_transition('program_error_shutdown', 'program_error', 'shutdown') self.machine.add_transition('program_error_fatal', 'program_error', 'program_finished') self.machine.add_transition('try_again', 'program_error', 'program_run') self.machine.add_transition('skip', 'program_error', 'program_load_instruction') self.machine.add_transition('fail', 'program_error', 'program_load_instruction') self.machine.add_transition('done', 'program_load_instruction', 'program_run') self.machine.add_transition('error', 'program_load_instruction', 'program_error') self.machine.add_transition('finished', 'program_load_instruction', 'program_finished') self.machine.add_transition('done', 'program_finished', 'waiting_for_action') self.machine.add_transition('finished', 'program_run', 'program_finished') self.machine.add_transition('pause', 'program_load_instruction', 'program_paused') self.machine.add_transition('resume', 'program_paused', 'program_run') # learning self.machine.add_transition('init_done', 'learning_init', 'learning_run') self.machine.add_transition('done', 'learning_step_done', 'learning_run') self.machine.add_transition('error_handled', 'learning_step_error', 'learning_run') self.machine.add_transition('error_fatal', 'learning_step_error', 'waiting_for_action') self.machine.add_transition('done', 'learning_done', 'waiting_for_action') self.machine.add_transition('learning_done', 'learning_run', 'learning_done') self.machine.add_transition('visualize_done', 'visualize_run', 'visualize_done') self.machine.add_transition('done', 'visualize_done', 'waiting_for_action') for transition in transitions: self.machine.add_transition(transition[0], transition[1], transition[2])
def __init__(self): """ Initializes the Model and registers the default state machine transitions available to all models. """ self.state = None self._endpoint_service = None states = [ State(n, on_enter='new_state_entered') for n in _model_states ] self._machine = Machine(model=self, states=states, initial='started', auto_transitions=False) self._machine.add_transition(trigger='fail', source=list(_model_states - {'terminated'}), dest='failed') self._machine.add_transition(trigger='initialize', source='started', dest='initializing', after='_do_initialize') self._machine.add_transition(trigger='initialized', source=['initializing', 'loading_data'], dest='initialized') self._machine.add_transition(trigger='load_data', source=['initialized', 'ready'], dest='loading_data', after='_do_load_data') self._machine.add_transition(trigger='build_model', source='initialized', dest='building_model', after='_do_build_model') self._machine.add_transition(trigger='ready', source=[ 'loading_data', 'building_model', 'training', 'predicting', 'saving_model', 'saving_predictions', 'resetting' ], dest='ready') self._machine.add_transition(trigger='train', source='ready', dest='training', after='_do_train') self._machine.add_transition(trigger='pause', source=['training', 'predicting'], dest='pausing', after='_do_pause') self._machine.add_transition(trigger='paused', source='pausing', dest='paused') self._machine.add_transition(trigger='resume_training', source='paused', dest='training', after='_do_resume_training') self._machine.add_transition(trigger='resume_predicting', source='paused', dest='predicting', after='_do_resume_predict') self._machine.add_transition(trigger='save_model', source=['ready', 'paused'], dest='saving_model', after='_do_save_model') self._machine.add_transition(trigger='predict', source='ready', dest='predicting', after='_do_predict') self._machine.add_transition(trigger='stream_predict', source='ready', dest='predicting', after='_do_stream_predict') self._machine.add_transition(trigger='save_predictions', source='ready', dest='saving_predictions', after='_do_save_predictions') self._machine.add_transition( trigger='terminate', source=list(_model_states - {'terminating', 'terminated', 'failed'}), dest='terminating', after='_do_terminate') self._machine.add_transition(trigger='terminated', source='terminating', dest='terminated') self._machine.add_transition( trigger='reset', source=list(_model_states - {'terminating', 'terminated'}), dest='resetting', after='_do_reset') self._model_built = False self._response = None
def __init__(self, endpoints, client, server=None, discovery_timeout=DISCOVERY_TIMEOUT_S, randomize_timeout=True): """Create a network manager""" self.__logger = logging.getLogger('network_manager') if randomize_timeout: self.discovery_timeout = random.randint( int(discovery_timeout * (1 - NetworkManager.DISCOVERY_TIMEOUT_RAND_FACTOR)), int(discovery_timeout * (1 + NetworkManager.DISCOVERY_TIMEOUT_RAND_FACTOR))) else: self.discovery_timeout = discovery_timeout self.__STATES = [ { 'name': 'initializing', 'on_enter': '_stop' }, { 'name': 'searching', 'timeout': self.discovery_timeout, 'on_timeout': '_start_server', 'on_enter': '_start_client' }, { 'name': 'connected' }, ] self.__TRANSITIONS = [{ 'trigger': 'search', 'source': 'initializing', 'dest': 'searching' }, { 'trigger': 'connected', 'source': 'searching', 'dest': 'connected' }, { 'trigger': 'disconnected', 'source': 'connected', 'dest': 'searching' }, { 'trigger': 'shutdown', 'source': '*', 'dest': 'initializing' }] Machine.__init__(self, states=self.__STATES, transitions=self.__TRANSITIONS, initial='initializing', auto_transitions=False) # Required to provide a client if None == client: raise AttributeError('Client must be specified') self.__roles = {'client': client, 'server': server} self.__role = 'client' if type(endpoints) is not list: self.__endpoints = [endpoints] else: self.__endpoints = endpoints self.__endpoints = [ endp for endp in self.__endpoints if endp is not None ] if not len(self.__endpoints): raise AttributeError('No endpoints specified')