Showing posts with label ChatGPT. Show all posts
Showing posts with label ChatGPT. Show all posts

Wednesday, December 13, 2023

Give your ChatBot personality with Azure OpenAI and C#

We will create a .NET 8.0 chatbot console application that uses the ChatGPT natural language model. This will be done using Azure OpenAI. The chatbot will have a distinct personality which will be reflected in its response.

Source Code: https://github.com/medhatelmasry/BotWithPersonality

Prerequisites

You will need the following to continue:
  • .NET 8 SDK
  • A C# code editor such as Visual Studio Code
  • An Azure subscription with access to the OpenAI Service

Getting started with Azure OpenAI service

To follow this tutorial, you will create an Azure OpenAI service under your Azure subscription. Follow these steps:

Navigate to the Azure portal at https://portal.azure.com/. 



Click on “Create a resource”.


Enter “openai” in the filter then select “openai”.


Choose your subscription then create a new resource group. In my case (as shown above), I created a new resource group named “OpenAI-RG”.


Continue with the selection of a region, provide a instance name (mze-openai in the example above) and select the “Standard S0” pricing tier. Click on the Next button.


Accept the default (All networks, including the internet, can access this resource.) on the Network tab then click on the Next button.


On the Tags tab, click on Next without making any changes.


Click the Create button on the “Review + submit” tab. Deployment takes about one minute. 


On the Overview blade, click on “Keys and Endpoint” in the left side navigation.


Copy KEY 1 and Endpoint then save the values in a text editor like Notepad.

We will need to create a model deployment that we can use for text completion. To do this, return to the Overview tab.


Open “Go to Azure OpenAI Studio” in a new browser tab.


Click on “Create new deployment”.


Click on “+ Create new deployment”.


For the model, select “gpt-35-turbo” and give the deployment a name which you need to remember as this will be configured in the app that we will soon develop. I called the deployment name gpt35-turbo-deployment. Click on the Create button.

As a summary, we will need the following parameters in our application:

SettingValue
KEY 1:this-is-a-fake-api-key
Endpoint:https://mze-openai.openai.azure.com/
Model deployment:gpt35-turbo-deployment

Next, we will create our console application.

Console Application

Create a console application with .NET 8.0:

dotnet new console -f net8.0 -o BotWithPersonality
cd BotWithPersonality

Add these two packages:

dotnet add package Azure.AI.OpenAI -v 1.0.0-beta.11
dotnet add package Microsoft.Extensions.Configuration.Json -v 8.0.0

 

Configuration Settings

The first package is for Azure OpenAI. The second package will help us read configuration settings from the appsettings.json file.

Create a file named apsettings.json and add to the following:

{
    "settings": {
      "deployment-name": "gpt35-turbo-deployment",
      "endpoint": "https://mze-openai.openai.azure.com/",
      "key": "this-is-a-fake-api-key"
    }
}

When our application gets built and packaged, we want this file to get copied to the output directory. Therefore, we need to add the following XML to the .csproj file just before the closing </Project> tag.

<ItemGroup>
  <None Include="*.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> 

In order to read the settings from appsettings.json, we need to create a helper method. Add a class named Utils.cs and add to it the following code to it:

public class Utils {
    public static string GetConfigValue(string config) {

        IConfigurationBuilder builder = new ConfigurationBuilder();

        if (System.IO.File.Exists("appsettings.json"))
            builder.AddJsonFile("appsettings.json", false, true);

        if (System.IO.File.Exists("appsettings.Development.json"))
            builder.AddJsonFile("appsettings.Development.json", false, true);

        IConfigurationRoot root = builder.Build();

        return root[config]!;
    }
}

As an example, if we want to read the endpoint value in appsettings.json, we can use the following statement:

Utils.GetConfigValue("settings:endpoint")

Building our ChatBot app

Delete whatever code there is in Program.cs and add these using statements at the top:

using Azure;
using Azure.AI.OpenAI;
using BotWithPersonality;

Let us first read the settings we need from appsettings.json. Therefore, append this code to Program.cs:

string ENDPOINT = Utils.GetConfigValue("settings:endpoint");
string KEY = Utils.GetConfigValue("settings:key");
string DEPLOYMENT_NAME = Utils.GetConfigValue("settings:deployment-name");

