From d0ad2f7efe69f0ebe859c1061daef101ce34de37 Mon Sep 17 00:00:00 2001 From: Alexander Bender <48275577+LXBdev@users.noreply.github.com> Date: Thu, 21 Dec 2023 13:38:28 +0100 Subject: [PATCH] DALL-E integration (#34) * DALL-E integration #27 * fix build * First ready version of Dall-E --------- Co-authored-by: Alexander --- .../Components/GenerateImageOptions.razor | 48 +++++++++++ OpenAIChatGPTBlazor/Pages/GenerateImage.razor | 59 +++++++++++++ .../Pages/GenerateImage.razor.cs | 84 +++++++++++++++++++ .../Pages/GenerateImage.razor.css | 79 +++++++++++++++++ OpenAIChatGPTBlazor/Shared/NavMenu.razor | 3 + Tests/UiTests/BasicTest.cs | 2 +- Tests/UiTests/GenerateImageTests.cs | 23 +++++ docker-compose.yml | 2 + 8 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 OpenAIChatGPTBlazor/Components/GenerateImageOptions.razor create mode 100644 OpenAIChatGPTBlazor/Pages/GenerateImage.razor create mode 100644 OpenAIChatGPTBlazor/Pages/GenerateImage.razor.cs create mode 100644 OpenAIChatGPTBlazor/Pages/GenerateImage.razor.css create mode 100644 Tests/UiTests/GenerateImageTests.cs diff --git a/OpenAIChatGPTBlazor/Components/GenerateImageOptions.razor b/OpenAIChatGPTBlazor/Components/GenerateImageOptions.razor new file mode 100644 index 0000000..d2dd2ca --- /dev/null +++ b/OpenAIChatGPTBlazor/Components/GenerateImageOptions.razor @@ -0,0 +1,48 @@ +@using Azure.AI.OpenAI + +
+ +
+
+ + + + + + + + + + + + + + + + +
+
+ + +@code { + [Parameter] + public string? DeploymentName { get; set; } + + private string _prompt = ""; + private string _size = ImageSize.Size1024x1024.ToString(); + private string _quality = ImageGenerationQuality.Standard.ToString(); + private string _style = ImageGenerationStyle.Natural.ToString(); + + public ImageGenerationOptions AsAzureOptions(string deploymentName) + { + return new() + { + DeploymentName = deploymentName, + Prompt = _prompt, + ImageCount = 1, + Size = _size, + Quality = _quality, + Style = _style + }; + } +} diff --git a/OpenAIChatGPTBlazor/Pages/GenerateImage.razor b/OpenAIChatGPTBlazor/Pages/GenerateImage.razor new file mode 100644 index 0000000..125004d --- /dev/null +++ b/OpenAIChatGPTBlazor/Pages/GenerateImage.razor @@ -0,0 +1,59 @@ +@page "/GenerateImage" +@using Azure.AI.OpenAI +@using System.Globalization +@using Microsoft.Extensions.Options; +@using Microsoft.FeatureManagement; +@using OpenAIChatGPTBlazor.Components +@inject IConfiguration Configuration +@inject OpenAIClient OpenAiClient +@inject IJSRuntime JS +@inject IFeatureManager FeatureManager +@inject IOptionsMonitor OpenAIOptions + +My DALL-E + +
+
+

+ Welcome to my Image Generation using OpenAI +

+
+
+ + @if (_loading) + { +
+
+

... please wait ...

+ } + @if (_warningMessage.Length > 0) + { +
+ Warning! @_warningMessage. +
+ } + +

@_revisedPrompt

+ + @if (_imageUrl is not null) + { + + } +
+
+ +
+ diff --git a/OpenAIChatGPTBlazor/Pages/GenerateImage.razor.cs b/OpenAIChatGPTBlazor/Pages/GenerateImage.razor.cs new file mode 100644 index 0000000..9024f61 --- /dev/null +++ b/OpenAIChatGPTBlazor/Pages/GenerateImage.razor.cs @@ -0,0 +1,84 @@ +using Microsoft.AspNetCore.Components.Web; +using OpenAIChatGPTBlazor.Components; + +namespace OpenAIChatGPTBlazor.Pages +{ + public partial class GenerateImage + { + private CancellationTokenSource? _searchCancellationTokenSource; + private string _warningMessage = string.Empty; + private string _next = string.Empty; + private bool _loading = true; + private GenerateImageOptions _optionsComponent = new(); + + private Uri? _imageUrl = null; + private string _revisedPrompt = string.Empty; + + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) + { + _loading = false; + this.StateHasChanged(); + } + } + + private async Task OnSubmitClick() + { + await RunSubmit(); + } + + private void OnAbortClick() + { + AbortSearch(); + } + + private async Task RunSubmit() + { + try + { + _loading = true; + this.StateHasChanged(); + + _searchCancellationTokenSource = new CancellationTokenSource(); + var res = await OpenAiClient.GetImageGenerationsAsync(_optionsComponent.AsAzureOptions("Dalle3"), _searchCancellationTokenSource.Token); + + foreach (var imageData in res.Value.Data) + { + _imageUrl = imageData.Url; + _revisedPrompt = imageData.RevisedPrompt; + } + + _loading = false; + _warningMessage = string.Empty; + } + catch (TaskCanceledException) when (_searchCancellationTokenSource?.IsCancellationRequested == true) + { + // Gracefully handle cancellation + } + catch (Exception ex) + { + _warningMessage = ex.Message; + } + finally + { + _loading = false; + } + } + + private void AbortSearch() + { + try + { + if (_searchCancellationTokenSource?.Token != null && _searchCancellationTokenSource.Token.CanBeCanceled) + { + _searchCancellationTokenSource.Cancel(); + } + } + catch (Exception ex) + { + _warningMessage = ex.Message; + } + } + } +} \ No newline at end of file diff --git a/OpenAIChatGPTBlazor/Pages/GenerateImage.razor.css b/OpenAIChatGPTBlazor/Pages/GenerateImage.razor.css new file mode 100644 index 0000000..47a29ba --- /dev/null +++ b/OpenAIChatGPTBlazor/Pages/GenerateImage.razor.css @@ -0,0 +1,79 @@ +article { + min-height: 100%; + max-height: 80vh; + display: flex; + flex-direction: column; +} + +.main { + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + flex: auto; +} + +.top-row { + background-color: #f7f7f7; + border-bottom: 1px solid #d6d5d5; + justify-content: flex-end; + height: 3.5rem; + display: flex; + align-items: center; +} + + .top-row h4 { + margin-top: 0.5rem + } + + .top-row ::deep a, .top-row .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + } + + .top-row a:first-child { + overflow: hidden; + text-overflow: ellipsis; + } + +@media (max-width: 640.98px) { + .top-row:not(.auth) { + display: none; + } + + .top-row.auth { + justify-content: space-between; + } + + .top-row a, .top-row .btn-link { + margin-left: 0; + } +} + +@media (min-width: 1025px) { + .top-row { + position: sticky; + top: 0; + z-index: 1; + } + + .top-row, article { + padding-left: 2rem !important; + padding-right: 1.5rem !important; + } +} + + +#nextArea { + margin-left: 6px; + margin-bottom: 6px; +} + +.img-container { + max-width: 100%; /* Ensures the div doesn’t exceed the width of the screen */ + height: auto; /* Optional: Ensures the height is auto-adjusted */ +} + + .img-container img { + max-width: 100%; /* Ensures the image doesn’t exceed the width of its container */ + height: auto; /* Maintains the aspect ratio of the image */ + display: block; /* Removes any extra space at the bottom inside the container */ + } diff --git a/OpenAIChatGPTBlazor/Shared/NavMenu.razor b/OpenAIChatGPTBlazor/Shared/NavMenu.razor index 8a147c3..8309c9e 100644 --- a/OpenAIChatGPTBlazor/Shared/NavMenu.razor +++ b/OpenAIChatGPTBlazor/Shared/NavMenu.razor @@ -13,6 +13,9 @@ Home + + DALL-E + diff --git a/Tests/UiTests/BasicTest.cs b/Tests/UiTests/BasicTest.cs index 7942afc..7dea7ca 100644 --- a/Tests/UiTests/BasicTest.cs +++ b/Tests/UiTests/BasicTest.cs @@ -8,7 +8,7 @@ namespace UiTests; [TestFixture] public class BasicTest : PageTest { - public static string BaseUrl = Environment.GetEnvironmentVariable("AppUrl") ?? "http://blazorserver:80"; + public static string BaseUrl = Environment.GetEnvironmentVariable("AppUrl") ?? "https://localhost:7128/"; [SetUp] public async Task SetUp() diff --git a/Tests/UiTests/GenerateImageTests.cs b/Tests/UiTests/GenerateImageTests.cs new file mode 100644 index 0000000..a5ffd6d --- /dev/null +++ b/Tests/UiTests/GenerateImageTests.cs @@ -0,0 +1,23 @@ +using Microsoft.Playwright; +using Microsoft.Playwright.NUnit; +using NUnit.Framework; + +namespace UiTests; + +[Parallelizable(ParallelScope.Self)] +[TestFixture] +public class GenerateImageTests : PageTest +{ + + [SetUp] + public async Task SetUp() + { + await Page.GotoAsync(Path.Combine(BasicTest.BaseUrl, "GenerateImage")); + } + + [Test] + public async Task PageShouldLoadAndShowHeading() + { + await Expect(Page.GetByText("Welcome to my Image Generation using OpenAI")).ToBeVisibleAsync(); + } +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 097d0a5..5cc4c17 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,8 @@ services: # working_dir: /tests/UiTests depends_on: - blazorserver + environment: + - AppUrl=http://blazorserver:80 # command: # - /bin/bash # - -c