mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-02-05 11:49:06 +00:00
Add profiles (#132)
This commit is contained in:
parent
00f45f8998
commit
d7c124926b
@ -93,6 +93,11 @@
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Warning" StartIcon="@Icons.Material.Filled.Refresh" OnClick="() => this.InnerResetForm()">
|
||||
Reset
|
||||
</MudButton>
|
||||
|
||||
@if (this.AllowProfiles)
|
||||
{
|
||||
<ProfileSelection MarginLeft="" @bind-CurrentProfile="@this.currentProfile"/>
|
||||
}
|
||||
</MudStack>
|
||||
</FooterContent>
|
||||
</InnerScrolling>
|
@ -55,6 +55,8 @@ public abstract partial class AssistantBase : ComponentBase
|
||||
private protected virtual RenderFragment? Body => null;
|
||||
|
||||
protected virtual bool ShowResult => true;
|
||||
|
||||
protected virtual bool AllowProfiles => true;
|
||||
|
||||
protected virtual bool ShowDedicatedProgress => false;
|
||||
|
||||
@ -72,6 +74,7 @@ public abstract partial class AssistantBase : ComponentBase
|
||||
private ContentBlock? resultingContentBlock;
|
||||
private string[] inputIssues = [];
|
||||
private bool isProcessing;
|
||||
private Profile currentProfile = Profile.NO_PROFILE;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
@ -79,6 +82,7 @@ public abstract partial class AssistantBase : ComponentBase
|
||||
{
|
||||
this.MightPreselectValues();
|
||||
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
|
||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
@ -118,7 +122,12 @@ public abstract partial class AssistantBase : ComponentBase
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Empty,
|
||||
Seed = this.RNG.Next(),
|
||||
SystemPrompt = this.SystemPrompt,
|
||||
SystemPrompt = !this.AllowProfiles ? this.SystemPrompt :
|
||||
$"""
|
||||
{this.SystemPrompt}
|
||||
|
||||
{this.currentProfile.ToSystemPrompt()}
|
||||
""",
|
||||
Blocks = [],
|
||||
};
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore
|
||||
Your response includes only the corrected text. Do not explain your changes. If no changes are needed,
|
||||
you return the text unchanged.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override bool ShowResult => false;
|
||||
|
||||
|
@ -24,6 +24,8 @@ public partial class AssistantIconFinder : AssistantBaseCore
|
||||
related to the keyword "buildings" might be the best match. Provide your keywords in a Markdown list without
|
||||
quotation marks.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
|
@ -24,6 +24,8 @@ public partial class AssistantRewriteImprove : AssistantBaseCore
|
||||
You follow the rules according to {this.SystemPromptLanguage()} in all your changes.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override bool ShowResult => false;
|
||||
|
||||
protected override bool ShowDedicatedProgress => true;
|
||||
|
@ -47,6 +47,8 @@ public partial class AssistantSynonyms : AssistantBaseCore
|
||||
the {this.SystemPromptLanguage()} language.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
|
@ -25,6 +25,8 @@ public partial class AssistantTextSummarizer : AssistantBaseCore
|
||||
a summary with the requested complexity. In any case, do not add any information.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
|
@ -21,6 +21,8 @@ public partial class AssistantTranslation : AssistantBaseCore
|
||||
language requires, e.g., shorter sentences, you should split the text into shorter sentences.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
|
16
app/MindWork AI Studio/Components/MudJustifiedText.cs
Normal file
16
app/MindWork AI Studio/Components/MudJustifiedText.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public class MudJustifiedText : MudText
|
||||
{
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.Align = Align.Justify;
|
||||
this.Style = "hyphens: auto; word-break: auto-phrase;";
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
@foreach(var item in this.Items)
|
||||
{
|
||||
<MudListItem T="string" Icon="@this.Icon" Style="display: flex; align-items: flex-start;">
|
||||
<MudText Typo="Typo.body1" Style="text-align: justify; hyphens: auto;"><b>@item.Header:</b> @item.Text</MudText>
|
||||
<MudText Typo="Typo.body1" Align="Align.Justify" Style="hyphens: auto; word-break: auto-phrase;"><b>@item.Header:</b> @item.Text</MudText>
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
10
app/MindWork AI Studio/Components/ProfileSelection.razor
Normal file
10
app/MindWork AI Studio/Components/ProfileSelection.razor
Normal file
@ -0,0 +1,10 @@
|
||||
<MudTooltip Text="You can switch between your profiles here">
|
||||
<MudMenu StartIcon="@Icons.Material.Filled.Person4" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@this.CurrentProfile.Name" Variant="Variant.Filled" Color="Color.Default" Class="@this.MarginClass">
|
||||
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles.GetAllProfiles())
|
||||
{
|
||||
<MudMenuItem OnClick="() => this.SelectionChanged(profile)">
|
||||
@profile.Name
|
||||
</MudMenuItem>
|
||||
}
|
||||
</MudMenu>
|
||||
</MudTooltip>
|
28
app/MindWork AI Studio/Components/ProfileSelection.razor.cs
Normal file
28
app/MindWork AI Studio/Components/ProfileSelection.razor.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ProfileSelection : ComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public Profile CurrentProfile { get; set; } = Profile.NO_PROFILE;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<Profile> CurrentProfileChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string MarginLeft { get; set; } = "ml-3";
|
||||
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
private string MarginClass => $"{this.MarginLeft}";
|
||||
|
||||
private async Task SelectionChanged(Profile profile)
|
||||
{
|
||||
this.CurrentProfile = profile;
|
||||
await this.CurrentProfileChanged.InvokeAsync(profile);
|
||||
}
|
||||
}
|
93
app/MindWork AI Studio/Dialogs/ProfileDialog.razor
Normal file
93
app/MindWork AI Studio/Dialogs/ProfileDialog.razor
Normal file
@ -0,0 +1,93 @@
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
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.
|
||||
</MudJustifiedText>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
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.
|
||||
</MudJustifiedText>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
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.
|
||||
</MudJustifiedText>
|
||||
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
|
||||
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
||||
<MudTextField
|
||||
T="string"
|
||||
@bind-Text="@this.DataName"
|
||||
Label="Profile Name"
|
||||
Class="mb-3"
|
||||
Immediate="@true"
|
||||
MaxLength="40"
|
||||
Counter="40"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Material.Filled.Badge"
|
||||
AdornmentColor="Color.Info"
|
||||
Validation="@this.ValidateName"
|
||||
Variant="Variant.Outlined"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
/>
|
||||
|
||||
<MudTextField
|
||||
T="string"
|
||||
@bind-Text="@this.DataNeedToKnow"
|
||||
Validation="@this.ValidateNeedToKnow"
|
||||
AdornmentIcon="@Icons.Material.Filled.ListAlt"
|
||||
Adornment="Adornment.Start"
|
||||
Immediate="@true"
|
||||
Label="What should the AI know about you?"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="6"
|
||||
AutoGrow="@true"
|
||||
MaxLines="12"
|
||||
MaxLength="444"
|
||||
Counter="444"
|
||||
Class="mb-3"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="Tell the AI something about yourself. What is your profession? How experienced are you in this profession? Which technologies do you like?"
|
||||
/>
|
||||
|
||||
<MudTextField
|
||||
T="string"
|
||||
@bind-Text="@this.DataActions"
|
||||
Validation="@this.ValidateActions"
|
||||
AdornmentIcon="@Icons.Material.Filled.ListAlt"
|
||||
Adornment="Adornment.Start"
|
||||
Immediate="@true"
|
||||
Label="What should the AI do for you?"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="6"
|
||||
AutoGrow="@true"
|
||||
MaxLines="12"
|
||||
MaxLength="256"
|
||||
Counter="256"
|
||||
Class="mb-3"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally."
|
||||
/>
|
||||
|
||||
</MudForm>
|
||||
<Issues IssuesData="@this.dataIssues"/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">Cancel</MudButton>
|
||||
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@if(this.IsEditing)
|
||||
{
|
||||
@:Update
|
||||
}
|
||||
else
|
||||
{
|
||||
@:Add
|
||||
}
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
168
app/MindWork AI Studio/Dialogs/ProfileDialog.razor.cs
Normal file
168
app/MindWork AI Studio/Dialogs/ProfileDialog.razor.cs
Normal file
@ -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!;
|
||||
|
||||
/// <summary>
|
||||
/// The profile's number in the list.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public uint DataNum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The profile's ID.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string DataId { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// The profile name chosen by the user.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string DataName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// What should the LLM know about you?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string DataNeedToKnow { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// What actions should the LLM take?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string DataActions { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Should the dialog be in editing mode?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool IsEditing { get; init; }
|
||||
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ILogger<ProviderDialog> Logger { get; init; } = null!;
|
||||
|
||||
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
|
||||
|
||||
/// <summary>
|
||||
/// The list of used profile names. We need this to check for uniqueness.
|
||||
/// </summary>
|
||||
private List<string> 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();
|
||||
}
|
@ -83,6 +83,9 @@
|
||||
@bind-Text="@this.DataInstanceName"
|
||||
Label="Instance Name"
|
||||
Class="mb-3"
|
||||
MaxLength="40"
|
||||
Counter="40"
|
||||
Immediate="@true"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Material.Filled.Lightbulb"
|
||||
AdornmentColor="Color.Info"
|
||||
|
@ -1,5 +1,3 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
|
||||
@ -128,6 +126,10 @@ public partial class ProviderDialog : ComponentBase
|
||||
{
|
||||
this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant();
|
||||
|
||||
// When using Fireworks, we must copy the model name:
|
||||
if (this.DataProvider is Providers.FIREWORKS)
|
||||
this.dataManuallyModel = this.DataModel.Id;
|
||||
|
||||
//
|
||||
// We cannot load the API key for self-hosted providers:
|
||||
//
|
||||
@ -245,40 +247,14 @@ public partial class ProviderDialog : ComponentBase
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^[a-zA-Z0-9\-_. ]+$")]
|
||||
private static partial Regex InstanceNameRegex();
|
||||
|
||||
private static readonly string[] RESERVED_NAMES = ["CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"];
|
||||
|
||||
|
||||
private string? ValidatingInstanceName(string instanceName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(instanceName))
|
||||
return "Please enter an instance name.";
|
||||
|
||||
if (instanceName.StartsWith(' ') || instanceName.StartsWith('.'))
|
||||
return "The instance name must not start with a space or a dot.";
|
||||
|
||||
if (instanceName.EndsWith(' ') || instanceName.EndsWith('.'))
|
||||
return "The instance name must not end with a space or a dot.";
|
||||
|
||||
if (instanceName.StartsWith('-') || instanceName.StartsWith('_'))
|
||||
return "The instance name must not start with a hyphen or an underscore.";
|
||||
|
||||
if (instanceName.Length > 255)
|
||||
return "The instance name must not exceed 255 characters.";
|
||||
|
||||
if (!InstanceNameRegex().IsMatch(instanceName))
|
||||
return "The instance name must only contain letters, numbers, spaces, hyphens, underscores, and dots.";
|
||||
|
||||
if (instanceName.Contains(" "))
|
||||
return "The instance name must not contain consecutive spaces.";
|
||||
|
||||
if (RESERVED_NAMES.Contains(instanceName.ToUpperInvariant()))
|
||||
return "This name is reserved and cannot be used.";
|
||||
|
||||
if (instanceName.Any(c => Path.GetInvalidFileNameChars().Contains(c)))
|
||||
return "The instance name contains invalid characters.";
|
||||
if (instanceName.Length > 40)
|
||||
return "The instance name must not exceed 40 characters.";
|
||||
|
||||
// The instance name must be unique:
|
||||
var lowerInstanceName = instanceName.ToLowerInvariant();
|
||||
|
@ -69,6 +69,8 @@
|
||||
<MudIconButton Icon="@Icons.Material.Filled.MoveToInbox" Disabled="@(!this.CanThreadBeSaved)" OnClick="() => this.MoveChatToWorkspace()"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
|
||||
<ProfileSelection CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged" />
|
||||
</MudToolBar>
|
||||
</FooterContent>
|
||||
</InnerScrolling>
|
||||
|
@ -35,6 +35,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
||||
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||
|
||||
private AIStudio.Settings.Provider providerSettings;
|
||||
private Profile currentProfile = Profile.NO_PROFILE;
|
||||
private ChatThread? chatThread;
|
||||
private bool hasUnsavedChanges;
|
||||
private bool isStreaming;
|
||||
@ -61,6 +62,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
||||
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
|
||||
|
||||
this.providerSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(Tools.Components.CHAT);
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<ChatThread>(Event.SEND_TO_CHAT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
{
|
||||
@ -118,6 +120,22 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
||||
private bool CanThreadBeSaved => this.chatThread is not null && this.chatThread.Blocks.Count > 0;
|
||||
|
||||
private string TooltipAddChatToWorkspace => $"Start new chat in workspace \"{this.currentWorkspaceName}\"";
|
||||
|
||||
private void ProfileWasChanged(Profile profile)
|
||||
{
|
||||
this.currentProfile = profile;
|
||||
if(this.chatThread is null)
|
||||
return;
|
||||
|
||||
this.chatThread = this.chatThread with
|
||||
{
|
||||
SystemPrompt = $"""
|
||||
{SystemPrompts.DEFAULT}
|
||||
|
||||
{this.currentProfile.ToSystemPrompt()}
|
||||
"""
|
||||
};
|
||||
}
|
||||
|
||||
private async Task SendMessage()
|
||||
{
|
||||
@ -135,7 +153,11 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = threadName,
|
||||
Seed = this.RNG.Next(),
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
SystemPrompt = $"""
|
||||
{SystemPrompts.DEFAULT}
|
||||
|
||||
{this.currentProfile.ToSystemPrompt()}
|
||||
""",
|
||||
Blocks = [],
|
||||
};
|
||||
}
|
||||
@ -320,7 +342,11 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Empty,
|
||||
Seed = this.RNG.Next(),
|
||||
SystemPrompt = "You are a helpful assistant!",
|
||||
SystemPrompt = $"""
|
||||
{SystemPrompts.DEFAULT}
|
||||
|
||||
{this.currentProfile.ToSystemPrompt()}
|
||||
""",
|
||||
Blocks = [],
|
||||
};
|
||||
}
|
||||
|
@ -11,6 +11,12 @@
|
||||
<MudExpansionPanels Class="mb-3" MultiExpansion="@false">
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Layers" HeaderText="Configure Providers">
|
||||
<MudText Typo="Typo.h4" Class="mb-3">Configured Providers</MudText>
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
What we call a provider is the combination of an LLM provider such as OpenAI and a model like GPT-4o.
|
||||
You can configure as many providers as you want. This way, you can use the appropriate model for each
|
||||
task. As an LLM provider, you can also choose local providers. However, to use this app, you must
|
||||
configure at least one provider.
|
||||
</MudJustifiedText>
|
||||
<MudTable Items="@this.SettingsManager.ConfigurationData.Providers" Class="border-dashed border rounded-lg">
|
||||
<ColGroup>
|
||||
<col style="width: 3em;"/>
|
||||
@ -68,12 +74,62 @@
|
||||
</MudButton>
|
||||
</ExpansionPanel>
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Person4" HeaderText="Configure Profiles">
|
||||
<MudText Typo="Typo.h4" Class="mb-3">Your Profiles</MudText>
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
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.
|
||||
</MudJustifiedText>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
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.
|
||||
</MudJustifiedText>
|
||||
<MudTable Items="@this.SettingsManager.ConfigurationData.Profiles" Class="border-dashed border rounded-lg">
|
||||
<ColGroup>
|
||||
<col style="width: 3em;"/>
|
||||
<col/>
|
||||
<col style="width: 40em;"/>
|
||||
</ColGroup>
|
||||
<HeaderContent>
|
||||
<MudTh>#</MudTh>
|
||||
<MudTh>Profile Name</MudTh>
|
||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Num</MudTd>
|
||||
<MudTd>@context.Name</MudTd>
|
||||
<MudTd Style="text-align: left;">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditProfile(context)">
|
||||
Edit
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteProfile(context)">
|
||||
Delete
|
||||
</MudButton>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@if(this.SettingsManager.ConfigurationData.Profiles.Count == 0)
|
||||
{
|
||||
<MudText Typo="Typo.h6" Class="mt-3">No profiles configured yet.</MudText>
|
||||
}
|
||||
|
||||
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddProfile">
|
||||
Add Profile
|
||||
</MudButton>
|
||||
</ExpansionPanel>
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Apps" HeaderText="App Options">
|
||||
<ConfigurationOption OptionDescription="Save energy?" LabelOn="Energy saving is enabled" LabelOff="Energy saving is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.IsSavingEnergy = updatedState)" OptionHelp="When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available."/>
|
||||
<ConfigurationOption OptionDescription="Enable spellchecking?" LabelOn="Spellchecking is enabled" LabelOff="Spellchecking is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections." />
|
||||
<ConfigurationSelect OptionDescription="Check for updates" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="How often should we check for app updates?"/>
|
||||
<ConfigurationSelect OptionDescription="Navigation bar behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="Select the desired behavior for the navigation bar."/>
|
||||
<ConfigurationProviderSelection Data="@this.availableProviders" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => "Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence.")"/>
|
||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence."/>
|
||||
</ExpansionPanel>
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Chat" HeaderText="Chat Options">
|
||||
@ -82,6 +138,7 @@
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<ConfigurationOption OptionDescription="Preselect chat options?" LabelOn="Chat options are preselected" LabelOff="No chat options are preselected" State="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Chat.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect chat options. This is might be useful when you prefer a specific provider."/>
|
||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider = selectedValue)"/>
|
||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for chats?"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
|
||||
@ -125,6 +182,7 @@
|
||||
<ConfigurationText OptionDescription="Preselect another programming language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" Icon="@Icons.Material.Filled.Code" Text="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage = updatedText)"/>
|
||||
}
|
||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider = selectedValue)"/>
|
||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
|
||||
@ -167,13 +225,13 @@
|
||||
<ConfigurationOption OptionDescription="Preselect whether participants needs to arrive and depart" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" LabelOn="Participants need to arrive and depart" LabelOff="Participants do not need to arrive and depart" State="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectArriveAndDepart)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Agenda.PreselectArriveAndDepart = updatedState)" />
|
||||
<ConfigurationSlider T="int" OptionDescription="Preselect the approx. lunch time" Min="30" Max="120" Step="5" Unit="minutes" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Value="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectLunchTime)" ValueUpdate="@(updatedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectLunchTime = updatedValue)" />
|
||||
<ConfigurationSlider T="int" OptionDescription="Preselect the approx. break time" Min="10" Max="60" Step="5" Unit="minutes" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Value="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectBreakTime)" ValueUpdate="@(updatedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectBreakTime = updatedValue)" />
|
||||
|
||||
<ConfigurationSelect OptionDescription="Preselect the agenda language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesTranslationData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedTargetLanguage = selectedValue)" OptionHelp="Which agenda language should be preselected?"/>
|
||||
@if (this.SettingsManager.ConfigurationData.Agenda.PreselectedTargetLanguage is CommonLanguages.OTHER)
|
||||
{
|
||||
<ConfigurationText OptionDescription="Preselect another agenda language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage = updatedText)"/>
|
||||
}
|
||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider = selectedValue)"/>
|
||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
|
||||
@ -215,6 +273,7 @@
|
||||
}
|
||||
<ConfigurationSelect OptionDescription="Preselect a writing style" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle)" Data="@ConfigurationSelectDataFactory.GetWritingStyles4EMailData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle = selectedValue)" OptionHelp="Which writing style should be preselected?"/>
|
||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider = selectedValue)"/>
|
||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
|
||||
@ -225,6 +284,7 @@
|
||||
<ConfigurationOption OptionDescription="Preselect the web content reader?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions || this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" LabelOn="Web content reader is preselected" LabelOff="Web content reader is not preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader = updatedState)" OptionHelp="When enabled, the web content reader is preselected. This is might be useful when you prefer to load legal content from the web very often."/>
|
||||
<ConfigurationOption OptionDescription="Preselect the content cleaner agent?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions || this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" LabelOn="Content cleaner agent is preselected" LabelOff="Content cleaner agent is not preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent = updatedState)" OptionHelp="When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the legal content before translating it."/>
|
||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider = selectedValue)"/>
|
||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
|
||||
|
@ -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<ProfileDialog>
|
||||
{
|
||||
{ x => x.IsEditing, false },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ProfileDialog>("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<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
private async Task EditProfile(Profile profile)
|
||||
{
|
||||
var dialogParameters = new DialogParameters<ProfileDialog>
|
||||
{
|
||||
{ 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<ProfileDialog>("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<bool>(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<ConfirmDialog>("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<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IMessageBusReceiver
|
||||
|
@ -130,4 +130,10 @@ public static class ConfigurationSelectDataFactory
|
||||
foreach (var voice in Enum.GetValues<SentenceStructure>())
|
||||
yield return new(voice.Name(), voice);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<string>> GetProfilesData(IEnumerable<Profile> profiles)
|
||||
{
|
||||
foreach (var profile in profiles.GetAllProfiles())
|
||||
yield return new(profile.Name, profile.Id);
|
||||
}
|
||||
}
|
@ -15,12 +15,22 @@ public sealed class Data
|
||||
/// List of configured providers.
|
||||
/// </summary>
|
||||
public List<Provider> Providers { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// List of configured profiles.
|
||||
/// </summary>
|
||||
public List<Profile> Profiles { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The next provider number to use.
|
||||
/// </summary>
|
||||
public uint NextProviderNum { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The next profile number to use.
|
||||
/// </summary>
|
||||
public uint NextProfileNum { get; set; } = 1;
|
||||
|
||||
public DataApp App { get; init; } = new();
|
||||
|
||||
public DataChat Chat { get; init; } = new();
|
||||
|
@ -55,4 +55,9 @@ public sealed class DataAgenda
|
||||
/// Preselect a agenda provider?
|
||||
/// </summary>
|
||||
public string PreselectedProvider { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Preselect a profile?
|
||||
/// </summary>
|
||||
public string PreselectedProfile { get; set; } = string.Empty;
|
||||
}
|
@ -27,4 +27,9 @@ public sealed class DataApp
|
||||
/// Should we preselect a provider for the entire app?
|
||||
/// </summary>
|
||||
public string PreselectedProvider { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Should we preselect a profile for the entire app?
|
||||
/// </summary>
|
||||
public string PreselectedProfile { get; set; } = string.Empty;
|
||||
}
|
@ -16,6 +16,11 @@ public sealed class DataChat
|
||||
/// Should we preselect a provider for the chat?
|
||||
/// </summary>
|
||||
public string PreselectedProvider { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Preselect a profile?
|
||||
/// </summary>
|
||||
public string PreselectedProfile { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Should we show the latest message after loading? When false, we show the first (aka oldest) message.
|
||||
|
@ -28,4 +28,9 @@ public sealed class DataCoding
|
||||
/// Which coding provider should be preselected?
|
||||
/// </summary>
|
||||
public string PreselectedProvider { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Preselect a profile?
|
||||
/// </summary>
|
||||
public string PreselectedProfile { get; set; } = string.Empty;
|
||||
}
|
@ -28,6 +28,11 @@ public sealed class DataEMail
|
||||
/// Preselect a provider?
|
||||
/// </summary>
|
||||
public string PreselectedProvider { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Preselect a profile?
|
||||
/// </summary>
|
||||
public string PreselectedProfile { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Preselect a greeting phrase?
|
||||
|
@ -26,4 +26,9 @@ public class DataLegalCheck
|
||||
/// The preselected translator provider.
|
||||
/// </summary>
|
||||
public string PreselectedProvider { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Preselect a profile?
|
||||
/// </summary>
|
||||
public string PreselectedProfile { get; set; } = string.Empty;
|
||||
}
|
59
app/MindWork AI Studio/Settings/Profile.cs
Normal file
59
app/MindWork AI Studio/Settings/Profile.cs
Normal file
@ -0,0 +1,59 @@
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public readonly record struct Profile(uint Num, string Id, string Name, string NeedToKnow, string Actions)
|
||||
{
|
||||
public static readonly Profile NO_PROFILE = new()
|
||||
{
|
||||
Name = "Use no profile",
|
||||
NeedToKnow = string.Empty,
|
||||
Actions = string.Empty,
|
||||
Id = Guid.Empty.ToString(),
|
||||
Num = uint.MaxValue,
|
||||
};
|
||||
|
||||
#region Overrides of ValueType
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents the profile in a human-readable format.
|
||||
/// </summary>
|
||||
/// <returns>A string that represents the profile in a human-readable format.</returns>
|
||||
public override string ToString() => this.Name;
|
||||
|
||||
#endregion
|
||||
|
||||
public string ToSystemPrompt()
|
||||
{
|
||||
if(this.Num == uint.MaxValue)
|
||||
return string.Empty;
|
||||
|
||||
var needToKnow =
|
||||
$"""
|
||||
What should you know about the user?
|
||||
|
||||
```
|
||||
{this.NeedToKnow}
|
||||
```
|
||||
""";
|
||||
|
||||
var actions =
|
||||
$"""
|
||||
The user wants you to consider the following things.
|
||||
|
||||
```
|
||||
{this.Actions}
|
||||
```
|
||||
""";
|
||||
|
||||
if (string.IsNullOrWhiteSpace(this.NeedToKnow))
|
||||
return actions;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(this.Actions))
|
||||
return needToKnow;
|
||||
|
||||
return $"""
|
||||
{needToKnow}
|
||||
|
||||
{actions}
|
||||
""";
|
||||
}
|
||||
}
|
@ -138,4 +138,24 @@ public sealed class SettingsManager(ILogger<SettingsManager> logger)
|
||||
|
||||
return this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedProvider);
|
||||
}
|
||||
|
||||
public Profile GetPreselectedProfile(Tools.Components component)
|
||||
{
|
||||
var preselection = component switch
|
||||
{
|
||||
Tools.Components.CHAT => this.ConfigurationData.Chat.PreselectOptions ? this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.Chat.PreselectedProfile) : default,
|
||||
Tools.Components.AGENDA_ASSISTANT => this.ConfigurationData.Agenda.PreselectOptions ? this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.Agenda.PreselectedProfile) : default,
|
||||
Tools.Components.CODING_ASSISTANT => this.ConfigurationData.Coding.PreselectOptions ? this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.Coding.PreselectedProfile) : default,
|
||||
Tools.Components.EMAIL_ASSISTANT => this.ConfigurationData.EMail.PreselectOptions ? this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.EMail.PreselectedProfile) : default,
|
||||
Tools.Components.LEGAL_CHECK_ASSISTANT => this.ConfigurationData.LegalCheck.PreselectOptions ? this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.LegalCheck.PreselectedProfile) : default,
|
||||
|
||||
_ => default,
|
||||
};
|
||||
|
||||
if (preselection != default)
|
||||
return preselection;
|
||||
|
||||
preselection = this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedProfile);
|
||||
return preselection != default ? preselection : Profile.NO_PROFILE;
|
||||
}
|
||||
}
|
13
app/MindWork AI Studio/Tools/ProfileExtensions.cs
Normal file
13
app/MindWork AI Studio/Tools/ProfileExtensions.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public static class ProfileExtensions
|
||||
{
|
||||
public static IEnumerable<Profile> GetAllProfiles(this IEnumerable<Profile> profiles)
|
||||
{
|
||||
yield return Profile.NO_PROFILE;
|
||||
foreach (var profile in profiles)
|
||||
yield return profile;
|
||||
}
|
||||
}
|
10
app/MindWork AI Studio/wwwroot/changelog/v0.9.7.md
Normal file
10
app/MindWork AI Studio/wwwroot/changelog/v0.9.7.md
Normal file
@ -0,0 +1,10 @@
|
||||
# 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 profiles to the chat interface. You can now select a profile for each chat or even change the profile during a chat.
|
||||
- Added profiles to some assistants. It makes no sense to have profiles for, e.g., translation, etc.
|
||||
- Added the possibility to preselect any of your profiles as the default profile for the entire app or configure individual profiles for assistants and chats.
|
||||
- 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.
|
||||
- Improved hyphenation for continuous text so that the rules of the respective language are taken into account where possible.
|
||||
- Improved the rules for provider names: unnecessary restrictions from earlier versions have been removed. You can now use emojis in your provider names when you like.
|
Loading…
Reference in New Issue
Block a user