Skip to content

Commit 09f5b83

Browse files
Implemented the "my tasks" assistant (#137)
1 parent 7a5f2d4 commit 09f5b83

16 files changed

+224
-2
lines changed

app/MindWork AI Studio/Assistants/AssistantBase.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
Reset
9595
</MudButton>
9696

97-
@if (this.AllowProfiles)
97+
@if (this.AllowProfiles && this.ShowProfileSelection)
9898
{
9999
<ProfileSelection MarginLeft="" @bind-CurrentProfile="@this.currentProfile"/>
100100
}

app/MindWork AI Studio/Assistants/AssistantBase.razor.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public abstract partial class AssistantBase : ComponentBase
5757
protected virtual bool ShowResult => true;
5858

5959
protected virtual bool AllowProfiles => true;
60+
61+
protected virtual bool ShowProfileSelection => true;
6062

6163
protected virtual bool ShowDedicatedProgress => false;
6264

@@ -69,12 +71,12 @@ public abstract partial class AssistantBase : ComponentBase
6971
protected AIStudio.Settings.Provider providerSettings;
7072
protected MudForm? form;
7173
protected bool inputIsValid;
74+
protected Profile currentProfile = Profile.NO_PROFILE;
7275

7376
protected ChatThread? chatThread;
7477
private ContentBlock? resultingContentBlock;
7578
private string[] inputIssues = [];
7679
private bool isProcessing;
77-
private Profile currentProfile = Profile.NO_PROFILE;
7880

7981
#region Overrides of ComponentBase
8082

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@attribute [Route(Routes.ASSISTANT_MY_TASKS)]
2+
@inherits AssistantBaseCore
3+
4+
<ProfileFormSelection Validation="@this.ValidateProfile" @bind-Profile="@this.currentProfile"/>
5+
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Text or email" Variant="Variant.Outlined" Lines="12" AutoGrow="@true" MaxLines="24" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
6+
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
7+
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
8+
9+
<MudButton Variant="Variant.Filled" Class="mb-3" OnClick="() => this.AnalyzeText()">
10+
Analyze text
11+
</MudButton>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using AIStudio.Settings;
2+
3+
namespace AIStudio.Assistants.MyTasks;
4+
5+
public partial class AssistantMyTasks : AssistantBaseCore
6+
{
7+
protected override Tools.Components Component => Tools.Components.MY_TASKS_ASSISTANT;
8+
9+
protected override string Title => "My Tasks";
10+
11+
protected override string Description =>
12+
"""
13+
You received a cryptic email that was sent to many recipients and you are now wondering
14+
if you need to do something? Copy the email into the input field. You also need to select
15+
a personal profile. In this profile, you should describe your role in the organization.
16+
The AI will then try to give you hints on what your tasks might be.
17+
""";
18+
19+
protected override string SystemPrompt =>
20+
$"""
21+
You are a friendly and professional business expert. You receive business emails, protocols,
22+
reports, etc. as input. Additionally, you know the user's role in the organization. The user
23+
wonders if any tasks arise for them in their role based on the text. You now try to give hints
24+
and advice on whether and what the user should do. When you believe there are no tasks for the
25+
user, you tell them this. You consider typical business etiquette in your advice.
26+
27+
You write your advice in the following language: {this.SystemPromptLanguage()}.
28+
""";
29+
30+
protected override IReadOnlyList<IButtonData> FooterButtons => [];
31+
32+
protected override bool ShowProfileSelection => false;
33+
34+
protected override void ResetFrom()
35+
{
36+
this.inputText = string.Empty;
37+
if (!this.MightPreselectValues())
38+
{
39+
this.selectedTargetLanguage = CommonLanguages.AS_IS;
40+
this.customTargetLanguage = string.Empty;
41+
}
42+
}
43+
44+
protected override bool MightPreselectValues()
45+
{
46+
if (this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)
47+
{
48+
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.MyTasks.PreselectedTargetLanguage;
49+
this.customTargetLanguage = this.SettingsManager.ConfigurationData.MyTasks.PreselectOtherLanguage;
50+
return true;
51+
}
52+
53+
return false;
54+
}
55+
56+
private string inputText = string.Empty;
57+
private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
58+
private string customTargetLanguage = string.Empty;
59+
60+
#region Overrides of ComponentBase
61+
62+
protected override async Task OnInitializedAsync()
63+
{
64+
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_MY_TASKS_ASSISTANT).FirstOrDefault();
65+
if (deferredContent is not null)
66+
this.inputText = deferredContent;
67+
68+
await base.OnInitializedAsync();
69+
}
70+
71+
#endregion
72+
73+
private string? ValidatingText(string text)
74+
{
75+
if(string.IsNullOrWhiteSpace(text))
76+
return "Please provide some text as input. For example, an email.";
77+
78+
return null;
79+
}
80+
81+
private string? ValidateProfile(Profile profile)
82+
{
83+
if(profile == default || profile == Profile.NO_PROFILE)
84+
return "Please select one of your profiles.";
85+
86+
return null;
87+
}
88+
89+
private string? ValidateCustomLanguage(string language)
90+
{
91+
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
92+
return "Please provide a custom language.";
93+
94+
return null;
95+
}
96+
97+
private string SystemPromptLanguage()
98+
{
99+
if(this.selectedTargetLanguage is CommonLanguages.AS_IS)
100+
return "Use the same language as the input";
101+
102+
if(this.selectedTargetLanguage is CommonLanguages.OTHER)
103+
return this.customTargetLanguage;
104+
105+
return this.selectedTargetLanguage.Name();
106+
}
107+
108+
private async Task AnalyzeText()
109+
{
110+
await this.form!.Validate();
111+
if (!this.inputIsValid)
112+
return;
113+
114+
this.CreateChatThread();
115+
var time = this.AddUserRequest(this.inputText);
116+
117+
await this.AddAIResponseAsync(time);
118+
}
119+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@using AIStudio.Settings
2+
3+
<MudSelect T="Profile" Strict="@true" Value="@this.Profile" ValueChanged="@this.SelectionChanged" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person4" Margin="Margin.Dense" Label="Select one of your profiles" Variant="Variant.Outlined" Class="mb-3" Validation="@this.Validation">
4+
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles.GetAllProfiles())
5+
{
6+
<MudSelectItem Value="profile">
7+
@profile.Name
8+
</MudSelectItem>
9+
}
10+
</MudSelect>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using AIStudio.Settings;
2+
3+
using Microsoft.AspNetCore.Components;
4+
5+
namespace AIStudio.Components;
6+
7+
public partial class ProfileFormSelection : ComponentBase
8+
{
9+
[Parameter]
10+
public Profile Profile { get; set; } = Profile.NO_PROFILE;
11+
12+
[Parameter]
13+
public EventCallback<Profile> ProfileChanged { get; set; }
14+
15+
[Parameter]
16+
public Func<Profile, string?> Validation { get; set; } = _ => null;
17+
18+
[Inject]
19+
private SettingsManager SettingsManager { get; init; } = null!;
20+
21+
private async Task SelectionChanged(Profile profile)
22+
{
23+
this.Profile = profile;
24+
await this.ProfileChanged.InvokeAsync(profile);
25+
}
26+
}

