Partager via


Ajouter des achèvements de conversation OpenAI à votre application bureautique WinUI 3 / SDK d'application Windows

Dans ce guide pratique, vous allez apprendre à intégrer l’API d’OpenAI à votre application de bureau WinUI 3 /Windows App SDK. Nous allons créer une interface de type conversation qui vous permet de générer des réponses aux messages à l’aide de l’API de saisie semi-automatique de conversation d’OpenAI :

Une application de conversation moins minimaliste.

Prérequis

Créer un projet

  1. Ouvrez Visual Studio et créez un projet via File>New>Project :
  2. Recherchez et sélectionnez le modèle de projet WinUIProjet de test codé de l’interface utilisateurBlank App, Packaged (WinUI 3 in Desktop).
  3. Spécifiez le nom du projet, le nom de la solution et l’annuaire. Dans cet exemple, notre ChatGPT_WinUI3 projet appartient à une ChatGPT_WinUI3 solution qui sera créée dans C:\Projects\.

La structure de fichiers par défaut présentée ci-dessous doit alors s’afficher dans votre Explorateur de solutions :

Structure des répertoires par défaut.

Définir vos variables d’environnement

Pour utiliser le Kit de développement logiciel (SDK) OpenAI, vous devez définir une variable d’environnement avec votre clé API. Dans cet exemple, nous allons utiliser la variable d'environnement OPENAI_API_KEY. Une fois que vous avez votre clé API à partir du tableau de bord du développeur OpenAI, vous pouvez définir la variable d’environnement à partir de la ligne de commande comme suit :

setx OPENAI_API_KEY <your-api-key>

Notez que cette méthode fonctionne bien pour le développement, mais vous souhaitez utiliser une méthode plus sécurisée pour les applications de production (par exemple, vous pouvez stocker votre clé API dans un coffre de clés sécurisé auquel un service distant peut accéder pour le compte de votre application). Consultez la page Best practices for OpenAI key safety (Bonnes pratiques pour la sécurité des clés OpenAI).

Installer le SDK .NET OpenAI

Dans le menu de View Visual Studio, sélectionnez Terminal. Une instance d’affichage doit s’afficher Developer Powershell . Exécutez la commande suivante dans le répertoire racine de votre projet pour installer le Kit de développement logiciel (SDK).

dotnet add package Betalgo.OpenAI

Initialiser le SDK

Dans MainWindow.xaml.cs, initialisez le Kit de développement logiciel (SDK) avec votre clé API :

//...
using OpenAI;
using OpenAI.Managers;
using OpenAI.ObjectModels.RequestModels;
using OpenAI.ObjectModels;

namespace ChatGPT_WinUI3
{
    public sealed partial class MainWindow : Window
    {
        private OpenAIService openAiService;

        public MainWindow()
        {
            this.InitializeComponent();
           
            var openAiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");

            openAiService = new OpenAIService(new OpenAiOptions(){
                ApiKey = openAiKey
            });
        }
    }
}

Créer l’interface utilisateur de conversation

Nous allons utiliser un StackPanel pour afficher une liste de messages et permettre TextBox aux utilisateurs d’entrer de nouveaux messages. Mettez à jour MainWindow.xaml comme suit :

<Window
    x:Class="ChatGPT_WinUI3.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ChatGPT_WinUI3"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
            <ListView x:Name="ConversationList" />
            <StackPanel Orientation="Horizontal">
                <TextBox x:Name="InputTextBox" HorizontalAlignment="Stretch"/>
                <Button x:Name="SendButton" Content="Send" Click="SendButton_Click"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

Implémenter l’envoi, la réception et l’affichage des messages

Ajoutez un gestionnaire d’événements SendButton_Click pour gérer l’envoi, la réception et l’affichage des messages :

public sealed partial class MainWindow : Window
{
    // ...

