|
| 1 | +--- |
| 2 | +title: Sample AI-Powered Group Chat with SignalR and OpenAI |
| 3 | +author: kevinguo-ed |
| 4 | +description: A tutorial explaining how SignalR and OpenAI are used together to build an AI-powered group chat |
| 5 | +ms.author: wpickett |
| 6 | +ms.date: 03/19/2025 |
| 7 | +uid: tutorials/ai-powered-group-chat |
| 8 | +--- |
| 9 | + |
| 10 | +# AI-Powered Group Chat sample with SignalR and OpenAI |
| 11 | + |
| 12 | +The AI-Powered Group Chat sample demonstrates how to integrate OpenAI's capabilities into a real-time group chat application using ASP.NET Core SignalR. |
| 13 | + |
| 14 | +* View or download [the complete sample code](https://github.com/microsoft/SignalR-Samples-AI/tree/main/AIStreaming). |
| 15 | + |
| 16 | +## Overview |
| 17 | + |
| 18 | +Integrating AI into applications is becoming essential for developers aiming to enhance user creativity, productivity, and overall experience. AI-powered features, such as intelligent chatbots, personalized recommendations, and contextual responses, add significant value to modern apps. While many AI-powered applications, like those inspired by ChatGPT, focus on interactions between a single user and an AI assistant, there's growing interest in exploring AI's potential within team environments. Developers are now asking, "What value can AI add to a team of collaborators?" |
| 19 | + |
| 20 | +This sample guide highlights the process of building a real-time group chat application. In this chat, a group of human collaborators can interact with an AI assistant that has access to the chat history. Any collaborator can invite the AI to assist by starting their message with `@gpt`. The finished app looks like this: |
| 21 | + |
| 22 | +:::image type="content" source="./ai-powered-group-chat.jpg" alt-text="user interface for the AI-powered group chat"::: |
| 23 | + |
| 24 | +This sample uses OpenAI for generating intelligent, context-aware responses and SignalR for delivering the response to users in a group. You can find the complete code [in this repo](https://github.com/microsoft/SignalR-Samples-AI/tree/main/AIStreaming). |
| 25 | + |
| 26 | +## Dependencies |
| 27 | + |
| 28 | +Either Azure OpenAI or OpenAI can be used for this project. Make sure to update the `endpoint` and `key` in `appsettings.json`. `OpenAIExtensions` reads the configuration when the app starts, and the configuration values for `endpoint` and `key` are required to authenticate and use either service. |
| 29 | + |
| 30 | +### [OpenAI](#tab/open-ai) |
| 31 | + |
| 32 | +To build this application, you will need the following: |
| 33 | +* ASP.NET Core: To create the web application and host the SignalR hub. |
| 34 | +* [SignalR](https://www.nuget.org/packages/Microsoft.AspNetCore.SignalR.Client): For real-time communication between clients and the server. |
| 35 | +* [OpenAI Client](https://www.nuget.org/packages/OpenAI): To interact with OpenAI's API for generating AI responses. |
| 36 | + |
| 37 | +### [Azure OpenAI](#tab/azure-open-ai) |
| 38 | + |
| 39 | +To build this application, you will need the following: |
| 40 | +* ASP.NET Core: To create the web application and host the SignalR hub. |
| 41 | +* [SignalR](https://www.nuget.org/packages/Microsoft.AspNetCore.SignalR.Client): For real-time communication between clients and the server. |
| 42 | +* [Azure OpenAI](https://www.nuget.org/packages/Azure.AI.OpenAI): `Azure.AI.OpenAI` |
| 43 | + |
| 44 | +--- |
| 45 | + |
| 46 | +## Implementation |
| 47 | + |
| 48 | +This section highlights the key parts of the code that integrate SignalR with OpenAI to create an AI-enhanced group chat experience. |
| 49 | + |
| 50 | +### Data flow |
| 51 | + |
| 52 | +The following diagram highlights the step-by-step communication and processing involved in using OpenAI services, employing an iterative approach to responses and data handling: |
| 53 | + |
| 54 | +:::image type="content" source="./sequence-diagram-ai-powered-group-chat.png" alt-text="sequence diagram for the AI-powered group chat"::: |
| 55 | + |
| 56 | +In the previous diagram: |
| 57 | + |
| 58 | +* The Client sends instructions to the Server, which then communicates with OpenAI to process these instructions. |
| 59 | +* OpenAI responds with partial completion data, which the Server forwards back to the Client. This process repeats multiple times for an iterative exchange of data between these components. |
| 60 | + |
| 61 | +### SignalR Hub integration |
| 62 | + |
| 63 | +The `GroupChatHub` class manages user connections, message broadcasting, and AI interactions. |
| 64 | + |
| 65 | +When a user sends a message starting with `@gpt`: |
| 66 | + |
| 67 | +* The hub forwards it to OpenAI, which generates a response. |
| 68 | +* The AI's response is streamed back to the group in real-time. |
| 69 | + |
| 70 | +The following code snippet demonstrates how the `CompleteChatStreamingAsync` method streams responses from OpenAI incrementally: |
| 71 | + |
| 72 | +```csharp |
| 73 | +var chatClient = _openAI.GetChatClient(_options.Model); |
| 74 | + |
| 75 | +await foreach (var completion in |
| 76 | + chatClient.CompleteChatStreamingAsync(messagesInludeHistory)) |
| 77 | +{ |
| 78 | + // ... |
| 79 | + // Buffering and sending the AI's response in chunks |
| 80 | + await Clients.Group(groupName).SendAsync( |
| 81 | + "newMessageWithId", |
| 82 | + "ChatGPT", |
| 83 | + id, |
| 84 | + totalCompletion.ToString()); |
| 85 | + // ... |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | +In the previous code: |
| 90 | + |
| 91 | +* `chatClient.CompleteChatStreamingAsync(messagesIncludeHistory)` initiates the streaming of AI responses. |
| 92 | +* The `totalCompletion.Append(content)` line accumulates the AI's response. |
| 93 | +* If the length of the buffered content exceeds 20 characters, the buffered content is sent to the clients using `Clients.Group(groupName).SendAsync`. |
| 94 | + |
| 95 | +This ensures that the AI's response is delivered to the users in real-time, providing a seamless and interactive chat experience. |
| 96 | + |
| 97 | +### Maintain context with history |
| 98 | + |
| 99 | +Every request to [OpenAI's Chat Completions API](https://platform.openai.com/docs/guides/chat-completions) is stateless. OpenAI doesn't store past interactions. In a chat app, what a user or an assistant has said is important for generating a response that's contextually relevant. To achieve this, include chat history in every request to the Completions API. |
| 100 | + |
| 101 | +The `GroupHistoryStore` class manages chat history for each group. It stores messages posted by both the users and AI assistants, ensuring that the conversation context is preserved across interactions. This context is crucial for generating coherent AI responses. |
| 102 | + |
| 103 | +The following code demonstrates how to store messages generated by the AI assistant in memory. The `UpdateGroupHistoryForAssistant` method is called to add the AI assistant's message to the group history, ensuring that the conversation context is maintained: |
| 104 | + |
| 105 | +```csharp |
| 106 | +public void UpdateGroupHistoryForAssistant(string groupName, string message) |
| 107 | +{ |
| 108 | + var chatMessages = _store.GetOrAdd(groupName, _ => InitiateChatMessages()); |
| 109 | + chatMessages.Add(new AssistantChatMessage(message)); |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +The `_history.GetOrAddGroupHistory` method is called to add the user's message to the group history, ensuring that the conversation context is maintained: |
| 114 | + |
| 115 | +```csharp |
| 116 | +_history.GetOrAddGroupHistory(groupName, userName, message); |
| 117 | +``` |
| 118 | + |
| 119 | +### Stream AI responses |
| 120 | + |
| 121 | +The `CompleteChatStreamingAsync()` method streams responses from OpenAI incrementally, which allows the app to send partial responses to the client as they're generated. |
| 122 | + |
| 123 | +The code uses a `StringBuilder` to accumulate the AI's response. It checks the length of the buffered content and sends it to the clients when it exceeds a certain threshold, for example, 20 characters. This approach ensures that users see the AI’s response as it forms, mimicking a human-like typing effect. |
| 124 | + |
| 125 | +```csharp |
| 126 | +totalCompletion.Append(content); |
| 127 | + |
| 128 | +if (totalCompletion.Length - lastSentTokenLength > 20) |
| 129 | +{ |
| 130 | + await Clients.Group(groupName).SendAsync( |
| 131 | + "newMessageWithId", |
| 132 | + "ChatGPT", |
| 133 | + id, |
| 134 | + totalCompletion.ToString()); |
| 135 | + |
| 136 | + lastSentTokenLength = totalCompletion.Length; |
| 137 | +} |
| 138 | +``` |
| 139 | + |
| 140 | +## Explore further |
| 141 | + |
| 142 | +This project opens up exciting possibilities for further enhancement: |
| 143 | +1. **Advanced AI features**: Leverage other OpenAI capabilities like sentiment analysis, translation, or summarization. |
| 144 | +1. **Incorporating multiple AI agents**: You can introduce multiple AI agents with distinct roles or expertise areas within the same chat. For example, one agent might focus on text generation while the other provides image or audio generation. This can create a richer and more dynamic user experience where different AI agents interact seamlessly with users and each other. |
| 145 | +1. **Share chat history between server instances**: Implement a database layer to persist chat history across sessions, allowing conversations to resume even after a disconnect. Beyond SQL or NO SQL based solutions, you can also explore using a caching service like Redis. It can significantly improve performance by storing frequently accessed data, such as chat history or AI responses, in memory. This reduces latency and offloads database operations, leading to faster response times, particularly in high-traffic scenarios. |
| 146 | +1. **Leveraging Azure SignalR Service**: [Azure SignalR Service](/azure/azure-signalr/signalr-overview) provides scalable and reliable real-time messaging for your application. By offloading the SignalR backplane to Azure, you can scale out the chat application easily to support thousands of concurrent users across multiple servers. Azure SignalR also simplifies management and provides built-in features like automatic reconnections. |
0 commit comments