Added a rewrite and text improvement assistant (#73)

This commit is contained in:
Thorsten Sommer 2024-08-13 20:53:09 +02:00 committed by GitHub
parent 05a7e44de4
commit 3e59f23193
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 302 additions and 11 deletions

View File

@ -17,7 +17,7 @@
</MudForm> </MudForm>
<Issues IssuesData="@this.inputIssues"/> <Issues IssuesData="@this.inputIssues"/>
@if (this.isProcessing) @if (this.ShowDedicatedProgress && this.isProcessing)
{ {
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="mb-6" /> <MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="mb-6" />
} }

View File

@ -37,6 +37,8 @@ public abstract partial class AssistantBase : ComponentBase
protected virtual bool ShowResult => true; protected virtual bool ShowResult => true;
protected virtual bool ShowDedicatedProgress => false;
protected virtual IReadOnlyList<ButtonData> FooterButtons => []; protected virtual IReadOnlyList<ButtonData> FooterButtons => [];
protected static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new(); protected static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
@ -143,6 +145,11 @@ public abstract partial class AssistantBase : ComponentBase
return aiText.Text; return aiText.Text;
} }
protected async Task CopyToClipboard(string text)
{
await this.Rust.CopyText2Clipboard(this.JsRuntime, this.Snackbar, text);
}
private static string? GetButtonIcon(string icon) private static string? GetButtonIcon(string icon)
{ {
if(string.IsNullOrWhiteSpace(icon)) if(string.IsNullOrWhiteSpace(icon))

View File

@ -13,6 +13,7 @@
<AssistantBlock Name="Text Summarizer" Description="Using a LLM to summarize a given text." Icon="@Icons.Material.Filled.TextSnippet" Link="/assistant/summarizer"/> <AssistantBlock Name="Text Summarizer" Description="Using a LLM to summarize a given text." Icon="@Icons.Material.Filled.TextSnippet" Link="/assistant/summarizer"/>
<AssistantBlock Name="Translation" Description="Translate text into another language." Icon="@Icons.Material.Filled.Translate" Link="/assistant/translation"/> <AssistantBlock Name="Translation" Description="Translate text into another language." Icon="@Icons.Material.Filled.Translate" Link="/assistant/translation"/>
<AssistantBlock Name="Grammar & Spelling" Description="Check grammar and spelling of a given text." Icon="@Icons.Material.Filled.Edit" Link="/assistant/grammar-spelling"/> <AssistantBlock Name="Grammar & Spelling" Description="Check grammar and spelling of a given text." Icon="@Icons.Material.Filled.Edit" Link="/assistant/grammar-spelling"/>
<AssistantBlock Name="Rewrite & Improve" Description="Rewrite and improve a given text for a chosen style." Icon="@Icons.Material.Filled.Edit" Link="/assistant/rewrite-improve"/>
</MudStack> </MudStack>
<MudText Typo="Typo.h4" Class="mb-2 mr-3 mt-6"> <MudText Typo="Typo.h4" Class="mb-2 mr-3 mt-6">

View File

@ -4,7 +4,7 @@ namespace AIStudio.Components.Pages.GrammarSpelling;
public partial class AssistantGrammarSpelling : AssistantBaseCore public partial class AssistantGrammarSpelling : AssistantBaseCore
{ {
protected override string Title => "Grammar and Spelling Checker"; protected override string Title => "Grammar & Spelling Checker";
protected override string Description => protected override string Description =>
""" """
@ -23,11 +23,29 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore
protected override bool ShowResult => false; protected override bool ShowResult => false;
protected override bool ShowDedicatedProgress => true;
protected override IReadOnlyList<ButtonData> FooterButtons => new[] protected override IReadOnlyList<ButtonData> FooterButtons => new[]
{ {
new ButtonData("Copy corrected text", Icons.Material.Filled.ContentCopy, Color.Default, string.Empty, this.CopyToClipboard), new ButtonData("Copy result", Icons.Material.Filled.ContentCopy, Color.Default, string.Empty, () => this.CopyToClipboard(this.correctedText)),
}; };
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
{
if (this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)
{
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedTargetLanguage;
this.customTargetLanguage = this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage;
this.providerSettings = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider);
}
await base.OnInitializedAsync();
}
#endregion
private string inputText = string.Empty; private string inputText = string.Empty;
private CommonLanguages selectedTargetLanguage; private CommonLanguages selectedTargetLanguage;
private string customTargetLanguage = string.Empty; private string customTargetLanguage = string.Empty;
@ -67,6 +85,7 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore
private async Task ProofreadText() private async Task ProofreadText()
{ {
await this.form!.Validate();
if (!this.inputIsValid) if (!this.inputIsValid)
return; return;
@ -76,9 +95,4 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore
this.correctedText = await this.AddAIResponseAsync(time); this.correctedText = await this.AddAIResponseAsync(time);
await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.correctedText); await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.correctedText);
} }
private async Task CopyToClipboard()
{
await this.Rust.CopyText2Clipboard(this.JsRuntime, this.Snackbar, this.correctedText);
}
} }