Next, let us give our chatbot a personality. We will tell Azure OpenAI that our chatbot has the personality of a developer from Newfouldland in Eastern Canada. Append this constant to the Program.cs:

const string SYSTEM_MESSAGE 
    = """
    You are a friendly assistant named DotNetBot. 
    You prefer to use Canadian Newfoundland English as your language and are an expert in the .NET runtime 
    and C# and F# programming languages.
    Response using Newfoundland colloquialisms and slang.
    """;
    
Create a new OpenAIClient by appending the following code to Program.cs:

var openAiClient = new OpenAIClient(
    new Uri(ENDPOINT),
    new AzureKeyCredential(KEY)
);

We will next define our ChatCompletionsOptions with a starter user message "Introduce yourself". Append this code to Program.cs:

var chatCompletionsOptions = new ChatCompletionsOptions
{
    DeploymentName = DEPLOYMENT_NAME, // Use DeploymentName for "model" with non-Azure clients
    Messages =
    {
        new ChatRequestSystemMessage(SYSTEM_MESSAGE),
        new ChatRequestUserMessage("Introduce yourself"),
    }
};

The ChatCompletionsOptions object is aware of the deployment model name and keeps track of the conversation between the user and the chatbot. Note that there are two chat messages pre-filled before the conversation even starts. One chat message is from the System (SYSTEM_MESSAGE) and gives the chat model instructions on what kind of chatbot it is supposed to be. In this case, we told the chat model to act like somebody from Newfoundland, Canada. Then we told the chatbot to introduce itself by adding a message as User saying "Introduce yourself.".

Now that we have set up the OpenAIClient, and  ChatCompletionsOptions, we can start calling the APIs. Append the following code to Program.cs to finalize the chatbot:

while (true)
{
    Console.WriteLine();
    Console.Write("DotNetBot: ");
    
    Response<ChatCompletions> chatCompletionsResponse = await openAiClient.GetChatCompletionsAsync(
        chatCompletionsOptions
    );

    var chatMessage = chatCompletionsResponse.Value.Choices[0].Message;
    Console.WriteLine($"[{chatMessage.Role.ToString().ToUpperInvariant()}]: {chatMessage.Content}");
    
    chatCompletionsOptions.Messages.Add(new ChatRequestUserMessage(chatMessage.Content));
    
    Console.WriteLine();
    
    Console.Write("Enter a message: ");
    var userMessage = Console.ReadLine();
    chatCompletionsOptions.Messages.Add(new ChatRequestUserMessage(userMessage));
}

Inside an infinite loop, the chatCompletionsOptions is passed to the openAiClient.GetChatCompletionsAsync method.

The response from the chat model is then written to the console and added to the chat history stored in chatCompletionsOptions.

Now the user is prompted to say anything, which will then be added to the chat history as well. Thereafter, the next iteration of the loop starts, which sends the chat history back to the chat model in Azure OpenAI. One can terminate the endless loop by hitting CTRL+C on the keyboard.

Let us run the app and see how it performs.

Run the app with:

dotnet run

Here is an example of the ongoing dialog:

DotNetBot: [ASSISTANT]: How's she going, buddy? I'm DotNetBot, your trusty virtual assistant from the Rock. I'm here to lend you a hand with all things .NET and programming in C# and F#. So whether you need help with some code or just wanna have a chin wag about tech, I'm your go-to. Don't be shy now, fire away with your questions!

Enter a message: How do you spell color?

DotNetBot: [ASSISTANT]: Sure ting, me ol' pal! In Canadian Newfoundland English, we like to spell "color" as "colour." It's a wee bit different than how they spell it down in other parts of the world, but we like to keep things unique and full of character around here. So, if you're ever writin' some code and need to use that word, remember to toss in that extra "u" for good measure. Happy codin', me son!

Enter a message: 

I asked the question "How do you spell color?" and it rambled on about how it is done in Newfoundland. Quite amusing....

Hope this was useful.

Monday, September 18, 2023

Connect Azure OpenAI Service to your custom SQL Server data

In this tutorial, we will lear how to connect your custom SQL Server data to Azure OpenAI. You need to have an active Azure subscription in order to proceed with this tutorial.

