Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react-chatbotify with Signal R #300

Open
div3791 opened this issue Mar 2, 2025 · 6 comments
Open

react-chatbotify with Signal R #300

div3791 opened this issue Mar 2, 2025 · 6 comments
Assignees
Labels
help wanted Extra attention is needed

Comments

@div3791
Copy link

div3791 commented Mar 2, 2025

Help Description:
I want to integrate Signal R for real time messaging with react-chatbotify. I also want to show typing indicator immedietly after user sends a message and typing indicator should be displayed untill any message doesnt come through Signal R event.

Library version:
2.0.0-beta.28.

Additional context:
I am using Vite + React + Signal R (Through Background Service Worker). When user opens chat window for first time, it should show welcome message. and after user sends first message, REST API is being called to send message to server and my chatbot is waiting for response from server through Signal R. I am using background service worker to manage signal connections and events

@div3791 div3791 added the help wanted Extra attention is needed label Mar 2, 2025
@div3791 div3791 changed the title [Help] react-chatbotify with Signal R Mar 2, 2025
@tjtanjin
Copy link
Owner

tjtanjin commented Mar 2, 2025

Hey @div3791! To preface, real-time messaging requires significant custom code currently. This is because there are inherent assumptions made within the core library (such as turn-based conversations) that must be addressed for live chat to work.

There are plans to make a live chat plugin, but the plugins for LLM are taking priority at the moment so until those are completed, live chat plugin is still a while away.

That said, you can go ahead and implement live chat by adopting the following approach. Note that I have yet to properly validate this approach but it is what comes to mind (my intention was to validate it when I start work on the live chat plugin but here goes):

  • Listen to change path event and detect when a path enters the block responsible for live chat
  • Within this block, all change path events have to be caught and prevented to avoid leaving the block (which includes blocking looping the block itself)
  • Listen to user message event to send the message to the backend for a response
  • When backend sends a message, params.injectMessage or params.streamMessage it into the frontend
  • Typing indicator will have to be done through a custom component that is injected and removed when required (core library cannot do it for you because it has no idea when your backend is done sending messages)

Overall a significant amount of code additions but what I've shared is the tentative approach I intend to use for the live chat plugin as well. Things may and will likely change when I get down to actual implementation but this is currently it. If this is urgent for you and you'd like to hack away at it, feel free to reach out on discord. I don't mind exploring solutions with you since this is a task I'll get to eventually as well :) Though note that even if we bounce ideas currently, you'll have to do most of the coding and testing because my hands are tied on other areas of work such as the LLM plugins 🥹

@div3791
Copy link
Author

div3791 commented Mar 2, 2025

Thanks a lot @tjtanjin for prompt reply!!!!!
Could you please give small example for the suggested approach? It would be a big favor from you.

