mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-06 05:29:40 +00:00
Finalized chat templates (#475)
This commit is contained in:
1 parent
df062ae6e6
commit
9e207974e9
23 files changed
+830
-241
No files matched your search
@@ -1,11 +1,8 @@
|
||||
@using AIStudio.Chat
|
||||
@using MudBlazor.Extensions
|
||||
@inherits MSGComponentBase
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
|
||||
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
|
||||
@T("Create your custom chat template to tailor the LLM's behavior for specific tasks or domains. Define a custom system prompt and provide an example conversation to design an AI experience perfectly suited to your requirements.")
|
||||
</MudJustifiedText>
|
||||
@@ -31,34 +28,40 @@
|
||||
Variant="Variant.Outlined"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
/>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mb-3 mt-3">
|
||||
@T("System Prompt")
|
||||
</MudText>
|
||||
|
||||
<MudTextField
|
||||
T="string"
|
||||
@bind-Text="@this.DataSystemPrompt"
|
||||
Validation="@this.ValidateSystemPrompt"
|
||||
AdornmentIcon="@Icons.Material.Filled.ListAlt"
|
||||
Adornment="Adornment.Start"
|
||||
Immediate="@true"
|
||||
Label="@T("What system prompt do you want to use?")"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="6"
|
||||
AutoGrow="@true"
|
||||
MaxLines="12"
|
||||
AutoGrow="@true"
|
||||
Class="mb-3"
|
||||
MaxLength="444"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Tell the AI your system prompt.")"
|
||||
/>
|
||||
|
||||
<MudTooltip Text="@T("Are you unsure which system prompt to use? You can simply start with the default system prompt that AI Studio uses for all chats.")">
|
||||
<MudButton Class="mb-3" Color="Color.Primary" OnClick="@this.UseDefaultSystemPrompt" Size="Size.Small" StartIcon="@Icons.Material.Filled.ListAlt" Variant="Variant.Filled">@T("Use the default system prompt")</MudButton>
|
||||
</MudTooltip>
|
||||
<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">
|
||||
@T("Use the default system prompt")
|
||||
</MudButton>
|
||||
|
||||
<MudTooltip Text="@T("Using some chat templates in tandem with profiles might cause issues. Therefore, you can preliminarily block the usage of profiles here.")">
|
||||
<MudSwitch @bind-Value="@this.AllowProfileUsage" Class="mb-3" Color="Color.Primary" Label="@T("Allow the use of profiles together with this chat template?")" ThumbIcon="@Icons.Material.Filled.Person4" ThumbIconColor="Color.Default" />
|
||||
</MudTooltip>
|
||||
<MudJustifiedText Class="mt-6" 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")" />
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mb-3">
|
||||
<MudText Typo="Typo.h6" Class="mb-3 mt-6">
|
||||
@T("Example Conversation")
|
||||
</MudText>
|
||||
|
||||
@@ -66,38 +69,73 @@
|
||||
@T("Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.")
|
||||
</MudJustifiedText>
|
||||
|
||||
<MudTable CanCancelEdit="true" Class="mt-3 mb-6" CommitEditTooltip="@T("Commit Changes")" Elevation="10" FixedHeader="true" Items="@ExampleConversation" Outlined="true" RowEditCancel="@this.ResetItemToOriginalValues" RowEditPreview="@this.BackupItem">
|
||||
<MudTable Items="@this.dataExampleConversation" FixedHeader="true" Hover="true" Class="mt-3 mb-3" CanCancelEdit="true" CancelEditTooltip="@T("Cancel")" CommitEditTooltip="@T("Commit Changes")" Outlined="true" RowEditCancel="@this.ResetItem" RowEditPreview="@this.BackupItem" EditTrigger="TableEditTrigger.RowClick" IsEditRowSwitchingBlocked="false" RowEditCommit="@this.CommitInlineEdit">
|
||||
<ColGroup>
|
||||
<col style="width: 16em;" />
|
||||
<col style="width: 10em;" />
|
||||
<col/>
|
||||
<col style="width: 16em;" />
|
||||
<col style="width: 10em;" />
|
||||
</ColGroup>
|
||||
<HeaderContent>
|
||||
<MudTh>@T("Role")</MudTh>
|
||||
<MudTh>@T("Entry")</MudTh>
|
||||
<MudTh Style="text-align:center">@T("Actions")</MudTh>
|
||||
<MudTh>@T("Actions")</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="@T("Role")">@context.Role.ToChatTemplateName()</MudTd>
|
||||
<MudTd DataLabel="@T("Message")">
|
||||
@(context.Content is ContentText textContent ? textContent.Text : context.Content?.ToString())
|
||||
<MudTd>
|
||||
@context.Role.ToChatTemplateName()
|
||||
</MudTd>
|
||||
<MudTd style="text-align: center">
|
||||
<MudIconButton Color="Color.Primary" Icon="@Icons.Material.Filled.Add" OnClick="@(() => AddNewMessageBelow(context))" />
|
||||
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="@(() => RemoveMessage(context))" />
|
||||
<MudTd>
|
||||
@switch(context.Content)
|
||||
{
|
||||
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)
|
||||
{
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("Add a new message below")">
|
||||
<MudIconButton Color="Color.Primary" Icon="@Icons.Material.Filled.Add" OnClick="@(() => this.AddMessageBelow(context))"/>
|
||||
</MudTooltip>
|
||||
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="@(() => this.RemoveMessage(context))"/>
|
||||
</MudStack>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
<RowEditingTemplate>
|
||||
<MudTd DataLabel="Role">
|
||||
<MudSelect Label="Role" @bind-Value="context.Role" Required>
|
||||
@foreach (var role in availableRoles)
|
||||
<MudTd>
|
||||
<MudSelect Label="@T("Role")" @bind-Value="@context.Role" Required="true">
|
||||
@foreach (var role in ChatRoles.ChatTemplateRoles())
|
||||
{
|
||||
<MudSelectItem Value="@role">@role.ToChatTemplateName()</MudSelectItem>
|
||||
<MudSelectItem Value="@role">
|
||||
@role.ToChatTemplateName()
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Message">
|
||||
<MudTextField AutoGrow="true" @bind-Value="context.Content.As<ContentText>()!.Text" Label="@T("Your message")" Required />
|
||||
<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")"/>
|
||||
break;
|
||||
|
||||
default:
|
||||
<MudText Typo="Typo.body2">
|
||||
@T("Only text content is supported in the editing mode yet.")
|
||||
</MudText>
|
||||
break;
|
||||
}
|
||||
</MudTd>
|
||||
</RowEditingTemplate>
|
||||
<PagerContent>
|
||||
@@ -106,23 +144,32 @@
|
||||
</MudTable>
|
||||
</MudForm>
|
||||
|
||||
<MudButton Class="mb-3" Color="Color.Primary" OnClick="@this.AddNewMessageToEnd" StartIcon="@Icons.Material.Filled.Add" Variant="Variant.Filled">@T("Add additional message")</MudButton>
|
||||
|
||||
@if (!this.isInlineEditOnGoing)
|
||||
{
|
||||
<MudButton Class="mb-6" Color="Color.Primary" OnClick="@this.AddMessageToEnd" StartIcon="@Icons.Material.Filled.Add" Variant="Variant.Filled">
|
||||
@T("Add a message")
|
||||
</MudButton>
|
||||
}
|
||||
|
||||
<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.isInlineEditOnGoing)
|
||||
{
|
||||
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@if (this.IsEditing)
|
||||
{
|
||||
@T("Update")
|
||||
}
|
||||
else
|
||||
{
|
||||
@T("Add")
|
||||
}
|
||||
</MudButton>
|
||||
}
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -42,13 +42,13 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
public bool IsEditing { get; init; }
|
||||
|
||||
[Parameter]
|
||||
public List<ContentBlock> ExampleConversation { get; set; } = [];
|
||||
public IReadOnlyCollection<ContentBlock> ExampleConversation { get; init; } = [];
|
||||
|
||||
[Parameter]
|
||||
public bool AllowProfileUsage { get; set; } = true;
|
||||
|
||||
[Inject]
|
||||
private ILogger<ProviderDialog> Logger { get; init; } = null!;
|
||||
private ILogger<ChatTemplateDialog> Logger { get; init; } = null!;
|
||||
|
||||
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
|
||||
|
||||
@@ -58,88 +58,16 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
private List<string> UsedNames { get; set; } = [];
|
||||
|
||||
private bool dataIsValid;
|
||||
private List<ContentBlock> dataExampleConversation = [];
|
||||
private string[] dataIssues = [];
|
||||
private string dataEditingPreviousName = string.Empty;
|
||||
private bool isInlineEditOnGoing;
|
||||
|
||||
private ContentBlock messageEntryBeforeEdit;
|
||||
private readonly IEnumerable<ChatRole> availableRoles = ChatRoles.ChatTemplateRoles().ToArray();
|
||||
private ContentBlock? messageEntryBeforeEdit;
|
||||
|
||||
// We get the form reference from Blazor code to validate it manually:
|
||||
private MudForm form = null!;
|
||||
|
||||
private ChatTemplate CreateChatTemplateSettings() => new()
|
||||
{
|
||||
Num = this.DataNum,
|
||||
Id = this.DataId,
|
||||
|
||||
Name = this.DataName,
|
||||
SystemPrompt = this.DataSystemPrompt,
|
||||
ExampleConversation = this.ExampleConversation,
|
||||
AllowProfileUsage = this.AllowProfileUsage,
|
||||
};
|
||||
|
||||
private void RemoveMessage(ContentBlock item)
|
||||
{
|
||||
this.ExampleConversation.Remove(item);
|
||||
}
|
||||
|
||||
private void AddNewMessageToEnd()
|
||||
{
|
||||
this.ExampleConversation ??= new List<ContentBlock>();
|
||||
|
||||
var newEntry = new ContentBlock
|
||||
{
|
||||
Role = ChatRole.USER, // Default to User
|
||||
Content = new ContentText(),
|
||||
ContentType = ContentType.TEXT,
|
||||
HideFromUser = true,
|
||||
Time = DateTimeOffset.Now,
|
||||
};
|
||||
|
||||
this.ExampleConversation.Add(newEntry);
|
||||
}
|
||||
|
||||
private void AddNewMessageBelow(ContentBlock currentItem)
|
||||
{
|
||||
|
||||
// Create new entry with a valid role
|
||||
var newEntry = new ContentBlock
|
||||
{
|
||||
Role = ChatRole.USER, // Default to User
|
||||
Content = new ContentText(),
|
||||
ContentType = ContentType.TEXT,
|
||||
HideFromUser = true,
|
||||
Time = DateTimeOffset.Now,
|
||||
};
|
||||
|
||||
// Rest of the method remains the same
|
||||
var index = this.ExampleConversation.IndexOf(currentItem);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
this.ExampleConversation.Insert(index + 1, newEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ExampleConversation.Add(newEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private void BackupItem(object element)
|
||||
{
|
||||
this.messageEntryBeforeEdit = new ContentBlock
|
||||
{
|
||||
Role = ((ContentBlock)element).Role,
|
||||
Content = ((ContentBlock)element).Content,
|
||||
};
|
||||
}
|
||||
|
||||
private void ResetItemToOriginalValues(object element)
|
||||
{
|
||||
((ContentBlock)element).Role = this.messageEntryBeforeEdit.Role;
|
||||
((ContentBlock)element).Content = this.messageEntryBeforeEdit.Content;
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@@ -154,6 +82,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
if(this.IsEditing)
|
||||
{
|
||||
this.dataEditingPreviousName = this.DataName.ToLowerInvariant();
|
||||
this.dataExampleConversation = this.ExampleConversation.Select(n => n.DeepClone()).ToList();
|
||||
}
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
@@ -170,6 +99,90 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private ChatTemplate CreateChatTemplateSettings() => new()
|
||||
{
|
||||
Num = this.DataNum,
|
||||
Id = this.DataId,
|
||||
|
||||
Name = this.DataName,
|
||||
SystemPrompt = this.DataSystemPrompt,
|
||||
ExampleConversation = this.dataExampleConversation,
|
||||
AllowProfileUsage = this.AllowProfileUsage,
|
||||
};
|
||||
|
||||
private void RemoveMessage(ContentBlock item)
|
||||
{
|
||||
this.dataExampleConversation.Remove(item);
|
||||
}
|
||||
|
||||
private void AddMessageToEnd()
|
||||
{
|
||||
var newEntry = new ContentBlock
|
||||
{
|
||||
Role = this.dataExampleConversation.Count is 0 ? ChatRole.USER : this.dataExampleConversation.Last().Role.SelectNextRoleForTemplate(),
|
||||
Content = new ContentText(),
|
||||
ContentType = ContentType.TEXT,
|
||||
HideFromUser = true,
|
||||
Time = DateTimeOffset.Now,
|
||||
};
|
||||
|
||||
this.dataExampleConversation.Add(newEntry);
|
||||
}
|
||||
|
||||
private void AddMessageBelow(ContentBlock currentItem)
|
||||
{
|
||||
var insertedEntry = new ContentBlock
|
||||
{
|
||||
Role = this.dataExampleConversation.Count is 0 ? ChatRole.USER : this.dataExampleConversation.Last().Role.SelectNextRoleForTemplate(),
|
||||
Content = new ContentText(),
|
||||
ContentType = ContentType.TEXT,
|
||||
HideFromUser = true,
|
||||
Time = DateTimeOffset.Now,
|
||||
};
|
||||
|
||||
// The rest of the method remains the same:
|
||||
var index = this.dataExampleConversation.IndexOf(currentItem);
|
||||
if (index >= 0)
|
||||
this.dataExampleConversation.Insert(index + 1, insertedEntry);
|
||||
else
|
||||
this.dataExampleConversation.Add(insertedEntry);
|
||||
}
|
||||
|
||||
private void BackupItem(object? element)
|
||||
{
|
||||
this.isInlineEditOnGoing = true;
|
||||
this.messageEntryBeforeEdit = element switch
|
||||
{
|
||||
ContentBlock block => block.DeepClone(),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
private void ResetItem(object? element)
|
||||
{
|
||||
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)
|
||||
{
|
||||
this.isInlineEditOnGoing = false;
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task Store()
|
||||
{
|
||||
@@ -179,6 +192,10 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
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();
|
||||
@@ -191,10 +208,10 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
this.MudDialog.Close(DialogResult.Ok(addedChatTemplateSettings));
|
||||
}
|
||||
|
||||
private string? ValidateSystemPrompt(string text)
|
||||
private string? ValidateExampleTextMessage(string message)
|
||||
{
|
||||
if(text.Length > 444)
|
||||
return T("The text must not exceed 444 characters.");
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
return T("Please enter a message for the example conversation.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<ConfigurationOption OptionDescription="@T("Preselect chat options?")" LabelOn="@T("Chat options are preselected")" LabelOff="@T("No chat options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Chat.PreselectOptions = updatedState)" OptionHelp="@T("When enabled, you can preselect chat options. This is might be useful when you prefer a specific provider.")"/>
|
||||
<ConfigurationProviderSelection Component="Components.CHAT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider = selectedValue)"/>
|
||||
<ConfigurationSelect OptionDescription="@T("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="@T("Would you like to set one of your profiles as the default for chats?")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preselect one of your chat templates?")" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedChatTemplate)" Data="@ConfigurationSelectDataFactory.GetChatTemplatesData(this.SettingsManager.ConfigurationData.ChatTemplates)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedChatTemplate = selectedValue)" OptionHelp="@T("Would you like to set one of your chat templates as the default for chats?")"/>
|
||||
</MudPaper>
|
||||
|
||||
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
||||
|
||||
Reference in new issue
Block a user