    private async void SendButton_Click(object sender, RoutedEventArgs e)
    {
        string userInput = InputTextBox.Text;
        if (!string.IsNullOrEmpty(userInput))
        {
            AddMessageToConversation($"User: {userInput}");
            InputTextBox.Text = string.Empty;
            var completionResult = await openAiService.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest()
            {
                Messages = new List<ChatMessage>
                {
                    ChatMessage.FromSystem("You are a helpful assistant."),
                    ChatMessage.FromUser(userInput)
                },
                Model = Models.Gpt_4_1106_preview,
                MaxTokens = 300
            });

            if (completionResult != null && completionResult.Successful) {
                AddMessageToConversation("GPT: " + completionResult.Choices.First().Message.Content);
            } else {
                AddMessageToConversation("GPT: Sorry, something bad happened: " + completionResult.Error?.Message);
            }
        }
    }

    private void AddMessageToConversation(string message)
    {
        ConversationList.Items.Add(message);
        ConversationList.ScrollIntoView(ConversationList.Items[ConversationList.Items.Last()]);
    }
}

Exécuter l’application

Exécutez l’application et essayez de discuter ! Un résultat semblable à celui-ci doit s’afficher :

Une application de conversation minimaliste.

Améliorer l’interface de conversation

Nous allons apporter les améliorations suivantes à l’interface de conversation :

  • Ajoutez un ScrollViewer à StackPanel permettant d’activer le défilement.
  • Ajoutez un TextBlock pour afficher la réponse GPT d’une manière plus visuellement distincte de l’entrée de l’utilisateur.
  • Ajoutez un ProgressBar permettant d’indiquer quand l’application attend une réponse de l’API GPT.
  • Centrez la StackPanel fenêtre, comme l’interface web de ChatGPT.
  • Assurez-vous que les messages sont encapsulés à la ligne suivante lorsqu’ils atteignent le bord de la fenêtre.
  • Augmentez la TextBox taille et la réactivité de la Enter clé.

À partir du haut :

Ajouter ScrollViewer

Encapsulez l’élément ListView dans un ScrollViewer pour activer le défilement vertical sur de longues conversations :

        <StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
            <ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
                <ListView x:Name="ConversationList" />
            </ScrollViewer>
            <!-- ... -->
        </StackPanel>

Utilisez TextBlock.

Modifiez la méthode pour styler l’entrée AddMessageToConversation de l’utilisateur et la réponse GPT différemment :

    // ...
    private void AddMessageToConversation(string message)
    {
        var messageBlock = new TextBlock();
        messageBlock.Text = message;
        messageBlock.Margin = new Thickness(5);
        if (message.StartsWith("User:"))
        {
            messageBlock.Foreground = new SolidColorBrush(Colors.LightBlue);
        }
        else
        {
            messageBlock.Foreground = new SolidColorBrush(Colors.LightGreen);
        }
        ConversationList.Items.Add(messageBlock);
        ConversationList.ScrollIntoView(ConversationList.Items.Last()); 
    }

Ajouter ProgressBar

Pour indiquer quand l’application attend une réponse, ajoutez un ProgressBar au StackPanel :

        <StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
            <ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
                <ListView x:Name="ConversationList" />
            </ScrollViewer>
            <ProgressBar x:Name="ResponseProgressBar" Height="5" IsIndeterminate="True" Visibility="Collapsed"/> <!-- new! -->
        </StackPanel>

Ensuite, mettez à jour le SendButton_Click gestionnaire d’événements pour afficher le ProgressBar temps d’attente d’une réponse :

    private async void SendButton_Click(object sender, RoutedEventArgs e)
    {
        ResponseProgressBar.Visibility = Visibility.Visible; // new!

        string userInput = InputTextBox.Text;
        if (!string.IsNullOrEmpty(userInput))
        {
            AddMessageToConversation("User: " + userInput);
            InputTextBox.Text = string.Empty;
            var completionResult = await openAiService.Completions.CreateCompletion(new CompletionCreateRequest()
            {
                Prompt = userInput,
                Model = Models.TextDavinciV3
            });

            if (completionResult != null && completionResult.Successful) {
                AddMessageToConversation("GPT: " + completionResult.Choices.First().Text);
            } else {
                AddMessageToConversation("GPT: Sorry, something bad happened: " + completionResult.Error?.Message);
            }
        }
        ResponseProgressBar.Visibility = Visibility.Collapsed; // new!
    }

