Closed acriptis closed 4 years ago
Hi,I like the idea and have added it to the backlog. Unfortunately I wont get round to it for a few weeks due to other planned work
@keiffster Finally I found the solution: I've inherited from BotClient the class named AIMLEmbeddedBotClient and reimplemented __init__
method (it required me to append source code root to sys.path to enable search of components and to change current working directory to the directory of config file because config files specify relative paths).
Another case is logging, I can not find the place but somewhere logging becomes = 10, when there is no config for logging (and this breaks the logging initialization). Do you know where parse_arguments produces _logging=10? (I've missed it in debug).
So finally it is quite dirty snippet, but it works:
import sys
import os
from programy.config.file.yaml_file import YamlConfigurationFile
from programy.config.programy import ProgramyConfiguration
from programy.clients.args import CommandLineClientArguments
from programy.clients.client import BotClient
from programy.utils.license.keys import LicenseKeys
from programy.utils.substitutions.substitues import Substitutions
from programy.clients.botfactory import BotFactory
from programy.clients.events.console.config import ConsoleConfiguration
class AIMLEmbeddedBotClient(BotClient):
def __init__(self, id, config_file_path, src_root_path=None):
"""
BotClient that can be initilized in runtime from YAML config
WARNING this module changes CWD (Current Working Directory)!
ProgramY has assumptions about current directories and uses environmental variables
to specify search paths when launched from bash scripts.
First, ProgramY uses file paths relative to config file.
Second, ProgramY allows to specify paths to modules which are in dot notation relative to
project root, which is different from config dir and usually placed 2 directories higher
In this module we gather all this configurations via parameters.
:param id: str, unique identifier of the bot
:param config_file_path: path to the YAML config file
:param src_root_path: sometimes YAML config path asserts that we reference to
modules which are part of another project, and src_modules_root_path - is a path from
which we look for specified modules. For example YAML config
joiner:
classname: templatey.processors.sentence_joiner_deduplicator.SentenceJoinerDeDuplicator
means that a class SentenceJoinerDeDuplicator will be searched from src_modules_root_path by appending dot prefixes.
"""
self._id = id
self._license_keys = LicenseKeys()
self._storage = None
self._scheduler = None
self._email = None
self._trigger_mgr = None
self._configuration = None
self._ping_responder = None
self._questions = 0
self._arguments = self.parse_arguments(argument_parser=None)
# hack:
if self._arguments._logging == 10:
self._arguments._logging = None
self.initiate_logging(self.arguments)
self._subsitutions = Substitutions()
if self.arguments.substitutions is not None:
self._subsitutions.load_substitutions(self.arguments.substitutions)
# specify configuration file
self._config_filename = config_file_path
self.load_configuration(self.arguments)
# self.parse_configuration()
self.load_storage()
##############################################################################
# set path because config uses relative paths
# this required so the files specified as relative paths in YAML will be interpreted
# correctly like in the example:
# categories_storage:
# dirs: ../../storage/categories
# subdirs: true
# extension: .aiml
current_dir_path = os.path.dirname(self._config_filename)
os.chdir(current_dir_path)
##############################################################################
##############################################################################
# to be able to find modules of such as SentenceDeduplicator
if not src_root_path:
src_root_path = os.path.dirname(os.path.dirname(current_dir_path))
src_root_path += "/src"
sys.path.append(src_root_path)
##############################################################################
self._bot_factory = BotFactory(self, self.configuration.client_configuration)
self.load_license_keys()
self.get_license_keys()
self._configuration.client_configuration.check_for_license_keys(self._license_keys)
self.load_scheduler()
self.load_renderer()
self.load_email()
self.load_trigger_manager()
self.load_ping_responder()
def get_client_configuration(self):
return ConsoleConfiguration()
def parse_arguments(self, argument_parser):
client_args = CommandLineClientArguments(self, parser=None)
return client_args
def load_configuration(self, arguments):
client_config = self.get_client_configuration()
self._configuration = ProgramyConfiguration(client_config)
yaml_file = YamlConfigurationFile()
yaml_file.load_from_file(self._config_filename, client_config, ".")
def process_question(self, client_context, question):
self._questions += 1
return client_context.bot.ask_question(client_context, question, responselogger=self)
def handle_user_message(self, user_id, message_text):
"""Interface method to retrieve response of particular bot for a message
from particular user"""
client_context = self.create_client_context(user_id)
response = self.process_question(client_context, message_text)
return response
Then I use it in following way:
embedded_bot = AIMLEmbeddedBotClient(id="koni", config_file_path=path_to_programy_config)
resp = embedded_bot.handle_user_message(user_id="test_user",
message_text="Hello I love you tell me what is your name")
Any tips and improvements to the code are appreciated!
Hello,
I need to initialize AIML Brain with several AIML files (not all in storage but subset of them) and then I would like to request the method
ask_question
(provide a context and the question) and receive a response (if it matched).What is the simplest way to do this? I've explored the embedded bot (https://github.com/keiffster/program-y/blob/master/src/programy/clients/embed/client.py#L24) but it requires configuration files, while I would like to pass some parameters (set of AIML files to be loaded to Brain) as arguments.
I explored some tests, like this: https://github.com/keiffster/program-y/blob/master/test/programytest/test_bot.py#L381-L399
Seems it is very similar to my case, but I dont understand what is the best way to specify set of allowed AIML files.
To be clear. I need to implement kind of this snippet:
Could you point to some examples in tests which may help to solve the problem or any other useful recommendations?
Thank you!