Skip to content

Commit

Permalink
Add bot_config param with Configuration handling (#181)
Browse files Browse the repository at this point in the history
* Add support for chatbot-specific configuration
Update module init to set default config envvars
Add config documentation to README.md

* Refactor init
Update `ChatBot.log` to use bot_id for log name instead of class name

* Minor logging refactor

* Cleanup changes
Update documentation

---------

Co-authored-by: Daniel McKnight <daniel@neon.ai>
  • Loading branch information
NeonDaniel and NeonDaniel authored Dec 8, 2023
1 parent 5858658 commit bd57bca
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 15 deletions.
42 changes: 40 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,49 @@ To utilize this repository for creating your own chat bots, install this package

You can install this package with the following command:

`pip install git+https://github.com/neongeckocom/chatbot-core`
`pip install neon-chatbot-core`

*Note*: It is recommended to install this to a virtual environment to avoid conflicts with package versions and commandline
entry points. Most IDE's (i.e. [PyCharm](https://www.jetbrains.com/pycharm/)) handle this for individual projects.

### Configuration

#### Bot-specific configuration
Configuration for chatbots should be defined in `~/.config/neon/chatbots.yaml`
by default. Chatbots may be configured as:
```yaml
chatbots:
<bot_id>: {}
```
> For Klat v1, `bot_id` is the `username` the bot connects as, for MQ connected
> bots, `bot_id` is the MQ `service_name`.

Any bot-specific configuration will be accessible as `self.bot_config`. For Klat
v1 connections, `password` should be specified in the `chatbots`
config section.

#### MQ Connection configuration
For v2 bots, MQ connections must also be configured. This should be completed in
the same `~/.config/neon/chatbots.yaml` file as the bot-specific config.

```yaml
MQ:
server: mq.neonaiservices.com
port: 5672
users:
<bot_id>:
user: neon_bot_submind
password: <MQ user `neon_bot_submind`'s password>
```

#### SocketIO Connection configuration
For v1 bots, SIO connections may be configured in `~/.config/neon/chatbots.yaml`:
```yaml
socket_io:
server: 2222.us
port: 8888
```
### Organizing your bots
It is recommended to create a module for each of your bots. You should use subdirectories, each containing `__init__.py`
that includes your `ChatBot` as well as any supporting configuration files, etc. You may also organize this as a
Expand Down Expand Up @@ -47,7 +85,7 @@ my_bots
└--my_bot.py
```
### Klat.com Credentials
### Legacy Klat.com Credentials
Bots should be able to login to [klat.com](https://klat.com); a YAML file containing credentials for each bot can be used
to save usernames and passwords for each bot. Each bot module should have a key matching the module name, a `username`,
and a `password`.
Expand Down
10 changes: 8 additions & 2 deletions chatbot_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,22 @@
# US Patents 2008-2021: US7424516, US20140161250, US20140177813, US8638908, US8068604, US8553852, US10530923, US10530924
# China Patent: CN102017585 - Europe Patent: EU2156652 - Patents Pending

from ovos_utils.log import LOG, log_deprecation
from os import environ
environ.setdefault("OVOS_CONFIG_BASE_FOLDER", "neon")
environ.setdefault("OVOS_CONFIG_FILENAME", "chatbots.yaml")

from neon_utils.decorators import module_property


@module_property
def _ChatBot():
LOG.debug(f"Getting class for {environ.get('CHATBOT_VERSION')}")
from chatbot_core.utils.version_utils import get_class
return get_class()


@module_property
def _ConversationControls():
from ovos_utils.log import log_deprecation
log_deprecation("import from `chatbot_core.utils.enum` directly",
"3.0.0")
from chatbot_core.utils.enum import ConversationControls
Expand All @@ -39,27 +41,31 @@ def _ConversationControls():

@module_property
def _ConversationState():
from ovos_utils.log import log_deprecation
log_deprecation("import from `chatbot_core.utils.enum` directly",
"3.0.0")
from chatbot_core.utils.enum import ConversationState
return ConversationState


def generate_random_response(*args, **kwargs):
from ovos_utils.log import log_deprecation
log_deprecation("import from `chatbot_core.utils.bot_utils` directly",
"3.0.0")
from chatbot_core.utils.bot_utils import generate_random_response
return generate_random_response(*args, **kwargs)


def clean_up_bot(*args, **kwargs):
from ovos_utils.log import log_deprecation
log_deprecation("import from `chatbot_core.utils.bot_utils` directly",
"3.0.0")
from chatbot_core.utils.bot_utils import clean_up_bot
return clean_up_bot(*args, **kwargs)


def grammar_check(*args, **kwargs):
from ovos_utils.log import log_deprecation
log_deprecation("import from `chatbot_core.utils.bot_utils` directly",
"3.0.0")
from chatbot_core.utils.bot_utils import grammar_check
Expand Down
13 changes: 11 additions & 2 deletions chatbot_core/chatbot_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from abc import ABC, abstractmethod
from queue import Queue
from typing import Optional
from ovos_config.config import Configuration

from neon_utils.log_utils import init_log
from ovos_utils.log import LOG
Expand All @@ -31,14 +32,22 @@
class ChatBotABC(ABC):
"""Abstract class gathering all the chatbot-related methods children should implement"""

def __init__(self):
def __init__(self, bot_id: str, config: dict = None):
"""
Common chatbot initialization
@param bot_id: ID of this chatbot, used to read configuration
@param config: Dict configuration for this chatbot
"""
self._bot_id = bot_id
self.bot_config = config or Configuration().get("chatbots",
{}).get(bot_id) or {}
self.shout_queue = Queue(maxsize=256)
self.__log = None

@property
def log(self):
if not self.__log:
self.__log = init_log(log_name=self.__class__.__name__)
self.__log = init_log(log_name=self._bot_id)
return self.__log

@abstractmethod
Expand Down
4 changes: 1 addition & 3 deletions chatbot_core/utils/bot_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@ def start_bots(domain: str = None, bot_dir: str = None, username: str = None,
credentials = load_credentials_yml(cred_file)
else:
credentials = {}

processes = []

# Check for specified bot to start
Expand Down Expand Up @@ -604,8 +603,7 @@ def run_mq_bot(chatbot_name: str, vhost: str = '/chatbots',
@returns: Started ChatBotV2 instance
"""
from neon_utils.log_utils import init_log
init_log({"logs": {"level_overrides": {"error": ['pika'],
"warning": ["filelock"]}}})
init_log(log_name=chatbot_name)
os.environ['CHATBOT_VERSION'] = 'v2'
run_kwargs = run_kwargs or dict()
init_kwargs = init_kwargs or dict()
Expand Down
3 changes: 2 additions & 1 deletion chatbot_core/utils/version_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from typing import Optional
from chatbot_core.chatbot_abc import ChatBotABC
from ovos_utils.log import LOG


def get_class() -> Optional[type(ChatBotABC)]:
Expand All @@ -33,7 +34,7 @@ def get_class() -> Optional[type(ChatBotABC)]:
from chatbot_core.v2 import ChatBot as ChatBot_v2

version = os.environ.get('CHATBOT_VERSION', 'v1').lower()

LOG.debug(f"version={version}")
chatbot_versions = {
'v1': ChatBot_v1,
'v2': ChatBot_v2
Expand Down
10 changes: 7 additions & 3 deletions chatbot_core/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,14 @@ def __init__(self, *args, **kwargs):
socket, domain, username, password, on_server, is_prompter = \
self.parse_init(*args, **kwargs)
LOG.info(f"Starting {username}")
socket = socket or start_socket()
ChatBotABC.__init__(self, username)
if not socket:
from ovos_config.config import Configuration
sio_config = Configuration().get("socket_io", {})
socket = start_socket(addr=sio_config.get("server"),
port=sio_config.get("port"))
init_nick = "Prompter" if is_prompter else ""
KlatApi.__init__(self, socket, domain, init_nick)
ChatBotABC.__init__(self)
# self.log.debug("Connector started")
self.on_server = on_server
self.is_prompter = is_prompter
Expand All @@ -52,7 +56,7 @@ def __init__(self, *args, **kwargs):
self.selected_history = list()

self.username = username
self.password = password
self.password = password or self.bot_config.get("password")

self.facilitator_nicks = ["proctor", "scorekeeper", "stenographer"]
self.response_probability = 75 # % probability for a bot to respond to an input in non-proctored conversation
Expand Down
6 changes: 4 additions & 2 deletions chatbot_core/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ class ChatBot(KlatAPIMQ, ChatBotABC):

def __init__(self, *args, **kwargs):
config, service_name, vhost, bot_type = self.parse_init(*args, **kwargs)
KlatAPIMQ.__init__(self, config, service_name, vhost)
ChatBotABC.__init__(self)
mq_config = config.get("MQ") or config
bot_config = config.get("chatbots", {}).get(service_name)
KlatAPIMQ.__init__(self, mq_config, service_name, vhost)
ChatBotABC.__init__(self, service_name, bot_config)
self.bot_type = bot_type
self.current_conversations = dict()
self.on_server = True
Expand Down

0 comments on commit bd57bca

Please sign in to comment.