Centrer le StackPanel

Pour centrer StackPanel et extraire les messages vers le bas du TextBox, ajustez les Grid paramètres dans MainWindow.xaml :

    <Grid VerticalAlignment="Bottom" HorizontalAlignment="Center">
        <!-- ... -->
    </Grid>

Encapsuler les messages

Pour vous assurer que les messages sont encapsulés à la ligne suivante lorsqu’ils atteignent le bord de la fenêtre, mettez à jour MainWindow.xaml pour utiliser un ItemsControl.

Remplacez ceci :

    <ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
        <ListView x:Name="ConversationList" />
    </ScrollViewer>

Par ceci :

    <ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
        <ItemsControl x:Name="ConversationList" Width="300">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Text}" TextWrapping="Wrap" Margin="5" Foreground="{Binding Color}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>

Nous allons ensuite introduire une MessageItem classe pour faciliter la liaison et la coloration :

    // ...
    public class MessageItem
    {
        public string Text { get; set; }
        public SolidColorBrush Color { get; set; }
    }
    // ...

Enfin, mettez à jour la AddMessageToConversation méthode pour utiliser la nouvelle MessageItem classe :

    // ...
    private void AddMessageToConversation(string message)
    {
        var messageItem = new MessageItem();
        messageItem.Text = message;
        messageItem.Color = message.StartsWith("User:") ? new SolidColorBrush(Colors.LightBlue) : new SolidColorBrush(Colors.LightGreen);
        ConversationList.Items.Add(messageItem);

        // handle scrolling
        ConversationScrollViewer.UpdateLayout();
        ConversationScrollViewer.ChangeView(null, ConversationScrollViewer.ScrollableHeight, null);
    }
    // ...

Améliorer le TextBox

Pour rendre la TextBox clé plus grande et réactive Enter, mettez à jour MainWindow.xaml comme suit :

    <!-- ... -->
    <StackPanel Orientation="Vertical" Width="300">
        <TextBox x:Name="InputTextBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" KeyDown="InputTextBox_KeyDown" TextWrapping="Wrap" MinHeight="100" MaxWidth="300"/>
        <Button x:Name="SendButton" Content="Send" Click="SendButton_Click" HorizontalAlignment="Right"/>
    </StackPanel>
    <!-- ... -->

Ensuite, ajoutez le InputTextBox_KeyDown gestionnaire d’événements pour gérer la Enter clé :

    //...
    private void InputTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
    {
        if (e.Key == Windows.System.VirtualKey.Enter && !string.IsNullOrWhiteSpace(InputTextBox.Text))
        {
            SendButton_Click(this, new RoutedEventArgs());
        }
    }
    //...

Exécuter l’application améliorée

Votre interface de conversation nouvelle et améliorée doit ressembler à ceci :

Une application de conversation moins minimaliste.

Récapitulatif

Voici les tâches que vous avez accomplies dans ce tutoriel :

  1. Vous avez ajouté les fonctionnalités d’API d’OpenAI à votre application de bureau WinUI 3 /Windows App SDK en installant un SDK de communauté et en l’initialisant avec votre clé API.
  2. Vous avez créé une interface de type conversation qui vous permet de générer des réponses aux messages à l’aide de l’API de saisie semi-automatique de conversation d’OpenAI.
  3. Vous avez amélioré l’interface de conversation par :
    1. ajout de ScrollViewer,
    2. à l’aide d’un TextBlock pour afficher la réponse GPT,
    3. ajout d’un ProgressBar élément permettant d’indiquer quand l’application attend une réponse de l’API GPT,
    4. centrer la StackPanel fenêtre,
    5. s’assurer que les messages sont encapsulés à la ligne suivante lorsqu’ils atteignent le bord de la fenêtre, et
    6. rendre le TextBox plus grand, redimensionnable et réactif à la Enter clé.