View File

@ -0,0 +1,12 @@
@using AIStudio.Tools
@page "/assistant/rewrite-improve"
@inherits AssistantBaseCore
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidateText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input to improve" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom language" />
<EnumSelection T="WritingStyles" NameFunc="@(style => style.Name())" @bind-Value="@this.selectedWritingStyle" Icon="@Icons.Material.Filled.Edit" Label="Writing style" AllowOther="@false" />
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
<MudButton Variant="Variant.Filled" Class="mb-3" OnClick="() => this.RewriteText()">
Improve
</MudButton>

View File

@ -0,0 +1,105 @@
using AIStudio.Tools;
namespace AIStudio.Components.Pages.RewriteImprove;
public partial class AssistantRewriteImprove : AssistantBaseCore
{
protected override string Title => "Rewrite & Improve Text";
protected override string Description =>
"""
Rewrite and improve your text. Please note, that the capabilities of the different LLM providers will vary.
""";
protected override string SystemPrompt =>
$"""
You are an expert in language and style. You receive a text as input. First, you review the text. If no
changes are needed, you return the text without modifications. If a change is necessary, you improve the
text. You can also correct spelling and grammar issues. You never add additional information. You never
ask the user for additional information. Your response only contains the improved text. You do not explain
your changes. If no changes are needed, you return the text unchanged.
The style of the text: {this.selectedWritingStyle.Prompt()}. You follow the rules according
to {this.SystemPromptLanguage()} in all your changes.
""";
protected override bool ShowResult => false;
#region Overrides of AssistantBase
protected override bool ShowDedicatedProgress => true;
#endregion
protected override IReadOnlyList<ButtonData> FooterButtons => new[]
{
new ButtonData("Copy result", Icons.Material.Filled.ContentCopy, Color.Default, string.Empty, () => this.CopyToClipboard(this.rewrittenText)),
};
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
{
if (this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)
{
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedTargetLanguage;
this.customTargetLanguage = this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedOtherLanguage;
this.providerSettings = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider);
this.selectedWritingStyle = this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle;
}
await base.OnInitializedAsync();
}
#endregion
private string inputText = string.Empty;
private CommonLanguages selectedTargetLanguage;
private string customTargetLanguage = string.Empty;
private string rewrittenText = string.Empty;
private WritingStyles selectedWritingStyle;
private string? ValidateText(string text)
{
if(string.IsNullOrWhiteSpace(text))
return "Please provide a text as input. You might copy the desired text from a document or a website.";
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 SystemPromptLanguage()
{
var lang = this.selectedTargetLanguage switch
{
CommonLanguages.AS_IS => "the source language",
CommonLanguages.OTHER => this.customTargetLanguage,
_ => $"{this.selectedTargetLanguage.Name()}",
};
if (string.IsNullOrWhiteSpace(lang))
return "the source language";
return lang;
}
private async Task RewriteText()
{
await this.form!.Validate();
if (!this.inputIsValid)
return;
this.CreateChatThread();
var time = this.AddUserRequest(this.inputText);
this.rewrittenText = await this.AddAIResponseAsync(time);
await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.rewrittenText);
}
}

View File

@ -0,0 +1,16 @@
namespace AIStudio.Components.Pages.RewriteImprove;
public enum WritingStyles
{
NOT_SPECIFIED = 0,
EVERYDAY,
BUSINESS,
SCIENTIFIC,
JOURNALISTIC,
LITERARY,
TECHNICAL,
MARKETING,
ACADEMIC,
LEGAL,
}

