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