def test_target_agency_to_target_agency(self): dm = construct_dm() dm.process_slots("sid001", [Slot("intent", "weather.query")]) dm.process_confirm('sid001', {'code': 0, 'message': ''}) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) [email protected](STATUS_WAIT_TARGET)''') dm.process_slots("sid002", [ Slot("intent", "casual_talk"), ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) [email protected](STATUS_WAIT_TARGET) casual_talk(STATUS_WAIT_ACTION_CONFIRM)''') dm.process_slots( "sid00x", [Slot("intent", "where.query"), Slot("location", "nike")]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) where.query(STATUS_STACKWAIT) nike(STATUS_WAIT_ACTION_CONFIRM)''')
def test_target_agency_to_agent(self): dm = construct_dm() dm.process_slots("sid001", [Slot("intent", "weather.query")]) dm.process_confirm('sid001', {'code': 0, 'message': ''}) assert (str(dm.context) == ''' Context: Slot(city=None) Slot(country=None) Slot(date=None) Slot(intent=weather.query) Slot(location=None) Slot(meteorology=None)''') assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) [email protected](STATUS_WAIT_TARGET)''') # switch to agent dm.process_slots("sid002", [ Slot("intent", "casual_talk"), ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) [email protected](STATUS_WAIT_TARGET) casual_talk(STATUS_WAIT_ACTION_CONFIRM)''') dm.process_confirm('sid002', {'code': 0, 'message': ''}) assert (dm.is_waiting) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) [email protected](STATUS_WAIT_TARGET)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(country=None) Slot(date=None) Slot(intent=None) Slot(location=None) Slot(meteorology=None)''') round_out_simulate(dm) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(country=None) Slot(date=None) Slot(intent=None) Slot(location=None) Slot(meteorology=None)''')
def process_request(self, intent, d_slots, related_slots, sid): """ Process question from device. It will parse question to (`Intent`, Slots, Slots) with NLU and query database or call thirdparty service. Returns ------- dict. """ ret = { "code": 0, "sid": "", "response": { "tts": "噪音导致的胡话", "web": {} }, "event": { "intent": intent, "slots": {} }, "nlu": { "intent": intent, "slots": {} }, "response_id": "nonsense" } if intent in [None, "nonsense"]: # When intent is invalid in current context, NLU return None. return ret if intent == "sensitive": ret["response"]["tts"] = "很抱歉,这个问题我还不太懂。" return ret slots = [Slot("intent", intent)] for slot_name, value_name in d_slots.items(): slots.append(Slot(slot_name, value_name)) ret = self._process_slots(slots, sid, intent) d_slots = {} for s_slot in related_slots: slot = self._dm.context[s_slot] if slot.value is not None: d_slots[slot.key] = slot.value ret["nlu"] = { "intent": intent, "slots": d_slots } return ret
def _deserialize_trigger_slots(self, data): for kv in data['trigger_slots']: split_index = kv.find('=') key = kv[0: split_index] value = kv[split_index + 1:] optional = key in data['optional_slots'] yield Slot(key, value, optional)
def test_clear_and_share(self): dm = self._mix_trigger() dm.process_confirm('sid004', { 'code': 0, }) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) Mix(Mix(weather.query))(STATUS_STACKWAIT) Mix(weather.query)(STATUS_STACKWAIT) weather.query(STATUS_DELAY_EXIST)''') round_out_simulate(dm) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) Mix(Mix(weather.query))(STATUS_STACKWAIT) Mix(weather.query)(STATUS_DELAY_EXIST)''') assert (str(dm.context) == ''' Context: Slot(city=Shenzheng) Slot(date=tomorrow) Slot(intent=None) Slot(location=None)''') dm.process_slots("sid00x", [ Slot("intent", "spots.query"), ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) Mix(Mix(weather.query))(STATUS_STACKWAIT) Mix(weather.query)(STATUS_STACKWAIT) spots.query(STATUS_STACKWAIT) all_city(STATUS_WAIT_ACTION_CONFIRM)''') assert (dm.debug_loop == 4)
def test_agent_action_timeout(self): dm = construct_dm() dm.process_slots("sid001", [Slot('intent', 'name.query')]) assert (dm.is_waiting) time.sleep((dm.stack.top().timeout + 1) * dm.debug_timeunit) assert (dm.is_waiting == False) assert (dm.debug_loop == 6)
def test_target_complete(self): # incomplete input dm = construct_dm() dm.process_slots( "sid001", [Slot("intent", "weather.query"), Slot("city", "Shenzheng")]) assert (str(dm.context) == ''' Context: Slot(city=Shenzheng) Slot(country=None) Slot(date=None) Slot(intent=weather.query) Slot(location=None) Slot(meteorology=None)''') assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) date(STATUS_WAIT_ACTION_CONFIRM)''') dm.process_confirm('sid001', {'code': 0, 'message': ''}) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) date(STATUS_WAIT_TARGET)''') # target complete dm.process_slots("sid002", [ Slot("date", "today"), Slot("meteorology", "snow"), Slot("intent", "weather.query") ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) result(STATUS_WAIT_ACTION_CONFIRM)''') assert (str(dm.context) == ''' Context: Slot(city=Shenzheng) Slot(country=None) Slot(date=today) Slot(intent=weather.query) Slot(location=None) Slot(meteorology=snow)''') dm.cancel_timer()
def test_success_confirm(self): dm = construct_dm() dm.process_slots("sid001", [Slot('intent', 'name.query')]) dm.process_confirm('sid001', {'code': 0, 'message': ''}) assert (dm.debug_loop == 1) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT)''')
def target_slots(self): """ Slots to filled by `TargetAgent` children. """ if self._target_slots: return self._target_slots for child in self.children: for c in child.target_slots: self._target_slots.add(Slot(c.key, None)) return self._target_slots
def test_failed_confirm(self): dm = construct_dm() dm.process_slots("sid001", [Slot('intent', 'name.query')]) dm.process_confirm('sid001', {'code': -1, 'message': ''}) assert (dm.debug_loop == 5) assert (dm._session._sid is None) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT)''') assert (dm.context['intent'].value is None)
def test_failed_confirm(self): dm = construct_dm() dm.process_slots( "sid001", [Slot("intent", "where.query"), Slot("location", "nike")]) dm.process_confirm('sid001', {'code': -1, 'message': ''}) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(country=None) Slot(date=None) Slot(intent=None) Slot(location=None) Slot(meteorology=None)''') assert (dm.debug_loop == 6)
def test_context(self): c1 = Slot("intent", "location.query") c2 = Slot("intent", "name.query", life_type="forever") c3 = Slot("location", "nike") c4 = Slot("date", "today") ctx = Context() ctx.add_slot(copy.deepcopy(c1)) ctx.update_slot("intent", copy.deepcopy(c2)) ctx.add_slot(copy.deepcopy(c3)) ctx.add_slot(copy.deepcopy(c4)) assert ctx["intent"].dirty assert not ctx["location"].dirty assert ctx.satisfied(copy.deepcopy(c2)) assert not ctx.satisfied(copy.deepcopy(c1)) assert not ctx.satisfied(copy.deepcopy(c3)) ctx.reset_slot('intent') assert not ctx["intent"].dirty assert ctx["intent"].life_type == "forever" ctx.update_slot("intent", c2)
def test_location_input(self): dm = construct_dm() dm.process_slots( "sid001", [Slot("intent", "where.query"), Slot("location", "nike")]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) where.query(STATUS_STACKWAIT) nike(STATUS_WAIT_ACTION_CONFIRM)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(country=None) Slot(date=None) Slot(intent=where.query) Slot(location=nike) Slot(meteorology=None)''') assert (dm.debug_loop == 2)
def test_action_waiting_agent_switch(self): dm = construct_dm() dm.process_slots("sid001", [Slot('intent', 'name.query')]) assert (dm.is_waiting) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) name.query(STATUS_WAIT_ACTION_CONFIRM)''') # switch to another agent ret = dm.process_slots("sid002", [ Slot("intent", "casual_talk"), ]) assert (ret['response_id'] == 'casual_talk') ret = dm.process_confirm('sid002', {'code': 0, 'message': ''}) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT)''') assert (dm.context['intent'].value is None) assert (not dm.is_waiting) assert (dm.debug_loop == 2)
def test_multi_entrance(self): dm = self._construct_dm() dm.biz_tree.show() dm.process_slots("sid002", [ Slot("intent", "weather.query"), ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) Mix(Mix(weather.query))(STATUS_STACKWAIT) Mix(weather.query)(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) [email protected](STATUS_WAIT_ACTION_CONFIRM)''') dm.cancel_timer()
def round_out_simulate(dm): count = 0 while count < dm.MAX_CONTEXT_RESERVED_ROUND: if dm._session._sid is None: new_sid = "sid00x%s" % count else: new_sid = dm._session._sid + 'a' dm.process_slots(new_sid, [Slot("intent", "casual_talk")]) dm.process_confirm(new_sid, { 'code': 0, 'message': '' }) count += 1 assert(count == dm.MAX_CONTEXT_RESERVED_ROUND)
def process_slots(self, d_slots, sid): """ Process slots input from device Parameters ---------- d_slots : dict, slots with diction format. sid : str, session id. Returns ------- dict. """ slots = [Slot(key, value) for key, value in d_slots.items()] return self._process_slots(slots, sid)
def test_default_target_agent_input_timeout(self): dm = construct_dm() dm.process_slots("sid001", [Slot("intent", "weather.query")]) dm.process_confirm('sid001', {'code': 0, 'message': ''}) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) [email protected](STATUS_WAIT_TARGET)''') round_out_simulate(dm) assert (dm.debug_loop == 8) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT)''') assert (dm.context["intent"].value is None)
def test_default_triggered(self): dm = construct_dm() dm.process_slots("sid001", [Slot("intent", "weather.query")]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) [email protected](STATUS_WAIT_ACTION_CONFIRM)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(country=None) Slot(date=None) Slot(intent=weather.query) Slot(location=None) Slot(meteorology=None)''') assert (dm.debug_loop == 2) dm.cancel_timer()
def test_process_confirm_case_agent_ignore_old_session(self): dm = construct_dm() dm.process_slots("sid001", [Slot('intent', 'name.query')]) assert (dm._session.valid_session("sid001")) assert (dm.is_waiting) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) name.query(STATUS_WAIT_ACTION_CONFIRM)''') # ignore old session dm.process_confirm('sid000', {'code': 0, 'message': ''}) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) name.query(STATUS_WAIT_ACTION_CONFIRM)''') assert (dm.is_waiting) time.sleep((dm.stack.top().timeout + 1) * dm.debug_timeunit) assert (not dm.is_waiting) assert (dm.debug_loop == 5)
def update_slots_by_backend(self, d_slots): """ Update by business service. Parameters ---------- d_slots : dict, dict of slots. Returns ------- dict. { "code": 0 // alway success } """ slots = [Slot(key, value) for key, value in d_slots.items()] self._dm.update_by_remote(slots) log.info(self._dm.context) return { "code": 0, }
def test_name_input(self): dm = construct_dm() dm.process_slots("sid001", [Slot('intent', 'name.query')]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) name.query(STATUS_WAIT_ACTION_CONFIRM)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(country=None) Slot(date=None) Slot(intent=name.query) Slot(location=None) Slot(meteorology=None)''') assert (dm._session.valid_session("sid001")) assert (dm.debug_loop == 1) time.sleep((dm.stack.top().timeout + 1) * dm.debug_timeunit) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT)''')
def _mix_trigger(self): dm = self._construct_dm() dm.process_slots("sid001", [ Slot("intent", "consume.query"), ]) dm.process_slots("sid002", [ Slot("intent", "travel.service"), ]) dm.process_confirm('sid002', { 'code': 0, }) dm.process_slots("sid003", [ Slot("intent", "consume.query"), ]) dm.process_confirm('sid003', { 'code': 0, }) dm.process_slots('sid004', [ Slot("intent", "weather.query"), Slot("date", "tomorrow"), Slot("city", "Shenzheng") ]) return dm
def test_optional(self): dm = construct_dm() dm.process_slots("sid001", [ Slot("intent", "weather.query"), Slot("city", "Shanghai"), Slot("date", "tomorrow"), Slot("meteorology", "rainy") ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) result(STATUS_WAIT_ACTION_CONFIRM)''') dm.process_confirm('sid001', {'code': 0, 'message': ''}) # The more slot match, the higher priority. # Three candicate nodes dm = construct_dm() dm.process_slots("sid001", [ Slot("intent", "weather.query"), Slot("city", "Beijing"), Slot("date", "tomorrow") ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) optional_none(STATUS_WAIT_ACTION_CONFIRM)''') dm.process_confirm('sid002', {'code': 0, 'message': ''}) # Given the same slot matches, # The more specified slots match, the higher priority. # Two candicate nodes dm = construct_dm() dm.process_slots("sid001", [ Slot("intent", "weather.query"), Slot("city", "Beijing"), Slot("date", "tomorrow"), Slot("meteorology", "rainy") ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) city_priority(STATUS_WAIT_ACTION_CONFIRM)''') dm.process_confirm('sid003', {'code': 0, 'message': ''}) # Addtional slots dm = construct_dm() dm.process_slots("sid001", [ Slot("intent", "weather.query"), Slot("city", "Beijing"), Slot("date", "tomorrow"), Slot("country", "china"), Slot("meteorology", "rainy"), ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) addtional_slot(STATUS_WAIT_ACTION_CONFIRM)''')
def test_mix_trigger(self): dm = self._construct_dm() dm.process_slots("sid001", [ Slot("intent", "consume.query"), ]) assert not dm.is_waiting assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(date=None) Slot(intent=None) Slot(location=None)''') # 2 context = dm.get_visible_units() priority_nodes = [agent[0] for agent in context["agents"]] assert (priority_nodes == [ 'home.service', 'travel.service', '*****@*****.**', 'city', 'date', u'result', 'nike', 'zhou_hei_ya', 'name.query', 'casual_talk' ]) assert (set(context["visible_slots"]) == set( ["date", "city", "location"])) dm.process_slots("sid002", [ Slot("intent", "travel.service"), ]) context = dm.get_visible_units() priority_nodes = [agent[0] for agent in context["agents"]] assert (priority_nodes == [ 'travel.service', 'travel_consume.query', 'travel_left.query', '*****@*****.**', 'city', 'date', 'result', 'home.service', u'nike', 'zhou_hei_ya', 'name.query', 'casual_talk', ]) assert (set(context["visible_slots"]) == set( ["date", "city", "location"])) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) Mix(Mix(weather.query))(STATUS_STACKWAIT) travel.service(STATUS_WAIT_ACTION_CONFIRM)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(date=None) Slot(intent=travel.service) Slot(location=None)''') dm.process_confirm('sid002', { 'code': 0, }) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) Mix(Mix(weather.query))(STATUS_DELAY_EXIST)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(date=None) Slot(intent=None) Slot(location=None)''') dm.process_slots("sid003", [ Slot("intent", "consume.query"), ]) # test ExpectAgenda assert (str(dm._agenda) == ''' ValidIntents: casual_talk consume.query home.service left.query name.query travel.service weather.query where.query ValidSlots: city date location''') assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) Mix(Mix(weather.query))(STATUS_STACKWAIT) travel_consume.query(STATUS_WAIT_ACTION_CONFIRM)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(date=None) Slot(intent=consume.query) Slot(location=None)''') assert (dm.is_waiting == True) # 3 dm.process_confirm('sid003', { 'code': 0, }) dm.process_slots('sid004', [ Slot("intent", "weather.query"), Slot("date", "tomorrow"), Slot("city", "Shenzheng") ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) Mix(Mix(weather.query))(STATUS_STACKWAIT) Mix(weather.query)(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) result(STATUS_WAIT_ACTION_CONFIRM)''') assert (str(dm.context) == ''' Context: Slot(city=Shenzheng) Slot(date=tomorrow) Slot(intent=weather.query) Slot(location=None)''') context = dm.get_visible_units() priority_nodes = [agent[0] for agent in context["agents"]] assert (priority_nodes == [ 'result', '*****@*****.**', 'city', 'date', 'all_city', 'travel.service', 'travel_consume.query', 'travel_left.query', 'home.service', 'nike', 'zhou_hei_ya', 'name.query', 'casual_talk' ]) assert (dm.debug_loop == 4) dm.cancel_timer()
def test_default_then_result(self): # default triggered dm = construct_dm() dm.process_slots("sid001", [Slot("intent", "weather.query")]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) [email protected](STATUS_WAIT_ACTION_CONFIRM)''') dm.process_confirm('sid001', {'code': 0, 'message': ''}) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) [email protected](STATUS_WAIT_TARGET)''') # result triggered dm.process_slots("sid002", [ Slot('intent', 'weather.query'), Slot('city', 'Shenzheng'), Slot('meteorology', 'snow'), Slot('date', 'today') ]) assert (str(dm.context) == ''' Context: Slot(city=Shenzheng) Slot(country=None) Slot(date=today) Slot(intent=weather.query) Slot(location=None) Slot(meteorology=snow)''') assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) result(STATUS_WAIT_ACTION_CONFIRM)''') dm.process_confirm('sid002', {'code': 0, 'message': ''}) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_DELAY_EXIST)''') # context preserve dm.process_slots("sid001", [ Slot('intent', 'weather.query'), Slot('city', 'Shanghai'), ]) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_STACKWAIT) result(STATUS_WAIT_ACTION_CONFIRM)''') assert (str(dm.context) == ''' Context: Slot(city=Shanghai) Slot(country=None) Slot(date=today) Slot(intent=weather.query) Slot(location=None) Slot(meteorology=snow)''') dm.process_confirm('sid001', {'code': 0, 'message': ''}) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT) weather.query(STATUS_DELAY_EXIST)''') # round out, clear context round_out_simulate(dm) assert (str(dm.stack) == ''' Stack: root(STATUS_STACKWAIT)''') assert (str(dm.context) == ''' Context: Slot(city=None) Slot(country=None) Slot(date=None) Slot(intent=None) Slot(location=None) Slot(meteorology=None)''')
def _deserialize_target_slots(self, data): for key in data['target_slots']: yield Slot(key)