Compartilhar via


Adicionar DALL-E ao seu aplicativo de área de trabalho WinUI 3 / Windows App SDK

Neste tutorial, integraremos os recursos de geração de imagem do DALL-E ao seu aplicativo de área de trabalho WinUI 3 / Windows App SDK.

Prerequisites

Instalar e inicializar o SDK do OpenAI

Verifique se a biblioteca .NET do OpenAI está instalada em seu projeto executando dotnet add package OpenAI na janela do terminal do Visual Studio. Inicialize o SDK com sua chave de API OpenAI da seguinte maneira:

//...
using OpenAI;
using OpenAI.Chat;

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

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

            openAiService = new(openAiKey);
        }
    }
}

Modifique a interface do usuário do aplicativo

Modifique o existente DateTemplate em MainWindow.xaml para incluir um controle Image que exibe imagens dentro da conversa:

<!-- ... existing XAML ... -->
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Vertical">
            <TextBlock Text="{Binding Text}" TextWrapping="Wrap" Margin="5" Foreground="{Binding Color}"/>
            <Image Source="{Binding ImageUrl}" Margin="5" Stretch="UniformToFill"/>
        </StackPanel>
    </DataTemplate>
</ItemsControl.ItemTemplate>
<!-- ... existing XAML ... -->

Observe que este tutorial pressupõe que você tenha uma interface de bate-papo com um TextBox e Button; consulte Como adicionar conclusões de bate-papo OpenAI ao seu aplicativo de área de trabalho WinUI 3 / Windows App SDK.

Implementar a geração de imagens DALL-E

No MainWindow.xaml.cs , adicione os seguintes métodos para lidar com a geração e exibição de imagens:

// ... existing using statements ...

private async void SendButton_Click(object sender, RoutedEventArgs e)
{
    ResponseProgressBar.Visibility = Visibility.Visible;
    string userInput = InputTextBox.Text;
    if (!string.IsNullOrEmpty(userInput))
    {
        InputTextBox.Text = string.Empty;
        // Use the DALL-E 3 model for image generation.
        ImageClient imageClient = openAiService.GetImageClient("dall-e-3");

        ClientResult<GeneratedImage> imageResult = await imageClient.GenerateImageAsync(userInput,
            new ImageGenerationOptions
            {
                Size = GeneratedImageSize.W1024xH1024,
                ResponseFormat = GeneratedImageFormat.Uri,
                EndUserId = "TestUser"
            });

        if (imageResult.Value != null)
        {
            AddImageMessageToConversation(imageResult.Value.ImageUri);
        }
        else
        {
            AddMessageToConversation("GPT: Sorry, something bad happened.");
        }
    }
    ResponseProgressBar.Visibility = Visibility.Collapsed;
}

