Skip to content

Commit 5b86ca5

Browse files
committed
feat: Add property for configuring llm to wait for user input
1 parent b51716d commit 5b86ca5

File tree

7 files changed

+46
-9
lines changed

7 files changed

+46
-9
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## v0.4.0 (21-10-2025)
2+
3+
**Added:**
4+
- Added a `waitForUserInput` property (defaults to `true`) within the `llmConnector` attribute - if set to `false`, the llm prompt will be fired upon entering the llm block (i.e. uses user input from previous block instead of waiting for the next user input)
5+
16
## v0.3.2 (12-06-2025)
27

38
**Fixed:**

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"files": [
1616
"./dist"
1717
],
18-
"version": "0.3.2",
18+
"version": "0.4.0",
1919
"description": "A generic LLM connector for integrating Large Language Models (LLMs) in React ChatBotify!",
2020
"type": "module",
2121
"main": "./dist/index.cjs",
@@ -48,7 +48,7 @@
4848
"peerDependencies": {
4949
"@mlc-ai/web-llm": "^0.2.78",
5050
"react": ">=16.14.0",
51-
"react-chatbotify": "^2.0.0-beta.39",
51+
"react-chatbotify": "^2.4.1",
5252
"react-dom": ">=16.14.0"
5353
},
5454
"devDependencies": {

src/core/useRcbPlugin.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,28 @@ import { useMessageHandler } from '../hooks/useMessageHandler';
2323
* @param pluginConfig configurations for the plugin
2424
*/
2525
const useRcbPlugin = (pluginConfig?: PluginConfig): ReturnType<Plugin> => {
26+
const blockLoadedRef = useRef<boolean>(false);
2627
const messagesRef = useRef<Message[]>([]);
2728
const providerRef = useRef<Provider | null>(null);
2829
const outputTypeRef = useRef<'character' | 'chunk' | 'full'>('chunk');
2930
const outputSpeedRef = useRef<number>(30);
3031
const historySizeRef = useRef<number>(0);
3132
const initialMessageRef = useRef<string>('');
33+
const waitForUserInputRef = useRef<boolean>(true);
3234
const errorMessageRef = useRef<string>('Unable to get response, please try again.');
3335
const onUserMessageRef = useRef<((msg: Message) => Promise<string | null>) | null>(null);
3436
const onKeyDownRef = useRef<((e: KeyboardEvent) => Promise<string | null>) | null>(null);
3537

3638
const { getFlow } = useFlow();
3739
const { speakAudio } = useAudio();
38-
const { messages, injectMessage, simulateStreamMessage, streamMessage, endStreamMessage } = useMessages();
40+
const {
41+
messages,
42+
injectMessage,
43+
simulateStreamMessage,
44+
streamMessage,
45+
endStreamMessage,
46+
getMessages
47+
} = useMessages();
3948
const { goToPath } = usePaths();
4049
const { toggleTextAreaDisabled, focusTextArea } = useTextArea();
4150
const { toggleIsBotTyping, getIsChatBotVisible } = useChatWindow();
@@ -54,25 +63,29 @@ const useRcbPlugin = (pluginConfig?: PluginConfig): ReturnType<Plugin> => {
5463
outputSpeedRef.current = block.llmConnector?.outputSpeed ?? 30;
5564
historySizeRef.current = block.llmConnector?.historySize ?? 0;
5665
initialMessageRef.current = block.llmConnector?.initialMessage ?? '';
66+
waitForUserInputRef.current = block.llmConnector?.waitForUserInput ?? true;
5767
errorMessageRef.current = block.llmConnector?.errorMessage ?? 'Unable to get response, please try again.';
5868
onUserMessageRef.current = block.llmConnector?.stopConditions?.onUserMessage ?? null;
5969
onKeyDownRef.current = block.llmConnector?.stopConditions?.onKeyDown ?? null;
6070
});
6171

6272
const refs = {
73+
blockLoadedRef,
6374
providerRef,
6475
messagesRef,
6576
outputTypeRef,
6677
outputSpeedRef,
6778
historySizeRef,
6879
initialMessageRef,
80+
waitForUserInputRef,
6981
errorMessageRef,
7082
onUserMessageRef,
7183
onKeyDownRef,
7284
};
7385

7486
const actions = {
7587
speakAudio,
88+
getMessages,
7689
injectMessage,
7790
simulateStreamMessage,
7891
streamMessage,

src/hooks/useMessageHandler.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,21 @@ const STREAM_DEBOUNCE_MS = 500;
2222
*/
2323
const useMessageHandler = (
2424
refs: {
25+
blockLoadedRef: React.MutableRefObject<boolean>;
2526
providerRef: React.MutableRefObject<Provider | null>;
2627
messagesRef: React.MutableRefObject<Message[]>;
2728
outputTypeRef: React.MutableRefObject<'character' | 'chunk' | 'full'>;
2829
outputSpeedRef: React.MutableRefObject<number>;
2930
historySizeRef: React.MutableRefObject<number>;
3031
initialMessageRef: React.MutableRefObject<string>;
32+
waitForUserInputRef: React.MutableRefObject<boolean>;
3133
errorMessageRef: React.MutableRefObject<string>;
3234
onUserMessageRef: React.MutableRefObject<((msg: Message) => Promise<string | null>) | null>;
3335
onKeyDownRef: React.MutableRefObject<((e: KeyboardEvent) => Promise<string | null>) | null>;
3436
},
3537
actions: {
3638
speakAudio: (text: string) => void;
39+
getMessages: (sender?: string, numMessages?: number, offset?: number) => Message[] | null;
3740
injectMessage: (content: string | JSX.Element, sender?: string) => Promise<Message | null>;
3841
simulateStreamMessage: (content: string, sender?: string) => Promise<Message | null>;
3942
streamMessage: (msg: string) => void;
@@ -47,6 +50,7 @@ const useMessageHandler = (
4750
) => {
4851
const { messagesRef, outputTypeRef, onUserMessageRef, onKeyDownRef, errorMessageRef } = refs;
4952
const {
53+
getMessages,
5054
injectMessage,
5155
simulateStreamMessage,
5256
toggleTextAreaDisabled,
@@ -137,6 +141,18 @@ const useMessageHandler = (
137141
window.addEventListener('keydown', onKey);
138142
return () => window.removeEventListener('keydown', onKey);
139143
}, []);
144+
145+
// calls handler once if block isn't loaded yet; then set it to true
146+
useEffect(() => {
147+
if (!refs.blockLoadedRef.current && refs.providerRef.current && !refs.waitForUserInputRef.current) {
148+
const lastUserMessage = getMessages('user', 1)?.pop();
149+
// create a mock event to trigger the handler
150+
// todo: this approach is a little hacky, consider refactoring later
151+
const fakeEvt = { data: { message: lastUserMessage } } as unknown as RcbPostInjectMessageEvent;
152+
handler(fakeEvt);
153+
refs.blockLoadedRef.current = true;
154+
}
155+
}, [handler]);
140156
};
141157

142158
export { useMessageHandler };

src/hooks/useProcessBlock.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import { Provider } from '../types/Provider';
1111
*/
1212
const useProcessBlock = (
1313
refs: {
14+
blockLoadedRef: React.MutableRefObject<boolean>;
1415
providerRef: React.MutableRefObject<Provider | null>;
1516
messagesRef: React.MutableRefObject<Message[]>;
1617
outputTypeRef: React.MutableRefObject<'character' | 'chunk' | 'full'>;
1718
outputSpeedRef: React.MutableRefObject<number>;
1819
historySizeRef: React.MutableRefObject<number>;
1920
initialMessageRef: React.MutableRefObject<string>;
21+
waitForUserInputRef: React.MutableRefObject<boolean>;
2022
errorMessageRef: React.MutableRefObject<string>;
2123
onUserMessageRef: React.MutableRefObject<((msg: Message) => Promise<string | null>) | null>;
2224
onKeyDownRef: React.MutableRefObject<((e: KeyboardEvent) => Promise<string | null>) | null>;

src/types/LlmConnectorBlock.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type LlmConnectorBlock = Block & {
1212
historySize?: number;
1313
initialMessage?: string;
1414
errorMessage?: string;
15+
waitForUserInput?: boolean;
1516
stopConditions?: {
1617
onUserMessage?: (message: Message) => Promise<string | null>;
1718
onKeyDown?: (event: KeyboardEvent) => Promise<string | null>;

0 commit comments

Comments
 (0)