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')
Beispiel #3
0
    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)
Beispiel #4
0
 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')
Beispiel #5
0
    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..')
Beispiel #6
0
    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 = []
Beispiel #7
0
    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()
Beispiel #10
0
    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])
Beispiel #11
0
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')
Beispiel #12
0
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
Beispiel #13
0
    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)
Beispiel #14
0
    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')
Beispiel #15
0
    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')
Beispiel #16
0
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
Beispiel #17
0
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)
Beispiel #18
0
    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
Beispiel #20
0
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
Beispiel #21
0
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
Beispiel #22
0
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        
Beispiel #23
0
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])
Beispiel #24
0
    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')