View File

@ -0,0 +1,40 @@
namespace AIStudio.Components.Pages.RewriteImprove;
public static class WritingStylesExtensions
{
public static string Name(this WritingStyles style)
{
return style switch
{
WritingStyles.EVERYDAY => "Everyday (personal texts, social media)",
WritingStyles.BUSINESS => "Business (business emails, reports, presentations)",
WritingStyles.SCIENTIFIC => "Scientific (scientific papers, research reports)",
WritingStyles.JOURNALISTIC => "Journalistic (magazines, newspapers, news)",
WritingStyles.LITERARY => "Literary (fiction, poetry)",
WritingStyles.TECHNICAL => "Technical (manuals, documentation)",
WritingStyles.MARKETING => "Marketing (advertisements, sales texts)",
WritingStyles.ACADEMIC => "Academic (essays, seminar papers)",
WritingStyles.LEGAL => "Legal (legal texts, contracts)",
_ => "Not specified",
};
}
public static string Prompt(this WritingStyles style)
{
return style switch
{
WritingStyles.EVERYDAY => "Use a everyday style like for personal texts, social media, and informal communication.",
WritingStyles.BUSINESS => "Use a business style like for business emails, reports, and presentations. Most important is clarity and professionalism.",
WritingStyles.SCIENTIFIC => "Use a scientific style like for scientific papers, research reports, and academic writing. Most important is precision and objectivity.",
WritingStyles.JOURNALISTIC => "Use a journalistic style like for magazines, newspapers, and news. Most important is readability and engaging content.",
WritingStyles.LITERARY => "Use a literary style like for fiction, poetry, and creative writing. Most important is creativity and emotional impact.",
WritingStyles.TECHNICAL => "Use a technical style like for manuals, documentation, and technical writing. Most important is clarity and precision.",
WritingStyles.MARKETING => "Use a marketing style like for advertisements, sales texts, and promotional content. Most important is persuasiveness and engagement.",
WritingStyles.ACADEMIC => "Use a academic style like for essays, seminar papers, and academic writing. Most important is clarity and objectivity.",
WritingStyles.LEGAL => "Use a legal style like for legal texts, contracts, and official documents. Most important is precision and legal correctness. Use formal legal language.",
_ => "Keep the style of the text as it is.",
};
}
}

View File

@ -174,6 +174,31 @@
</MudPaper> </MudPaper>
</ExpansionPanel> </ExpansionPanel>
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Edit" HeaderText="Assistant: Grammar & Spelling Checker">
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
<ConfigurationOption OptionDescription="Preselect grammar & spell checker options?" LabelOn="Grammar & spell checker options are preselected" LabelOff="No grammar & spell checker options are preselected" State="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect the grammar & spell checker options. This is might be useful when you prefer a specific language or LLM model."/>
<ConfigurationSelect OptionDescription="Preselect the target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesTranslationData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedTargetLanguage = selectedValue)" OptionHelp="Which target language should be preselected?"/>
@if (this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedTargetLanguage is CommonLanguages.OTHER)
{
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage = updatedText)"/>
}
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider = selectedValue)"/>
</MudPaper>
</ExpansionPanel>
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Edit" HeaderText="Assistant: Rewrite & Improve Text">
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
<ConfigurationOption OptionDescription="Preselect rewrite & improve text options?" LabelOn="Rewrite & improve text options are preselected" LabelOff="No rewrite & improve text options are preselected" State="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect the rewrite & improve text options. This is might be useful when you prefer a specific language or LLM model."/>
<ConfigurationSelect OptionDescription="Preselect the target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesTranslationData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedTargetLanguage = selectedValue)" OptionHelp="Which target language should be preselected?"/>
@if (this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedTargetLanguage is CommonLanguages.OTHER)
{
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedOtherLanguage = updatedText)"/>
}
<ConfigurationSelect OptionDescription="Preselect a writing style" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle)" Data="@ConfigurationSelectDataFactory.GetWritingStylesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle = selectedValue)" OptionHelp="Which writing style should be preselected?"/>
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider = selectedValue)"/>
</MudPaper>
</ExpansionPanel>
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.TextFields" HeaderText="Agent: Text Content Cleaner Options"> <ExpansionPanel HeaderIcon="@Icons.Material.Filled.TextFields" HeaderText="Agent: Text Content Cleaner Options">
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg"> <MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
<MudText Typo="Typo.body1" Class="mb-3"> <MudText Typo="Typo.body1" Class="mb-3">