app/MindWork AI Studio/Pages/Assistants.razor

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
</MudText>
2323
<MudStack Row="@true" Wrap="@Wrap.Wrap" Class="mb-3">
2424
<AssistantBlock Name="E-Mail" Description="Generate an e-mail for a given context." Icon="@Icons.Material.Filled.Email" Link="@Routes.ASSISTANT_EMAIL"/>
25+
<AssistantBlock Name="My Tasks" Description="Analyze a text or an email for tasks you need to complete." Icon="@Icons.Material.Filled.Task" Link="@Routes.ASSISTANT_MY_TASKS"/>
2526
<AssistantBlock Name="Agenda Planner" Description="Generate an agenda for a given meeting, seminar, etc." Icon="@Icons.Material.Filled.CalendarToday" Link="@Routes.ASSISTANT_AGENDA"/>
2627
<AssistantBlock Name="Legal Check" Description="Ask a question about a legal document." Icon="@Icons.Material.Filled.Gavel" Link="@Routes.ASSISTANT_LEGAL_CHECK"/>
2728
<AssistantBlock Name="Icon Finder" Description="Using a LLM to find an icon for a given context." Icon="@Icons.Material.Filled.FindInPage" Link="@Routes.ASSISTANT_ICON_FINDER"/>

app/MindWork AI Studio/Pages/Settings.razor