Exemple de code complet :

<?xml version="1.0" encoding="utf-8"?>
<Window
    x:Class="ChatGPT_WinUI3.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ChatGPT_WinUI3"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid VerticalAlignment="Bottom" HorizontalAlignment="Center">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center">
            <ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
                <ItemsControl x:Name="ConversationList" Width="300">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Text}" TextWrapping="Wrap" Margin="5" Foreground="{Binding Color}"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </ScrollViewer>
            <ProgressBar x:Name="ResponseProgressBar" Height="5" IsIndeterminate="True" Visibility="Collapsed"/>
            <StackPanel Orientation="Vertical" Width="300">
                <TextBox x:Name="InputTextBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" KeyDown="InputTextBox_KeyDown" TextWrapping="Wrap" MinHeight="100" MaxWidth="300"/>
                <Button x:Name="SendButton" Content="Send" Click="SendButton_Click" HorizontalAlignment="Right"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using System;
using System.Collections.Generic;
using System.Linq;

using OpenAI;
using OpenAI.Managers;
using OpenAI.ObjectModels.RequestModels;
using OpenAI.ObjectModels;

namespace ChatGPT_WinUI3
{
    public class MessageItem
    {
        public string Text { get; set; }
        public SolidColorBrush Color { get; set; }
    }

    public sealed partial class MainWindow : Window
    {
        private OpenAIService openAiService;

        public MainWindow()
        {
            this.InitializeComponent();

            var openAiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");

            openAiService = new OpenAIService(new OpenAiOptions(){
                ApiKey = openAiKey
            });
        }

        private async void SendButton_Click(object sender, RoutedEventArgs e)
        {
            ResponseProgressBar.Visibility = Visibility.Visible;

            string userInput = InputTextBox.Text;
            if (!string.IsNullOrEmpty(userInput))
            {
                AddMessageToConversation("User: " + userInput);
                InputTextBox.Text = string.Empty;
                var completionResult = await openAiService.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest()
                {
                    Messages = new List<ChatMessage>
                    {
                        ChatMessage.FromSystem("You are a helpful assistant."),
                        ChatMessage.FromUser(userInput)
                    },
                    Model = Models.Gpt_4_1106_preview,
                    MaxTokens = 300
                });

                Console.WriteLine(completionResult.ToString());

                if (completionResult != null && completionResult.Successful)
                {
                    AddMessageToConversation("GPT: " + completionResult.Choices.First().Message.Content);
                }
                else
                {
                    AddMessageToConversation("GPT: Sorry, something bad happened: " + completionResult.Error?.Message);
                }
            }
            ResponseProgressBar.Visibility = Visibility.Collapsed;
        }

        private void AddMessageToConversation(string message)
        {
            var messageItem = new MessageItem();
            messageItem.Text = message;
            messageItem.Color = message.StartsWith("User:") ? new SolidColorBrush(Colors.LightBlue) : new SolidColorBrush(Colors.LightGreen);
            ConversationList.Items.Add(messageItem);

            // handle scrolling
            ConversationScrollViewer.UpdateLayout();
            ConversationScrollViewer.ChangeView(null, ConversationScrollViewer.ScrollableHeight, null);
        }

        private void InputTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
        {
            if (e.Key == Windows.System.VirtualKey.Enter && !string.IsNullOrWhiteSpace(InputTextBox.Text))
            {
                SendButton_Click(this, new RoutedEventArgs());
            }
        }
    }
}