diff --git a/app/MindWork AI Studio/Dialogs/ProfileDialog.razor b/app/MindWork AI Studio/Dialogs/ProfileDialog.razor
new file mode 100644
index 00000000..fb44439a
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/ProfileDialog.razor
@@ -0,0 +1,93 @@
+
+
+
+ Store personal data about yourself in various profiles so that the AIs know your personal context.
+ This saves you from having to explain your context each time, for example, in every chat. When you
+ have different roles, you can create a profile for each role.
+
+
+
+ Are you a project manager in a research facility? You might want to create a profile for your project
+ management activities, one for your scientific work, and a profile for when you need to write program
+ code. In these profiles, you can record how much experience you have or which methods you like or
+ dislike using. Later, you can choose when and where you want to use each profile.
+
+
+
+ The name of the profile is mandatory. Each profile must have a unique name. Whether you provide
+ information about yourself or only fill out the actions is up to you. Only one of these pieces
+ is required.
+
+
+ @* ReSharper disable once CSharpWarnings::CS8974 *@
+
+
+
+
+
+
+
+
+
+
+ Cancel
+
+ @if(this.IsEditing)
+ {
+ @:Update
+ }
+ else
+ {
+ @:Add
+ }
+
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/ProfileDialog.razor.cs b/app/MindWork AI Studio/Dialogs/ProfileDialog.razor.cs
new file mode 100644
index 00000000..7391c1d5
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/ProfileDialog.razor.cs
@@ -0,0 +1,168 @@
+using AIStudio.Settings;
+
+using Microsoft.AspNetCore.Components;
+
+namespace AIStudio.Dialogs;
+
+public partial class ProfileDialog : ComponentBase
+{
+ [CascadingParameter]
+ private MudDialogInstance MudDialog { get; set; } = null!;
+
+ ///
+ /// The profile's number in the list.
+ ///
+ [Parameter]
+ public uint DataNum { get; set; }
+
+ ///
+ /// The profile's ID.
+ ///
+ [Parameter]
+ public string DataId { get; set; } = Guid.NewGuid().ToString();
+
+ ///
+ /// The profile name chosen by the user.
+ ///
+ [Parameter]
+ public string DataName { get; set; } = string.Empty;
+
+ ///
+ /// What should the LLM know about you?
+ ///
+ [Parameter]
+ public string DataNeedToKnow { get; set; } = string.Empty;
+
+ ///
+ /// What actions should the LLM take?
+ ///
+ [Parameter]
+ public string DataActions { get; set; } = string.Empty;
+
+ ///
+ /// Should the dialog be in editing mode?
+ ///
+ [Parameter]
+ public bool IsEditing { get; init; }
+
+ [Inject]
+ private SettingsManager SettingsManager { get; init; } = null!;
+
+ [Inject]
+ private ILogger Logger { get; init; } = null!;
+
+ private static readonly Dictionary SPELLCHECK_ATTRIBUTES = new();
+
+ ///
+ /// The list of used profile names. We need this to check for uniqueness.
+ ///
+ private List UsedNames { get; set; } = [];
+
+ private bool dataIsValid;
+ private string[] dataIssues = [];
+ private string dataEditingPreviousName = string.Empty;
+
+ // We get the form reference from Blazor code to validate it manually:
+ private MudForm form = null!;
+
+ private Profile CreateProfileSettings() => new()
+ {
+ Num = this.DataNum,
+ Id = this.DataId,
+
+ Name = this.DataName,
+ NeedToKnow = this.DataNeedToKnow,
+ Actions = this.DataActions,
+ };
+
+ #region Overrides of ComponentBase
+
+ protected override async Task OnInitializedAsync()
+ {
+ // Configure the spellchecking for the instance name input:
+ this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES);
+
+ // Load the used instance names:
+ this.UsedNames = this.SettingsManager.ConfigurationData.Profiles.Select(x => x.Name.ToLowerInvariant()).ToList();
+
+ // When editing, we need to load the data:
+ if(this.IsEditing)
+ {
+ this.dataEditingPreviousName = this.DataName.ToLowerInvariant();
+ }
+
+ await base.OnInitializedAsync();
+ }
+
+ 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(!this.IsEditing && firstRender)
+ this.form.ResetValidation();
+
+ await base.OnAfterRenderAsync(firstRender);
+ }
+
+ #endregion
+
+ private async Task Store()
+ {
+ await this.form.Validate();
+
+ // When the data is not valid, we don't store it:
+ if (!this.dataIsValid)
+ return;
+
+ // Use the data model to store the profile.
+ // We just return this data to the parent component:
+ var addedProfileSettings = this.CreateProfileSettings();
+
+ if(this.IsEditing)
+ this.Logger.LogInformation($"Edited profile '{addedProfileSettings.Name}'.");
+ else
+ this.Logger.LogInformation($"Created profile '{addedProfileSettings.Name}'.");
+
+ this.MudDialog.Close(DialogResult.Ok(addedProfileSettings));
+ }
+
+ private string? ValidateNeedToKnow(string text)
+ {
+ if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions))
+ return "Please enter what the LLM should know about you and/or what actions it should take.";
+
+ if(text.Length > 444)
+ return "The text must not exceed 444 characters.";
+
+ return null;
+ }
+
+ private string? ValidateActions(string text)
+ {
+ if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions))
+ return "Please enter what the LLM should know about you and/or what actions it should take.";
+
+ if(text.Length > 256)
+ return "The text must not exceed 256 characters.";
+
+ return null;
+ }
+
+ private string? ValidateName(string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ return "Please enter a profile name.";
+
+ if (name.Length > 40)
+ return "The profile name must not exceed 40 characters.";
+
+ // The instance name must be unique:
+ var lowerName = name.ToLowerInvariant();
+ if (lowerName != this.dataEditingPreviousName && this.UsedNames.Contains(lowerName))
+ return "The profile name must be unique; the chosen name is already in use.";
+
+ return null;
+ }
+
+ private void Cancel() => this.MudDialog.Cancel();
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Pages/Settings.razor b/app/MindWork AI Studio/Pages/Settings.razor
index 620d3d1c..509813c8 100644
--- a/app/MindWork AI Studio/Pages/Settings.razor
+++ b/app/MindWork AI Studio/Pages/Settings.razor
@@ -74,6 +74,55 @@
+
+ Your Profiles
+
+ Store personal data about yourself in various profiles so that the AIs know your personal context.
+ This saves you from having to explain your context each time, for example, in every chat. When you
+ have different roles, you can create a profile for each role.
+
+
+
+ Are you a project manager in a research facility? You might want to create a profile for your project
+ management activities, one for your scientific work, and a profile for when you need to write program
+ code. In these profiles, you can record how much experience you have or which methods you like or
+ dislike using. Later, you can choose when and where you want to use each profile.
+
+
+
+
+
+
+
+
+ #
+ Profile Name
+ Actions
+
+
+ @context.Num
+ @context.Name
+
+
+ Edit
+
+
+ Delete
+
+
+
+
+
+ @if(this.SettingsManager.ConfigurationData.Profiles.Count == 0)
+ {
+ No profiles configured yet.
+ }
+
+
+ Add Profile
+
+
+
diff --git a/app/MindWork AI Studio/Pages/Settings.razor.cs b/app/MindWork AI Studio/Pages/Settings.razor.cs
index daa216b4..5c09f985 100644
--- a/app/MindWork AI Studio/Pages/Settings.razor.cs
+++ b/app/MindWork AI Studio/Pages/Settings.razor.cs
@@ -160,6 +160,73 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
this.availableProviders.Add(new (provider.InstanceName, provider.Id));
}
+ #endregion
+
+ #region Profile related
+
+ private async Task AddProfile()
+ {
+ var dialogParameters = new DialogParameters
+ {
+ { x => x.IsEditing, false },
+ };
+
+ var dialogReference = await this.DialogService.ShowAsync("Add Profile", dialogParameters, DialogOptions.FULLSCREEN);
+ var dialogResult = await dialogReference.Result;
+ if (dialogResult is null || dialogResult.Canceled)
+ return;
+
+ var addedProfile = (Profile)dialogResult.Data!;
+ addedProfile = addedProfile with { Num = this.SettingsManager.ConfigurationData.NextProfileNum++ };
+
+ this.SettingsManager.ConfigurationData.Profiles.Add(addedProfile);
+
+ await this.SettingsManager.StoreSettings();
+ await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED);
+ }
+
+ private async Task EditProfile(Profile profile)
+ {
+ var dialogParameters = new DialogParameters
+ {
+ { x => x.DataNum, profile.Num },
+ { x => x.DataId, profile.Id },
+ { x => x.DataName, profile.Name },
+ { x => x.DataNeedToKnow, profile.NeedToKnow },
+ { x => x.DataActions, profile.Actions },
+ { x => x.IsEditing, true },
+ };
+
+ var dialogReference = await this.DialogService.ShowAsync("Edit Profile", dialogParameters, DialogOptions.FULLSCREEN);
+ var dialogResult = await dialogReference.Result;
+ if (dialogResult is null || dialogResult.Canceled)
+ return;
+
+ var editedProfile = (Profile)dialogResult.Data!;
+ this.SettingsManager.ConfigurationData.Profiles[this.SettingsManager.ConfigurationData.Profiles.IndexOf(profile)] = editedProfile;
+
+ await this.SettingsManager.StoreSettings();
+ await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED);
+ }
+
+ private async Task DeleteProfile(Profile profile)
+ {
+ var dialogParameters = new DialogParameters
+ {
+ { "Message", $"Are you sure you want to delete the profile '{profile.Name}'?" },
+ };
+
+ var dialogReference = await this.DialogService.ShowAsync("Delete Profile", dialogParameters, DialogOptions.FULLSCREEN);
+ var dialogResult = await dialogReference.Result;
+ if (dialogResult is null || dialogResult.Canceled)
+ return;
+
+ this.SettingsManager.ConfigurationData.Profiles.Remove(profile);
+ await this.SettingsManager.StoreSettings();
+
+ await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED);
+ }
+
#endregion
#region Implementation of IMessageBusReceiver
diff --git a/app/MindWork AI Studio/Settings/DataModel/Data.cs b/app/MindWork AI Studio/Settings/DataModel/Data.cs
index 586c763d..f1dc8867 100644
--- a/app/MindWork AI Studio/Settings/DataModel/Data.cs
+++ b/app/MindWork AI Studio/Settings/DataModel/Data.cs
@@ -15,12 +15,22 @@ public sealed class Data
/// List of configured providers.
///
public List Providers { get; init; } = [];
+
+ ///
+ /// List of configured profiles.
+ ///
+ public List Profiles { get; init; } = [];
///
/// The next provider number to use.
///
public uint NextProviderNum { get; set; } = 1;
+ ///
+ /// The next profile number to use.
+ ///
+ public uint NextProfileNum { get; set; } = 1;
+
public DataApp App { get; init; } = new();
public DataChat Chat { get; init; } = new();
diff --git a/app/MindWork AI Studio/Settings/Profile.cs b/app/MindWork AI Studio/Settings/Profile.cs
new file mode 100644
index 00000000..cbcdc5c3
--- /dev/null
+++ b/app/MindWork AI Studio/Settings/Profile.cs
@@ -0,0 +1,14 @@
+namespace AIStudio.Settings;
+
+public readonly record struct Profile(uint Num, string Id, string Name, string NeedToKnow, string Actions)
+{
+ #region Overrides of ValueType
+
+ ///
+ /// Returns a string that represents the profile in a human-readable format.
+ ///
+ /// A string that represents the profile in a human-readable format.
+ public override string ToString() => this.Name;
+
+ #endregion
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/wwwroot/changelog/v0.9.7.md b/app/MindWork AI Studio/wwwroot/changelog/v0.9.7.md
index 81c3b28d..4cc05a15 100644
--- a/app/MindWork AI Studio/wwwroot/changelog/v0.9.7.md
+++ b/app/MindWork AI Studio/wwwroot/changelog/v0.9.7.md
@@ -1,4 +1,5 @@
# v0.9.7, build 182 (2024-09-09 xx:xx UTC)
+- Added the possibility to define multiple profiles in the settings. Use profiles to share some information about you with the AI.
- Added an introductory description to the provider settings.
- Added an indicator for the current and maximal length of the provider instance name.
- Fixed the bug that the model name for Fireworks was not loaded when editing the provider settings.