View File

@ -69,6 +69,7 @@ public partial class AssistantTranslation : AssistantBaseCore
private async Task TranslateText(bool force) private async Task TranslateText(bool force)
{ {
await this.form!.Validate();
if (!this.inputIsValid) if (!this.inputIsValid)
return; return;

View File

@ -1,6 +1,7 @@
using AIStudio.Components.Pages.Agenda; using AIStudio.Components.Pages.Agenda;
using AIStudio.Components.Pages.Coding; using AIStudio.Components.Pages.Coding;
using AIStudio.Components.Pages.IconFinder; using AIStudio.Components.Pages.IconFinder;
using AIStudio.Components.Pages.RewriteImprove;
using AIStudio.Components.Pages.TextSummarizer; using AIStudio.Components.Pages.TextSummarizer;
using AIStudio.Settings.DataModel; using AIStudio.Settings.DataModel;
using AIStudio.Tools; using AIStudio.Tools;
@ -99,4 +100,10 @@ public static class ConfigurationSelectDataFactory
foreach (var number in Enum.GetValues<NumberParticipants>()) foreach (var number in Enum.GetValues<NumberParticipants>())
yield return new(number.Name(), number); yield return new(number.Name(), number);
} }
public static IEnumerable<ConfigurationSelectData<WritingStyles>> GetWritingStylesData()
{
foreach (var style in Enum.GetValues<WritingStyles>())
yield return new(style.Name(), style);
}
} }

View File

@ -38,4 +38,8 @@ public sealed class Data
public DataTextContentCleaner TextContentCleaner { get; init; } = new(); public DataTextContentCleaner TextContentCleaner { get; init; } = new();
public DataAgenda Agenda { get; init; } = new(); public DataAgenda Agenda { get; init; } = new();
public DataGrammarSpelling GrammarSpelling { get; init; } = new();
public DataRewriteImprove RewriteImprove { get; init; } = new();
} }

View File

@ -0,0 +1,26 @@
using AIStudio.Tools;
namespace AIStudio.Settings.DataModel;
public sealed class DataGrammarSpelling
{
/// <summary>
/// Preselect any rewrite options?
/// </summary>
public bool PreselectOptions { get; set; }
/// <summary>
/// Preselect the target language?
/// </summary>
public CommonLanguages PreselectedTargetLanguage { get; set; }
/// <summary>
/// Preselect any other language?
/// </summary>
public string PreselectedOtherLanguage { get; set; } = string.Empty;
/// <summary>
/// Preselect a provider?
/// </summary>
public string PreselectedProvider { get; set; } = string.Empty;
}

View File

@ -0,0 +1,32 @@
using AIStudio.Components.Pages.RewriteImprove;
using AIStudio.Tools;
namespace AIStudio.Settings.DataModel;
public sealed class DataRewriteImprove
{
/// <summary>
/// Preselect any rewrite options?
/// </summary>
public bool PreselectOptions { get; set; }
/// <summary>
/// Preselect the target language?
/// </summary>
public CommonLanguages PreselectedTargetLanguage { get; set; }
/// <summary>
/// Preselect any other language?
/// </summary>
public string PreselectedOtherLanguage { get; set; } = string.Empty;
/// <summary>
/// Preselect any writing style?
/// </summary>
public WritingStyles PreselectedWritingStyle { get; set; }
/// <summary>
/// Preselect a provider?
/// </summary>
public string PreselectedProvider { get; set; } = string.Empty;
}

View File

@ -1,4 +1,5 @@
# v0.8.8, build 170 # v0.8.8, build 170
- Added a grammar and spell checker assistant - Added a grammar and spell checker assistant
- Improved all assistants by showing a progress bar while processing - Added a rewrite and text improvement assistant
- Fixed validation for the translation assistant
- Upgraded MudBlazor to v7.6.0 - Upgraded MudBlazor to v7.6.0