3/6/2023 Admin

Build Your Own ChatGPT Client in Blazor


image

The OpenAI GPT-3.5-Turbo API is a powerful tool that allows developers to create cutting-edge applications that can understand, analyze, and generate human-like text. With Microsoft Blazor, we can harness the power of this API to create a seamless client experience. In this blog post, we will explore how we can use Microsoft Blazor to build a client that calls the new OpenAI GPT-3.5-Turbo API, and demonstrate some of the exciting possibilities that this combination brings.

 

image

The primary difference between this example and the previous example covered in: Calling OpenAI GPT-3 From Microsoft Blazor is that this example calls the OpenAI GPT-3.5-Turbo API and that allows us to create interactions that have context. Meaning, the response in each interaction is based on the interactions that preceded it. 

For example, when we ask the API to “List 5 reasons you would want to use this combination for web development” and click the Call ChatGPT button…

 

image

It processes the response…

 

image

…and the response is based on the previous back and forth interactions. It knows we were talking about Blazor and OpenAI.

 

Creating The Application

image

Use Visual Studio 2022 or higher.

image

Create a Blazor Server project.

image

Add the OpenAI-DotNet NuGet package.

image

Add a section like this to the appsettings.json file, replacing the Organization and ApiKey values with your own OpenAI values:

 

  "OpenAIServiceOptions": {
    "Organization": "** Your OpenAI Organization **",
    "ApiKey": "** Your OpenAI ApiKey **"
  }

 

(See this article for the steps you need to get an OpenAI key and Organization Id)

image

Open the Index.razor page and replace all the code with the following code:

 

@page "/"
@using OpenAI;
@using OpenAI.Chat;
@using OpenAI.Models;
@inject IConfiguration _configuration
@inject IJSRuntime _jsRuntime
<PageTitle>Index</PageTitle>

 

Add the following Styles to the page:

 

<style>
    textarea {
        border: 1px dashed #888;
        border-radius: 5px;
        width: 80%;
        overflow: auto;
        background: #f7f7f7
    }
    /* improved CSS for speech bubbles */
    .assistant, .user {
        position: relative;
        font-family: arial;
        font-size: 1.1em;
        border-radius: 10px;
        padding: 20px;
        margin-bottom: 20px;
    }
        .assistant:after, .user:after {
            content: '';
            border: 20px solid transparent;
            position: absolute;
            margin-top: -30px;
        }
    .user {
        background: #03a9f4;
        color: #fff;
        margin-left: 20%;
        margin-right: 100px;
        top: 30%;
        text-align: right;
    }
    .assistant {
        background: #4CAF50;
        color: #fff;
        margin-left: 100px;
        margin-right: 20%;
    }
    .user:after {
        border-left-color: #03a9f4;
        border-right: 0;
        right: -20px;
    }
    .assistant:after {
        border-right-color: #4CAF50;
        border-left: 0;
        left: -20px;
    }
    .msg {
        font-size: medium;
    }
</style>

 

Add the HTML markup:

 

<h1>ChatGPT</h1>
<p style="font-size:small"><b>Total Tokens:</b> @TotalTokens</p>
<div id="chatcontainer" style="height:550px; width:80%; overflow: scroll;">
    @foreach (var item in messages)
    {
        <div>
            @if (item.Role == Role.User)
            {
                <div style="float: right; margin-right: 20px; margin-top: 10px">
                    <b>Human</b>
                </div>
                <div class="@item.Role.ToString().ToLower()">
                    <div class="msg">
                        @item.Prompt
                        <br /><br />
                        <div style="font-size:xx-small;">
                            <i><b>(@item.Tokens)</b> Tokens</i>
                        </div>
                    </div>
                </div>
            }
            else
            {
                <div style="float: left; margin-left: 20px; margin-top: 10px">
                    <b>ChatGPT&nbsp;&nbsp;</b>
                </div>
                <div class="@item.Role.ToString().ToLower()">
                    <div class="msg">
                        @if (item.Prompt != null)
                        {
                            @((MarkupString)item.Prompt)
                        }
                        <br /><br />
                        <div style="font-size:xx-small;">
                            <i><b>(@item.Tokens)</b> Tokens</i>
                        </div>
                    </div>
                </div>
            }
        </div>
    }
</div>
@if (!Processing)
{
    <textarea rows="3" cols="60" @bind="prompt" />
    <br />
    <button class="btn btn-primary"
        @onclick="CallChatGPT">
        Call ChatGPT
    </button>
    <span>&nbsp;</span>
    <button class="btn btn-info"
        @onclick="RestartChatGPT">
        Restart
    </button>
}
else
{
    <br>
    <h4>Processing...</h4>
}
<br /><p style="color:red">@ErrorMessage</p>

 