private void AddImageMessageToConversation(Uri imageUrl)
{
    var imageMessage = new MessageItem
    {
        ImageUrl = imageUrl.ToString()
    };
    ConversationList.Items.Add(imageMessage);

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

O imageClient.GenerateImageAsync() método é responsável por chamar a API DALL-E da OpenAI. Consulte os exemplos do .NET do OpenAI no GitHub para obter mais exemplos de uso.

Tip

Tente solicitar ao Microsoft Copilot algumas sugestões sobre diferentes maneiras de usar as APIs de DALL-E e chat em seu aplicativo.

Observe a ImageUrl presença de MessageItem na classe. Esta é uma nova propriedade:

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

Executar e testar

Execute seu aplicativo, insira um prompt e clique no botão "Enviar". Você deverá ver algo como:

Uma captura de tela do aplicativo de demonstração de geração de imagem WinUI.

Personalize a interface do usuário por si mesmo

Tente adicionar alguns botões de opção à interface do usuário para selecionar se deseja incluir uma imagem na conversa. Em seguida, você pode modificar o método SendButton_Click para chamar condicionalmente o método de geração de imagem com base na seleção do radio button.

Recap

Neste guia, você aprendeu a:

  1. Aceite solicitações de imagem de usuários dentro de um <TextBox>arquivo.
  2. Gere imagens usando a API OpenAI DALL-E.
  3. Exibir a imagem em um <Image>.

Arquivos de código completos

Veja a seguir os arquivos de código completos para a interface de chat com geração de imagens DALL-E. O código foi atualizado para usar botões de opção para chamar, de forma condicional, o chat ou a geração de imagem, conforme sugerido na seção Personalizar a interface do usuário por conta própria acima.

<?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>
                            <StackPanel Orientation="Vertical">
                                <TextBlock Text="{Binding Text}" TextWrapping="Wrap" Margin="5" Foreground="{Binding Color}"/>
                                <Image Source="{Binding ImageUrl}" Margin="5" Stretch="UniformToFill"/>
                            </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </ScrollViewer>
            <ProgressBar x:Name="ResponseProgressBar" Height="5" IsIndeterminate="True" Visibility="Collapsed"/>
            <StackPanel Orientation="Vertical" Width="300">
                <RadioButtons Header="Query type:">
                    <RadioButton x:Name="chatRadioButton" Content="Chat" IsChecked="True"/>
                    <RadioButton x:Name="imageRadioButton" Content="Image"/>
                </RadioButtons>
                <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 System.Threading.Tasks;

using OpenAI;
using OpenAI.Chat;
using OpenAI.Images;

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

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

        public MainWindow()
        {
            this.InitializeComponent();

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

            openAiService = new(openAiKey);
        }

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

            try
            {
                if (imageRadioButton.IsChecked == true)
                {
                    await ProcessImageRequestAsync(userInput);
                }
                else
                {
                    await ProcessChatRequestAsync(userInput);
                }
            }
            catch (Exception ex)
            {
                AddMessageToConversation($"GPT: Sorry, something bad happened: {ex.Message}");
            }
            finally
            {
                ResponseProgressBar.Visibility = Visibility.Collapsed;
            }
        }

        private async Task ProcessImageRequestAsync(string userInput)
        {
            if (!string.IsNullOrEmpty(userInput))
            {
                InputTextBox.Text = string.Empty;
                // Use the DALL-E 3 model for image generation.
                ImageClient imageClient = openAiService.GetImageClient("dall-e-3");

                ClientResult<GeneratedImage> imageResult = await imageClient.GenerateImageAsync(userInput,
                    new ImageGenerationOptions
                    {
                        Size = GeneratedImageSize.W1024xH1024,
                        ResponseFormat = GeneratedImageFormat.Uri,
                        EndUserId = "TestUser"
                    });

                if (imageResult.Value != null)
                {
                    AddImageMessageToConversation(imageResult.Value.ImageUri);
                }
                else
                {
                    AddMessageToConversation("GPT: Sorry, something bad happened.");
                }
            }
        }

        private async Task ProcessChatRequestAsync(string userInput)
        {
            if (!string.IsNullOrEmpty(userInput))
            {
                AddMessageToConversation($"User: {userInput}");
                InputTextBox.Text = string.Empty;
                var chatClient = openAiService.GetChatClient("gpt-4o");
                var chatOptions = new ChatCompletionOptions
                {
                    MaxOutputTokenCount = 300
                };
                var completionResult = await chatClient.CompleteChatAsync(
                    [
                        ChatMessage.CreateSystemMessage("You are a helpful assistant."),
                        ChatMessage.CreateUserMessage(userInput)
                    ],
                    chatOptions);

                if (completionResult != null && completionResult.Value.Content.Count > 0)
                {
                    AddMessageToConversation($"GPT: {completionResult.Value.Content.First().Text}");
                }
                else
                {
                    AddMessageToConversation($"GPT: Sorry, something bad happened: {completionResult?.Value.Refusal ?? "Unknown error."}");
                }
            }
        }

        private void AddImageMessageToConversation(Uri imageUrl)
        {
            var imageMessage = new MessageItem
            {
                ImageUrl = imageUrl.ToString()
            };
            ConversationList.Items.Add(imageMessage);

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

        private void AddMessageToConversation(string message)
        {
            var messageItem = new MessageItem
            {
                Text = message,
                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());
            }
        }
    }
}