def test_it_should_add_parse_meta_to_intent_meta(self): greet_intent = Intent('greet') lights_intent = Intent('lights_on', room='kitchen') self.interpreter.parse = MagicMock(return_value=[ greet_intent, lights_intent, ]) self.agent.parse('hello, can you turn the lights on in kitchen', latitude=49.44, longitude=1.09) expect(greet_intent.meta).to.equal({ 'latitude': 49.44, 'longitude': 1.09 }) expect(lights_intent.meta).to.equal({ 'latitude': 49.44, 'longitude': 1.09 }) expect(last_request.intent.meta).to.equal({ 'latitude': 49.44, 'longitude': 1.09 })
def test_it_should_handle_multiple_intents(self): self.interpreter.parse = MagicMock(return_value=[ Intent('greet'), Intent('block'), Intent('lights_on', room='kitchen'), ]) self.agent.parse('hello, can you turn the lights on in kitchen') self.on_answer.assert_called_once_with('Hello you!', None, raw_text='Hello you!') self.on_done.assert_called_once_with(True) expect(self.on_thinking.call_count).to.equal( 2) # One for greet, the other for block greet_id = last_request.id expect(self.agent.state).to.equal('block') # Since the block intent does not call it to test it more easily, call the agent.done now to process the next intent self.agent.done() self.on_answer.assert_called_with( 'Turning lights on in kitchen', None, raw_text='Turning lights on in kitchen') expect(self.on_done.call_count).to.equal(3) expect(self.on_thinking.call_count).to.equal(3) expect(last_request.id).to_not.equal(greet_id) expect(self.agent.state).to.equal(STATE_ASLEEP)
def test_it_should_not_be_able_to_transition_to_nested_state_if_not_in_the_context( self): self.handlers._data['manage_list/rename'] = MagicMock() self.agent.go('manage_list/rename', intent=Intent('manage_list/rename')) self.handlers._data['manage_list/rename'].assert_not_called() self.agent.go('manage_list', intent=Intent('manage_list')) self.agent.go('manage_list/rename', intent=Intent('manage_list/rename')) self.handlers._data['manage_list/rename'].assert_called_once()
def test_it_should_cancel_intent_when_cancel_is_caught(self): self.interpreter.parse = MagicMock(return_value=[Intent('lights_on')]) self.agent.parse('turn the lights on') expect(self.agent.state).to.equal(STATE_ASK) self.interpreter.parse = MagicMock(return_value=[Intent(STATE_CANCEL)]) self.agent.parse('cancel') self.on_answer.assert_called_once_with('Cancelled', None, raw_text='Cancelled') expect(self.agent.state).to.equal(STATE_ASLEEP)
def test_it_should_be_in_the_ask_state_when_a_skill_ask_for_slot(self): self.interpreter.parse = MagicMock( return_value=[Intent('get_forecast')]) self.agent.parse('will it be sunny') initial_request_id = last_request.id self.on_ask.assert_called_once_with('date', 'When?', None, raw_text='When?') self.on_done.assert_called_once_with(True) self.on_answer.assert_not_called() expect(self.agent.state).to.equal(STATE_ASK) self.agent.parse('tomorrow') expect(last_request.intent.slot('date').first().value).to.equal( 'tomorrow') self.on_ask.assert_any_call('city', 'Where?', None, raw_text='Where?') expect(self.on_done.call_count).to.equal(2) self.on_answer.assert_not_called() expect(self.agent.state).to.equal(STATE_ASK) self.agent.parse('rouen') expect( last_request.intent.slot('city').first().value).to.equal('rouen') self.on_answer.assert_called_once() expect(self.on_answer.call_args[0][0]).to.equal( 'Looking in rouen for tomorrow') expect(self.agent.state).to.equal(STATE_ASLEEP) expect(self.on_done.call_count).to.equal(3) expect(last_request.id).to.equal(initial_request_id)
def test_it_should_go_back_to_the_asleep_state_if_an_exception_occurs_in_a_skill( self): self.interpreter.parse = MagicMock( return_value=[Intent('raise_exception')]) self.agent.parse('will raise an exception') self.on_done.assert_called_once_with(False) expect(self.agent.state).to.equal(STATE_ASLEEP)
def test_it_should_handle_context_switching_from_a_skill(self): self.on_context.assert_called_once_with(None) self.on_context.reset_mock() self.interpreter.parse = MagicMock( return_value=[Intent('manage_list')]) self.agent.parse('I want to manage a list') self.on_context.assert_called_once_with('manage_list') self.on_context.reset_mock() self.interpreter.parse.assert_called_once() call_args = self.interpreter.parse.call_args[0] expect(call_args).to.have.length_of(2) expect(call_args[0]).to.equal('I want to manage a list') expect(call_args[1]).to.be.a(list) expect(call_args[1]).to.have.length_of(3) expect(call_args[1]).to.contain(STATE_CANCEL) expect(call_args[1]).to.contain('manage_list') expect(call_args[1]).to.contain('open_context') self.interpreter.parse.reset_mock() expect(self.agent.current_context).to.equal('manage_list') expect(self.agent._current_scopes).to.have.length_of(3) expect(self.agent._current_scopes).to.contain(STATE_CANCEL) expect(self.agent._current_scopes).to.contain('manage_list/rename') expect(self.agent._current_scopes).to.contain('manage_list/close') self.interpreter.parse = MagicMock( return_value=[Intent('manage_list/close')]) self.agent.parse('I am done with that list') self.on_context.assert_called_once_with(None) self.interpreter.parse.assert_called_once() call_args = self.interpreter.parse.call_args[0] expect(call_args).to.have.length_of(2) expect(call_args[0]).to.equal('I am done with that list') expect(call_args[1]).to.be.a(list) expect(call_args[1]).to.have.length_of(3) expect(call_args[1]).to.contain(STATE_CANCEL) expect(call_args[1]).to.contain('manage_list/rename') expect(call_args[1]).to.contain('manage_list/close') expect(self.agent.current_context).to.be.none
def test_it_should_return_to_the_root_context_if_cancelled_and_not_specific_handler( self): self.interpreter.parse = MagicMock( return_value=[Intent('open_context')]) self.agent.parse('Switch to a context') expect(self.agent.current_context).to.equal('open_context') self.interpreter.parse = MagicMock(return_value=[Intent(STATE_CANCEL)]) self.agent.parse('Cancel') expect(self.agent.current_context).to.be.none expect(self.agent._current_scopes).to.have.length_of(3) expect(self.agent._current_scopes).to.contain(STATE_CANCEL) expect(self.agent._current_scopes).to.contain('manage_list') expect(self.agent._current_scopes).to.contain('open_context')
def test_it_should_do_nothing_and_returns_to_the_asleep_state_when_no_handler_was_found( self): self.interpreter.parse = MagicMock( return_value=[Intent('intent_without_handler')]) self.agent.parse('an intent without a handler') self.on_done.assert_called_once_with(False) expect(self.agent.state).to.equal(STATE_ASLEEP)
def test_it_should_pass_answer_meta_to_the_handler(self): self.interpreter.parse = MagicMock( return_value=[Intent('with_meta', name='Julien')]) self.agent.parse('trigger with_meta handler') self.on_answer.assert_called_once_with('Hello **Julien**', None, raw_text='Hello Julien', trigger_listening=True)
def test_it_should_handle_simple_intent(self): self.interpreter.parse = MagicMock(return_value=[Intent('greet')]) self.agent.parse('hello') self.on_answer.assert_called_once_with('Hello you!', None, raw_text='Hello you!') self.on_done.assert_called_once_with(True) expect(self.agent.state).to.equal(STATE_ASLEEP)
def test_it_should_pass_ask_meta_to_the_handler(self): self.interpreter.parse = MagicMock(return_value=[Intent('with_meta')]) self.agent.parse('trigger with_meta handler') self.on_ask.assert_called_once_with('name', 'Whom?', None, raw_text='Whom?', special_meta='something')
def test_it_should_trigger_intent(self): self.agent.queue_intent(Intent('greet')) expect(last_request.intent.name).to.equal('greet') self.on_answer.assert_called_once_with('Hello you!', None, raw_text='Hello you!') self.on_done.assert_called_once_with(True) self.on_thinking.assert_called_once() expect(self.agent.state).to.equal(STATE_ASLEEP)
def test_it_should_parse_kwargs_as_slot_values(self): intent = Intent('get_forecast', date='today', city=['Paris', 'New York']) expect(intent.name).to.be.equal('get_forecast') expect(intent.slots).to.have.length_of(2) expect(intent.slots).to.have.key('date') expect(intent.slots).to.have.key('city') date = intent.slot('date') expect(date).to.be.a(SlotValues) expect(date).to.have.length_of(1) expect(date.first().value).to.equal('today') city = intent.slot('city') expect(city).to.be.a(SlotValues) expect(city).to.have.length_of(2) expect(city.first().value).to.equal('Paris') expect(city.last().value).to.equal('New York')
def queue_intent(self, intent: Union[str, Intent], **slots) -> None: """Queue the given intent and process it if the agent is asleep. Args: intent (str, Intent): Intent to process as soon as possible slots (dict): When intent is an str, parses those data as slot values """ if isinstance(intent, str): intent = Intent(intent, **slots) self._intents_queue.append(intent) if self.state == STATE_ASLEEP: # pylint: disable=no-member self._process_next_intent()
def test_it_should_have_cards(self): self.interpreter.parse = MagicMock(return_value=[ Intent('get_forecast', date='tomorrow', city='rouen') ]) self.agent.parse('will it rain in rouen tomorrow') self.on_answer.assert_called_once() call_args = self.on_answer.call_args[0] expect(call_args).to.have.length_of(2) expect(call_args[0]).to.equal('Looking in rouen for tomorrow') expect(call_args[1]).to.be.a(list) expect(call_args[1]).to.have.length_of(1) expect(call_args[1][0].header).to.equal('rouen') expect(call_args[1][0].text).to.equal('tomorrow')
def test_it_should_match_on_choices_when_asking_with_choices(self): self.interpreter.parse = MagicMock(return_value=[Intent('lights_on')]) self.agent.parse('turn the lights on') self.on_ask.assert_called_once_with( 'room', 'Which ones?', ['kitchen', 'living room', 'bedroom'], raw_text='Which ones?') expect(self.agent.state).to.equal(STATE_ASK) self.agent.parse('in the living room') self.on_answer.assert_called_with( 'Turning lights on in living room', None, raw_text='Turning lights on in living room') expect(self.on_done.call_count).to.equal(2)
def test_it_should_call_context_specific_handler_if_any_for_builtin_intents( self): self.interpreter.parse = MagicMock( return_value=[Intent('manage_list')]) self.agent.parse('I want to manage a list') expect(self.agent.current_context).to.equal('manage_list') self.interpreter.parse = MagicMock(return_value=[]) self.agent.parse('Buy some eggs') expect(self.agent.current_context).to.equal('manage_list') expect(self.agent.meta).to.have.key('list') expect(self.agent.meta['list']).to.contain('Buy some eggs') self.agent.parse('Buy milk too') expect(self.agent.current_context).to.equal('manage_list') expect(self.agent.meta).to.have.key('list') expect(self.agent.meta['list']).to.have.length_of(2) expect(self.agent.meta['list']).to.contain('Buy milk too')
def test_it_should_contains_the_intent_name(self): intent = Intent('get_forecast') expect(intent.name).to.be.equal('get_forecast') expect(intent.meta).to.be.empty expect(intent.slots).to.be.empty
def parse(self, msg: str, **meta) -> None: """Parse a raw message. The interpreter will be used to determine which intent(s) has been formulated by the user and the state machine will move to the appropriate state calling the right skill handler. It will also handle some specific intents such as the cancel one and ask states. Args: msg (str): Raw message to parse meta (dict): Optional metadata to add to the request object """ self._logger.info('Parsing sentence "%s"', msg) intents = self._interpreter.parse( msg, self._current_scopes) or [Intent(STATE_FALLBACK, text=msg)] # Add meta to each parsed intents for intent in intents: intent.meta.update(meta) cancel_intent = next((i for i in intents if i.name == STATE_CANCEL), None) self._logger.info('"%d" intent(s) found: %s', len(intents), ', '.join([str(i) for i in intents])) # Either way, extend the intent queue with new intents if self.state != STATE_ASK: # pylint: disable=no-member intents = [i for i in intents if i.name != STATE_CANCEL] self._intents_queue.extend(intents) # If the user wants to cancel the current action, immediately go to the cancel state if cancel_intent: self.go(STATE_CANCEL, intent=cancel_intent) else: if self.state == STATE_ASK: # pylint: disable=no-member # If choices are limited, try to extract a match if self._choices: msg = find_match(self._choices, msg) # Here msg will be None if choices could not be matched, so nothing # should be done anymore if msg: values = self._interpreter.parse_slot( self._request.intent.name, self._asked_slot, msg) # Update slots and meta self._request.intent.update_slots( **{self._asked_slot: values}) self._request.intent.meta.update(meta) self._logger.info('Updated slot "%s" with values %s', self._asked_slot, ['"%s"' % v for v in values]) self.go(self._request.intent.name, intent=self._request.intent) elif self.state == STATE_ASLEEP: # pylint: disable=no-member self._process_next_intent()