When I tried to add custom component in injectMessage( it added to the chat window. but when I received signal r message, and tried to delete last message which is typing indicator component by message id it doesnt remove the typing indicator. I tried to check local storage where all the chats are being stored, Some times I can see typing indicator is not geeting deleted. and some times I got different message Id when I saved result of awat injectMessage(<TypingIndicatorCustomComponent/>) than saved message id. so may be that is causing not removal of typing indicator

let id = await injectMessage(<TypingIndicatorComponent />);
        console.log('Submitted Saved Indicator Id: ', id);
        setTypingIndicatorMessageId(id!);

Removal logic of typing indicator written in different file

const {messages, removeMessage, injectMessage} = useMessages();
let indicatorMessageItem = messages.findIndex(item=>item.content.toString().includes('<div'));
if (indicatorMessageItem !== -1) { 
          let removedMessageId = await removeMessage(messages[indicatorMessageItem].id);
          console.log(removedMessageId);
}

As a thanks and gratitude gesture, I will contribute to make this library more robust which can support signal r and other sockets once my current project is completed!!!

Thanks & Best Regards,
Divyesh Shani

@tjtanjin
Copy link
Owner

tjtanjin commented Mar 2, 2025

Hey @div3791, unfortunately I'm overseas at the moment and don't have my PC/laptop with me 🥹 I'll only be back in the middle of march so until then, I don't have a conducive setup for development-related work.

With that said, I can provide you with a couple of links I believe will come in useful:

  • Plugin example - plugins follow the same pattern of listening for events and responding to it with chatbot interactions via hooks
  • Change Path Event - when a new path is entered, you probably want to check if this is the path you want to handle live chat within
  • Pre Inject Message Event - within the live chat path, you want to listen on all pre inject message events (from users) and have them sent to your backend server or do whatever additional processing you need
  • Params - you're already using this, so I trust you know what's useful here

From the brief implementation you shared above, I suggest you store the id of the typing indicator and pass it directly into removeMessage instead of pulling the item out (is there a reason you're doing this?)

Appreciate your interest to help, happy to have you around as work for the live chat plugin kicks in 😊

@div3791
Copy link
Author

div3791 commented Mar 2, 2025

Thanks @tjtanjin I will go through the links given by you and will back to you!!

@div3791
Copy link
Author

div3791 commented Mar 2, 2025

const { injectMessage, removeMessage } = useMessages();
let removedMessageId = await removeMessage(typingId);

here typingId is string message id.

This is not removing message from chat window as well as local storage @tjtanjin

@tjtanjin
Copy link
Owner

tjtanjin commented Mar 3, 2025

const { injectMessage, removeMessage } = useMessages();
let removedMessageId = await removeMessage(typingId);

here typingId is string message id.

This is not removing message from chat window as well as local storage @tjtanjin

Have you checked if your id passed in is actually correct?

Here's what works on the playground (formatting might be off, was trying it from my mobile):

const MyChatBotWrapper = () => {
    const { toggleAudio } = useAudio();
    const { restartFlow } = useFlow();
    const { showToast } = useToasts();
    const { playNotificationSound } = useNotifications();
    const { injectMessage, removeMessage } = useMessages();
    const idRef = React.useRef(null);

    const settings = {
        general: {embedded: true},
        chatHistory: {storageKey: "example_custom_hooks"},
        audio: {disabled: false}
    }

    const flow={
        start: {
            message: "Welcome to the playground 🥳! Edit and experiment as you wish!",
            path: "end_loop"
        },
        end_loop: {
            message: (params) => `Received: ${params.userInput}`,
            path: "end_loop"
        }
    }

    return (
        <>
          <ExampleButton onClick={toggleAudio} text="Click me to toggle audio!"/>
          <ExampleButton onClick={restartFlow} text="Click me to reset the flow!"/>
          <ExampleButton onClick={() => showToast("Hello there!")} text="Click me to show a toast!"/>
          <ExampleButton onClick={playNotificationSound} text="Click me to play a notification sound!"/>
          <ExampleButton onClick={async () => idRef.current = await injectMessage("Hello I'm an injected message!")} text="Click me to inject a message!"/>
          <ExampleButton onClick={() => removeMessage(idRef.current)} text="Click me to remove a message!"/>
          <ChatBot settings={settings} flow={flow}/>
        </>
    );
};

const MyChatBotProvider = () => {
    return (
        <ChatBotProvider>
            <MyChatBotWrapper/>
        </ChatBotProvider>
    );
};

// button to test above feature
const exampleButtonStyle = {
    backgroundColor: '#491D8D',
    color: 'white',
    border: 'none',
    padding: '10px 20px',
    textAlign: 'center',
    textDecoration: 'none',
    display: 'inline-block',
    fontSize: '16px',
    borderRadius: '5px',
    cursor: 'pointer',
    transition: 'background-color 0.2s',
    margin: 10,
};
const ExampleButton = (props) => {
    return (
        <button onClick={props.onClick} style={exampleButtonStyle}>{props.text}</button>
    );
};

render(<MyChatBotProvider/>)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants