From d3bc687772a3628bea32b8f3c3ad8d4c11c1995d Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 14 Jan 2024 22:55:14 +0100 Subject: [PATCH 01/20] Introduced Klat personas management with persistent synchronisation + fallback on the initial implementation --- neon_llm_core/rmq.py | 20 +++++- neon_llm_core/utils/__init__.py | 25 +++++++ neon_llm_core/{ => utils}/config.py | 4 +- neon_llm_core/utils/constants.py | 27 +++++++ neon_llm_core/utils/personas/provider.py | 91 ++++++++++++++++++++++++ neon_llm_core/utils/personas/state.py | 48 +++++++++++++ 6 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 neon_llm_core/utils/__init__.py rename neon_llm_core/{ => utils}/config.py (97%) create mode 100644 neon_llm_core/utils/constants.py create mode 100644 neon_llm_core/utils/personas/provider.py create mode 100644 neon_llm_core/utils/personas/state.py diff --git a/neon_llm_core/rmq.py b/neon_llm_core/rmq.py index c042542..6f2015b 100644 --- a/neon_llm_core/rmq.py +++ b/neon_llm_core/rmq.py @@ -31,8 +31,10 @@ from neon_mq_connector.utils.rabbit_utils import create_mq_callback from ovos_utils.log import LOG -from neon_llm_core.config import load_config +from neon_llm_core.utils.config import load_config from neon_llm_core.llm import NeonLLM +from neon_llm_core.utils.constants import LLM_VHOST +from neon_llm_core.utils.personas.provider import PersonasProvider class NeonLLMMQConnector(MQConnector, ABC): @@ -45,11 +47,13 @@ def __init__(self): self.ovos_config = load_config() mq_config = self.ovos_config.get("MQ", dict()) super().__init__(config=mq_config, service_name=self.service_name) - self.vhost = "/llm" + self.vhost = LLM_VHOST self.register_consumers() self._model = None self._bots = list() + self._personas_provider = PersonasProvider(service_name=self.name, + ovos_config=self.ovos_config) if self.ovos_config.get("llm_bots", {}).get(self.name): from neon_llm_core.chatbot import LLMBot @@ -242,3 +246,15 @@ def compose_opinion_prompt(respondent_nick: str, question: str, @param answer: respondent's response to the question """ pass + + def run(self, run_consumers: bool = True, run_sync: bool = True, + run_observer: bool = True, **kwargs): + super().run(run_consumers=run_consumers, + run_sync=run_sync, + run_observer=run_observer, + **kwargs) + self._personas_provider.start_sync() + + def stop(self): + super().stop() + self._personas_provider.stop_sync() diff --git a/neon_llm_core/utils/__init__.py b/neon_llm_core/utils/__init__.py new file mode 100644 index 0000000..250e4cd --- /dev/null +++ b/neon_llm_core/utils/__init__.py @@ -0,0 +1,25 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 NeonGecko.com Inc. +# BSD-3 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/neon_llm_core/config.py b/neon_llm_core/utils/config.py similarity index 97% rename from neon_llm_core/config.py rename to neon_llm_core/utils/config.py index a4c2e51..7dfff89 100644 --- a/neon_llm_core/config.py +++ b/neon_llm_core/utils/config.py @@ -31,6 +31,8 @@ from ovos_utils.log import LOG from ovos_config.config import Configuration +from neon_llm_core.utils.constants import LLM_VHOST + def load_config() -> dict: """ @@ -56,4 +58,4 @@ class LLMMQConfig: ask_response_queue: str ask_appraiser_queue: str ask_discusser_queue: str - vhost: str = '/llm' + vhost: str = LLM_VHOST diff --git a/neon_llm_core/utils/constants.py b/neon_llm_core/utils/constants.py new file mode 100644 index 0000000..7acff7a --- /dev/null +++ b/neon_llm_core/utils/constants.py @@ -0,0 +1,27 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 NeonGecko.com Inc. +# BSD-3 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +LLM_VHOST = '/llm' diff --git a/neon_llm_core/utils/personas/provider.py b/neon_llm_core/utils/personas/provider.py new file mode 100644 index 0000000..610613e --- /dev/null +++ b/neon_llm_core/utils/personas/provider.py @@ -0,0 +1,91 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 NeonGecko.com Inc. +# BSD-3 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import os +from time import time + +from neon_mq_connector.utils import RepeatingTimer +from neon_mq_connector.utils.client_utils import send_mq_request +from ovos_utils.log import LOG + +from neon_llm_core.utils.constants import LLM_VHOST +from neon_llm_core.utils.personas.state import PersonaHandlersState + + +class PersonasProvider: + + PERSONA_STATE_TTL = int(os.getenv("PERSONA_STATE_TTL", 15 * 60)) + PERSONA_SYNC_INTERVAL = int(os.getenv("PERSONA_SYNC_INTERVAL", 5 * 60)) + + def __init__(self, service_name: str, ovos_config: dict): + self.service_name = service_name + self._persona_handlers_state = PersonaHandlersState(service_name=service_name, + ovos_config=ovos_config) + self._personas = [] # list of personas available for given service + self._persona_last_sync = 0 + self._persona_sync_thread = None + + @property + def persona_sync_thread(self): + """Creates new synchronization thread which fetches Klat personas""" + if not (isinstance(self._persona_sync_thread, RepeatingTimer) and + self._persona_sync_thread.is_alive()): + self._persona_sync_thread = RepeatingTimer(self.PERSONA_SYNC_INTERVAL, + self._fetch_persona_config) + self._persona_sync_thread.daemon = True + return self._persona_sync_thread + + @property + def personas(self): + return self._personas + + @personas.setter + def personas(self, data): + now = int(time()) + LOG.debug(f'Setting personas={data}') + if data and isinstance(data, list): + self._personas = data + self._persona_last_sync = now + elif now - self._persona_last_sync > self.PERSONA_STATE_TTL: + LOG.warning(f'Persona state TTL expired, resetting personas config') + self._personas = [] + self._persona_handlers_state.init_default_handlers() + + def _fetch_persona_config(self): + response = send_mq_request(vhost=LLM_VHOST, + request_data={"service_name": self.service_name}, + target_queue="get_configured_personas") + self.personas = response.get('items', []) + for persona in self.personas: + if persona: + self._persona_handlers_state.add_persona_handler(persona=persona) + + def start_sync(self): + self.persona_sync_thread.start() + + def stop_sync(self): + if self._persona_sync_thread: + self._persona_sync_thread.cancel() + self._persona_sync_thread = None diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py new file mode 100644 index 0000000..03289db --- /dev/null +++ b/neon_llm_core/utils/personas/state.py @@ -0,0 +1,48 @@ +import time +from typing import Dict, Union + +from ovos_utils import LOG + +from neon_llm_core.chatbot import LLMBot + + +class PersonaHandlersState: + + def __init__(self, service_name: str, ovos_config: dict): + self._created_items: Dict[str, LLMBot] = {} + self.service_name = service_name + self.ovos_config = ovos_config + self.mq_config = ovos_config.get('MQ', {}) + self.init_default_handlers() + + def init_default_handlers(self): + if self.ovos_config.get("llm_bots", {}).get(self.service_name): + from neon_llm_core.chatbot import LLMBot + LOG.info(f"Chatbot(s) configured for: {self.service_name}") + for persona in self.ovos_config['llm_bots'][self.service_name]: + self.add_persona_handler(persona=persona) + + def add_persona_handler(self, persona: dict) -> Union[LLMBot, None]: + persona_name = persona['name'] + if persona_name in list(self._created_items): + if self._created_items[persona_name].persona != persona: + LOG.warning(f"Overriding already existing persona: '{persona_name}' with new data={persona}") + self._created_items[persona_name].stop() + # time to gracefully stop the submind + time.sleep(0.5) + else: + LOG.warning('Persona config provided is identical to existing, skipping') + return self._created_items[persona_name] + if not persona.get('enabled', True): + LOG.warning(f"Persona disabled: {persona['name']}") + return + # Get a configured username to use for LLM submind connections + if self.mq_config.get("users", {}).get("neon_llm_submind"): + self.ovos_config["MQ"]["users"][persona['name']] = self.mq_config['users']['neon_llm_submind'] + bot = LLMBot(llm_name=self.service_name, service_name=persona['name'], + persona=persona, config=self.ovos_config, + vhost="/chatbots") + bot.run() + LOG.info(f"Started chatbot: {bot.service_name}") + self._created_items[persona_name] = bot + return bot From 98acc6f59dede28a9dde08c7f19787328ca2b34c Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 14 Jan 2024 22:58:58 +0100 Subject: [PATCH 02/20] Fixed dependency + license notice --- neon_llm_core/chatbot.py | 2 +- neon_llm_core/utils/personas/state.py | 28 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/neon_llm_core/chatbot.py b/neon_llm_core/chatbot.py index 72499d3..405da11 100644 --- a/neon_llm_core/chatbot.py +++ b/neon_llm_core/chatbot.py @@ -29,7 +29,7 @@ from neon_mq_connector.utils.client_utils import send_mq_request from ovos_utils.log import LOG -from neon_llm_core.config import LLMMQConfig +from neon_llm_core.utils.config import LLMMQConfig class LLMBot(ChatBot): diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py index 03289db..42936d1 100644 --- a/neon_llm_core/utils/personas/state.py +++ b/neon_llm_core/utils/personas/state.py @@ -1,3 +1,31 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2022 Neongecko.com Inc. +# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds, +# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo +# BSD-3 License +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + import time from typing import Dict, Union From 32696283d857161447faa161e3f657af320fa291 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sat, 24 Feb 2024 13:27:04 +0100 Subject: [PATCH 03/20] Fixed networking --- neon_llm_core/rmq.py | 19 ------------------- neon_llm_core/utils/personas/provider.py | 4 +++- neon_llm_core/utils/personas/state.py | 1 - 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/neon_llm_core/rmq.py b/neon_llm_core/rmq.py index 6f2015b..7d6ef9a 100644 --- a/neon_llm_core/rmq.py +++ b/neon_llm_core/rmq.py @@ -55,25 +55,6 @@ def __init__(self): self._personas_provider = PersonasProvider(service_name=self.name, ovos_config=self.ovos_config) - if self.ovos_config.get("llm_bots", {}).get(self.name): - from neon_llm_core.chatbot import LLMBot - LOG.info(f"Chatbot(s) configured for: {self.name}") - for persona in self.ovos_config['llm_bots'][self.name]: - # Spawn a service for each persona to support @user requests - if not persona.get('enabled', True): - LOG.warning(f"Persona disabled: {persona['name']}") - continue - # Get a configured username to use for LLM submind connections - if mq_config.get("users", {}).get("neon_llm_submind"): - self.ovos_config["MQ"]["users"][persona['name']] = \ - mq_config['users']['neon_llm_submind'] - bot = LLMBot(llm_name=self.name, service_name=persona['name'], - persona=persona, config=self.ovos_config, - vhost="/chatbots") - bot.run() - LOG.info(f"Started chatbot: {bot.service_name}") - self._bots.append(bot) - def register_consumers(self): for idx in range(self.model_config.get("num_parallel_processes", 1)): self.register_consumer(name=f"neon_llm_{self.name}_ask_{idx}", diff --git a/neon_llm_core/utils/personas/provider.py b/neon_llm_core/utils/personas/provider.py index 610613e..2a24d9a 100644 --- a/neon_llm_core/utils/personas/provider.py +++ b/neon_llm_core/utils/personas/provider.py @@ -74,9 +74,11 @@ def personas(self, data): self._persona_handlers_state.init_default_handlers() def _fetch_persona_config(self): + queue = "get_configured_personas" response = send_mq_request(vhost=LLM_VHOST, request_data={"service_name": self.service_name}, - target_queue="get_configured_personas") + target_queue=queue, + response_queue=f'{queue}.{self.service_name}.response') self.personas = response.get('items', []) for persona in self.personas: if persona: diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py index 42936d1..c21b306 100644 --- a/neon_llm_core/utils/personas/state.py +++ b/neon_llm_core/utils/personas/state.py @@ -45,7 +45,6 @@ def __init__(self, service_name: str, ovos_config: dict): def init_default_handlers(self): if self.ovos_config.get("llm_bots", {}).get(self.service_name): - from neon_llm_core.chatbot import LLMBot LOG.info(f"Chatbot(s) configured for: {self.service_name}") for persona in self.ovos_config['llm_bots'][self.service_name]: self.add_persona_handler(persona=persona) From 5fba3e798fa7395e35785c9bdef29826beff2971 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sat, 9 Mar 2024 22:05:25 +0100 Subject: [PATCH 04/20] added package annotation to personas folder --- neon_llm_core/utils/personas/__init__.py | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 neon_llm_core/utils/personas/__init__.py diff --git a/neon_llm_core/utils/personas/__init__.py b/neon_llm_core/utils/personas/__init__.py new file mode 100644 index 0000000..250e4cd --- /dev/null +++ b/neon_llm_core/utils/personas/__init__.py @@ -0,0 +1,25 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 NeonGecko.com Inc. +# BSD-3 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 05f4506fcdf080cdef764638b17ea313aa0f9563 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sat, 9 Mar 2024 22:45:07 +0100 Subject: [PATCH 05/20] removed response queue --- neon_llm_core/chatbot.py | 2 +- neon_llm_core/utils/personas/provider.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/neon_llm_core/chatbot.py b/neon_llm_core/chatbot.py index 405da11..e8884d9 100644 --- a/neon_llm_core/chatbot.py +++ b/neon_llm_core/chatbot.py @@ -37,7 +37,7 @@ class LLMBot(ChatBot): def __init__(self, *args, **kwargs): ChatBot.__init__(self, *args, **kwargs) self.bot_type = "submind" - self.base_llm = kwargs.get("llm_name") # chat_gpt, fastchat, etc. + self.base_llm = kwargs.get("llm_name") # chatgpt, fastchat, etc. self.persona = kwargs.get("persona") self.mq_queue_config = self.get_llm_mq_config(self.base_llm) LOG.info(f'Initialised config for llm={self.base_llm}|' diff --git a/neon_llm_core/utils/personas/provider.py b/neon_llm_core/utils/personas/provider.py index 2a24d9a..657cd49 100644 --- a/neon_llm_core/utils/personas/provider.py +++ b/neon_llm_core/utils/personas/provider.py @@ -77,8 +77,7 @@ def _fetch_persona_config(self): queue = "get_configured_personas" response = send_mq_request(vhost=LLM_VHOST, request_data={"service_name": self.service_name}, - target_queue=queue, - response_queue=f'{queue}.{self.service_name}.response') + target_queue=queue) self.personas = response.get('items', []) for persona in self.personas: if persona: From 6668af27c85c8fc8e2ed6ffbe5131158d83706c0 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 10 Mar 2024 15:15:41 +0100 Subject: [PATCH 06/20] added support for optiona; loading of legacy config --- neon_llm_core/utils/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neon_llm_core/utils/config.py b/neon_llm_core/utils/config.py index 7dfff89..a6733b1 100644 --- a/neon_llm_core/utils/config.py +++ b/neon_llm_core/utils/config.py @@ -25,6 +25,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import json +import os from dataclasses import dataclass from os.path import join, dirname, isfile @@ -38,7 +39,7 @@ def load_config() -> dict: """ Load and return a configuration object, """ - legacy_config_path = "/app/app/config.json" + legacy_config_path = os.getenv("NEON_LLM_LEGACY_CONFIG", "/app/app/config.json") if isfile(legacy_config_path): LOG.warning(f"Deprecated configuration found at {legacy_config_path}") with open(legacy_config_path) as f: From 2a41d44831593fe3def6a6fbf10117bdcea76338 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 10 Mar 2024 15:56:53 +0100 Subject: [PATCH 07/20] Refactored configs loading --- neon_llm_core/utils/config.py | 37 +++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/neon_llm_core/utils/config.py b/neon_llm_core/utils/config.py index a6733b1..c75be02 100644 --- a/neon_llm_core/utils/config.py +++ b/neon_llm_core/utils/config.py @@ -29,31 +29,48 @@ from dataclasses import dataclass from os.path import join, dirname, isfile +from typing import Union + +from neon_utils.log_utils import init_log from ovos_utils.log import LOG from ovos_config.config import Configuration from neon_llm_core.utils.constants import LLM_VHOST -def load_config() -> dict: - """ - Load and return a configuration object, - """ +def load_legacy_config() -> Union[dict, None]: legacy_config_path = os.getenv("NEON_LLM_LEGACY_CONFIG", "/app/app/config.json") if isfile(legacy_config_path): LOG.warning(f"Deprecated configuration found at {legacy_config_path}") with open(legacy_config_path) as f: config = json.load(f) + init_log(config=config) return config - config = Configuration() - if not config: - LOG.warning(f"No configuration found! falling back to defaults") - default_config_path = join(dirname(__file__), "default_config.json") - with open(default_config_path) as f: - config = json.load(f) + + +load_ovos_config = Configuration + + +def load_default_config() -> Union[dict, None]: + LOG.warning(f"No configuration found! falling back to defaults") + default_config_path = join(dirname(__file__), "default_config.json") + with open(default_config_path) as f: + config = json.load(f) return config +def load_config() -> Union[dict, None]: + """ + Load and return a configuration object, + """ + configs_loading_order = (load_legacy_config, load_ovos_config, load_default_config,) + for config_loader in configs_loading_order: + LOG.info(f'Trying to load configs with {config_loader.__name__}') + config = config_loader() + if config: + return config + + @dataclass class LLMMQConfig: ask_response_queue: str From 28431bfb6dd8baebbbaa75d7490e06a31d7a7af2 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 10 Mar 2024 15:58:08 +0100 Subject: [PATCH 08/20] Refactored configs loading --- neon_llm_core/utils/config.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/neon_llm_core/utils/config.py b/neon_llm_core/utils/config.py index c75be02..17c9af1 100644 --- a/neon_llm_core/utils/config.py +++ b/neon_llm_core/utils/config.py @@ -41,7 +41,6 @@ def load_legacy_config() -> Union[dict, None]: legacy_config_path = os.getenv("NEON_LLM_LEGACY_CONFIG", "/app/app/config.json") if isfile(legacy_config_path): - LOG.warning(f"Deprecated configuration found at {legacy_config_path}") with open(legacy_config_path) as f: config = json.load(f) init_log(config=config) @@ -65,9 +64,9 @@ def load_config() -> Union[dict, None]: """ configs_loading_order = (load_legacy_config, load_ovos_config, load_default_config,) for config_loader in configs_loading_order: - LOG.info(f'Trying to load configs with {config_loader.__name__}') config = config_loader() if config: + LOG.info(f'Applied configs from loader={config_loader.__name__}()') return config From 18f17551b6829d7c8a3a342daeda1415df4ead03 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 10 Mar 2024 16:14:52 +0100 Subject: [PATCH 09/20] setting default mq config for client utils from legacy config --- neon_llm_core/utils/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/neon_llm_core/utils/config.py b/neon_llm_core/utils/config.py index 17c9af1..286c795 100644 --- a/neon_llm_core/utils/config.py +++ b/neon_llm_core/utils/config.py @@ -36,6 +36,7 @@ from ovos_config.config import Configuration from neon_llm_core.utils.constants import LLM_VHOST +import neon_mq_connector.utils.client_utils as mq_connector_client_utils def load_legacy_config() -> Union[dict, None]: @@ -44,6 +45,7 @@ def load_legacy_config() -> Union[dict, None]: with open(legacy_config_path) as f: config = json.load(f) init_log(config=config) + mq_connector_client_utils._default_mq_config = config.get("MQ") return config From 2f9d464684c12a839c43f17ee56bfe27e164ae88 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 10 Mar 2024 17:25:37 +0100 Subject: [PATCH 10/20] Added pydantic-based model validation of incoming data --- neon_llm_core/utils/personas/models.py | 42 ++++++++++++++++++++++++ neon_llm_core/utils/personas/provider.py | 9 ++--- neon_llm_core/utils/personas/state.py | 29 ++++++++-------- requirements/requirements.txt | 3 +- 4 files changed, 63 insertions(+), 20 deletions(-) create mode 100644 neon_llm_core/utils/personas/models.py diff --git a/neon_llm_core/utils/personas/models.py b/neon_llm_core/utils/personas/models.py new file mode 100644 index 0000000..fa100ad --- /dev/null +++ b/neon_llm_core/utils/personas/models.py @@ -0,0 +1,42 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 NeonGecko.com Inc. +# BSD-3 +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from pydantic import BaseModel, computed_field + + +class PersonaModel(BaseModel): + name: str + user_id: str = None + description: str + enabled: bool = True + + @computed_field + @property + def id(self) -> str: + persona_id = self.name + if self.user_id: + persona_id += f"_{self.user_id}" + return persona_id diff --git a/neon_llm_core/utils/personas/provider.py b/neon_llm_core/utils/personas/provider.py index 657cd49..c562baf 100644 --- a/neon_llm_core/utils/personas/provider.py +++ b/neon_llm_core/utils/personas/provider.py @@ -31,6 +31,7 @@ from ovos_utils.log import LOG from neon_llm_core.utils.constants import LLM_VHOST +from neon_llm_core.utils.personas.models import PersonaModel from neon_llm_core.utils.personas.state import PersonaHandlersState @@ -38,6 +39,7 @@ class PersonasProvider: PERSONA_STATE_TTL = int(os.getenv("PERSONA_STATE_TTL", 15 * 60)) PERSONA_SYNC_INTERVAL = int(os.getenv("PERSONA_SYNC_INTERVAL", 5 * 60)) + GET_CONFIGURED_PERSONAS_QUEUE = "get_configured_personas" def __init__(self, service_name: str, ovos_config: dict): self.service_name = service_name @@ -74,14 +76,13 @@ def personas(self, data): self._persona_handlers_state.init_default_handlers() def _fetch_persona_config(self): - queue = "get_configured_personas" response = send_mq_request(vhost=LLM_VHOST, request_data={"service_name": self.service_name}, - target_queue=queue) + target_queue=PersonasProvider.GET_CONFIGURED_PERSONAS_QUEUE) self.personas = response.get('items', []) for persona in self.personas: - if persona: - self._persona_handlers_state.add_persona_handler(persona=persona) + persona = PersonaModel.parse_obj(obj=persona) + self._persona_handlers_state.add_persona_handler(persona=persona) def start_sync(self): self.persona_sync_thread.start() diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py index c21b306..3f40d85 100644 --- a/neon_llm_core/utils/personas/state.py +++ b/neon_llm_core/utils/personas/state.py @@ -32,6 +32,7 @@ from ovos_utils import LOG from neon_llm_core.chatbot import LLMBot +from neon_llm_core.utils.personas.models import PersonaModel class PersonaHandlersState: @@ -47,29 +48,27 @@ def init_default_handlers(self): if self.ovos_config.get("llm_bots", {}).get(self.service_name): LOG.info(f"Chatbot(s) configured for: {self.service_name}") for persona in self.ovos_config['llm_bots'][self.service_name]: - self.add_persona_handler(persona=persona) + self.add_persona_handler(persona=PersonaModel.parse_obj(obj=persona)) - def add_persona_handler(self, persona: dict) -> Union[LLMBot, None]: - persona_name = persona['name'] - if persona_name in list(self._created_items): - if self._created_items[persona_name].persona != persona: - LOG.warning(f"Overriding already existing persona: '{persona_name}' with new data={persona}") - self._created_items[persona_name].stop() + def add_persona_handler(self, persona: PersonaModel) -> Union[LLMBot, None]: + if persona.id in list(self._created_items): + if self._created_items[persona.id].persona != persona: + LOG.warning(f"Overriding already existing persona: '{persona.id}' with new data={persona}") + self._created_items[persona.id].stop() # time to gracefully stop the submind time.sleep(0.5) else: LOG.warning('Persona config provided is identical to existing, skipping') - return self._created_items[persona_name] - if not persona.get('enabled', True): - LOG.warning(f"Persona disabled: {persona['name']}") + return self._created_items[persona.id] + if not persona.enabled: + LOG.warning(f"Persona disabled: '{persona.id}'") return # Get a configured username to use for LLM submind connections - if self.mq_config.get("users", {}).get("neon_llm_submind"): - self.ovos_config["MQ"]["users"][persona['name']] = self.mq_config['users']['neon_llm_submind'] - bot = LLMBot(llm_name=self.service_name, service_name=persona['name'], - persona=persona, config=self.ovos_config, + self.ovos_config["MQ"]["users"][persona.name] = self.mq_config['users'][f'neon_llm_{self.service_name}'] + bot = LLMBot(llm_name=self.service_name, service_name=persona.name, + persona=persona.model_dump(), config=self.ovos_config, vhost="/chatbots") bot.run() LOG.info(f"Started chatbot: {bot.service_name}") - self._created_items[persona_name] = bot + self._created_items[persona.id] = bot return bot diff --git a/requirements/requirements.txt b/requirements/requirements.txt index b9f3251..4662454 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,4 +1,5 @@ # networking neon-mq-connector>=0.7.1a4 ovos-utils~=0.0.32 -ovos-config~=0.0.10 \ No newline at end of file +ovos-config~=0.0.10 +pydantic==2.6.3 \ No newline at end of file From d4dcaf6ff47feeba0f5ab475ff65c34f9891d4f7 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 10 Mar 2024 17:34:51 +0100 Subject: [PATCH 11/20] Remapping persona_name to name --- neon_llm_core/utils/personas/provider.py | 1 + 1 file changed, 1 insertion(+) diff --git a/neon_llm_core/utils/personas/provider.py b/neon_llm_core/utils/personas/provider.py index c562baf..efb6c2e 100644 --- a/neon_llm_core/utils/personas/provider.py +++ b/neon_llm_core/utils/personas/provider.py @@ -81,6 +81,7 @@ def _fetch_persona_config(self): target_queue=PersonasProvider.GET_CONFIGURED_PERSONAS_QUEUE) self.personas = response.get('items', []) for persona in self.personas: + persona.setdefault('name', persona.pop('persona_name', None)) persona = PersonaModel.parse_obj(obj=persona) self._persona_handlers_state.add_persona_handler(persona=persona) From f42e01ce24469e216475eb64ac47de66403431ed Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 10 Mar 2024 17:37:36 +0100 Subject: [PATCH 12/20] Added optional annotation for user id property on PersonaModel --- neon_llm_core/utils/personas/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neon_llm_core/utils/personas/models.py b/neon_llm_core/utils/personas/models.py index fa100ad..0d5e1d8 100644 --- a/neon_llm_core/utils/personas/models.py +++ b/neon_llm_core/utils/personas/models.py @@ -23,15 +23,16 @@ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from typing import Optional from pydantic import BaseModel, computed_field class PersonaModel(BaseModel): name: str - user_id: str = None description: str enabled: bool = True + user_id: Optional[str] = None @computed_field @property From 1b33d4cd93a0b74cb1d0d006d357724d0dc18b27 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 10 Mar 2024 17:44:34 +0100 Subject: [PATCH 13/20] Rollback to use of neon_llm_submind as MQ user --- neon_llm_core/utils/personas/state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py index 3f40d85..683551a 100644 --- a/neon_llm_core/utils/personas/state.py +++ b/neon_llm_core/utils/personas/state.py @@ -64,7 +64,7 @@ def add_persona_handler(self, persona: PersonaModel) -> Union[LLMBot, None]: LOG.warning(f"Persona disabled: '{persona.id}'") return # Get a configured username to use for LLM submind connections - self.ovos_config["MQ"]["users"][persona.name] = self.mq_config['users'][f'neon_llm_{self.service_name}'] + self.ovos_config["MQ"]["users"][persona.name] = self.mq_config['users']['neon_llm_submind'] bot = LLMBot(llm_name=self.service_name, service_name=persona.name, persona=persona.model_dump(), config=self.ovos_config, vhost="/chatbots") From e11117d84caf34bfec73d50a3bf6822ae4a3ba1b Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 10 Mar 2024 18:07:42 +0100 Subject: [PATCH 14/20] Fixed issues in add_persona_handler --- neon_llm_core/utils/personas/provider.py | 1 + neon_llm_core/utils/personas/state.py | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/neon_llm_core/utils/personas/provider.py b/neon_llm_core/utils/personas/provider.py index efb6c2e..8d9e3a5 100644 --- a/neon_llm_core/utils/personas/provider.py +++ b/neon_llm_core/utils/personas/provider.py @@ -86,6 +86,7 @@ def _fetch_persona_config(self): self._persona_handlers_state.add_persona_handler(persona=persona) def start_sync(self): + self._fetch_persona_config() self.persona_sync_thread.start() def stop_sync(self): diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py index 683551a..f089264 100644 --- a/neon_llm_core/utils/personas/state.py +++ b/neon_llm_core/utils/personas/state.py @@ -51,12 +51,16 @@ def init_default_handlers(self): self.add_persona_handler(persona=PersonaModel.parse_obj(obj=persona)) def add_persona_handler(self, persona: PersonaModel) -> Union[LLMBot, None]: + persona_dict = persona.model_dump() if persona.id in list(self._created_items): - if self._created_items[persona.id].persona != persona: + if self._created_items[persona.id].persona != persona_dict: LOG.warning(f"Overriding already existing persona: '{persona.id}' with new data={persona}") - self._created_items[persona.id].stop() - # time to gracefully stop the submind - time.sleep(0.5) + try: + self._created_items[persona.id].stop() + # time to gracefully stop the submind + time.sleep(0.5) + except Exception as ex: + LOG.warning(f'Failed to gracefully stop {persona.id}, ex={str(ex)}') else: LOG.warning('Persona config provided is identical to existing, skipping') return self._created_items[persona.id] @@ -66,7 +70,7 @@ def add_persona_handler(self, persona: PersonaModel) -> Union[LLMBot, None]: # Get a configured username to use for LLM submind connections self.ovos_config["MQ"]["users"][persona.name] = self.mq_config['users']['neon_llm_submind'] bot = LLMBot(llm_name=self.service_name, service_name=persona.name, - persona=persona.model_dump(), config=self.ovos_config, + persona=persona_dict, config=self.ovos_config, vhost="/chatbots") bot.run() LOG.info(f"Started chatbot: {bot.service_name}") From 92a5e0eeb6d67ddfbfe5005fda8181924d876a51 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Sun, 10 Mar 2024 18:17:27 +0100 Subject: [PATCH 15/20] reduced log level to debug when persona configs are identical --- neon_llm_core/utils/personas/state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py index f089264..ec45c24 100644 --- a/neon_llm_core/utils/personas/state.py +++ b/neon_llm_core/utils/personas/state.py @@ -62,7 +62,7 @@ def add_persona_handler(self, persona: PersonaModel) -> Union[LLMBot, None]: except Exception as ex: LOG.warning(f'Failed to gracefully stop {persona.id}, ex={str(ex)}') else: - LOG.warning('Persona config provided is identical to existing, skipping') + LOG.debug('Persona config provided is identical to existing, skipping') return self._created_items[persona.id] if not persona.enabled: LOG.warning(f"Persona disabled: '{persona.id}'") From 658faf7f83889bd6fc8f31b15406e6ca92d600f1 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Mon, 18 Mar 2024 21:42:45 +0100 Subject: [PATCH 16/20] Added cleaning up unfetched personas --- neon_llm_core/utils/personas/provider.py | 12 ++++++++---- neon_llm_core/utils/personas/state.py | 11 ++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/neon_llm_core/utils/personas/provider.py b/neon_llm_core/utils/personas/provider.py index 8d9e3a5..5123d2f 100644 --- a/neon_llm_core/utils/personas/provider.py +++ b/neon_llm_core/utils/personas/provider.py @@ -70,6 +70,7 @@ def personas(self, data): if data and isinstance(data, list): self._personas = data self._persona_last_sync = now + self._persona_handlers_state.clean_up_personas(exclude_items=self._personas) elif now - self._persona_last_sync > self.PERSONA_STATE_TTL: LOG.warning(f'Persona state TTL expired, resetting personas config') self._personas = [] @@ -79,11 +80,14 @@ def _fetch_persona_config(self): response = send_mq_request(vhost=LLM_VHOST, request_data={"service_name": self.service_name}, target_queue=PersonasProvider.GET_CONFIGURED_PERSONAS_QUEUE) - self.personas = response.get('items', []) - for persona in self.personas: - persona.setdefault('name', persona.pop('persona_name', None)) - persona = PersonaModel.parse_obj(obj=persona) + response_data = response.get('items', []) + personas = [] + for item in response_data: + item.setdefault('name', item.pop('persona_name', None)) + persona = PersonaModel.parse_obj(obj=item) self._persona_handlers_state.add_persona_handler(persona=persona) + personas.append(persona) + self.personas = personas def start_sync(self): self._fetch_persona_config() diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py index ec45c24..70c4a69 100644 --- a/neon_llm_core/utils/personas/state.py +++ b/neon_llm_core/utils/personas/state.py @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import time -from typing import Dict, Union +from typing import Dict, Union, List from ovos_utils import LOG @@ -45,6 +45,7 @@ def __init__(self, service_name: str, ovos_config: dict): self.init_default_handlers() def init_default_handlers(self): + self._created_items = {} if self.ovos_config.get("llm_bots", {}).get(self.service_name): LOG.info(f"Chatbot(s) configured for: {self.service_name}") for persona in self.ovos_config['llm_bots'][self.service_name]: @@ -76,3 +77,11 @@ def add_persona_handler(self, persona: PersonaModel) -> Union[LLMBot, None]: LOG.info(f"Started chatbot: {bot.service_name}") self._created_items[persona.id] = bot return bot + + def clean_up_personas(self, ignore_items: List[PersonaModel] = None): + connected_personas = set(self._created_items) + ignored_persona_ids = set(persona.id for persona in ignore_items or []) + personas_to_remove = connected_personas - ignored_persona_ids + for persona_id in personas_to_remove: + LOG.info(f'Removing persona_id = {persona_id}') + self._created_items.pop(persona_id, None) From 7aca6aea9d989cf23610efdc49de02ab4e62f243 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Mon, 18 Mar 2024 21:43:07 +0100 Subject: [PATCH 17/20] Adding LLM named suffix to prevent collision in MQ services --- neon_llm_core/utils/personas/state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py index 70c4a69..9f696a1 100644 --- a/neon_llm_core/utils/personas/state.py +++ b/neon_llm_core/utils/personas/state.py @@ -70,7 +70,7 @@ def add_persona_handler(self, persona: PersonaModel) -> Union[LLMBot, None]: return # Get a configured username to use for LLM submind connections self.ovos_config["MQ"]["users"][persona.name] = self.mq_config['users']['neon_llm_submind'] - bot = LLMBot(llm_name=self.service_name, service_name=persona.name, + bot = LLMBot(llm_name=self.service_name, service_name=f"{persona.id}-{self.service_name}", persona=persona_dict, config=self.ovos_config, vhost="/chatbots") bot.run() From 9590012228c41b2a7879b30f254c56846d293ae4 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Mon, 18 Mar 2024 22:00:12 +0100 Subject: [PATCH 18/20] Fixed issue with failing of getting personas configs --- neon_llm_core/utils/personas/state.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py index 9f696a1..f36e76d 100644 --- a/neon_llm_core/utils/personas/state.py +++ b/neon_llm_core/utils/personas/state.py @@ -69,8 +69,9 @@ def add_persona_handler(self, persona: PersonaModel) -> Union[LLMBot, None]: LOG.warning(f"Persona disabled: '{persona.id}'") return # Get a configured username to use for LLM submind connections - self.ovos_config["MQ"]["users"][persona.name] = self.mq_config['users']['neon_llm_submind'] - bot = LLMBot(llm_name=self.service_name, service_name=f"{persona.id}-{self.service_name}", + persona_id = f"{persona.id}-{self.service_name}" + self.ovos_config["MQ"]["users"][persona_id] = self.mq_config['users']['neon_llm_submind'] + bot = LLMBot(llm_name=self.service_name, service_name=persona_id, persona=persona_dict, config=self.ovos_config, vhost="/chatbots") bot.run() From 9616381fe4de42434a5fa757d6b8340615182238 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Mon, 18 Mar 2024 22:02:23 +0100 Subject: [PATCH 19/20] small fix --- neon_llm_core/utils/personas/provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neon_llm_core/utils/personas/provider.py b/neon_llm_core/utils/personas/provider.py index 5123d2f..aa81702 100644 --- a/neon_llm_core/utils/personas/provider.py +++ b/neon_llm_core/utils/personas/provider.py @@ -70,7 +70,7 @@ def personas(self, data): if data and isinstance(data, list): self._personas = data self._persona_last_sync = now - self._persona_handlers_state.clean_up_personas(exclude_items=self._personas) + self._persona_handlers_state.clean_up_personas(ignore_items=self._personas) elif now - self._persona_last_sync > self.PERSONA_STATE_TTL: LOG.warning(f'Persona state TTL expired, resetting personas config') self._personas = [] From 0a48463a9c77846adcd46da7a0c396b16ab785c9 Mon Sep 17 00:00:00 2001 From: NeonKirill Date: Thu, 21 Mar 2024 20:14:16 +0100 Subject: [PATCH 20/20] changed suffixing persona id with underscore --- neon_llm_core/utils/personas/state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neon_llm_core/utils/personas/state.py b/neon_llm_core/utils/personas/state.py index f36e76d..94be76d 100644 --- a/neon_llm_core/utils/personas/state.py +++ b/neon_llm_core/utils/personas/state.py @@ -69,7 +69,7 @@ def add_persona_handler(self, persona: PersonaModel) -> Union[LLMBot, None]: LOG.warning(f"Persona disabled: '{persona.id}'") return # Get a configured username to use for LLM submind connections - persona_id = f"{persona.id}-{self.service_name}" + persona_id = f"{persona.id}_{self.service_name}" self.ovos_config["MQ"]["users"][persona_id] = self.mq_config['users']['neon_llm_submind'] bot = LLMBot(llm_name=self.service_name, service_name=persona_id, persona=persona_dict, config=self.ovos_config,