From e03408f70d4c1c329a092aa496822f7cf5f5078f Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Mon, 25 Nov 2024 16:30:31 -0800 Subject: [PATCH 1/6] Defines an SIO and MQ `configured_personas_changed` event that is emitted any time the server makes a change to the database Leaves existing logic untouched to allow clients to request an update from the server at any time Relates to https://github.com/NeonGeckoCom/neon-llm-core/issues/8 --- chat_server/blueprints/personas.py | 19 ++++++++++++-- services/klatchat_observer/controller.py | 33 +++++++++++++++++++++--- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/chat_server/blueprints/personas.py b/chat_server/blueprints/personas.py index 6c0c8abd..8d11f210 100644 --- a/chat_server/blueprints/personas.py +++ b/chat_server/blueprints/personas.py @@ -25,9 +25,12 @@ # 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 json + from fastapi import APIRouter from starlette.responses import JSONResponse +from chat_server.server_utils.api_dependencies import CurrentUserModel from chat_server.server_utils.enums import RequestModelType, UserRoles from chat_server.server_utils.http_exceptions import ( ItemNotFoundException, @@ -47,7 +50,7 @@ PersonaData, ) from chat_server.server_utils.api_dependencies.validators import permitted_access - +from chat_server.sio.server import sio from utils.database_utils.mongo_utils import MongoFilter, MongoLogicalOperators from utils.database_utils.mongo_utils.queries.wrapper import MongoDocumentsAPI @@ -61,7 +64,7 @@ async def list_personas( current_user: CurrentUserData, request_model: ListPersonasQueryModel = permitted_access(ListPersonasQueryModel), -): +) -> JSONResponse: """Lists personas matching query params""" filters = [] if request_model.llms: @@ -112,6 +115,7 @@ async def add_persona( if existing_model: raise DuplicatedItemException MongoDocumentsAPI.PERSONAS.add_item(data=request_model.model_dump()) + await _notify_personas_changed() return KlatAPIResponse.OK @@ -131,6 +135,7 @@ async def set_persona( MongoDocumentsAPI.PERSONAS.update_item( filters=mongo_filter, data=request_model.model_dump() ) + await _notify_personas_changed() return KlatAPIResponse.OK @@ -140,6 +145,7 @@ async def delete_persona( ): """Deletes persona""" MongoDocumentsAPI.PERSONAS.delete_item(item_id=request_model.persona_id) + await _notify_personas_changed() return KlatAPIResponse.OK @@ -157,4 +163,13 @@ async def toggle_persona_state( ) if updated_data.matched_count == 0: raise ItemNotFoundException + await _notify_personas_changed() return KlatAPIResponse.OK + + +async def _notify_personas_changed(): + response = await list_personas(CurrentUserModel(_id="", nickname="", + first_name="", last_name=""), + ListPersonasQueryModel(only_enabled=True)) + enabled_personas = json.loads(response.body.decode()) + sio.emit("configured_personas_changed", enabled_personas) diff --git a/services/klatchat_observer/controller.py b/services/klatchat_observer/controller.py index ef959c1c..873eabcb 100644 --- a/services/klatchat_observer/controller.py +++ b/services/klatchat_observer/controller.py @@ -28,6 +28,8 @@ import json import re import time +from typing import Optional + import cachetools.func from threading import Event, Timer @@ -304,7 +306,7 @@ def get_neon_service(self, wait_timeout: int = 10) -> None: LOG.info("Joining sync consumer") sync_consumer.join() if not self.neon_service_event.is_set(): - LOG.warning(f"Failed to get neon_service in {wait_timeout} seconds") + LOG.warning(f"Failed to get neon response in {wait_timeout} seconds") self.__neon_service_id = "" def register_sio_handlers(self): @@ -327,6 +329,8 @@ def register_sio_handlers(self): handler=self.request_revoke_submind_ban_from_conversation, ) self._sio.on("auth_expired", handler=self._handle_auth_expired) + self._sio.on("configured_personas_changed", + handler=self._handle_personas_changed) def connect_sio(self): """ @@ -396,6 +400,7 @@ def get_neon_request_structure(msg_data: dict): if requested_skill == "tts": utterance = msg_data.pop("utterance", "") or msg_data.pop("text", "") request_dict = { + "msg_type": "neon.get_tts", "data": { "utterance": utterance, "text": utterance, @@ -404,12 +409,14 @@ def get_neon_request_structure(msg_data: dict): } elif requested_skill == "stt": request_dict = { + "msg_type": "neon.get_stt", "data": { "audio_data": msg_data.pop("audio_data", msg_data["message_body"]), } } else: request_dict = { + "msg_type": "recognizer_loop:utterance", "data": { "utterances": [msg_data["message_body"]], }, @@ -419,13 +426,17 @@ def get_neon_request_structure(msg_data: dict): return request_dict def __handle_neon_recipient(self, recipient_data: dict, msg_data: dict): + """ + Handle a chat message intended for Neon. + """ msg_data.setdefault("message_body", msg_data.pop("messageText", "")) msg_data.setdefault("message_id", msg_data.pop("messageID", "")) recipient_data.setdefault("context", {}) pattern = re.compile("Neon", re.IGNORECASE) msg_data["message_body"] = ( - pattern.sub("", msg_data["message_body"], 1).strip("<>@,.:|- ").capitalize() + pattern.sub("", msg_data["message_body"], 1).strip("<>@,.:|- \n") ) + # This is really referencing an MQ endpoint (i.e. stt, tts), not a skill msg_data.setdefault( "requested_skill", recipient_data["context"].pop("service", "recognizer") ) @@ -774,6 +785,7 @@ def on_subminds_state(self, body: dict): @create_mq_callback() def on_get_configured_personas(self, body: dict): + # Handles request to get all defined personas response_data = self._fetch_persona_api(user_id=body.get("user_id")) response_data["items"] = [ item @@ -791,7 +803,7 @@ def on_get_configured_personas(self, body: dict): ) @cachetools.func.ttl_cache(ttl=15) - def _fetch_persona_api(self, user_id: str) -> dict: + def _fetch_persona_api(self, user_id: Optional[str]) -> dict: query_string = self._build_persona_api_query(user_id=user_id) url = f"{self.server_url}/personas/list?{query_string}" try: @@ -803,12 +815,25 @@ def _fetch_persona_api(self, user_id: str) -> dict: data = {"items": []} return data + def _handle_personas_changed(self, data: dict): + """ + SIO handler called when configured personas are modified. This emits an + MQ message to allow any connected listeners to maintain a set of known + personas. + """ + self.send_message( + request_data=data, + vhost=self.get_vhost("llm"), + queue="configured_personas_changed", + expiration=5000, + ) + def _refresh_default_persona_llms(self, data): for item in data["items"]: if default_llm := item.get("default_llm"): self.default_persona_llms[item["id"]] = item["id"] + "_" + default_llm - def _build_persona_api_query(self, user_id: str) -> str: + def _build_persona_api_query(self, user_id: Optional[str]) -> str: url_query_params = f"only_enabled=true" if user_id: url_query_params += f"&user_id={user_id}" From f32363e89cdcf0e6acc2ce88283f542bc72af3d7 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Tue, 26 Nov 2024 13:22:57 -0800 Subject: [PATCH 2/6] Refactor persona change notifications to be scoped per-LLM to match existing implementation --- chat_server/blueprints/personas.py | 36 ++++++++++++++++++------ services/klatchat_observer/controller.py | 13 +++++---- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/chat_server/blueprints/personas.py b/chat_server/blueprints/personas.py index 8d11f210..c4910149 100644 --- a/chat_server/blueprints/personas.py +++ b/chat_server/blueprints/personas.py @@ -27,6 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import json +from typing import List, Optional from fastapi import APIRouter from starlette.responses import JSONResponse @@ -115,7 +116,7 @@ async def add_persona( if existing_model: raise DuplicatedItemException MongoDocumentsAPI.PERSONAS.add_item(data=request_model.model_dump()) - await _notify_personas_changed() + await _notify_personas_changed(request_model.supported_llms) return KlatAPIResponse.OK @@ -135,7 +136,7 @@ async def set_persona( MongoDocumentsAPI.PERSONAS.update_item( filters=mongo_filter, data=request_model.model_dump() ) - await _notify_personas_changed() + await _notify_personas_changed(request_model.supported_llms) return KlatAPIResponse.OK @@ -167,9 +168,28 @@ async def toggle_persona_state( return KlatAPIResponse.OK -async def _notify_personas_changed(): - response = await list_personas(CurrentUserModel(_id="", nickname="", - first_name="", last_name=""), - ListPersonasQueryModel(only_enabled=True)) - enabled_personas = json.loads(response.body.decode()) - sio.emit("configured_personas_changed", enabled_personas) +async def _notify_personas_changed(supported_llms: Optional[List[str]] = None): + """ + Emit an SIO event for each LLM affected by a persona change. This sends a + complete set of personas rather than only the changed one to prevent sync + conflicts and simplify client-side logic. + :param supported_llms: List of LLM names affected by a transaction. If None, + then updates all LLMs listed in database configuration + """ + resp = await list_personas(CurrentUserModel(_id="", nickname="", + first_name="", last_name=""), + ListPersonasQueryModel(only_enabled=True)) + enabled_personas = json.loads(resp.body.decode()) + valid_personas = {} + if supported_llms: + # Only broadcast updates for LLMs affected by an insert/change request + for llm in supported_llms: + valid_personas[llm] = [per for per in enabled_personas["items"] if + llm in per["supported_llms"]] + else: + # Delete request does not have LLM context, update everything + for persona in enabled_personas["items"]: + for llm in persona["supported_llms"]: + valid_personas.setdefault(llm, []) + valid_personas[llm].append(persona) + sio.emit("configured_personas_changed", {"personas": valid_personas}) diff --git a/services/klatchat_observer/controller.py b/services/klatchat_observer/controller.py index 873eabcb..88dfc0fc 100644 --- a/services/klatchat_observer/controller.py +++ b/services/klatchat_observer/controller.py @@ -821,12 +821,13 @@ def _handle_personas_changed(self, data: dict): MQ message to allow any connected listeners to maintain a set of known personas. """ - self.send_message( - request_data=data, - vhost=self.get_vhost("llm"), - queue="configured_personas_changed", - expiration=5000, - ) + for llm, personas in data["personas"].items(): + self.send_message( + request_data={"items": personas}, + vhost=self.get_vhost("llm"), + queue=f"{llm}_personas_input", + expiration=5000, + ) def _refresh_default_persona_llms(self, data): for item in data["items"]: From f6cc4b44458dbad056c6f659d323722f1a009be0 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Tue, 26 Nov 2024 13:51:03 -0800 Subject: [PATCH 3/6] Remove irrelevant changes to observer Change comment to docstring --- services/klatchat_observer/controller.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/services/klatchat_observer/controller.py b/services/klatchat_observer/controller.py index 88dfc0fc..ff56afd7 100644 --- a/services/klatchat_observer/controller.py +++ b/services/klatchat_observer/controller.py @@ -306,7 +306,7 @@ def get_neon_service(self, wait_timeout: int = 10) -> None: LOG.info("Joining sync consumer") sync_consumer.join() if not self.neon_service_event.is_set(): - LOG.warning(f"Failed to get neon response in {wait_timeout} seconds") + LOG.warning(f"Failed to get neon_service in {wait_timeout} seconds") self.__neon_service_id = "" def register_sio_handlers(self): @@ -400,7 +400,6 @@ def get_neon_request_structure(msg_data: dict): if requested_skill == "tts": utterance = msg_data.pop("utterance", "") or msg_data.pop("text", "") request_dict = { - "msg_type": "neon.get_tts", "data": { "utterance": utterance, "text": utterance, @@ -409,14 +408,12 @@ def get_neon_request_structure(msg_data: dict): } elif requested_skill == "stt": request_dict = { - "msg_type": "neon.get_stt", "data": { "audio_data": msg_data.pop("audio_data", msg_data["message_body"]), } } else: request_dict = { - "msg_type": "recognizer_loop:utterance", "data": { "utterances": [msg_data["message_body"]], }, @@ -434,9 +431,8 @@ def __handle_neon_recipient(self, recipient_data: dict, msg_data: dict): recipient_data.setdefault("context", {}) pattern = re.compile("Neon", re.IGNORECASE) msg_data["message_body"] = ( - pattern.sub("", msg_data["message_body"], 1).strip("<>@,.:|- \n") + pattern.sub("", msg_data["message_body"], 1).strip("<>@,.:|- ").capitalize() ) - # This is really referencing an MQ endpoint (i.e. stt, tts), not a skill msg_data.setdefault( "requested_skill", recipient_data["context"].pop("service", "recognizer") ) @@ -785,7 +781,9 @@ def on_subminds_state(self, body: dict): @create_mq_callback() def on_get_configured_personas(self, body: dict): - # Handles request to get all defined personas + """ + Handles requests to get all defined personas for a specific LLM service + """ response_data = self._fetch_persona_api(user_id=body.get("user_id")) response_data["items"] = [ item From f1cb2173f6943eb96ca3cc9c4d727df7534c4f12 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Tue, 26 Nov 2024 15:12:05 -0800 Subject: [PATCH 4/6] Refactor `notify_personas_changed` into `server_utils` submodule per review --- chat_server/blueprints/personas.py | 39 +++----------- chat_server/server_utils/socketio_utils.py | 63 ++++++++++++++++++++++ 2 files changed, 69 insertions(+), 33 deletions(-) create mode 100644 chat_server/server_utils/socketio_utils.py diff --git a/chat_server/blueprints/personas.py b/chat_server/blueprints/personas.py index c4910149..cd53e965 100644 --- a/chat_server/blueprints/personas.py +++ b/chat_server/blueprints/personas.py @@ -25,13 +25,10 @@ # 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 json -from typing import List, Optional from fastapi import APIRouter from starlette.responses import JSONResponse -from chat_server.server_utils.api_dependencies import CurrentUserModel from chat_server.server_utils.enums import RequestModelType, UserRoles from chat_server.server_utils.http_exceptions import ( ItemNotFoundException, @@ -51,7 +48,7 @@ PersonaData, ) from chat_server.server_utils.api_dependencies.validators import permitted_access -from chat_server.sio.server import sio +from chat_server.server_utils.socketio_utils import notify_personas_changed from utils.database_utils.mongo_utils import MongoFilter, MongoLogicalOperators from utils.database_utils.mongo_utils.queries.wrapper import MongoDocumentsAPI @@ -116,7 +113,7 @@ async def add_persona( if existing_model: raise DuplicatedItemException MongoDocumentsAPI.PERSONAS.add_item(data=request_model.model_dump()) - await _notify_personas_changed(request_model.supported_llms) + await notify_personas_changed(request_model.supported_llms) return KlatAPIResponse.OK @@ -136,7 +133,7 @@ async def set_persona( MongoDocumentsAPI.PERSONAS.update_item( filters=mongo_filter, data=request_model.model_dump() ) - await _notify_personas_changed(request_model.supported_llms) + await notify_personas_changed(request_model.supported_llms) return KlatAPIResponse.OK @@ -146,7 +143,7 @@ async def delete_persona( ): """Deletes persona""" MongoDocumentsAPI.PERSONAS.delete_item(item_id=request_model.persona_id) - await _notify_personas_changed() + await notify_personas_changed() return KlatAPIResponse.OK @@ -164,32 +161,8 @@ async def toggle_persona_state( ) if updated_data.matched_count == 0: raise ItemNotFoundException - await _notify_personas_changed() + await notify_personas_changed() return KlatAPIResponse.OK -async def _notify_personas_changed(supported_llms: Optional[List[str]] = None): - """ - Emit an SIO event for each LLM affected by a persona change. This sends a - complete set of personas rather than only the changed one to prevent sync - conflicts and simplify client-side logic. - :param supported_llms: List of LLM names affected by a transaction. If None, - then updates all LLMs listed in database configuration - """ - resp = await list_personas(CurrentUserModel(_id="", nickname="", - first_name="", last_name=""), - ListPersonasQueryModel(only_enabled=True)) - enabled_personas = json.loads(resp.body.decode()) - valid_personas = {} - if supported_llms: - # Only broadcast updates for LLMs affected by an insert/change request - for llm in supported_llms: - valid_personas[llm] = [per for per in enabled_personas["items"] if - llm in per["supported_llms"]] - else: - # Delete request does not have LLM context, update everything - for persona in enabled_personas["items"]: - for llm in persona["supported_llms"]: - valid_personas.setdefault(llm, []) - valid_personas[llm].append(persona) - sio.emit("configured_personas_changed", {"personas": valid_personas}) + diff --git a/chat_server/server_utils/socketio_utils.py b/chat_server/server_utils/socketio_utils.py new file mode 100644 index 00000000..b2d31fa4 --- /dev/null +++ b/chat_server/server_utils/socketio_utils.py @@ -0,0 +1,63 @@ +# 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 json + +from typing import Optional, List + +from chat_server.server_utils.api_dependencies import (CurrentUserModel, + ListPersonasQueryModel) +from chat_server.sio.server import sio + + +async def notify_personas_changed(supported_llms: Optional[List[str]] = None): + """ + Emit an SIO event for each LLM affected by a persona change. This sends a + complete set of personas rather than only the changed one to prevent sync + conflicts and simplify client-side logic. + :param supported_llms: List of LLM names affected by a transaction. If None, + then updates all LLMs listed in database configuration + """ + from chat_server.blueprints.personas import list_personas + resp = await list_personas(CurrentUserModel(_id="", nickname="", + first_name="", last_name=""), + ListPersonasQueryModel(only_enabled=True)) + enabled_personas = json.loads(resp.body.decode()) + valid_personas = {} + if supported_llms: + # Only broadcast updates for LLMs affected by an insert/change request + for llm in supported_llms: + valid_personas[llm] = [per for per in enabled_personas["items"] if + llm in per["supported_llms"]] + else: + # Delete request does not have LLM context, update everything + for persona in enabled_personas["items"]: + for llm in persona["supported_llms"]: + valid_personas.setdefault(llm, []) + valid_personas[llm].append(persona) + sio.emit("configured_personas_changed", {"personas": valid_personas}) From 78875402b5c82c14d32577b6ec5d8efbab9ccbbd Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Mon, 2 Dec 2024 17:30:19 -0800 Subject: [PATCH 5/6] Add locking around `configured_personas_changed` to ensure timestamps are in the same order of persona responses Includes `update_time` in TTL cached query so cached responses include accurate timestamps --- chat_server/server_utils/socketio_utils.py | 42 +++++++++++++--------- services/klatchat_observer/controller.py | 5 ++- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/chat_server/server_utils/socketio_utils.py b/chat_server/server_utils/socketio_utils.py index b2d31fa4..2c4412ab 100644 --- a/chat_server/server_utils/socketio_utils.py +++ b/chat_server/server_utils/socketio_utils.py @@ -27,14 +27,19 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import json +from time import time from typing import Optional, List +from asyncio import Lock from chat_server.server_utils.api_dependencies import (CurrentUserModel, ListPersonasQueryModel) from chat_server.sio.server import sio +_LOCK = Lock() + + async def notify_personas_changed(supported_llms: Optional[List[str]] = None): """ Emit an SIO event for each LLM affected by a persona change. This sends a @@ -44,20 +49,23 @@ async def notify_personas_changed(supported_llms: Optional[List[str]] = None): then updates all LLMs listed in database configuration """ from chat_server.blueprints.personas import list_personas - resp = await list_personas(CurrentUserModel(_id="", nickname="", - first_name="", last_name=""), - ListPersonasQueryModel(only_enabled=True)) - enabled_personas = json.loads(resp.body.decode()) - valid_personas = {} - if supported_llms: - # Only broadcast updates for LLMs affected by an insert/change request - for llm in supported_llms: - valid_personas[llm] = [per for per in enabled_personas["items"] if - llm in per["supported_llms"]] - else: - # Delete request does not have LLM context, update everything - for persona in enabled_personas["items"]: - for llm in persona["supported_llms"]: - valid_personas.setdefault(llm, []) - valid_personas[llm].append(persona) - sio.emit("configured_personas_changed", {"personas": valid_personas}) + async with _LOCK: + resp = await list_personas(CurrentUserModel(_id="", nickname="", + first_name="", last_name=""), + ListPersonasQueryModel(only_enabled=True)) + update_time = time() + enabled_personas = json.loads(resp.body.decode()) + valid_personas = {} + if supported_llms: + # Only broadcast updates for LLMs affected by an insert/change request + for llm in supported_llms: + valid_personas[llm] = [per for per in enabled_personas["items"] if + llm in per["supported_llms"]] + else: + # Delete request does not have LLM context, update everything + for persona in enabled_personas["items"]: + for llm in persona["supported_llms"]: + valid_personas.setdefault(llm, []) + valid_personas[llm].append(persona) + sio.emit("configured_personas_changed", {"personas": valid_personas, + "update_time": update_time}) diff --git a/services/klatchat_observer/controller.py b/services/klatchat_observer/controller.py index ff56afd7..2c8cdbf7 100644 --- a/services/klatchat_observer/controller.py +++ b/services/klatchat_observer/controller.py @@ -807,6 +807,7 @@ def _fetch_persona_api(self, user_id: Optional[str]) -> dict: try: response = self._fetch_klat_server(url=url) data = response.json() + data['update_time'] = time.time() self._refresh_default_persona_llms(data=data) except KlatAPIAuthorizationError: LOG.error(f"Failed to fetch personas from {url = }") @@ -821,7 +822,9 @@ def _handle_personas_changed(self, data: dict): """ for llm, personas in data["personas"].items(): self.send_message( - request_data={"items": personas}, + request_data={ + "items": personas, + "update_time": data.get("update_time") or time.time()}, vhost=self.get_vhost("llm"), queue=f"{llm}_personas_input", expiration=5000, From eda76104668005529f1bc534215fcbccb0608f24 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Thu, 5 Dec 2024 12:13:54 -0800 Subject: [PATCH 6/6] Remove unnecessary newline changes --- chat_server/blueprints/personas.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/chat_server/blueprints/personas.py b/chat_server/blueprints/personas.py index cd53e965..c8b7f70a 100644 --- a/chat_server/blueprints/personas.py +++ b/chat_server/blueprints/personas.py @@ -163,6 +163,3 @@ async def toggle_persona_state( raise ItemNotFoundException await notify_personas_changed() return KlatAPIResponse.OK - - -