|
| 1 | +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System |
| 2 | +# All trademark and other rights reserved by their respective owners |
| 3 | +# Copyright 2008-2021 NeonGecko.com Inc. |
| 4 | +# BSD-3 |
| 5 | +# Redistribution and use in source and binary forms, with or without |
| 6 | +# modification, are permitted provided that the following conditions are met: |
| 7 | +# 1. Redistributions of source code must retain the above copyright notice, |
| 8 | +# this list of conditions and the following disclaimer. |
| 9 | +# 2. Redistributions in binary form must reproduce the above copyright notice, |
| 10 | +# this list of conditions and the following disclaimer in the documentation |
| 11 | +# and/or other materials provided with the distribution. |
| 12 | +# 3. Neither the name of the copyright holder nor the names of its |
| 13 | +# contributors may be used to endorse or promote products derived from this |
| 14 | +# software without specific prior written permission. |
| 15 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 16 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| 17 | +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 18 | +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| 19 | +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 20 | +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 21 | +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
| 22 | +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| 23 | +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| 24 | +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 25 | +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | +import os |
| 27 | +from time import time |
| 28 | + |
| 29 | +from neon_mq_connector.utils import RepeatingTimer |
| 30 | +from neon_mq_connector.utils.client_utils import send_mq_request |
| 31 | +from ovos_utils.log import LOG |
| 32 | + |
| 33 | +from neon_llm_core.utils.constants import LLM_VHOST |
| 34 | +from neon_llm_core.utils.personas.state import PersonaHandlersState |
| 35 | + |
| 36 | + |
| 37 | +class PersonasProvider: |
| 38 | + |
| 39 | + PERSONA_STATE_TTL = int(os.getenv("PERSONA_STATE_TTL", 15 * 60)) |
| 40 | + PERSONA_SYNC_INTERVAL = int(os.getenv("PERSONA_SYNC_INTERVAL", 5 * 60)) |
| 41 | + |
| 42 | + def __init__(self, service_name: str, ovos_config: dict): |
| 43 | + self.service_name = service_name |
| 44 | + self._persona_handlers_state = PersonaHandlersState(service_name=service_name, |
| 45 | + ovos_config=ovos_config) |
| 46 | + self._personas = [] # list of personas available for given service |
| 47 | + self._persona_last_sync = 0 |
| 48 | + self._persona_sync_thread = None |
| 49 | + |
| 50 | + @property |
| 51 | + def persona_sync_thread(self): |
| 52 | + """Creates new synchronization thread which fetches Klat personas""" |
| 53 | + if not (isinstance(self._persona_sync_thread, RepeatingTimer) and |
| 54 | + self._persona_sync_thread.is_alive()): |
| 55 | + self._persona_sync_thread = RepeatingTimer(self.PERSONA_SYNC_INTERVAL, |
| 56 | + self._fetch_persona_config) |
| 57 | + self._persona_sync_thread.daemon = True |
| 58 | + return self._persona_sync_thread |
| 59 | + |
| 60 | + @property |
| 61 | + def personas(self): |
| 62 | + return self._personas |
| 63 | + |
| 64 | + @personas.setter |
| 65 | + def personas(self, data): |
| 66 | + now = int(time()) |
| 67 | + LOG.debug(f'Setting personas={data}') |
| 68 | + if data and isinstance(data, list): |
| 69 | + self._personas = data |
| 70 | + self._persona_last_sync = now |
| 71 | + elif now - self._persona_last_sync > self.PERSONA_STATE_TTL: |
| 72 | + LOG.warning(f'Persona state TTL expired, resetting personas config') |
| 73 | + self._personas = [] |
| 74 | + self._persona_handlers_state.init_default_handlers() |
| 75 | + |
| 76 | + def _fetch_persona_config(self): |
| 77 | + response = send_mq_request(vhost=LLM_VHOST, |
| 78 | + request_data={"service_name": self.service_name}, |
| 79 | + target_queue="get_configured_personas") |
| 80 | + self.personas = response.get('items', []) |
| 81 | + for persona in self.personas: |
| 82 | + if persona: |
| 83 | + self._persona_handlers_state.add_persona_handler(persona=persona) |
| 84 | + |
| 85 | + def start_sync(self): |
| 86 | + self.persona_sync_thread.start() |
| 87 | + |
| 88 | + def stop_sync(self): |
| 89 | + if self._persona_sync_thread: |
| 90 | + self._persona_sync_thread.cancel() |
| 91 | + self._persona_sync_thread = None |
0 commit comments