System prompt works now!

This commit is contained in:
Peer Schütt 2025-05-16 15:54:46 +02:00
parent 5ff5a926b5
commit 527b2076fe
10 changed files with 146 additions and 29 deletions

View File

@ -30,6 +30,11 @@ public sealed record ChatThread
/// </summary> /// </summary>
public string SelectedProfile { get; set; } = string.Empty; public string SelectedProfile { get; set; } = string.Empty;
/// <summary>
/// Specifies the profile selected for the chat thread.
/// </summary>
public string SelectedChatTemplate { get; set; } = string.Empty;
/// <summary> /// <summary>
/// The data source options for this chat thread. /// The data source options for this chat thread.
/// </summary> /// </summary>
@ -70,6 +75,8 @@ public sealed record ChatThread
/// </summary> /// </summary>
public List<ContentBlock> Blocks { get; init; } = []; public List<ContentBlock> Blocks { get; init; } = [];
private bool allowProfile = true;
/// <summary> /// <summary>
/// Prepares the system prompt for the chat thread. /// Prepares the system prompt for the chat thread.
/// </summary> /// </summary>
@ -84,16 +91,50 @@ public sealed record ChatThread
/// <returns>The prepared system prompt.</returns> /// <returns>The prepared system prompt.</returns>
public string PrepareSystemPrompt(SettingsManager settingsManager, ChatThread chatThread, ILogger logger) public string PrepareSystemPrompt(SettingsManager settingsManager, ChatThread chatThread, ILogger logger)
{ {
//
// Prepare the prompts using the chatTemplate:
//
string systemPromptTextWithChatTemplate;
var logMessage = $"Using no chat template for chat thread '{chatThread.Name}'.";
if (string.IsNullOrWhiteSpace(chatThread.SelectedChatTemplate))
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
else
{
if(!Guid.TryParse(chatThread.SelectedChatTemplate, out var chatTeamplateId))
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
else
{
if(chatThread.SelectedChatTemplate == ChatTemplate.NO_CHATTEMPLATE.Id || chatTeamplateId == Guid.Empty)
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
else
{
var chatTemplate = settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatThread.SelectedChatTemplate);
if(chatTemplate == default)
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
else
{
logMessage = $"Using chat template '{chatTemplate.Name}' for chat thread '{chatThread.Name}'.";
this.allowProfile = chatTemplate.AllowProfileUsage;
systemPromptTextWithChatTemplate = $"""
{chatTemplate.ToSystemPrompt()}
""";
}
}
}
}
logger.LogInformation(logMessage);
var isAugmentedDataAvailable = !string.IsNullOrWhiteSpace(chatThread.AugmentedData); var isAugmentedDataAvailable = !string.IsNullOrWhiteSpace(chatThread.AugmentedData);
var systemPromptWithAugmentedData = isAugmentedDataAvailable switch var systemPromptWithAugmentedData = isAugmentedDataAvailable switch
{ {
true => $""" true => $"""
{chatThread.SystemPrompt} {systemPromptTextWithChatTemplate}
{chatThread.AugmentedData} {chatThread.AugmentedData}
""", """,
false => chatThread.SystemPrompt, false => systemPromptTextWithChatTemplate,
}; };
if(isAugmentedDataAvailable) if(isAugmentedDataAvailable)
@ -101,12 +142,13 @@ public sealed record ChatThread
else else
logger.LogInformation("No augmented data is available for the chat thread."); logger.LogInformation("No augmented data is available for the chat thread.");
// //
// Prepare the system prompt: // Prepare the system prompt:
// //
string systemPromptText; string systemPromptText;
var logMessage = $"Using no profile for chat thread '{chatThread.Name}'."; logMessage = $"Using no profile for chat thread '{chatThread.Name}'.";
if (string.IsNullOrWhiteSpace(chatThread.SelectedProfile)) if ((string.IsNullOrWhiteSpace(chatThread.SelectedProfile)) || (this.allowProfile is false))
systemPromptText = systemPromptWithAugmentedData; systemPromptText = systemPromptWithAugmentedData;
else else
{ {

View File

@ -108,6 +108,7 @@
} }
<ProfileSelection CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged"/> <ProfileSelection CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged"/>
<ChatTemplateSelection CurrentChatTemplate="@this.currentChatTemplate" CurrentChatTemplateChanged="@this.ChatTemplateWasChanged"/>
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager)) @if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
{ {

View File

@ -46,6 +46,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
private DataSourceSelection? dataSourceSelectionComponent; private DataSourceSelection? dataSourceSelectionComponent;
private DataSourceOptions earlyDataSourceOptions = new(); private DataSourceOptions earlyDataSourceOptions = new();
private Profile currentProfile = Profile.NO_PROFILE; private Profile currentProfile = Profile.NO_PROFILE;
private ChatTemplate currentChatTemplate = ChatTemplate.NO_CHATTEMPLATE;
private bool hasUnsavedChanges; private bool hasUnsavedChanges;
private bool mustScrollToBottomAfterRender; private bool mustScrollToBottomAfterRender;
private InnerScrolling scrollingArea = null!; private InnerScrolling scrollingArea = null!;
@ -77,6 +78,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
// Get the preselected profile: // Get the preselected profile:
this.currentProfile = this.SettingsManager.GetPreselectedProfile(Tools.Components.CHAT); this.currentProfile = this.SettingsManager.GetPreselectedProfile(Tools.Components.CHAT);
// Get the preselected chat template:
// this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(Tools.Components.CHAT); // TODO
// //
// Check for deferred messages of the kind 'SEND_TO_CHAT', // Check for deferred messages of the kind 'SEND_TO_CHAT',
// aka the user sends an assistant result to the chat: // aka the user sends an assistant result to the chat:
@ -321,6 +325,20 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
await this.ChatThreadChanged.InvokeAsync(this.ChatThread); await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
} }
private async Task ChatTemplateWasChanged(ChatTemplate chatTemplate)
{
this.currentChatTemplate = chatTemplate;
if(this.ChatThread is null)
return;
this.ChatThread = this.ChatThread with
{
SelectedChatTemplate = this.currentChatTemplate.Id,
};
await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
}
private IReadOnlyList<DataSourceAgentSelected> GetAgentSelectedDataSources() private IReadOnlyList<DataSourceAgentSelected> GetAgentSelectedDataSources()
{ {
if (this.ChatThread is null) if (this.ChatThread is null)
@ -415,6 +433,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
{ {
SelectedProvider = this.Provider.Id, SelectedProvider = this.Provider.Id,
SelectedProfile = this.currentProfile.Id, SelectedProfile = this.currentProfile.Id,
SelectedChatTemplate = this.currentChatTemplate.Id,
SystemPrompt = SystemPrompts.DEFAULT, SystemPrompt = SystemPrompts.DEFAULT,
WorkspaceId = this.currentWorkspaceId, WorkspaceId = this.currentWorkspaceId,
ChatId = Guid.NewGuid(), ChatId = Guid.NewGuid(),
@ -432,9 +451,10 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
if (string.IsNullOrWhiteSpace(this.ChatThread.Name)) if (string.IsNullOrWhiteSpace(this.ChatThread.Name))
this.ChatThread.Name = this.ExtractThreadName(this.userInput); this.ChatThread.Name = this.ExtractThreadName(this.userInput);
// Update provider and profile: // Update provider, profile and chat template:
this.ChatThread.SelectedProvider = this.Provider.Id; this.ChatThread.SelectedProvider = this.Provider.Id;
this.ChatThread.SelectedProfile = this.currentProfile.Id; this.ChatThread.SelectedProfile = this.currentProfile.Id;
this.ChatThread.SelectedChatTemplate = this.currentChatTemplate.Id;
} }
var time = DateTimeOffset.Now; var time = DateTimeOffset.Now;
@ -645,6 +665,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
{ {
SelectedProvider = this.Provider.Id, SelectedProvider = this.Provider.Id,
SelectedProfile = this.currentProfile.Id, SelectedProfile = this.currentProfile.Id,
SelectedChatTemplate = this.currentChatTemplate.Id,
SystemPrompt = SystemPrompts.DEFAULT, SystemPrompt = SystemPrompts.DEFAULT,
WorkspaceId = this.currentWorkspaceId, WorkspaceId = this.currentWorkspaceId,
ChatId = Guid.NewGuid(), ChatId = Guid.NewGuid(),
@ -756,6 +777,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
{ {
var chatProvider = this.ChatThread?.SelectedProvider; var chatProvider = this.ChatThread?.SelectedProvider;
var chatProfile = this.ChatThread?.SelectedProfile; var chatProfile = this.ChatThread?.SelectedProfile;
var chatChatTemplate = this.ChatThread?.SelectedChatTemplate;
switch (this.SettingsManager.ConfigurationData.Chat.LoadingProviderBehavior) switch (this.SettingsManager.ConfigurationData.Chat.LoadingProviderBehavior)
{ {
@ -783,6 +805,14 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
if(this.currentProfile == default) if(this.currentProfile == default)
this.currentProfile = Profile.NO_PROFILE; this.currentProfile = Profile.NO_PROFILE;
} }
// Try to select the chat template:
if (!string.IsNullOrWhiteSpace(chatChatTemplate))
{
this.currentChatTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
if(this.currentChatTemplate == default)
this.currentChatTemplate = ChatTemplate.NO_CHATTEMPLATE;
}
} }
private async Task ToggleWorkspaceOverlay() private async Task ToggleWorkspaceOverlay()

View File

@ -0,0 +1,11 @@
@inherits MSGComponentBase
<MudTooltip Text="@T("You can switch between your chat templates here")" Placement="Placement.Top">
<MudMenu TransformOrigin="@Origin.BottomLeft" AnchorOrigin="Origin.TopLeft" StartIcon="@Icons.Material.Filled.Person4" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@this.CurrentChatTemplate.Name" Variant="Variant.Filled" Color="Color.Default" Class="@this.MarginClass">
@foreach (var chatTemplate in this.SettingsManager.ConfigurationData.ChatTemplates.GetAllChatTemplates())
{
<MudMenuItem OnClick="() => this.SelectionChanged(chatTemplate)">
@chatTemplate.Name
</MudMenuItem>
}
</MudMenu>
</MudTooltip>

View File

@ -0,0 +1,28 @@
using AIStudio.Settings;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Components;
public partial class ChatTemplateSelection : MSGComponentBase
{
[Parameter]
public ChatTemplate CurrentChatTemplate { get; set; } = ChatTemplate.NO_CHATTEMPLATE;
[Parameter]
public EventCallback<ChatTemplate> CurrentChatTemplateChanged { get; set; }
[Parameter]
public string MarginLeft { get; set; } = "ml-3";
[Parameter]
public string MarginRight { get; set; } = string.Empty;
private string MarginClass => $"{this.MarginLeft} {this.MarginRight}";
private async Task SelectionChanged(ChatTemplate chatTemplate)
{
this.CurrentChatTemplate = chatTemplate;
await this.CurrentChatTemplateChanged.InvokeAsync(chatTemplate);
}
}

View File

@ -15,7 +15,7 @@
<ColGroup> <ColGroup>
<col style="width: 3em;"/> <col style="width: 3em;"/>
<col/> <col/>
<col style="width: 40em;"/> <col style="width: 20em;"/>
</ColGroup> </ColGroup>
<HeaderContent> <HeaderContent>
<MudTh>#</MudTh> <MudTh>#</MudTh>

View File

@ -46,14 +46,12 @@
Lines="6" Lines="6"
AutoGrow="@true" AutoGrow="@true"
MaxLines="12" MaxLines="12"
MaxLength="444"
Counter="444"
Class="mb-3" Class="mb-3"
UserAttributes="@SPELLCHECK_ATTRIBUTES" UserAttributes="@SPELLCHECK_ATTRIBUTES"
HelperText="@T("Tell the AI your system prompt.")" HelperText="@T("Tell the AI your system prompt.")"
/> />
<MudSwitch @bind-Value="allowProfile" Color="Color.Primary" Label="@T("Allow using profiles together with this chat template?")"/> <MudSwitch @bind-Value="allowProfileUsage" Color="Color.Primary" Label="@T("Allow using profiles together with this chat template?")"/>
<MudText> What should you know about the additional messages? TODO</MudText> <MudText> What should you know about the additional messages? TODO</MudText>
@ -73,16 +71,20 @@
</HeaderContent> </HeaderContent>
<RowTemplate> <RowTemplate>
<MudTd DataLabel="@T("Role")">@context.Role</MudTd> <MudTd DataLabel="@T("Role")">@context.Role</MudTd>
<MudTd DataLabel="@T("Message")">@context.Content</MudTd> <MudTd DataLabel="@T("Message")">
@(context.Content is ContentText textContent ? textContent.Text : context.Content?.ToString())
</MudTd>
<MudTd style="text-align: center"> <MudTd style="text-align: center">
<MudIconButton Icon="@Icons.Material.Filled.Add" <MudIconButton Icon="@Icons.Material.Filled.Add"
Color="Color.Success" Color="Color.Primary"
Size="Size.Small" Size="Size.Small"
OnClick="@(() => AddNewMessageBelow(context))"/> OnClick="@(() => AddNewMessageBelow(context))"
Variant="Variant.Filled"/>
<MudIconButton Icon="@Icons.Material.Filled.Delete" <MudIconButton Icon="@Icons.Material.Filled.Delete"
Color="Color.Error" Color="Color.Error"
Size="Size.Small" Size="Size.Small"
OnClick="@(() => RemoveMessage(context))"/> OnClick="@(() => RemoveMessage(context))"
Variant="Variant.Filled"/>
</MudTd> </MudTd>
</RowTemplate> </RowTemplate>
<RowEditingTemplate> <RowEditingTemplate>
@ -90,24 +92,12 @@
<MudSelect Label="Role" @bind-Value="context.Role" Required> <MudSelect Label="Role" @bind-Value="context.Role" Required>
@foreach (var role in availableRoles) @foreach (var role in availableRoles)
{ {
<MudSelectItem Value="@role">@role</MudSelectItem> <MudSelectItem Value="@role">@role.ToName()</MudSelectItem>
} }
</MudSelect> </MudSelect>
</MudTd> </MudTd>
<MudTd DataLabel="Message"> <MudTd DataLabel="Message">
<MudTextField Label="Your message" AutoGrow="true" MaxLines="10" @bind-Value="context.Content.As<ContentText>()!.Text" Required /> <MudTextField Label="Your message" AutoGrow="true" @bind-Value="context.Content.As<ContentText>()!.Text" Required />
</MudTd>
<MudTd style="text-align: center">
<MudIconButton Icon="@Icons.Material.Filled.Add"
Color="Color.Success"
Size="Size.Small"
OnClick="@(() => AddNewMessageBelow(context))"
/>
<MudIconButton Icon="@Icons.Material.Filled.Delete"
Color="Color.Error"
Size="Size.Small"
OnClick="@(() => RemoveMessage(context))"
/>
</MudTd> </MudTd>
</RowEditingTemplate> </RowEditingTemplate>
<PagerContent> <PagerContent>

View File

@ -62,7 +62,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
// private readonly List<ContentBlock> additionalMessagesEntries = []; // private readonly List<ContentBlock> additionalMessagesEntries = [];
// private readonly List<string> availableRoles = ["User", "Assistant"]; // private readonly List<string> availableRoles = ["User", "Assistant"];
private readonly IEnumerable<ChatRole> availableRoles = ChatRoles.ChatTemplateRoles().ToArray(); private readonly IEnumerable<ChatRole> availableRoles = ChatRoles.ChatTemplateRoles().ToArray();
private bool allowProfile = true; private bool allowProfileUsage = true;
// We get the form reference from Blazor code to validate it manually: // We get the form reference from Blazor code to validate it manually:
private MudForm form = null!; private MudForm form = null!;
@ -75,6 +75,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
Name = this.DataName, Name = this.DataName,
SystemPrompt = this.DataSystemPrompt, SystemPrompt = this.DataSystemPrompt,
AdditionalMessages = this.AdditionalMessages, AdditionalMessages = this.AdditionalMessages,
AllowProfileUsage = allowProfileUsage,
}; };
private void RemoveMessage(ContentBlock item) private void RemoveMessage(ContentBlock item)

View File

@ -3,7 +3,7 @@ using AIStudio.Tools.PluginSystem;
namespace AIStudio.Settings; namespace AIStudio.Settings;
public readonly record struct ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, List<ContentBlock> AdditionalMessages) public readonly record struct ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, List<ContentBlock> AdditionalMessages, bool AllowProfileUsage)
{ {
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ChatTemplate).Namespace, nameof(ChatTemplate)); private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ChatTemplate).Namespace, nameof(ChatTemplate));
@ -14,6 +14,7 @@ public readonly record struct ChatTemplate(uint Num, string Id, string Name, str
Id = Guid.Empty.ToString(), Id = Guid.Empty.ToString(),
Num = uint.MaxValue, Num = uint.MaxValue,
AdditionalMessages = [], AdditionalMessages = [],
AllowProfileUsage = true,
}; };
#region Overrides of ValueType #region Overrides of ValueType

View File

@ -0,0 +1,13 @@
using AIStudio.Settings;
namespace AIStudio.Tools;
public static class ChatTemplateExtensions
{
public static IEnumerable<ChatTemplate> GetAllChatTemplates(this IEnumerable<ChatTemplate> chatTemplates)
{
yield return ChatTemplate.NO_CHATTEMPLATE;
foreach (var chatTemplate in chatTemplates)
yield return chatTemplate;
}
}