From a5ebce4815dbe2fd3e808bce8700cbee0c4d0428 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 14 Jul 2024 15:15:15 +0200 Subject: [PATCH] Implemented the icon finder assistant --- .../IconFinder/AssistantIconFinder.razor | 68 +++++++++ .../IconFinder/AssistantIconFinder.razor.cs | 134 ++++++++++++++++++ .../Pages/IconFinder/IconSourceExtensions.cs | 40 ++++++ .../Pages/IconFinder/IconSources.cs | 13 ++ 4 files changed, 255 insertions(+) create mode 100644 app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor create mode 100644 app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor.cs create mode 100644 app/MindWork AI Studio/Components/Pages/IconFinder/IconSourceExtensions.cs create mode 100644 app/MindWork AI Studio/Components/Pages/IconFinder/IconSources.cs diff --git a/app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor b/app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor new file mode 100644 index 00000000..a7b8132d --- /dev/null +++ b/app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor @@ -0,0 +1,68 @@ +@page "/assistant/icons" +@using AIStudio.Chat +@using AIStudio.Settings + + + Icon Finder + + + + + + + Finding the right icon for a context, such as for a piece of text, is not easy. The first challenge: + You need to extract a concept from your context, such as from a text. Let's take an example where + your text contains statements about multiple departments. The sought-after concept could be "departments." + The next challenge is that we need to anticipate the bias of the icon designers: under the search term + "departments," there may be no relevant icons or only unsuitable ones. Depending on the icon source, + it might be more effective to search for "buildings," for instance. LLMs assist you with both steps. + + + + + + + @foreach (var source in Enum.GetValues()) + { + @source.Name() + } + + @if (this.selectedIconSource is not IconSources.GENERIC) + { + Open website + } + + + + @foreach (var provider in this.SettingsManager.ConfigurationData.Providers) + { + + } + + + + Find icons + + + + @if (this.inputIssues.Any()) + { + + Issues + + @foreach (var issue in this.inputIssues) + { + + @issue + + } + + + } + + @if (this.resultingContentBlock is not null) + { + + } + + \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor.cs b/app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor.cs new file mode 100644 index 00000000..4f173f7e --- /dev/null +++ b/app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor.cs @@ -0,0 +1,134 @@ +using AIStudio.Chat; +using AIStudio.Provider; +using AIStudio.Settings; + +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Components.Pages.IconFinder; + +public partial class AssistantIconFinder : ComponentBase +{ + [Inject] + private SettingsManager SettingsManager { get; set; } = null!; + + [Inject] + public IJSRuntime JsRuntime { get; init; } = null!; + + [Inject] + public Random RNG { get; set; } = null!; + + private ChatThread? chatThread; + private ContentBlock? resultingContentBlock; + private AIStudio.Settings.Provider selectedProvider; + private MudForm form = null!; + private bool inputIsValid; + private string[] inputIssues = []; + private string inputContext = string.Empty; + private IconSources selectedIconSource; + + #region Overrides of ComponentBase + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + // Reset the validation when not editing and on the first render. + // We don't want to show validation errors when the user opens the dialog. + if(firstRender) + this.form.ResetValidation(); + + await base.OnAfterRenderAsync(firstRender); + } + + #endregion + + private string? ValidatingContext(string context) + { + if(string.IsNullOrWhiteSpace(context)) + return "Please provide a context. This will help the AI to find the right icon. You might type just a keyword or copy a sentence from your text, e.g., from a slide where you want to use the icon."; + + return null; + } + + private string? ValidatingProvider(AIStudio.Settings.Provider provider) + { + if(provider.UsedProvider == Providers.NONE) + return "Please select a provider."; + + return null; + } + + private async Task FindIcon() + { + await this.form.Validate(); + if (!this.inputIsValid) + return; + + // + // Create a new chat thread: + // + this.chatThread = new() + { + WorkspaceId = Guid.Empty, + ChatId = Guid.NewGuid(), + Name = string.Empty, + Seed = this.RNG.Next(), + SystemPrompt = SYSTEM_PROMPT, + Blocks = [], + }; + + // + // Add the user's request to the thread: + // + var time = DateTimeOffset.Now; + this.chatThread.Blocks.Add(new ContentBlock + { + Time = time, + ContentType = ContentType.TEXT, + Role = ChatRole.USER, + Content = new ContentText + { + Text = + $""" + {this.selectedIconSource.Prompt()} I search for an icon for the following context: + + ``` + {this.inputContext} + ``` + """, + }, + }); + + // + // Add the AI response to the thread: + // + var aiText = new ContentText + { + // We have to wait for the remote + // for the content stream: + InitialRemoteWait = true, + }; + + this.resultingContentBlock = new ContentBlock + { + Time = time, + ContentType = ContentType.TEXT, + Role = ChatRole.AI, + Content = aiText, + }; + + this.chatThread?.Blocks.Add(this.resultingContentBlock); + + // Use the selected provider to get the AI response. + // By awaiting this line, we wait for the entire + // content to be streamed. + await aiText.CreateFromProviderAsync(this.selectedProvider.UsedProvider.CreateProvider(this.selectedProvider.InstanceName, this.selectedProvider.Hostname), this.JsRuntime, this.SettingsManager, this.selectedProvider.Model, this.chatThread); + } + + private const string SYSTEM_PROMPT = + """ + I can search for icons using US English keywords. Please help me come up with the right search queries. + I don't want you to translate my requests word-for-word into US English. Instead, you should provide keywords + that are likely to yield suitable icons. For example, I might ask for an icon about departments, but icons + related to the keyword "buildings" might be the best match. Provide your keywords in a Markdown list without + quotation marks. + """; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Pages/IconFinder/IconSourceExtensions.cs b/app/MindWork AI Studio/Components/Pages/IconFinder/IconSourceExtensions.cs new file mode 100644 index 00000000..42acf8cf --- /dev/null +++ b/app/MindWork AI Studio/Components/Pages/IconFinder/IconSourceExtensions.cs @@ -0,0 +1,40 @@ +namespace AIStudio.Components.Pages.IconFinder; + +public static class IconSourceExtensions +{ + public static string Name(this IconSources iconSource) => iconSource switch + { + IconSources.FLAT_ICON => "Flaticon", + IconSources.FONT_AWESOME => "Font Awesome", + IconSources.MATERIAL_ICONS => "Material Icons", + IconSources.FEATHER_ICONS => "Feather Icons", + IconSources.BOOTSTRAP_ICONS => "Bootstrap Icons", + IconSources.ICONS8 => "Icons8", + + _ => "Generic", + }; + + public static string Prompt(this IconSources iconSource) => iconSource switch + { + IconSources.FLAT_ICON => "My icon source is Flaticon.", + IconSources.FONT_AWESOME => "I look for an icon on Font Awesome. Please provide just valid icon names. Valid icon names are using the format `fa-icon-name`.", + IconSources.MATERIAL_ICONS => "I look for a Material icon. Please provide just valid icon names. Valid icon names are using the format `IconName`.", + IconSources.FEATHER_ICONS => "My icon source is Feather Icons. Please provide just valid icon names. Valid icon names usiing the format `icon-name`.", + IconSources.BOOTSTRAP_ICONS => "I look for an icon for Bootstrap. Please provide just valid icon names. Valid icon names are using the format `bi-icon-name`.", + IconSources.ICONS8 => "I look for an icon on Icons8.", + + _ => string.Empty, + }; + + public static string URL(this IconSources iconSource) => iconSource switch + { + IconSources.FLAT_ICON => "https://www.flaticon.com/", + IconSources.FONT_AWESOME => "https://fontawesome.com/", + IconSources.MATERIAL_ICONS => "https://material.io/resources/icons/", + IconSources.FEATHER_ICONS => "https://feathericons.com/", + IconSources.BOOTSTRAP_ICONS => "https://icons.getbootstrap.com/", + IconSources.ICONS8 => "https://icons8.com/", + + _ => string.Empty, + }; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Pages/IconFinder/IconSources.cs b/app/MindWork AI Studio/Components/Pages/IconFinder/IconSources.cs new file mode 100644 index 00000000..fe477f9f --- /dev/null +++ b/app/MindWork AI Studio/Components/Pages/IconFinder/IconSources.cs @@ -0,0 +1,13 @@ +namespace AIStudio.Components.Pages.IconFinder; + +public enum IconSources +{ + GENERIC, + + ICONS8, + FLAT_ICON, + FONT_AWESOME, + MATERIAL_ICONS, + FEATHER_ICONS, + BOOTSTRAP_ICONS, +} \ No newline at end of file