mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-08 08:49:39 +00:00
Added a read-only view for managed profiles and chat templates (#813)
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Determine run mode (push) Waiting to run
This commit is contained in:
1 parent
fc7197ec93
commit
e04879fd7f
16 files changed
+454
-240
No files matched your search
@@ -10,7 +10,7 @@
|
||||
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
|
||||
@T("The name of the chat template is mandatory. Each chat template must have a unique name.")
|
||||
</MudJustifiedText>
|
||||
|
||||
|
||||
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
|
||||
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
||||
<MudTextField
|
||||
@@ -26,9 +26,10 @@
|
||||
AdornmentColor="Color.Info"
|
||||
Validation="@this.ValidateName"
|
||||
Variant="Variant.Outlined"
|
||||
ReadOnly="@this.IsReadOnly"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
/>
|
||||
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mb-3 mt-3">
|
||||
@T("System Prompt")
|
||||
</MudText>
|
||||
@@ -47,16 +48,17 @@
|
||||
Class="mb-3"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Tell the AI your system prompt.")"
|
||||
ReadOnly="@this.IsReadOnly"
|
||||
/>
|
||||
|
||||
|
||||
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
|
||||
@T("Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats.")
|
||||
</MudJustifiedText>
|
||||
<MudButton Class="mb-3" Color="Color.Default" OnClick="@this.UseDefaultSystemPrompt" StartIcon="@Icons.Material.Filled.ListAlt" Variant="Variant.Filled">
|
||||
<MudButton Class="mb-3" Color="Color.Default" OnClick="@this.UseDefaultSystemPrompt" StartIcon="@Icons.Material.Filled.ListAlt" Variant="Variant.Filled" Disabled="@this.IsReadOnly">
|
||||
@T("Use the default system prompt")
|
||||
</MudButton>
|
||||
<ReadFileContent Text="@T("Load system prompt from file")" @bind-FileContent="@this.DataSystemPrompt"/>
|
||||
|
||||
<ReadFileContent Text="@T("Load system prompt from file")" @bind-FileContent="@this.DataSystemPrompt" Disabled="@this.IsReadOnly"/>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mb-3 mt-6">
|
||||
@T("Predefined User Input")
|
||||
</MudText>
|
||||
@@ -77,6 +79,7 @@
|
||||
Class="mb-3"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Tell the AI your predefined user input.")"
|
||||
ReadOnly="@this.IsReadOnly"
|
||||
/>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mb-3 mt-6">
|
||||
@@ -92,6 +95,7 @@
|
||||
UseSmallForm="false"
|
||||
CatchAllDocuments="true"
|
||||
ValidateMediaFileTypes="false"
|
||||
Disabled="@this.IsReadOnly"
|
||||
/>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mb-3 mt-6">
|
||||
@@ -100,8 +104,8 @@
|
||||
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
|
||||
@T("Using some chat templates in tandem with profiles might cause issues. Therefore, you might prohibit the usage of profiles here.")
|
||||
</MudJustifiedText>
|
||||
<MudTextSwitch @bind-Value="@this.AllowProfileUsage" Color="Color.Primary" Label="@T("Allow the use of profiles together with this chat template?")" LabelOn="@T("Yes, allow profiles when using this template")" LabelOff="@T("No, prohibit profile use for this template")" />
|
||||
|
||||
<MudTextSwitch @bind-Value="@this.AllowProfileUsage" Color="Color.Primary" Label="@T("Allow the use of profiles together with this chat template?")" LabelOn="@T("Yes, allow profiles when using this template")" LabelOff="@T("No, prohibit profile use for this template")" Disabled="@this.IsReadOnly" />
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mb-3 mt-6">
|
||||
@T("Example Conversation")
|
||||
</MudText>
|
||||
@@ -129,18 +133,18 @@
|
||||
case ContentText textContent:
|
||||
<MudTextField AutoGrow="true" Value="@textContent.Text" Placeholder="@T("Enter a message")" ReadOnly="true" Variant="Variant.Text" Validation="@this.ValidateExampleTextMessage"/>
|
||||
break;
|
||||
|
||||
|
||||
case ContentImage { SourceType: ContentImageSource.URL or ContentImageSource.LOCAL_PATH } imageContent:
|
||||
<MudImage Src="@imageContent.Source" Alt="@T("Image content")" Fluid="true" />
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
@T("Unsupported content type")
|
||||
break;
|
||||
}
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
@if (!this.isInlineEditOnGoing)
|
||||
@if (!this.isInlineEditOnGoing && !this.IsReadOnly)
|
||||
{
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("Add a new message below")">
|
||||
@@ -153,22 +157,29 @@
|
||||
</RowTemplate>
|
||||
<RowEditingTemplate>
|
||||
<MudTd>
|
||||
<MudSelect Label="@T("Role")" @bind-Value="@context.Role" Required="true">
|
||||
@foreach (var role in ChatRoles.ChatTemplateRoles())
|
||||
{
|
||||
<MudSelectItem Value="@role">
|
||||
@role.ToChatTemplateName()
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
@if (this.IsReadOnly)
|
||||
{
|
||||
@context.Role.ToChatTemplateName()
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudSelect Label="@T("Role")" @bind-Value="@context.Role" Required="true">
|
||||
@foreach (var role in ChatRoles.ChatTemplateRoles())
|
||||
{
|
||||
<MudSelectItem Value="@role">
|
||||
@role.ToChatTemplateName()
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
}
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
@switch(context.Content)
|
||||
{
|
||||
case ContentText textContent:
|
||||
<MudTextField AutoGrow="true" @bind-Value="@textContent.Text" Label="@T("The message")" Required="true" Immediate="true" Placeholder="@T("Enter a message")"/>
|
||||
<MudTextField AutoGrow="true" @bind-Value="@textContent.Text" Label="@T("The message")" Required="true" Immediate="true" Placeholder="@T("Enter a message")" ReadOnly="@this.IsReadOnly"/>
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
<MudText Typo="Typo.body2">
|
||||
@T("Only text content is supported in the editing mode yet.")
|
||||
@@ -182,8 +193,8 @@
|
||||
</PagerContent>
|
||||
</MudTable>
|
||||
</MudForm>
|
||||
|
||||
@if (!this.isInlineEditOnGoing)
|
||||
|
||||
@if (!this.isInlineEditOnGoing && !this.IsReadOnly)
|
||||
{
|
||||
<MudButton Class="mb-6" Color="Color.Primary" OnClick="@this.AddMessageToEnd" StartIcon="@Icons.Material.Filled.Add" Variant="Variant.Filled">
|
||||
@T("Add a message")
|
||||
@@ -193,22 +204,31 @@
|
||||
<Issues IssuesData="@this.dataIssues"/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||
@T("Cancel")
|
||||
</MudButton>
|
||||
|
||||
@if (!this.isInlineEditOnGoing)
|
||||
@if (this.IsReadOnly)
|
||||
{
|
||||
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@if (this.IsEditing)
|
||||
{
|
||||
@T("Update")
|
||||
}
|
||||
else
|
||||
{
|
||||
@T("Add")
|
||||
}
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||
@T("Close")
|
||||
</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||
@T("Cancel")
|
||||
</MudButton>
|
||||
|
||||
@if (!this.isInlineEditOnGoing)
|
||||
{
|
||||
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@if (this.IsEditing)
|
||||
{
|
||||
@T("Update")
|
||||
}
|
||||
else
|
||||
{
|
||||
@T("Add")
|
||||
}
|
||||
</MudButton>
|
||||
}
|
||||
}
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -16,37 +16,40 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public uint DataNum { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The chat template's ID.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string DataId { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The chat template name chosen by the user.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string DataName { get; set; } = string.Empty;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// What is the system prompt?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string DataSystemPrompt { get; set; } = string.Empty;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// What is the predefined user prompt?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string PredefinedUserPrompt { get; set; } = string.Empty;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Should the dialog be in editing mode?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool IsEditing { get; init; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public bool IsReadOnly { get; init; }
|
||||
|
||||
[Parameter]
|
||||
public IReadOnlyCollection<ContentBlock> ExampleConversation { get; init; } = [];
|
||||
|
||||
@@ -55,23 +58,23 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
|
||||
[Parameter]
|
||||
public bool AllowProfileUsage { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
|
||||
[Parameter]
|
||||
public bool CreateFromExistingChatThread { get; set; }
|
||||
|
||||
[Parameter]
|
||||
|
||||
[Parameter]
|
||||
public ChatThread? ExistingChatThread { get; set; }
|
||||
|
||||
|
||||
[Inject]
|
||||
private ILogger<ChatTemplateDialog> Logger { get; init; } = null!;
|
||||
|
||||
|
||||
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The list of used chat template names. We need this to check for uniqueness.
|
||||
/// </summary>
|
||||
private List<string> UsedNames { get; set; } = [];
|
||||
|
||||
|
||||
private bool dataIsValid;
|
||||
private List<ContentBlock> dataExampleConversation = [];
|
||||
private HashSet<FileAttachment> fileAttachments = [];
|
||||
@@ -80,20 +83,20 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
private bool isInlineEditOnGoing;
|
||||
|
||||
private ContentBlock? messageEntryBeforeEdit;
|
||||
|
||||
|
||||
// We get the form reference from Blazor code to validate it manually:
|
||||
private MudForm form = null!;
|
||||
|
||||
|
||||
#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.ChatTemplates.Select(x => x.Name.ToLowerInvariant()).ToList();
|
||||
|
||||
|
||||
// When editing, we need to load the data:
|
||||
if(this.IsEditing)
|
||||
{
|
||||
@@ -108,7 +111,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
this.dataExampleConversation = this.ExistingChatThread.Blocks.Select(n => n.DeepClone(true)).ToList();
|
||||
this.DataName = this.ExistingChatThread.Name;
|
||||
}
|
||||
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
@@ -118,7 +121,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -128,28 +131,34 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
{
|
||||
Num = this.DataNum,
|
||||
Id = this.DataId,
|
||||
|
||||
|
||||
Name = this.DataName,
|
||||
SystemPrompt = this.DataSystemPrompt,
|
||||
PredefinedUserPrompt = this.PredefinedUserPrompt,
|
||||
ExampleConversation = this.dataExampleConversation,
|
||||
FileAttachments = this.fileAttachments.Select(attachment => attachment.Normalize()).ToList(),
|
||||
AllowProfileUsage = this.AllowProfileUsage,
|
||||
|
||||
|
||||
EnterpriseConfigurationPluginId = Guid.Empty,
|
||||
IsEnterpriseConfiguration = false,
|
||||
};
|
||||
|
||||
private void RemoveMessage(ContentBlock item)
|
||||
{
|
||||
if (this.IsReadOnly)
|
||||
return;
|
||||
|
||||
this.dataExampleConversation.Remove(item);
|
||||
}
|
||||
|
||||
private void AddMessageToEnd()
|
||||
{
|
||||
if (this.IsReadOnly)
|
||||
return;
|
||||
|
||||
var newEntry = new ContentBlock
|
||||
{
|
||||
Role = this.dataExampleConversation.Count is 0 ? ChatRole.USER : this.dataExampleConversation.Last().Role.SelectNextRoleForTemplate(),
|
||||
Role = this.dataExampleConversation.Count is 0 ? ChatRole.USER : this.dataExampleConversation.Last().Role.SelectNextRoleForTemplate(),
|
||||
Content = new ContentText(),
|
||||
ContentType = ContentType.TEXT,
|
||||
HideFromUser = true,
|
||||
@@ -161,6 +170,9 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
|
||||
private void AddMessageBelow(ContentBlock currentItem)
|
||||
{
|
||||
if (this.IsReadOnly)
|
||||
return;
|
||||
|
||||
var insertedEntry = new ContentBlock
|
||||
{
|
||||
Role = this.dataExampleConversation.Count is 0 ? ChatRole.USER : this.dataExampleConversation.Last().Role.SelectNextRoleForTemplate(),
|
||||
@@ -169,7 +181,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
HideFromUser = true,
|
||||
Time = DateTimeOffset.Now,
|
||||
};
|
||||
|
||||
|
||||
// The rest of the method remains the same:
|
||||
var index = this.dataExampleConversation.IndexOf(currentItem);
|
||||
if (index >= 0)
|
||||
@@ -177,71 +189,83 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
else
|
||||
this.dataExampleConversation.Add(insertedEntry);
|
||||
}
|
||||
|
||||
|
||||
private void BackupItem(object? element)
|
||||
{
|
||||
if (this.IsReadOnly)
|
||||
return;
|
||||
|
||||
this.isInlineEditOnGoing = true;
|
||||
this.messageEntryBeforeEdit = element switch
|
||||
{
|
||||
ContentBlock block => block.DeepClone(),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
private void ResetItem(object? element)
|
||||
{
|
||||
if (this.IsReadOnly)
|
||||
return;
|
||||
|
||||
this.isInlineEditOnGoing = false;
|
||||
switch (element)
|
||||
{
|
||||
case ContentBlock block:
|
||||
if (this.messageEntryBeforeEdit is null)
|
||||
return; // No backup to restore from
|
||||
|
||||
|
||||
block.Content = this.messageEntryBeforeEdit.Content?.DeepClone();
|
||||
block.Role = this.messageEntryBeforeEdit.Role;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
private void CommitInlineEdit(object? element)
|
||||
{
|
||||
if (this.IsReadOnly)
|
||||
return;
|
||||
|
||||
this.isInlineEditOnGoing = false;
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
|
||||
private async Task Store()
|
||||
{
|
||||
if (this.IsReadOnly)
|
||||
return;
|
||||
|
||||
await this.form.Validate();
|
||||
|
||||
|
||||
// When the data is not valid, we don't store it:
|
||||
if (!this.dataIsValid)
|
||||
return;
|
||||
|
||||
|
||||
// When an inline edit is ongoing, we cannot store the data:
|
||||
if (this.isInlineEditOnGoing)
|
||||
return;
|
||||
|
||||
|
||||
// Use the data model to store the chat template.
|
||||
// We just return this data to the parent component:
|
||||
var addedChatTemplateSettings = this.CreateChatTemplateSettings();
|
||||
|
||||
|
||||
if(this.IsEditing)
|
||||
this.Logger.LogInformation($"Edited chat template '{addedChatTemplateSettings.Name}'.");
|
||||
else
|
||||
this.Logger.LogInformation($"Created chat template '{addedChatTemplateSettings.Name}'.");
|
||||
|
||||
|
||||
this.MudDialog.Close(DialogResult.Ok(addedChatTemplateSettings));
|
||||
}
|
||||
|
||||
|
||||
private string? ValidateExampleTextMessage(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
return T("Please enter a message for the example conversation.");
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -249,20 +273,23 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return T("Please enter a name for the chat template.");
|
||||
|
||||
|
||||
if (name.Length > 40)
|
||||
return T("The chat template 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 T("The chat template name must be unique; the chosen name is already in use.");
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void UseDefaultSystemPrompt()
|
||||
{
|
||||
if (this.IsReadOnly)
|
||||
return;
|
||||
|
||||
this.DataSystemPrompt = SystemPrompts.DEFAULT;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
AdornmentColor="Color.Info"
|
||||
Validation="@this.ValidateName"
|
||||
Variant="Variant.Outlined"
|
||||
ReadOnly="@this.IsReadOnly"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
/>
|
||||
|
||||
@@ -44,8 +45,9 @@
|
||||
MaxLines="12"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Tell the AI something about yourself. What is your profession? How experienced are you in this profession? Which technologies do you like?")"
|
||||
ReadOnly="@this.IsReadOnly"
|
||||
/>
|
||||
<ReadFileContent @bind-FileContent="@this.DataNeedToKnow"/>
|
||||
<ReadFileContent @bind-FileContent="@this.DataNeedToKnow" Disabled="@this.IsReadOnly"/>
|
||||
|
||||
<MudTextField
|
||||
T="string"
|
||||
@@ -62,8 +64,9 @@
|
||||
Class="mt-10"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("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.")"
|
||||
ReadOnly="@this.IsReadOnly"
|
||||
/>
|
||||
<ReadFileContent @bind-FileContent="@this.DataActions"/>
|
||||
<ReadFileContent @bind-FileContent="@this.DataActions" Disabled="@this.IsReadOnly"/>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body2" Class="mb-3 mt-3">
|
||||
@T("Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task.")
|
||||
@@ -73,18 +76,27 @@
|
||||
<Issues IssuesData="@this.dataIssues"/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||
@T("Cancel")
|
||||
</MudButton>
|
||||
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@if(this.IsEditing)
|
||||
{
|
||||
@T("Update")
|
||||
}
|
||||
else
|
||||
{
|
||||
@T("Add")
|
||||
}
|
||||
</MudButton>
|
||||
@if (this.IsReadOnly)
|
||||
{
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||
@T("Close")
|
||||
</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||
@T("Cancel")
|
||||
</MudButton>
|
||||
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@if(this.IsEditing)
|
||||
{
|
||||
@T("Update")
|
||||
}
|
||||
else
|
||||
{
|
||||
@T("Add")
|
||||
}
|
||||
</MudButton>
|
||||
}
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -15,19 +15,19 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
/// </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>
|
||||
@@ -39,27 +39,30 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string DataActions { get; set; } = string.Empty;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Should the dialog be in editing mode?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool IsEditing { get; init; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public bool IsReadOnly { get; init; }
|
||||
|
||||
[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!;
|
||||
|
||||
@@ -70,7 +73,7 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
Name = this.DataName,
|
||||
NeedToKnow = this.DataNeedToKnow,
|
||||
Actions = this.DataActions,
|
||||
|
||||
|
||||
EnterpriseConfigurationPluginId = Guid.Empty,
|
||||
IsEnterpriseConfiguration = false,
|
||||
};
|
||||
@@ -81,16 +84,16 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
|
||||
@@ -100,37 +103,40 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
// 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()
|
||||
{
|
||||
if (this.IsReadOnly)
|
||||
return;
|
||||
|
||||
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 T("Please enter what the LLM should know about you and/or what actions it should take.");
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -138,7 +144,7 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions))
|
||||
return T("Please enter what the LLM should know about you and/or what actions it should take.");
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -146,15 +152,15 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return T("Please enter a profile name.");
|
||||
|
||||
|
||||
if (name.Length > 40)
|
||||
return T("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 T("The profile name must be unique; the chosen name is already in use.");
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,9 +33,14 @@
|
||||
<MudTd>
|
||||
@if (context.IsEnterpriseConfiguration)
|
||||
{
|
||||
<MudTooltip Text="@T("This template is managed by your organization.")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
|
||||
</MudTooltip>
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("This template is managed by your organization.")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("View")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Visibility" OnClick="@(() => this.ViewChatTemplate(context))"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -6,24 +6,24 @@ namespace AIStudio.Dialogs.Settings;
|
||||
|
||||
public partial class SettingsDialogChatTemplate : SettingsDialogBase
|
||||
{
|
||||
[Parameter]
|
||||
[Parameter]
|
||||
public bool CreateTemplateFromExistingChatThread { get; set; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public ChatThread? ExistingChatThread { get; set; }
|
||||
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
if (this.CreateTemplateFromExistingChatThread)
|
||||
if (this.CreateTemplateFromExistingChatThread)
|
||||
await this.AddChatTemplate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private async Task AddChatTemplate()
|
||||
{
|
||||
var dialogParameters = new DialogParameters<ChatTemplateDialog>
|
||||
@@ -41,21 +41,21 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
|
||||
var dialogResult = await dialogReference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
return;
|
||||
|
||||
|
||||
var addedChatTemplate = (ChatTemplate)dialogResult.Data!;
|
||||
addedChatTemplate = addedChatTemplate with { Num = this.SettingsManager.ConfigurationData.NextChatTemplateNum++ };
|
||||
|
||||
|
||||
this.SettingsManager.ConfigurationData.ChatTemplates.Add(addedChatTemplate);
|
||||
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
|
||||
private async Task EditChatTemplate(ChatTemplate chatTemplate)
|
||||
{
|
||||
if (chatTemplate == ChatTemplate.NO_CHAT_TEMPLATE || chatTemplate.IsEnterpriseConfiguration)
|
||||
return;
|
||||
|
||||
|
||||
var dialogParameters = new DialogParameters<ChatTemplateDialog>
|
||||
{
|
||||
{ x => x.DataNum, chatTemplate.Num },
|
||||
@@ -68,34 +68,53 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
|
||||
{ x => x.FileAttachments, chatTemplate.FileAttachments },
|
||||
{ x => x.AllowProfileUsage, chatTemplate.AllowProfileUsage },
|
||||
};
|
||||
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ChatTemplateDialog>(T("Edit Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
var dialogResult = await dialogReference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
return;
|
||||
|
||||
|
||||
var editedChatTemplate = (ChatTemplate)dialogResult.Data!;
|
||||
this.SettingsManager.ConfigurationData.ChatTemplates[this.SettingsManager.ConfigurationData.ChatTemplates.IndexOf(chatTemplate)] = editedChatTemplate;
|
||||
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
private async Task ViewChatTemplate(ChatTemplate chatTemplate)
|
||||
{
|
||||
var dialogParameters = new DialogParameters<ChatTemplateDialog>
|
||||
{
|
||||
{ x => x.DataNum, chatTemplate.Num },
|
||||
{ x => x.DataId, chatTemplate.Id },
|
||||
{ x => x.DataName, chatTemplate.Name },
|
||||
{ x => x.DataSystemPrompt, chatTemplate.SystemPrompt },
|
||||
{ x => x.PredefinedUserPrompt, chatTemplate.PredefinedUserPrompt },
|
||||
{ x => x.IsEditing, true },
|
||||
{ x => x.IsReadOnly, true },
|
||||
{ x => x.ExampleConversation, chatTemplate.ExampleConversation },
|
||||
{ x => x.FileAttachments, chatTemplate.FileAttachments },
|
||||
{ x => x.AllowProfileUsage, chatTemplate.AllowProfileUsage },
|
||||
};
|
||||
|
||||
await this.DialogService.ShowAsync<ChatTemplateDialog>(T("View Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
}
|
||||
|
||||
private async Task DeleteChatTemplate(ChatTemplate chatTemplate)
|
||||
{
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the chat template '{0}'?"), chatTemplate.Name) },
|
||||
};
|
||||
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
var dialogResult = await dialogReference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
return;
|
||||
|
||||
|
||||
this.SettingsManager.ConfigurationData.ChatTemplates.Remove(chatTemplate);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
|
||||
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,14 @@
|
||||
<MudTd>
|
||||
@if (context.IsEnterpriseConfiguration)
|
||||
{
|
||||
<MudTooltip Text="@T("This profile is managed by your organization.")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
|
||||
</MudTooltip>
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("This profile is managed by your organization.")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("View")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Visibility" OnClick="() => this.ViewProfile(context)"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -10,21 +10,21 @@ public partial class SettingsDialogProfiles : SettingsDialogBase
|
||||
{
|
||||
{ x => x.IsEditing, false },
|
||||
};
|
||||
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ProfileDialog>(T("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>
|
||||
@@ -36,19 +36,35 @@ public partial class SettingsDialogProfiles : SettingsDialogBase
|
||||
{ x => x.DataActions, profile.Actions },
|
||||
{ x => x.IsEditing, true },
|
||||
};
|
||||
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ProfileDialog>(T("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 ViewProfile(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 },
|
||||
{ x => x.IsReadOnly, true },
|
||||
};
|
||||
|
||||
await this.DialogService.ShowAsync<ProfileDialog>(T("View Profile"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
}
|
||||
|
||||
private async Task ExportProfile(Profile profile)
|
||||
{
|
||||
if (!this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
|
||||
@@ -68,15 +84,15 @@ public partial class SettingsDialogProfiles : SettingsDialogBase
|
||||
{
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the profile '{0}'?"), profile.Name) },
|
||||
};
|
||||
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user