def get_dialogue_intents(dialogue: Dict, exclude_none: bool = True) -> Set[str]: """Returns the intents in a dialogue. Parameters ---------- dialogue Nested dictionary containing dialogue and annotations. exclude_none If True, the `NONE` intent is not included in the intents set. Returns ------- intents A set of intents contained in the dialogue. """ intents = set() for turn in dialogue_iterator(dialogue, user=True, system=False): for frame in turn['frames']: intent = frame['state']['active_intent'] if exclude_none: if intent == 'NONE': continue else: intents.add(intent) else: intents.add(intent) return intents
def _get_entity_slots( split: Literal['train', 'test', 'dev']) -> Dict[str, Dict[str, Set[str]]]: """Find the slots that are always specified by the system when a call to a "search" intent is made (referred to as "entity" slots). Parameters ---------- split The data split for which the entity slots are to be determined Returns ------- entity_slots_map A mapping of the form:: { 'service_name': {'intent_1': {'slot_name',...} ... } """ # select only dialogues where the system speaks about an entity following a search call filtered_dialogues = filter_by_intent_type(split, transactional=True, search=True) search_only = filter_by_intent_type(split, transactional=False, search=True) for file_id, dial_ids in search_only.items(): if file_id in filtered_dialogues: filtered_dialogues[file_id].update(dial_ids) else: filtered_dialogues[file_id] = dial_ids # iterate through selected dialogues to find slots that are always specified after a # call to a given intent ("entity slot"). entity_slots_map = defaultdict(lambda: collections.defaultdict(set)) search_intents = set(get_intents_by_type()['search']) for file in filtered_dialogues.keys(): for fp, dial in file_iterator(file, return_only=filtered_dialogues[file]): for turn in dialogue_iterator(dial, user=False, system=True): if 'service_call' in (frame := turn['frames'][0]): service = frame['service'] intent = frame['service_call']['method'] service_results = frame['service_results'] if intent in search_intents and service_results: mentioned_slots = { entry['slot'] for entry in frame['slots'] } if intent in entity_slots_map[service]: entity_slots_map[service][intent] = \ entity_slots_map[service][intent].intersection(mentioned_slots) else: entity_slots_map[service][intent] = mentioned_slots
def has_requestables(dialogue: dict) -> bool: """Returns `True` if the user requests information from the system and false otherwise. """ for turn in dialogue_iterator(dialogue, user=True, system=False): for frame in turn['frames']: if frame['state']['requested_slots']: return True return False
def get_dialogues_by_type( intents_mapping: dict) -> Dict[str, Dict[str, List[str]]]: """Returns a mapping from dialogue type (transactional intent only, search intent only, mixed intent) to dialogue IDs. Returns ------- dialogues_by_type: A mapping of the form:: { 'transactional: ['dialogue_id',...], 'search': ['dialogue_id',...], 'mixed_intent': ['dialogue_id',...] } """ dialogues_by_type = collections.defaultdict( lambda: collections.defaultdict(list)) for split in _SPLIT_NAMES: for _, dial in split_iterator(split): dialogue_id = dial['dialogue_id'] transactional, search = False, False for turn in dialogue_iterator(dial, user=True, system=False): for frame in turn['frames']: active_intent = frame['state']['active_intent'] if active_intent != 'NONE': if active_intent in intents_mapping['transactional']: transactional = True else: search = True if transactional and search: break if transactional and not search: dialogues_by_type[split]['transactional'].append(dialogue_id) elif search and not transactional: dialogues_by_type[split]['search'].append(dialogue_id) else: dialogues_by_type[split]['mixed_intent'].append(dialogue_id) for intent_type in dialogues_by_type: dialogues_by_type[split][intent_type].sort(key=dial_sort_key) return dialogues_by_type
def test_get_entity_slots_map(n_dialogues, split, trials): expected_output = get_entity_slots_map() for _ in range(trials): for dial in random_sampler(split, n_dialogues): # output of sampler is (filename, dial) dial_id = dial[1]['dialogue_id'] mixed_intent = dial_id in metadata.MIXED_INTENT_DIALOGUES search = dial_id in metadata.SEARCH_DIALOGUES if mixed_intent or search: for turn in dialogue_iterator(dial, user=False, system=True): frame = turn['frames'][0] intent = frame['service_call']['method'] if ('service_call' in frame) and (intent in metadata.SEARCH_INTENTS): if 'service_results' in frame: expected = expected_output[frame['service']][intent] actual = set(slot_dict['slot'] for slot_dict in frame['slots']) assert not expected - actual
def _get_requestables(dialogue: dict) -> Set[str]: """Get requestable slots in a given dialogue. Parameters ---------- dialogue A nested dictionary representation of the dialogue in SGD format. Returns ------- requestables Set of slots which appear in the ``['state']['requested_slots']`` of all user frames. """ requestables = set() for turn in dialogue_iterator(dialogue, user=True, system=False): for frame in turn['frames']: if reqs := frame['state']['requested_slots']: requestables.update(reqs)