Search code examples
c#angularsignalrchatbotasp.net-core-signalr

Real-time chat streaming application using SignalR in C# and Angular


I am developing a chat application using C# and Angular. My controller in C# has a custom made Text Generative Algorithm which generates the response based on the user input from frontend. This response gets generated in chunks. I want to display the response on the frontend as the chunks are generated (also called as chat streaming, Streaming allows the model to generate and display text incrementally rather than waiting until the entire response is generated).

I tried using SignalR for real-time communication on the client and server side to display the chunks as in when they are generated. I created a new Hub Connection with URL "/MessageHub" and then started the Hub Connection. There are no errors building and starting the hub. Then I tried to send the user message from client side to server side using the invoke method of Hub Connection, but at this point, getting the error as Connection cannot be established.

Code in MessageHub Class in C#:

        public async Task SendMessage(string message, int requestId)
        {
            var response = _chatbotProvider.GetResponse(message, requestId);

            try
            {
                await Clients.All.SendAsync("ReceiveMessage", response);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

Code in Angular to create and start the Hub Connection.

ngOnInit(): void {
    this._hubConnection = new HubConnectionBuilder().withUrl("/MessageHub").build();

    this._hubConnection.start().then(() => console.log('Connection Started!'))
    .catch(err => console.log('Error while establishing connection'));

  }

  sendMessage(){
    this.requestId = this.route.snapshot.params['requestId'];

    this._hubConnection.invoke('SendMessage', this.userMessage, this.requestId).catch(err => console.error(err));

    this.chatMessages.push({ role: 'user', content: this.userMessage });

    this._hubConnection.on("ReceiveMessage", (botResponse) => {
      this.chatMessages.push({ role: 'assistant', content: botResponse})
    });
    console.log(this.chatMessages)
    this.userMessage = '';
  }

Getting the error as below:

What I am expecting is the chat streaming functionality in my application (Just as the response is generated in ChatGPT, chunk by chunk).

Or is there any other method with which I can achieve the same?


Solution

  • Make sure that requestId is of type int, as is the type in the SendMessage on the server side

    You can give it a try.

    this.requestId = parseInt(this.route.snapshot.params['requestId'], 10);
    

    Here is my test result.

    enter image description here

        public async Task SendMessage(string message, int requestId)
        {
            var response = GenerateResponse(message, requestId); 
    
            foreach (char c in response)
            {
                await Clients.Caller.SendAsync("ReceiveMessage", c.ToString());
                await Task.Delay(50); 
            }
        }
    
        private string GenerateResponse(string message, int requestId)
        {
            return "In 2024, everyone will be happy in the new year."; 
        }
    

    You can refer my sample code, it's simple html, not angular.

    @{
        ViewData["Title"] = "SignalR";
    }
    <!DOCTYPE html>
    <html>
    <head>
        <title>Chat App</title>
    </head>
    <body>
        <div>
            <input type="text" id="userMessage" placeholder="Enter message" />
            <button onclick="sendMessage()">Send Message</button>
        </div>
        <div id="chatWindow"></div>
    
        <script>
            //var connection = new signalR.HubConnectionBuilder().withUrl("/MessageHub").build();
            var chatWindow = document.getElementById("chatWindow");
    
            connection.on("ReceiveMessage", function (message) {
                chatWindow.innerHTML += message;
            });
    
            connection.start().catch(function (err) {
                return console.error(err.toString());
            });
    
            function sendMessage() {
                var message = document.getElementById("userMessage").value;
                // Using 1001 mock this.requestId
                connection.invoke("SendMessage", message, 1001).catch(function (err) {
                    return console.error(err.toString());
                });
            }
        </script>
    </body>
    </html>