Create Azure Cognitive Search

Point your browser to https://portal.azure.com/. Enter 'search' in the search field at the top of the page, then click on "Cognitive Search" from the short list of services.
 

On the resulting "Azure AI services | Cognitive search" page, click on the Create button.


On the "Create a search service" page:

  • choose a suitable subscription if you have more than one
  • create a new resource group named openai-and-sql-server
  • let the "Service name" be openai-and-sql-server-service-name
  • let the location be a data center closer to where you are. In my case I chose "East US".
  • leave the "Pricing tier" as Standard

Click on the blue "Review + create" button, then click on blue "Create" button. Once deployment is complete, click on the blue "Go to resource" button. This takes you to the page of the search resource that you just created.

Click on the blue Import button under "Connect your data".


On the import data page, let "Data Source" be Samples. Then click on realestate-us-sample.

Click on the blue "Next: Add cognitive skills (Optional)" button at the bottom of the page.

On the "Import data" page, click on the blue "Skip to: Customize target index" at the bottom of the page.


You will be taken to the "Customize target index" tab. Click on the blue "Next: Create an indexer" button at the bottom of the page.


In the "Create an indexer" tab, click on the blue Submit button.


In your main service page, click on indexers on the left side. Wait until the indexer you just created shows that it is successfully provisioned.


Click on Overview on the left-side menu, then click on the "Search explorer" link.


On the "Search explorer" page, we can query the database. The Index field is defaulted to the index that was just created. In the "Query string" field, enter search=condo, then click on the blue Search button.  You will see results for condos.


Azure OpenAI Service

We are now ready to connect this cognitive search service with OpenAI. Click on the Home link in the top left corner of the page.


In the filter field at the top, enter the word openai, then select Azure OpenAI.


Click on the "+ Create" link. 


Fill out the form parameters. I entered the data shown below.


Click on the blue Next button at the bottom of the page. Accept default values on the Network tab then click on blue Next button.


Also, accept default values on the Tags tab then click on blue Next button.


Click on the blue Create button when it appears at the bottom of tthe page.


Deployment takes some time. When it is complete, cclik on the blue "Go to resource" button.


Click on the Explore button.


Click on "Bring your own data".



We will need to create a deployment. Click on "Create new deployment".


Expand "Advanced options. Choose the gpt-35-turbo model, give the deployment a name (gpt-35-turbo-deploy), leave Default for "Content Filter". Click on the Create button.


On the "Data source" tab, choose as follows:

