Skip to content

Support ChatBot health checks #203

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/license_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ jobs:
license_tests:
uses: neongeckocom/.github/.github/workflows/license_tests.yml@master
with:
packages-exclude: '^(bs4|nvidia|bitstruct|audioread|klat-connector|neon-chatbot-core|tqdm|dnspython|RapidFuzz|typing_extensions|wheel|urllib).*'
packages-exclude: '^(bs4|nvidia|bitstruct|audioread|klat-connector|neon-chatbot-core|tqdm|dnspython|RapidFuzz|typing_extensions|wheel|urllib|click|typing-inspection).*'
8 changes: 8 additions & 0 deletions chatbot_core/chatbot_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

from neon_utils.log_utils import init_log
from ovos_utils.log import LOG
from ovos_utils.process_utils import ProcessStatus


class ChatBotABC(ABC):
Expand All @@ -45,6 +46,13 @@ def __init__(self, bot_id: str, config: dict = None):
self.shout_queue = Queue(maxsize=256)
self.__log = None

@property
def status(self) -> Optional[ProcessStatus]:
""""Get a ProcessStatus object for this bot"""
if hasattr(self, '_status'):
return self._status
return None

@property
def log(self):
if not self.__log:
Expand Down
9 changes: 8 additions & 1 deletion chatbot_core/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import argparse
import click

from typing import Optional
from os.path import expanduser, relpath

from ovos_utils import wait_for_exit_signal
Expand All @@ -40,11 +41,17 @@ def chatbot_core_cli(version: bool = False):


@chatbot_core_cli.command(help="Start an MQ chatbot")
@click.option("--health-check-server-port", "-hp", type=int, default=None,
help="Port for health check server to listen on")
@click.argument("bot_entrypoint")
def start_mq_bot(bot_entrypoint):
def start_mq_bot(bot_entrypoint, health_check_server_port: Optional[int] = None):
os.environ['CHATBOT_VERSION'] = 'v2'
from chatbot_core.utils.bot_utils import run_mq_bot
bot = run_mq_bot(bot_entrypoint)
if health_check_server_port:
from neon_utils.process_utils import start_health_check_server
start_health_check_server(bot.status, health_check_server_port,
bot.check_health)
wait_for_exit_signal()
bot.stop()

Expand Down
10 changes: 9 additions & 1 deletion chatbot_core/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from threading import Thread
from klat_connector.klat_api import KlatApi
from klat_connector import start_socket
from ovos_utils.log import LOG
from ovos_utils.process_utils import ProcessStatus

from chatbot_core.utils.enum import ConversationState, ConversationControls, BotTypes
from chatbot_core.utils.string_utils import remove_prefix
Expand All @@ -35,6 +35,8 @@

class ChatBot(KlatApi, ChatBotABC):
def __init__(self, *args, **kwargs):
self._status = ProcessStatus("chatbot")
self._status.set_alive()
socket, domain, username, password, on_server, is_prompter = \
self.parse_init(*args, **kwargs)
ChatBotABC.__init__(self, username)
Expand Down Expand Up @@ -94,6 +96,11 @@ def __init__(self, *args, **kwargs):

self.shout_thread = Thread(target=self._handle_next_shout, daemon=True)
self.shout_thread.start()
self._status.set_ready()

def check_health(self) -> bool:
# Unimplemented health check, assume service is healthy
return True

def parse_init(self, *args, **kwargs) -> tuple:
"""Parses dynamic params input to ChatBot v1"""
Expand Down Expand Up @@ -637,6 +644,7 @@ def exit(self):
from chatbot_core.utils.bot_utils import clean_up_bot
# import sys
# self.socket.disconnect()
self._status.set_stopping()
while not self.shout_queue.empty():
self.shout_queue.get(timeout=1)
clean_up_bot(self)
Expand Down
18 changes: 18 additions & 0 deletions chatbot_core/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@

from neon_mq_connector.utils import RepeatingTimer
from neon_mq_connector.utils.rabbit_utils import create_mq_callback
from neon_mq_connector.connector import MQConnector
from klat_connector.mq_klat_api import KlatAPIMQ
from pika.exchange_type import ExchangeType
from neon_data_models.models.api.mq.chatbots import ChatbotsMqRequest, \
ChatbotsMqSubmindResponse
from neon_data_models.enum import CcaiState as ConversationState
from ovos_utils.process_utils import ProcessStatus

from chatbot_core.utils.enum import BotTypes
from chatbot_core.chatbot_abc import ChatBotABC
Expand All @@ -41,6 +43,8 @@ class ChatBot(KlatAPIMQ, ChatBotABC):

def __init__(self, *args, **kwargs):
config, service_name, vhost, bot_type = self.parse_init(*args, **kwargs)
self._status = ProcessStatus(service_name)
self._status.set_alive()
mq_config = config.get("MQ") or config
bot_config = config.get("chatbots", {}).get(service_name)
KlatAPIMQ.__init__(self, mq_config, service_name, vhost)
Expand All @@ -57,6 +61,18 @@ def __init__(self, *args, **kwargs):
self.shout_thread = RepeatingTimer(function=self._handle_next_shout,
interval=kwargs.get('shout_thread_interval', 10))
self.shout_thread.start()
self._status.set_ready()

def check_health(self) -> bool:
if not MQConnector.check_health(self):
self.log.error("MQ connection is not healthy")
self._status.set_error("MQ connection is not healthy")
return False
if not self.shout_thread.is_alive():
self.log.error("Shout thread is not alive")
self._status.set_error("Shout thread is not alive")
return False
return True

def parse_init(self, *args, **kwargs) -> tuple:
"""Parses dynamic params input to ChatBot v2"""
Expand Down Expand Up @@ -620,10 +636,12 @@ def stop_shout_thread(self):
self.shout_thread = None

def shutdown(self):
# TODO: not used?
self.shout_thread.cancel()
self.shout_thread.join()

def stop(self):
self._status.set_stopping()
self.stop_shout_thread()
KlatAPIMQ.stop(self)

Expand Down
7 changes: 4 additions & 3 deletions requirements/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
click~=8.0
klat-connector~=0.8
neon-mq-connector~=0.8
neon-utils[network,sentry]~=1.12
neon-mq-connector~=0.8,>=0.9.1a1
neon-utils[network,sentry]~=1.12,>=1.12.2a2
ovos-utils~=0.0
ovos-bus-client~=0.0,>=0.0.5
psutil~=5.7
neon-data-models~=0.0,>=0.0.2a2
neon-data-models~=0.0,>=0.0.2a2
Loading