From 912ed3dfa7f76c66bcb902afeacb2dc24f0ffeb1 Mon Sep 17 00:00:00 2001 From: FosanzDev Date: Sun, 22 Feb 2026 17:05:59 +0100 Subject: [PATCH] feat: Implement intermediate listener for changed settings. This way you can read what changed and return a new set of settings dinamically. --- backend/chainlit/__init__.py | 2 ++ backend/chainlit/callbacks.py | 17 +++++++++++++++++ backend/chainlit/config.py | 1 + backend/chainlit/socket.py | 9 +++++++++ frontend/src/components/ChatSettings/index.tsx | 14 ++++++++++++-- libs/react-client/src/useChatInteract.ts | 8 ++++++++ 6 files changed, 49 insertions(+), 2 deletions(-) diff --git a/backend/chainlit/__init__.py b/backend/chainlit/__init__.py index 01dd8ca9a2..99dd9d15b2 100644 --- a/backend/chainlit/__init__.py +++ b/backend/chainlit/__init__.py @@ -82,6 +82,7 @@ on_mcp_connect, on_mcp_disconnect, on_message, + on_settings_edit, on_settings_update, on_shared_thread_view, on_slack_reaction_added, @@ -202,6 +203,7 @@ def acall(self): "on_mcp_connect", "on_mcp_disconnect", "on_message", + "on_settings_edit", "on_settings_update", "on_shared_thread_view", "on_slack_reaction_added", diff --git a/backend/chainlit/callbacks.py b/backend/chainlit/callbacks.py index bf5d5e8c31..eaad24e06e 100644 --- a/backend/chainlit/callbacks.py +++ b/backend/chainlit/callbacks.py @@ -449,6 +449,23 @@ def on_settings_update( return func +def on_settings_edit( + func: Callable[[Dict[str, Any]], Any], +) -> Callable[[Dict[str, Any]], Any]: + """ + Hook to react to the user editing any settings (on the fly). + + Args: + func (Callable[], Any]): The hook to execute while settings are being edited. + + Returns: + Callable[], Any]: The decorated hook. + """ + + config.code.on_settings_edit = wrap_user_function(func, with_task=True) + return func + + def data_layer( func: Callable[[], BaseDataLayer], ) -> Callable[[], BaseDataLayer]: diff --git a/backend/chainlit/config.py b/backend/chainlit/config.py index ede5adacd0..cdc8ff5828 100644 --- a/backend/chainlit/config.py +++ b/backend/chainlit/config.py @@ -397,6 +397,7 @@ class CodeSettings(BaseModel): on_audio_end: Optional[Callable[[], Any]] = None on_mcp_connect: Optional[Callable] = None on_mcp_disconnect: Optional[Callable] = None + on_settings_edit: Optional[Callable[[Dict[str, Any]], Any]] = None on_settings_update: Optional[Callable[[Dict[str, Any]], Any]] = None set_chat_profiles: Optional[ Callable[[Optional["User"], Optional["str"]], Awaitable[List["ChatProfile"]]] diff --git a/backend/chainlit/socket.py b/backend/chainlit/socket.py index dd969e091b..48f7644ffe 100644 --- a/backend/chainlit/socket.py +++ b/backend/chainlit/socket.py @@ -444,3 +444,12 @@ async def change_settings(sid, settings: Dict[str, Any]): if config.code.on_settings_update: await config.code.on_settings_update(settings) + + +@sio.on("chat_settings_edit") +async def edit_settings(sid, settings: Dict[str, Any]): + """Handle change settings edit from the UI (on the fly).""" + init_ws_context(sid) + + if config.code.on_settings_edit: + await config.code.on_settings_edit(settings) diff --git a/frontend/src/components/ChatSettings/index.tsx b/frontend/src/components/ChatSettings/index.tsx index c60c86c124..3d30044723 100644 --- a/frontend/src/components/ChatSettings/index.tsx +++ b/frontend/src/components/ChatSettings/index.tsx @@ -1,5 +1,6 @@ +import isEqual from 'lodash/isEqual'; import mapValues from 'lodash/mapValues'; -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { useForm } from 'react-hook-form'; import { useRecoilState, useSetRecoilState } from 'recoil'; @@ -29,7 +30,7 @@ export default function ChatSettingsModal() { const { chatSettingsValue, chatSettingsInputs, chatSettingsDefaultValue } = useChatData(); - const { updateChatSettings } = useChatInteract(); + const { updateChatSettings, editChatSettings } = useChatInteract(); const [chatSettingsOpen, setChatSettingsOpen] = useRecoilState( chatSettingsOpenState ); @@ -72,6 +73,15 @@ export default function ChatSettingsModal() { }; const values = watch(); + const prevValues = useRef(values); + + useEffect(() => { + if (!isEqual(values, prevValues.current)) { + editChatSettings(values); + prevValues.current = values; + } + }, [values, editChatSettings]); + const tabInputs = chatSettingsInputs.filter( (input: any) => Array.isArray(input?.inputs) && input.inputs.length > 0 ); diff --git a/libs/react-client/src/useChatInteract.ts b/libs/react-client/src/useChatInteract.ts index c80eb2aa40..a6a79f597b 100644 --- a/libs/react-client/src/useChatInteract.ts +++ b/libs/react-client/src/useChatInteract.ts @@ -145,6 +145,13 @@ const useChatInteract = () => { [session?.socket] ); + const editChatSettings = useCallback( + (values: object) => { + session?.socket.emit('chat_settings_edit', values); + }, + [session?.socket] + ); + const stopTask = useCallback(() => { setMessages((oldMessages) => oldMessages.map((m) => { @@ -178,6 +185,7 @@ const useChatInteract = () => { stopTask, setIdToResume, updateChatSettings, + editChatSettings, toggleMessageFavorite }; };