+13
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,19 @@
299299
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider = selectedValue)"/>
300300
</MudPaper>
301301
</ExpansionPanel>
302+
303+
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Task" HeaderText="Assistant: My Tasks">
304+
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
305+
<ConfigurationOption OptionDescription="Preselect options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect options. This is might be useful when you prefer a specific language or LLM model."/>
306+
<ConfigurationSelect OptionDescription="Preselect the language" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesOptionalData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedTargetLanguage = selectedValue)" OptionHelp="Which language should be preselected?"/>
307+
@if (this.SettingsManager.ConfigurationData.MyTasks.PreselectedTargetLanguage is CommonLanguages.OTHER)
308+
{
309+
<ConfigurationText OptionDescription="Preselect another language" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.MyTasks.PreselectOtherLanguage = updatedText)"/>
310+
}
311+
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
312+
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider = selectedValue)"/>
313+
</MudPaper>
314+
</ExpansionPanel>
302315

303316
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.TextFields" HeaderText="Agent: Text Content Cleaner Options">
304317
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">

app/MindWork AI Studio/Routes.razor.cs

+1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ public sealed partial class Routes
2020
public const string ASSISTANT_EMAIL = "/assistant/email";
2121
public const string ASSISTANT_LEGAL_CHECK = "/assistant/legal-check";
2222
public const string ASSISTANT_SYNONYMS = "/assistant/synonyms";
23+
public const string ASSISTANT_MY_TASKS = "/assistant/my-tasks";
2324
// ReSharper restore InconsistentNaming
2425
}

app/MindWork AI Studio/Settings/DataModel/Data.cs

+2
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,6 @@ public sealed class Data
5858
public DataLegalCheck LegalCheck { get; set; } = new();
5959

6060
public DataSynonyms Synonyms { get; set; } = new();
61+
62+
public DataMyTasks MyTasks { get; set; } = new();
6163
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace AIStudio.Settings.DataModel;
2+
3+
public sealed class DataMyTasks
4+
{
5+
/// <summary>
6+
/// Do you want to preselect any options?
7+
/// </summary>
8+
public bool PreselectOptions { get; set; }
9+
10+
/// <summary>
11+
/// Preselect the target language?
12+
/// </summary>
13+
public CommonLanguages PreselectedTargetLanguage { get; set; }
14+
15+
/// <summary>
16+
/// Preselect any other language?
17+
/// </summary>
18+
public string PreselectOtherLanguage { get; set; } = string.Empty;
19+
20+
/// <summary>
21+
/// The preselected provider.
22+
/// </summary>
23+
public string PreselectedProvider { get; set; } = string.Empty;
24+
25+
/// <summary>
26+
/// Preselect a profile?
27+
/// </summary>
28+
public string PreselectedProfile { get; set; } = string.Empty;
29+
}

app/MindWork AI Studio/Settings/SettingsManager.cs

