Source code for deeppavlov.skills.aiml_skill.aiml_skill

# Copyright 2017 Neural Networks and Deep Learning lab, MIPT
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.

import uuid
from logging import getLogger
from pathlib import Path
from typing import Tuple, Optional, List

import aiml

from deeppavlov.core.common.registry import register
from deeppavlov.core.models.component import Component

log = getLogger(__name__)

[docs]@register("aiml_skill") class AIMLSkill(Component): """Skill wraps python-aiml library into DeepPavlov interfrace. AIML uses directory with AIML scripts which are loaded at initialization and used as patterns for answering at each step. """ def __init__(self, path_to_aiml_scripts: str, positive_confidence: float = 0.66, null_response: str = "I don't know what to answer you", null_confidence: float = 0.33, **kwargs ) -> None: """ Construct skill: read AIML scripts, load AIML kernel Args: path_to_aiml_scripts: string path to folder with AIML scripts null_response: Response string to answer if no AIML Patterns matched positive_confidence: The confidence of response if response was found in AIML scripts null_confidence: The confidence when AIML scripts has no rule for responding and system returns null_response """ # we need absolute path (expanded for user home and resolved if it relative path): self.path_to_aiml_scripts = Path(path_to_aiml_scripts).expanduser().resolve()"path_to_aiml_scripts is: `{self.path_to_aiml_scripts}`") self.positive_confidence = positive_confidence self.null_confidence = null_confidence self.null_response = null_response self.kernel = aiml.Kernel() # to block AIML output: self.kernel._verboseMode = False self._load_scripts() def _load_scripts(self) -> None: """ Scripts are loaded recursively from files with extensions .xml and .aiml Returns: None """ # learn kernel to all aimls in directory tree: all_files = sorted(self.path_to_aiml_scripts.rglob('*.*')) learned_files = [] for each_file_path in all_files: if each_file_path.suffix in ['.aiml', '.xml']: # learn the script file self.kernel.learn(str(each_file_path)) learned_files.append(each_file_path) if not learned_files: log.warning(f"No .aiml or .xml files found for AIML Kernel in directory {self.path_to_aiml_scripts}") def process_step(self, utterance_str: str, user_id: any) -> Tuple[str, float]: response = self.kernel.respond(utterance_str, sessionID=user_id) # here put your estimation of confidence: if response: # print(f"AIML responds: {response}") confidence = self.positive_confidence else: # print("AIML responses silently...") response = self.null_response confidence = self.null_confidence return response, confidence def _generate_user_id(self) -> str: """Here you put user id generative logic if you want to implement it in the skill. Returns: user_id: Random generated user ID. """ return uuid.uuid1().hex def __call__(self, utterances_batch: List[str], states_batch: Optional[List] = None) -> Tuple[List[str], List[float], list]: """Returns skill inference result. Returns batches of skill inference results, estimated confidence levels and up to date states corresponding to incoming utterance batch. Args: utterances_batch: A batch of utterances of str type. states_batch: A batch of arbitrary typed states for each utterance. Returns: response: A batch of arbitrary typed skill inference results. confidence: A batch of float typed confidence levels for each of skill inference result. output_states_batch: A batch of arbitrary typed states for each utterance. """ # grasp user_ids from states batch. # We expect that skill receives None or dict of state for each utterance. # if state has user_id then skill uses it, otherwise it generates user_id and calls the # user with this name in further. # In this implementation we use current datetime for generating uniqe ids output_states_batch = [] user_ids = [] if states_batch is None: # generate states batch matching batch of utterances: states_batch = [None] * len(utterances_batch) for state in states_batch: if not state: user_id = self._generate_user_id() new_state = {'user_id': user_id} elif 'user_id' not in state: new_state = state user_id = self._generate_user_id() new_state['user_id'] = self._generate_user_id() else: new_state = state user_id = new_state['user_id'] user_ids.append(user_id) output_states_batch.append(new_state) confident_responses = map(self.process_step, utterances_batch, user_ids) responses_batch, confidences_batch = zip(*confident_responses) return responses_batch, confidences_batch, output_states_batch