Select data source: Azure Cognitive Search
Subscription: {your azure subscription}
Azure Cognitive Search service: {the cognitive service you created earlier}
Azure Cognitive Search Index: {the cognitive search index that was created earlier.

Enable "I acknowledge that connecting to an Azure Cognitive Search account will incue usage too my account", then click on Next.


Choose all the fields for "Content data". 



Choose description for "Field name" and Title fields then click on Next.


Chooose Keyword for "Search type", then click oon Next.


Finally, click on the "Save and close" button on the "Review and finish" tab.


On the "Chat playground" page, the data source is automatically connected to the chat session.


Enter the following in the chat field: property in renton. Then, hit ENTER on your keyboard.


A response similar to the following will appear:


Conclusion

We were able to link Azure OpenAI with custom data and generate output through ChatGPT chat. The data source, of course, can be any enterprise relational data.

Saturday, June 10, 2023

Using ChatGPT Chat Completion API with ASP.NET Blazor Web Assembly

In this tutorial you will create a very simple Blazor Web Assembly application that interacts with ChatGPT.

Prerequisites

You will need the following:

  • .NET 7.0 or higher
  • Visual Studio Code
  • A ChatGPT account with https://openai.com

Get an ChatGPT API key from openai.com

Visit https://openai.com and create an account. The login page looks like this:


Click on your profile and select “View API keys”. On the next page, click on the “+ Create new secret key” button.


Give your key a name, then click on “Generate secret key”.


An API key is generated. You must save this key somewhere because this is your only chance to view it as it cannot be viewed again. Click on the copy button and paste it is a safe place.

Creating ASP.NET Blazor Web Assembly App

We will create a very simple Chat Completion client-side blazor app that discusses topics relating to the National Basketball Association (NBA).

In a working folder, execute the following terminal window commands to create an ASP.NET Blazor Web Assembly application named ChatGPTBlazorWasm:

dotnet new blazorwasm -n ChatGPTBlazorWasm
cd ChatGPTBlazorWasm

Open the blazor application in VS Code with:

code .

Replace contents of Pages/Index.razor with the following code:

@page "/"
@inject HttpClient httpClient

<h1>ChatGPT with Blazor WebAssembly (NBA)</h1>
<textarea @bind="dialog" cols="100" rows="15"></textarea>
<br />
<button @onclick="GetOpenAIChatCompletions" class="btn btn-success">Call Completion</button>

@code {
    private string? dialog;

    protected override void OnInitialized()
    {
        const string? apiKey = "fake-chatgpt-api-key";
        httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");

        Message[] messages = new Message[] {
            new Message { Role = "system", Content = "You are a helpful assistant." },
            new Message { Role = "user", Content = "Who won the NBA in 2020?" },
            new Message { Role = "assistant", Content = "The Los Angeles Lakers won the NBA in 2020." },
            new Message { Role = "user", Content = "Where was it played?" },
        };

        dialog = "";
        foreach (var message in messages)
        {
            dialog += $"role: {message.Role}\ncontent: {message.Content}\n\n";
        }
    }

    private async Task GetOpenAIChatCompletions()
    {
        string? text = this.dialog;

        // remove any * in text
        text = text!.Replace("*", "");

        var lines = text.Split('\n');

        // delete empty items in lines array
        for (var i = 0; i < lines.Length; i++)
        {
            if (lines[i] == "")
            {
                lines = lines.Take(i).Concat(lines.Skip(i + 1)).ToArray();
                i--;
            }
        }

        var result = new List<Message>();

        for (var i = 0; i < lines.Length; i += 2)
        {
            if (lines[i] == "")
            {
                continue;
            }
            var r = lines[i].Split(": ")[1];
            var c = lines[i + 1].Split(": ")[1];
            result.Add(new Message { Role = r, Content = c });
        }

        var response = await httpClient.PostAsJsonAsync("https://api.openai.com/v1/chat/completions", new
        {
            max_tokens = 50,
            n = 1,
            stop = "\n",
            model = "gpt-3.5-turbo",
            temperature = 0.5,
            messages = result
        });

        var data = await response.Content.ReadFromJsonAsync<OpenAIResponse>();

        Message? message = data!.choices![0].message;
        var content = message!.Content;
        var role = message!.Role;

        var reply = $"role: {role}\ncontent: {content}";

        this.dialog += "*" + reply + "*" + Environment.NewLine + Environment.NewLine;
    }

    private class Message
    {
        public string? Role { get; set; }
        public string? Content { get; set; }
    }

    private class OpenAIResponse
    {
        public Choice[]? choices { get; set; }
    }

    private class Choice
    {
        public Message? message { get; set; }
    }
}

Explaining the above code:

  • The UI consists of a textarea and button.
  • OnInitialized() method
    • remember to set the value of apiKey with the key that you obtained from https://openai.com
    • an HTTP header with Authorization key is created with the appropriate Bearer value
    • array named messages is declared with the initial dialog text
    • the messages array is used to set the contents of the textarea
  • GetOpenAIChatCompletions() method
    • this method is called when the button is clicked
    • a POST request is made to the endpoint at https://api.openai.com/v1/chat/completions
    • response from the server is appended to the textarea. In order to distinguish the response from the rest of the dialog, it is surrounded by *
After replacing the value of apiKey in the onInitialized() method, run the application by executing the following command i a terminal window:

dotnet watch

You should see the following page:


Note the text grounded in the NBA context about where the 2020 finals were played. Click on the green "Call Completion" button. The response indicates the the finals were held during the COVID pandemic in Orlando.
 

Let’s ask about what happened a year earlier in 2019 by appending this additional text:

role: user
content: How about in 2019? Which team won and where was it played?

Click on the “Call Completion” button.  Our page now looks like this:



We are told that the 2019 NBA champions were the Toronto Raptors.

You can see from this very simple tutorial that it is quite easy to incorporate ChaGPT into your Blazor Web Assembly applications.