Skip to content

Commit

Permalink
Added pydantic-based model validation of incoming data
Browse files Browse the repository at this point in the history
  • Loading branch information
kirgrim committed Mar 10, 2024
1 parent 18f1755 commit 2f9d464
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 20 deletions.
42 changes: 42 additions & 0 deletions neon_llm_core/utils/personas/models.py
Original file line number Diff line number Diff line change
@@ -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
9 changes: 5 additions & 4 deletions neon_llm_core/utils/personas/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@
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


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
Expand Down Expand Up @@ -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()
Expand Down
29 changes: 14 additions & 15 deletions neon_llm_core/utils/personas/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
3 changes: 2 additions & 1 deletion requirements/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# networking
neon-mq-connector>=0.7.1a4
ovos-utils~=0.0.32
ovos-config~=0.0.10
ovos-config~=0.0.10
pydantic==2.6.3

0 comments on commit 2f9d464

Please sign in to comment.