Add the following fields:

 

    string Organization = "";
    string ApiKey = "";
    List<MessageSave> messages = new List<MessageSave>();
    string prompt = "Write a 10 word description of OpenAI ChatGPT";
    string ErrorMessage = "";
    bool Processing = false;
    int TotalTokens = 0;

 

Add the OnInitialized method that will run when the page first loads. This will retrieve the Organization Id and ApiKey from the appsettings.json file:

 

    protected override void OnInitialized()
    {
        Organization = _configuration["OpenAIServiceOptions:Organization"] ?? "";
        ApiKey = _configuration["OpenAIServiceOptions:ApiKey"] ?? "";
    }

 

image

Open the _Host.cshtml page and add the following JavaScript method:

 

    <script>
        window.ScrollToBottom = (elementName) => {
            element = document.getElementById(elementName);
            element.scrollTop = element.scrollHeight - element.clientHeight;
        }
    </script>

 

This method will scroll the chat screen to the bottom, so that the active part of the conversation is always visible.

 

image

Return to the Index.razor page, and add the OnAfterRenderAsync method that will call the ScrollToBottom JavaScript method we just added:

 

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        try
        {
            await _jsRuntime.InvokeAsync<string>("ScrollToBottom", "chatcontainer");
        }
        catch
        {
            // do nothing if this fails
        }
    }

 

Add the following class to hold the Messages:

 

    public class MessageSave
    {
        public string? Prompt { get; set; }
        public Role Role { get; set; }
        public int Tokens { get; set; }
    }

 

Add the following method that will be raised when the Restart button is clicked:

 

    void RestartChatGPT()
    {
        prompt = "Write a 10 word description of OpenAI ChatGPT";
        messages = new List<MessageSave>();
        TotalTokens = 0;
        ErrorMessage = "";
        StateHasChanged();
    }

 

Finally add the following method to call the ChatGPT API when the Call ChatGPT button is clicked:

 

    async Task CallChatGPT()
    {
        try
        {
            // Set Processing to true to indicate that the method is processing
            Processing = true;
            // Call StateHasChanged to refresh the UI
            StateHasChanged();
            // Clear any previous error messages
            ErrorMessage = "";
            // Create a new OpenAIClient object
            // with the provided API key and organization
            var api = new OpenAIClient(new OpenAIAuthentication(ApiKey, Organization));
            // Create a new list of chatMessages objects and initialize it with the
            // system's introductory message
            var chatMessages = new List<Message>();
            chatMessages.Add(new Message(Role.System, "You are helpful Assistant"));
            // Add all existing messages to chatMessages
            foreach (var item in messages)
            {
                chatMessages.Add(new Message(item.Role, item.Prompt));
            }
            // Add the new message to chatMessages
            chatMessages.Add(new Message(Role.User, prompt));
            // Call ChatGPT
            // Create a new ChatRequest object with the chat prompts and pass
            // it to the API's GetCompletionAsync method
            var chatRequest = new ChatRequest(chatMessages);
            var result = await api.ChatEndpoint.GetCompletionAsync(chatRequest);
            // Create a new MessageSave object with the user's prompt and other
            // details and add it to the messages list
            messages.Add(new MessageSave
                {
                    Prompt = prompt,
                    Role = Role.User,
                    Tokens = result.Usage.PromptTokens
                });
            // Create a new MessageSave object with the response and other details
            // and add it to the messages list
            messages.Add(new MessageSave
                {
                    Prompt = result.FirstChoice.Message,
                    Role = Role.Assistant,
                    Tokens = result.Usage.CompletionTokens
                });
            // Update the total number of tokens used by the API
            TotalTokens = TotalTokens + result.Usage.TotalTokens;
        }
        catch (Exception ex)
        {
            // Set ErrorMessage to the exception message if an error occurs
            ErrorMessage = ex.Message;
        }
        finally
        {
            // Clear the prompt variable
            prompt = "";
            // Set Processing to false to indicate 
            // that the method is done processing
            Processing = false;
            // Call StateHasChanged to refresh the UI
            StateHasChanged();
        }
    }

 

 

image

When we run the application, we can interact with it using normal conversational language.

 

Links

Calling OpenAI GPT-3 From Microsoft Blazor

Blazor and Azure OpenAI

 

Download

The project is available on the Downloads page on this site.

You must have Visual Studio 2022 (or higher) installed to run the code.

An unhandled error has occurred. Reload 🗙