KI-Kunst-Kirsten-Kloeckner/KIKunstKirstenKlöckner/Pages/AiArt.razor

407 lines
15 KiB
Plaintext
Raw Normal View History

2023-08-02 21:33:09 +02:00
@page "/aiart"
2023-08-04 15:45:38 +02:00
2023-08-02 18:33:28 +02:00
@using OpenAI_API
@using OpenAI_API.Chat
@using OpenAI_API.Models
2023-10-10 16:21:00 +02:00
@using DataAccess.Data
@using DataAccess.Models
2023-10-11 14:41:43 +02:00
@using KIKunstKirstenKlöckner.Data
@using System.Diagnostics
2023-08-02 21:33:09 +02:00
2023-08-05 01:40:55 +02:00
@inject IConfiguration Config
2023-08-02 21:33:09 +02:00
@inject TooltipService TooltipService
2023-10-11 14:41:43 +02:00
@inject NotificationService NotificationService
2023-08-02 21:33:09 +02:00
@inject DialogService DialogService
2023-08-02 18:33:28 +02:00
@inject WunschInfoData WunschInfoData;
2023-10-11 14:41:43 +02:00
@inject ImageGenerator ImageGenerator;
2023-10-10 16:21:00 +02:00
2023-08-02 18:33:28 +02:00
<PageTitle>AiArt</PageTitle>
2023-10-10 17:39:37 +02:00
<section class="about_section layout_padding" style="background-image: url('images/5KeineAngstvorFehlern2014.jpeg'); background-size: cover; background-repeat: no-repeat; background-blend-mode:lighten">
<div class="container">
<RadzenStack Orientation="Orientation.Vertical" AlignItems="AlignItems.Center">
2023-08-18 14:57:12 +02:00
<h1>Wunschbilder von KI nur für dich</h1>
<RadzenText TextStyle="TextStyle.H2">Nenne uns deinen Wunsch:</RadzenText>
<RadzenTextBox @bind-Value=@_userIdea Placeholder="Dein Wunsch"/>
@* Zusätzliche Optionen *@
<RadzenPanel AllowCollapse="true" Style="width: 500px;" Text="Mehr Optionen">
<ChildContent>
<RadzenCard class="rz-mt-4">
<RadzenStack Orientation="Orientation.Horizontal"
MouseEnter="@(args => ShowTemperatureTooltip(args))"
MouseLeave="TooltipService.Close"
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap">
<RadzenText>Temperature:</RadzenText>
2023-10-11 14:41:43 +02:00
<RadzenSlider @bind-Value=@_temperature TValue="float"
Step="0.1" Min="0.0m" Max="2.0m">
</RadzenSlider>
2023-10-11 14:41:43 +02:00
<RadzenText>@_temperature</RadzenText>
</RadzenStack>
<RadzenStack Orientation="Orientation.Horizontal"
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap">
<RadzenText>Resolution:</RadzenText>
<RadzenStack Orientation="Orientation.Horizontal">
<RadzenNumeric ShowUpDown = "false" TValue = "int?" @bind-Value=@width />
x
<RadzenNumeric ShowUpDown = "false" TValue = "int?" @bind-Value=@height />
</RadzenStack>
</RadzenStack>
2023-10-11 14:41:43 +02:00
<RadzenStack Orientation="Orientation.Horizontal"
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap">
2023-10-14 14:33:35 +02:00
<RadzenCheckBox @bind-Value=@_useGpt4 Name="UseGPT4"/>
<RadzenLabel Text="Verwende GPT 4" Component="UseGPT4" Style="margin-left: 8px; vertical-align: middle;" />
2023-10-11 14:41:43 +02:00
</RadzenStack>
</RadzenCard>
</ChildContent>
</RadzenPanel>
2023-08-02 21:33:09 +02:00
<RadzenButton Visible=@_buttonVisible Click=@(async ()=> await GenerateImagesAsync())>Generate</RadzenButton>
2023-08-03 23:24:10 +02:00
<RadzenText TextStyle="TextStyle.H4">Die Idee, die gemalt wird:</RadzenText>
2023-08-04 15:54:07 +02:00
2023-08-04 15:54:37 +02:00
<RadzenCard class="rz-mt-4" Style="width: 800px;">
2023-10-11 14:41:43 +02:00
<RadzenText>@_imageIdea</RadzenText>
2023-08-04 15:54:07 +02:00
</RadzenCard>
<RadzenProgressBarCircular Visible=@_progressVisible ProgressBarStyle="ProgressBarStyle.Secondary" Value="100" ShowValue="false" Mode="ProgressBarMode.Indeterminate" />
<RadzenText Visible=@_progressVisible TextStyle="TextStyle.H6" Text=@BusyMessage></RadzenText>
2023-08-03 23:24:10 +02:00
<RadzenImage Path=@_imageUrl></RadzenImage>
2023-08-02 21:33:09 +02:00
<RadzenText Visible=@_addonsVisible TextStyle="TextStyle.H2">Verändere hier dein Bild durch Worte:</RadzenText>
<RadzenTextBox Visible=@_addonsVisible @bind-Value=@_updateRequest Placeholder="z.B. Mehr Farben" />
<RadzenButton Visible=@_bothVisible Click=@(async ()=> await UpdateImagesAsync())>Generate</RadzenButton>
<RadzenCard>
<RadzenRow Style="width:24.5em" Gap="0.5rem" RowGap="0.5rem">
<RadzenColumn Size="6">
<FlippingImage ImageUrl="@_imageUrls[0]" HideImage="false"
Show="@(_imageStates[0] == ImageState.FadeIn)" FlipTo="FlippingImage.FlipDirection.Up"
Click="() => ShowImageDialog(_imageUrls[0])" />
2023-10-14 14:33:35 +02:00
@_imagePromts[0]
</RadzenColumn>
<RadzenColumn Size="6">
<FlippingImage ImageUrl="@_imageUrls[1]" HideImage="false"
Show="@(_imageStates[1] == ImageState.FadeIn)" FlipTo="FlippingImage.FlipDirection.Right" FlipDelay="200"
Click="() => ShowImageDialog(_imageUrls[1])" />
2023-10-14 14:33:35 +02:00
@_imagePromts[1]
</RadzenColumn>
<RadzenColumn Size="6">
<FlippingImage ImageUrl="@_imageUrls[2]" HideImage="false"
Show="@(_imageStates[2] == ImageState.FadeIn)" FlipTo="FlippingImage.FlipDirection.Left" FlipDelay="600"
Click="() => ShowImageDialog(_imageUrls[2])" />
2023-10-14 14:33:35 +02:00
@_imagePromts[2]
</RadzenColumn>
<RadzenColumn Size="6">
<FlippingImage ImageUrl="@_imageUrls[3]" HideImage="false"
Show="@(_imageStates[3] == ImageState.FadeIn)" FlipTo="FlippingImage.FlipDirection.Down" FlipDelay="400"
Click="() => ShowImageDialog(_imageUrls[3])" />
2023-10-14 14:33:35 +02:00
@_imagePromts[3]
</RadzenColumn>
</RadzenRow>
</RadzenCard>
</RadzenStack>
2023-08-18 14:57:12 +02:00
</div>
</section>
2023-08-02 18:33:28 +02:00
@code {
2023-10-11 14:41:43 +02:00
/// <summary>
/// Wenn <see langword="true"/> wird GPT4 verwendet um die Idee zu interpretieren.
/// </summary>
private bool _useGpt4;
private int maxAddons = 2;
private int amountOfAddons = 0; // wird später geändert
2023-08-02 21:33:09 +02:00
private bool _progressVisible = false;
2023-08-03 23:24:10 +02:00
private bool _buttonVisible = true;
private bool _addonsVisible = false;
private bool _bothVisible = false;
2023-08-02 21:33:09 +02:00
public string BusyMessage { get; set; } = "Initial Message";
private string?[] _imageUrls = new string?[4];
2023-10-14 14:33:35 +02:00
private string?[] _imagePromts = new string?[4];
private ImageState[] _imageStates = new ImageState[4];
enum ImageState
{
FadeOut,
2023-10-11 14:41:43 +02:00
FadeIn
2023-08-02 21:33:09 +02:00
}
async Task ShowImageDialog(string imageUrl)
{
var result = await DialogService.OpenAsync("", ds =>
2023-09-20 15:43:10 +02:00
@<div>
<RadzenImage Style="object-fit: contain; width: 100%; height:100%;" Path="@imageUrl"/>
</div>, new DialogOptions() { CloseDialogOnOverlayClick = true });
}
2023-08-02 21:33:09 +02:00
void ShowTooltip(ElementReference elementReference, string text, TooltipOptions? options = null) => TooltipService.Open(elementReference, text, options);
void ShowTemperatureTooltip(ElementReference elementReference) => TooltipService.Open(elementReference, ds =>
@<div>
2023-10-11 14:41:43 +02:00
Gibt an, wie <em>kreativ</em> ChatGPT sein soll.<br/>
</div>
,
2023-08-02 21:33:09 +02:00
new() { Position = TooltipPosition.Bottom, Duration = null});
2023-09-20 12:22:52 +02:00
private string _imageIdea = "";
2023-08-02 21:33:09 +02:00
2023-10-11 14:41:43 +02:00
private float _temperature = 0.9f;
private int? width = 1024;
private int? height = 1024;
private string _userIdea = "";
private string _updateRequest = "";
2023-08-05 01:40:55 +02:00
private OpenAIAPI _openAiApi;
2023-10-11 14:41:43 +02:00
private Conversation? _conversation;
2023-08-02 18:33:28 +02:00
2023-08-02 21:33:09 +02:00
private string _basePrompt;
2023-09-20 12:22:52 +02:00
private string _ideaPrompt;
2023-08-02 21:33:09 +02:00
2023-08-03 23:24:10 +02:00
private string _imageUrl;
private WunschInfoModel? _wunschInfo;
2023-08-02 21:33:09 +02:00
//protected override async Task OnInitializedAsync()
//{
// _basePrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"\wwwroot\prompt.txt"}");
//}
async Task UpdateBusyMessage(string newMessage)
{
BusyMessage = newMessage;
await InvokeAsync(StateHasChanged);
}
2023-08-04 15:37:07 +02:00
2023-08-05 01:40:55 +02:00
private string _openAiApiKey = "";
2023-10-10 16:21:00 +02:00
protected override async Task OnInitializedAsync()
2023-08-03 23:24:10 +02:00
{
2023-08-05 01:40:55 +02:00
_openAiApiKey = Config.GetValue<string>("API:OpenAI");
2023-08-04 15:37:07 +02:00
2023-08-05 01:40:55 +02:00
_openAiApi = new OpenAIAPI(_openAiApiKey);
2023-08-04 15:37:07 +02:00
2023-10-10 16:21:00 +02:00
await base.OnInitializedAsync();
2023-08-03 23:24:10 +02:00
}
2023-08-02 21:33:09 +02:00
2023-10-14 14:33:35 +02:00
/// <summary>
/// Setzt alle Felder zurück, die zu einem bestimmten Wunsch gehören.
/// </summary>
2023-10-11 14:41:43 +02:00
private void ClearOldGeneration()
{
2023-10-11 14:41:43 +02:00
// Bilder verbergen
for (int i = 0; i < 4; i++)
2023-10-14 14:33:35 +02:00
{
2023-10-11 14:41:43 +02:00
_imageStates[i] = ImageState.FadeOut;
2023-10-14 14:33:35 +02:00
_imagePromts[i] = null;
}
2023-10-11 14:41:43 +02:00
_imageIdea = "";
}
2023-08-18 11:52:58 +02:00
2023-10-14 14:33:35 +02:00
/// <summary>
/// Gibt ChatGPT den Wunsch und erlangt die Bild Idee.
/// </summary>
/// <returns></returns>
2023-10-11 14:41:43 +02:00
private async Task RequestImageIdeaAsync()
{
string ideaBasePrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"/wwwroot/idea_prompt.txt"}");
2023-10-11 14:41:43 +02:00
ChatRequest chatRequest = new ChatRequest
{
2023-10-11 14:41:43 +02:00
Temperature = _temperature,
Model = _useGpt4 ? Model.GPT4 : Model.ChatGPTTurbo
};
2023-10-11 14:41:43 +02:00
_conversation = _openAiApi.Chat.CreateConversation(chatRequest);
2023-10-11 14:41:43 +02:00
// Wunsch an GPT senden und Bild Idee anfordern
_conversation.AppendUserInput(ideaBasePrompt + " " + _userIdea);
2023-10-11 14:41:43 +02:00
_imageIdea = await _conversation.GetResponseFromChatbotAsync();
2023-10-14 14:33:35 +02:00
}
2023-10-14 14:33:35 +02:00
/// <summary>
/// Fordert für mehrere Bilder Bild-Prompts an und generiert die dazugehörigen Bilder.
/// </summary>
private async Task RequestImagesAsync(WunschInfoModel wunschInfo, string requestImagePrompt)
2023-10-14 14:33:35 +02:00
{
// Nachricht mit Bildpromt anfrage senden
_conversation!.AppendUserInput(requestImagePrompt);
// Task-Liste, damit wir parallel Prompts anfordern und Bilder generieren können.
Task[] imagePromts = new Task[4];
for (int i = 0; i < 4; i++)
{
imagePromts[i] = RequestImagePromptAndGenerateImageAsync(i, wunschInfo);
2023-10-14 14:33:35 +02:00
}
await Task.WhenAll(imagePromts);
}
/// <summary>
/// Fordert einen Bild Prompt an und generiert ein Bild für diesen.
/// </summary>
/// <param name="index">Der Index des Bildes (für UI zeug)</param>
/// <param name="wunschInfo">Der Wunsch für den ein Bild erzeugt wird.</param>
private async Task RequestImagePromptAndGenerateImageAsync(int index, WunschInfoModel wunschInfo)
2023-10-14 14:33:35 +02:00
{
// Bild Prompt von ChatGPT anfordern
string imagePrompt = await _conversation!.GetResponseFromChatbotAsync();
// Keywords anhängen um Kirstens Stil zu aktivieren.
// TODO: Gucken, ob wir dem Watercolor bums brauchen
imagePrompt = "kkkk " + imagePrompt;// + " kkkk Watercolor + ink on paper, Pen drawing, wet-on-wet technique, dry-on-dry technique, dabbing technique. ";
// Debug only: Promt anzeigen
_imagePromts[index] = imagePrompt;
await InvokeAsync(StateHasChanged);
string? imageUrl = await GenerateImageAsync(imagePrompt, wunschInfo);
// TODO: Fehler im UI anzeigen (zur Zeit bleibt einfach Ladebalken)
_imageUrls[index] = imageUrl;
_imageStates[index] = ImageState.FadeIn;
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// Erzeugt ein Bild für den gegebenen Prompt und Wunsch.
/// </summary>
/// <param name="imagePrompt">Der Bild Prompt</param>
/// <param name="wunschInfo">Der Wunsch.</param>
/// <returns>Die URL, falls das Bild generiert wurde; oder null, wenn kein Bild generiert werden konnte.</returns>
private async Task<string?> GenerateImageAsync(string imagePrompt, WunschInfoModel wunschInfo)
{
try
{
string? imageUrl = (await ImageGenerator.GenerateImageAsync(imagePrompt, wunschInfo, width, height))?.Dateiname;
// Kein Bild -> Fehler
if (imageUrl == null)
{
bool? retry = await DialogService.Confirm(
"Leider konnte das Bild nicht gemalt werden. Möchtest du es noch eimal versuchen?",
"Ups, ein Fehler ist aufgetreten...",
new ConfirmOptions { OkButtonText = "Ja", CancelButtonText = "Nein" });
if (retry == true)
{
imageUrl = (await ImageGenerator.GenerateImageAsync(imagePrompt, wunschInfo, width, height))?.Dateiname;
}
}
return imageUrl;
}
catch (Exception e)
{
NotificationService.Notify(new NotificationMessage()
{
Summary = "Es ist ein Fehler beim Erzeugen der Bilder aufgetreten, bitte versuche es erneut."
});
}
return null;
}
2023-10-11 14:41:43 +02:00
/// <summary>
/// Generiert Bilder oder aktualisiert sie mit dem neuen Prompt.
/// </summary>
private async Task GenerateImagesAsync()
2023-08-02 21:33:09 +02:00
{
_progressVisible = true;
2023-08-03 23:24:10 +02:00
_buttonVisible = false;
2023-08-02 21:33:09 +02:00
ClearOldGeneration();
2023-10-11 14:41:43 +02:00
amountOfAddons = maxAddons;
_addonsVisible = false;
_bothVisible = _buttonVisible && _addonsVisible;
await UpdateBusyMessage("Kirstens Assistent zerbricht sich über deine Idee den Kopf...");
2023-09-20 12:22:52 +02:00
await RequestImageIdeaAsync();
2023-08-02 21:33:09 +02:00
await UpdateBusyMessage("Kirstens Assistent hat eine Idee! Er wird sie nun malen...");
_wunschInfo = new()
{
2023-10-11 14:41:43 +02:00
BildBeschreibung = _imageIdea,
2023-10-14 14:33:35 +02:00
BildPrompt = "Individuelle Bild Prompts",
Datum = DateTime.Now,
GPTModel = _conversation!.Model,
Wunsch = _userIdea,
2023-10-14 14:33:35 +02:00
VorherigerWunsch = null
};
2023-10-11 14:41:43 +02:00
try
{
await WunschInfoData.AddWunschInfoAsync(_wunschInfo);
}
2023-10-11 14:41:43 +02:00
catch (Exception e)
{
NotificationService.Notify(new NotificationMessage()
{
Summary = "Es ist ein Fehler aufgetreten, bitte versuche es erneut."
});
return;
}
string requestImagePrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"/wwwroot/image_prompt.txt"}");
await RequestImagesAsync(_wunschInfo, requestImagePrompt);
2023-10-11 14:41:43 +02:00
2023-08-02 21:33:09 +02:00
_progressVisible = false;
2023-08-03 23:24:10 +02:00
_buttonVisible = true;
if (amountOfAddons > 0)
{
_addonsVisible = true;
_bothVisible = _buttonVisible && _addonsVisible;
await InvokeAsync(StateHasChanged);
}
else
{
_addonsVisible = false;
_bothVisible = false;
}
2023-08-02 18:33:28 +02:00
}
private async Task UpdateImagesAsync()
{
Debug.Assert(_conversation != null);
string updatePrompt = $"Gebe nun einen neuen Prompt unter berücksichtigung vorheriger Anweisungen und passe ihn folgender Maßen an: {_updateRequest}";
_wunschInfo = new()
{
BildBeschreibung = _updateRequest,
BildPrompt = "Individuelle Bild Prompts",
Datum = DateTime.Now,
GPTModel = _conversation.Model,
Wunsch = _userIdea,
VorherigerWunsch = _wunschInfo!.Id
};
try
{
await WunschInfoData.AddWunschInfoAsync(_wunschInfo);
}
catch (Exception e)
{
NotificationService.Notify(new NotificationMessage()
{
Summary = "Es ist ein Fehler aufgetreten, bitte versuche es erneut."
});
return;
}
await RequestImagesAsync(_wunschInfo, updatePrompt);
}
2023-08-02 18:33:28 +02:00
}