From adbb1cb0c74173b475299722f85ea81c5c8c1945 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Wed, 21 Aug 2024 21:03:17 +0200 Subject: [PATCH] Added an e-mail writing assistant --- .../Assistants/EMail/AssistantEMail.razor | 27 ++ .../Assistants/EMail/AssistantEMail.razor.cs | 250 ++++++++++++++++++ .../Assistants/EMail/WritingStyles.cs | 11 + .../EMail/WritingStylesExtensions.cs | 24 ++ app/MindWork AI Studio/Pages/Assistants.razor | 1 + app/MindWork AI Studio/Routes.razor.cs | 1 + app/MindWork AI Studio/Tools/Event.cs | 1 + app/MindWork AI Studio/Tools/SendTo.cs | 1 + .../wwwroot/changelog/v0.8.12.md | 1 + 9 files changed, 317 insertions(+) create mode 100644 app/MindWork AI Studio/Assistants/EMail/AssistantEMail.razor create mode 100644 app/MindWork AI Studio/Assistants/EMail/AssistantEMail.razor.cs create mode 100644 app/MindWork AI Studio/Assistants/EMail/WritingStyles.cs create mode 100644 app/MindWork AI Studio/Assistants/EMail/WritingStylesExtensions.cs diff --git a/app/MindWork AI Studio/Assistants/EMail/AssistantEMail.razor b/app/MindWork AI Studio/Assistants/EMail/AssistantEMail.razor new file mode 100644 index 00000000..77f79fd6 --- /dev/null +++ b/app/MindWork AI Studio/Assistants/EMail/AssistantEMail.razor @@ -0,0 +1,27 @@ +@attribute [Route(Routes.ASSISTANT_EMAIL)] +@inherits AssistantBaseCore + + +@if (this.provideHistory) +{ + + + +} + + + + + @foreach (var contentLine in this.bulletPointsLines) + { + @contentLine + } + + + + + + + + Create e-mail + \ No newline at end of file diff --git a/app/MindWork AI Studio/Assistants/EMail/AssistantEMail.razor.cs b/app/MindWork AI Studio/Assistants/EMail/AssistantEMail.razor.cs new file mode 100644 index 00000000..3b745108 --- /dev/null +++ b/app/MindWork AI Studio/Assistants/EMail/AssistantEMail.razor.cs @@ -0,0 +1,250 @@ +using System.Text; + +using AIStudio.Chat; +using AIStudio.Tools; + +namespace AIStudio.Assistants.EMail; + +public partial class AssistantEMail : AssistantBaseCore +{ + protected override string Title => "E-Mail"; + + protected override string Description => + """ + Provide a list of bullet points and some basic information for an e-mail. The assistant will generate an e-mail based on that input. + """; + + protected override string SystemPrompt => + $""" + You are an automated system that writes emails. {this.SystemPromptHistory()} The user provides you with bullet points on what + he want to address in the response. Regarding the writing style of the email: {this.selectedWritingStyle.Prompt()} + {this.SystemPromptGreeting()} {this.SystemPromptName()} You write the email in the following language: {this.SystemPromptLanguage()}. + """; + + protected override IReadOnlyList FooterButtons => + [ + new SendToButton + { + Self = SendTo.EMAIL_ASSISTANT, + }, + ]; + + protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with + { + SystemPrompt = SystemPrompts.DEFAULT, + }; + + protected override void ResetFrom() + { + this.inputBulletPoints = string.Empty; + this.bulletPointsLines.Clear(); + this.selectedFoci = []; + this.provideHistory = false; + this.inputHistory = string.Empty; + if (!this.MightPreselectValues()) + { + this.inputName = string.Empty; + this.selectedTargetLanguage = CommonLanguages.AS_IS; + this.customTargetLanguage = string.Empty; + this.selectedWritingStyle = WritingStyles.NONE; + this.inputSalutation = string.Empty; + } + } + + protected override bool MightPreselectValues() + { + // if (this.SettingsManager.ConfigurationData.Translation.PreselectOptions) + // { + // this.liveTranslation = this.SettingsManager.ConfigurationData.Translation.PreselectLiveTranslation; + // this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.Translation.PreselectedTargetLanguage; + // this.customTargetLanguage = this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage; + // this.providerSettings = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.SettingsManager.ConfigurationData.Translation.PreselectedProvider); + // return true; + // } + + return false; + } + + private const string PLACEHOLDER_BULLET_POINTS = """ + - The last meeting was good + - Thank you for feedback + - Next is milestone 3 + - I need your input by next Wednesday + """; + + private WritingStyles selectedWritingStyle = WritingStyles.NONE; + private string inputSalutation = string.Empty; + private string inputBulletPoints = string.Empty; + private readonly List bulletPointsLines = []; + private IEnumerable selectedFoci = new HashSet(); + private string inputName = string.Empty; + private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS; + private string customTargetLanguage = string.Empty; + private bool provideHistory; + private string inputHistory = string.Empty; + + #region Overrides of ComponentBase + + protected override async Task OnInitializedAsync() + { + this.MightPreselectValues(); + var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages(Event.SEND_TO_EMAIL_ASSISTANT).FirstOrDefault(); + if (deferredContent is not null) + this.inputBulletPoints = deferredContent; + + await base.OnInitializedAsync(); + } + + #endregion + + private string? ValidateBulletPoints(string content) + { + if(string.IsNullOrWhiteSpace(content)) + return "Please provide some content for the e-mail."; + + var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries); + foreach (var line in lines) + if(!line.TrimStart().StartsWith('-')) + return "Please start each line of your content list with a dash (-) to create a bullet point list."; + + return null; + } + + private string? ValidateTargetLanguage(CommonLanguages language) + { + if(language is CommonLanguages.AS_IS) + return "Please select a target language for the e-mail."; + + return null; + } + + private string? ValidateCustomLanguage(string language) + { + if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language)) + return "Please provide a custom language."; + + return null; + } + + private string? ValidateWritingStyle(WritingStyles style) + { + if(style == WritingStyles.NONE) + return "Please select a writing style for the e-mail."; + + return null; + } + + private string? ValidateHistory(string history) + { + if(this.provideHistory && string.IsNullOrWhiteSpace(history)) + return "Please provide some history for the e-mail."; + + return null; + } + + private void OnContentChanged(string content) + { + this.bulletPointsLines.Clear(); + var previousSelectedFoci = new HashSet(); + foreach (var line in content.AsSpan().EnumerateLines()) + { + var trimmedLine = line.Trim(); + if (trimmedLine.StartsWith("-")) + trimmedLine = trimmedLine[1..].Trim(); + + if (trimmedLine.Length == 0) + continue; + + var finalLine = trimmedLine.ToString(); + if(this.selectedFoci.Any(x => x.StartsWith(finalLine, StringComparison.InvariantCultureIgnoreCase))) + previousSelectedFoci.Add(finalLine); + + this.bulletPointsLines.Add(finalLine); + } + + this.selectedFoci = previousSelectedFoci; + } + + private string SystemPromptHistory() + { + if (this.provideHistory) + return "You receive the previous conversation as context."; + + return string.Empty; + } + + private string SystemPromptGreeting() + { + if(!string.IsNullOrWhiteSpace(this.inputSalutation)) + return $"Your greeting should consider the following formulation: {this.inputSalutation}."; + + return string.Empty; + } + + private string SystemPromptName() + { + if(!string.IsNullOrWhiteSpace(this.inputName)) + return $"For the closing phrase of the email, please use the following name: {this.inputName}."; + + return string.Empty; + } + + private string SystemPromptLanguage() + { + if(this.selectedTargetLanguage is CommonLanguages.AS_IS) + return "Use the same language as the input"; + + if(this.selectedTargetLanguage is CommonLanguages.OTHER) + return this.customTargetLanguage; + + return this.selectedTargetLanguage.Name(); + } + + private string PromptFoci() + { + if(!this.selectedFoci.Any()) + return string.Empty; + + var sb = new StringBuilder(); + sb.AppendLine("I want to amplify the following points:"); + foreach (var focus in this.selectedFoci) + sb.AppendLine($"- {focus}"); + + return sb.ToString(); + } + + private string PromptHistory() + { + if(!this.provideHistory) + return string.Empty; + + return $""" + The previous conversation was: + + ``` + {this.inputHistory} + ``` + """; + } + + private async Task CreateMail() + { + await this.form!.Validate(); + if (!this.inputIsValid) + return; + + this.CreateChatThread(); + var time = this.AddUserRequest( + $""" + {this.PromptHistory()} + + My bullet points for the e-mail are: + + {this.inputBulletPoints} + + {this.PromptFoci()} + """); + + await this.AddAIResponseAsync(time); + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Assistants/EMail/WritingStyles.cs b/app/MindWork AI Studio/Assistants/EMail/WritingStyles.cs new file mode 100644 index 00000000..dfc728a3 --- /dev/null +++ b/app/MindWork AI Studio/Assistants/EMail/WritingStyles.cs @@ -0,0 +1,11 @@ +namespace AIStudio.Assistants.EMail; + +public enum WritingStyles +{ + NONE = 0, + + BUSINESS_FORMAL, + BUSINESS_INFORMAL, + ACADEMIC, + PERSONAL, +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Assistants/EMail/WritingStylesExtensions.cs b/app/MindWork AI Studio/Assistants/EMail/WritingStylesExtensions.cs new file mode 100644 index 00000000..119f8042 --- /dev/null +++ b/app/MindWork AI Studio/Assistants/EMail/WritingStylesExtensions.cs @@ -0,0 +1,24 @@ +namespace AIStudio.Assistants.EMail; + +public static class WritingStylesExtensions +{ + public static string Name(this WritingStyles style) => style switch + { + WritingStyles.ACADEMIC => "Academic", + WritingStyles.PERSONAL => "Personal", + WritingStyles.BUSINESS_FORMAL => "Business formal", + WritingStyles.BUSINESS_INFORMAL => "Business informal", + + _ => "Not specified", + }; + + public static string Prompt(this WritingStyles style) => style switch + { + WritingStyles.ACADEMIC => "Use an academic style for communication in an academic context like between students and professors.", + WritingStyles.PERSONAL => "Use a personal style for communication between friends and family.", + WritingStyles.BUSINESS_FORMAL => "Use a formal business style for this e-mail.", + WritingStyles.BUSINESS_INFORMAL => "Use an informal business style for this e-mail.", + + _ => "Use a formal business style for this e-mail.", + }; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Pages/Assistants.razor b/app/MindWork AI Studio/Pages/Assistants.razor index 82acbfea..6fecfd6b 100644 --- a/app/MindWork AI Studio/Pages/Assistants.razor +++ b/app/MindWork AI Studio/Pages/Assistants.razor @@ -20,6 +20,7 @@ Business + diff --git a/app/MindWork AI Studio/Routes.razor.cs b/app/MindWork AI Studio/Routes.razor.cs index 3d770830..f94fee16 100644 --- a/app/MindWork AI Studio/Routes.razor.cs +++ b/app/MindWork AI Studio/Routes.razor.cs @@ -17,5 +17,6 @@ public sealed partial class Routes public const string ASSISTANT_SUMMARIZER = "/assistant/summarizer"; public const string ASSISTANT_CODING = "/assistant/coding"; public const string ASSISTANT_AGENDA = "/assistant/agenda"; + public const string ASSISTANT_EMAIL = "/assistant/email"; // ReSharper restore InconsistentNaming } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/Event.cs b/app/MindWork AI Studio/Tools/Event.cs index 8103e182..c5cfad7f 100644 --- a/app/MindWork AI Studio/Tools/Event.cs +++ b/app/MindWork AI Studio/Tools/Event.cs @@ -25,4 +25,5 @@ public enum Event SEND_TO_CODING_ASSISTANT, SEND_TO_TEXT_SUMMARIZER_ASSISTANT, SEND_TO_CHAT, + SEND_TO_EMAIL_ASSISTANT, } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/SendTo.cs b/app/MindWork AI Studio/Tools/SendTo.cs index 477f3b6e..b29483ab 100644 --- a/app/MindWork AI Studio/Tools/SendTo.cs +++ b/app/MindWork AI Studio/Tools/SendTo.cs @@ -11,6 +11,7 @@ public enum SendTo AGENDA_ASSISTANT, CODING_ASSISTANT, TEXT_SUMMARIZER_ASSISTANT, + EMAIL_ASSISTANT, CHAT, } \ No newline at end of file diff --git a/app/MindWork AI Studio/wwwroot/changelog/v0.8.12.md b/app/MindWork AI Studio/wwwroot/changelog/v0.8.12.md index fe993c44..a7129d67 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v0.8.12.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v0.8.12.md @@ -1,3 +1,4 @@ # v0.8.12, build 174 +- Added an e-mail writing assistant. - Improved the content validation for the agenda assistant. - Improved the language handling of the agenda assistant. \ No newline at end of file