+2
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ public Provider GetPreselectedProvider(Tools.Components component)
129129
Tools.Components.EMAIL_ASSISTANT => this.ConfigurationData.EMail.PreselectOptions ? this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.EMail.PreselectedProvider) : default,
130130
Tools.Components.LEGAL_CHECK_ASSISTANT => this.ConfigurationData.LegalCheck.PreselectOptions ? this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.LegalCheck.PreselectedProvider) : default,
131131
Tools.Components.SYNONYMS_ASSISTANT => this.ConfigurationData.Synonyms.PreselectOptions ? this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.Synonyms.PreselectedProvider) : default,
132+
Tools.Components.MY_TASKS_ASSISTANT => this.ConfigurationData.MyTasks.PreselectOptions ? this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.MyTasks.PreselectedProvider) : default,
132133

133134
_ => default,
134135
};
@@ -148,6 +149,7 @@ public Profile GetPreselectedProfile(Tools.Components component)
148149
Tools.Components.CODING_ASSISTANT => this.ConfigurationData.Coding.PreselectOptions ? this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.Coding.PreselectedProfile) : default,
149150
Tools.Components.EMAIL_ASSISTANT => this.ConfigurationData.EMail.PreselectOptions ? this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.EMail.PreselectedProfile) : default,
150151
Tools.Components.LEGAL_CHECK_ASSISTANT => this.ConfigurationData.LegalCheck.PreselectOptions ? this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.LegalCheck.PreselectedProfile) : default,
152+
Tools.Components.MY_TASKS_ASSISTANT => this.ConfigurationData.MyTasks.PreselectOptions ? this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.MyTasks.PreselectedProfile) : default,
151153

152154
_ => default,
153155
};

app/MindWork AI Studio/Tools/Components.cs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public enum Components
1414
EMAIL_ASSISTANT,
1515
LEGAL_CHECK_ASSISTANT,
1616
SYNONYMS_ASSISTANT,
17+
MY_TASKS_ASSISTANT,
1718

1819
CHAT,
1920
}

app/MindWork AI Studio/Tools/Event.cs

+1
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ public enum Event
2828
SEND_TO_EMAIL_ASSISTANT,
2929
SEND_TO_LEGAL_CHECK_ASSISTANT,
3030
SEND_TO_SYNONYMS_ASSISTANT,
31+
SEND_TO_MY_TASKS_ASSISTANT,
3132
}

app/MindWork AI Studio/Tools/SendToExtensions.cs

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public static class SendToExtensions
1414
Components.EMAIL_ASSISTANT => "E-Mail Assistant",
1515
Components.LEGAL_CHECK_ASSISTANT => "Legal Check Assistant",
1616
Components.SYNONYMS_ASSISTANT => "Synonym Assistant",
17+
Components.MY_TASKS_ASSISTANT => "My Tasks Assistant",
1718

1819
Components.CHAT => "New Chat",
1920

@@ -32,6 +33,7 @@ public static class SendToExtensions
3233
Components.TEXT_SUMMARIZER_ASSISTANT => new(Event.SEND_TO_TEXT_SUMMARIZER_ASSISTANT, Routes.ASSISTANT_SUMMARIZER),
3334
Components.LEGAL_CHECK_ASSISTANT => new(Event.SEND_TO_LEGAL_CHECK_ASSISTANT, Routes.ASSISTANT_LEGAL_CHECK),
3435
Components.SYNONYMS_ASSISTANT => new(Event.SEND_TO_SYNONYMS_ASSISTANT, Routes.ASSISTANT_SYNONYMS),
36+
Components.MY_TASKS_ASSISTANT => new(Event.SEND_TO_MY_TASKS_ASSISTANT, Routes.ASSISTANT_MY_TASKS),
3537

3638
Components.CHAT => new(Event.SEND_TO_CHAT, Routes.CHAT),
3739

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# v0.9.8, build 183 (2024-09-09 13:00 UTC)
2+
- Added the "my tasks" assistant to analyze, e.g., business emails and extract related tasks for your role.

0 commit comments

Comments
 (0)