Messages are now stored successfully

This commit is contained in:
Peer Schütt 2025-05-16 14:51:24 +02:00
parent 5f2da6acf6
commit 5ff5a926b5
7 changed files with 89 additions and 104 deletions

View File

@ -4,7 +4,7 @@ public static class ChatRoles
{ {
public static IEnumerable<ChatRole> ChatTemplateRoles() public static IEnumerable<ChatRole> ChatTemplateRoles()
{ {
yield return ChatRole.SYSTEM; yield return ChatRole.USER;
yield return ChatRole.AI; yield return ChatRole.AI;
} }
} }

View File

@ -18,12 +18,12 @@ public class ContentBlock
/// <summary> /// <summary>
/// The content of the block. /// The content of the block.
/// </summary> /// </summary>
public IContent? Content { get; init; } public IContent? Content { get; set; }
/// <summary> /// <summary>
/// The role of the content block in the chat thread, e.g., user, AI, etc. /// The role of the content block in the chat thread, e.g., user, AI, etc.
/// </summary> /// </summary>
public ChatRole Role { get; init; } = ChatRole.NONE; public ChatRole Role { get; set; } = ChatRole.NONE;
/// <summary> /// <summary>
/// Should the content block be hidden from the user? /// Should the content block be hidden from the user?

View File

@ -5,11 +5,11 @@
@T("Your Chat Templates") @T("Your Chat Templates")
</MudText> </MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-3"> <MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("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.") @T("Explain chat Templates TODO")
</MudJustifiedText> </MudJustifiedText>
<MudJustifiedText Typo="Typo.body1" Class="mb-3"> <MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("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.") @T("Explain chat Templates TODO")
</MudJustifiedText> </MudJustifiedText>
<MudTable Items="@this.SettingsManager.ConfigurationData.ChatTemplates" Hover="@true" Class="border-dashed border rounded-lg"> <MudTable Items="@this.SettingsManager.ConfigurationData.ChatTemplates" Hover="@true" Class="border-dashed border rounded-lg">
<ColGroup> <ColGroup>

View File

@ -36,10 +36,10 @@ public partial class SettingsPanelChatTemplates : SettingsPanelBase
{ x => x.DataNum, chatTemplate.Num }, { x => x.DataNum, chatTemplate.Num },
{ x => x.DataId, chatTemplate.Id }, { x => x.DataId, chatTemplate.Id },
{ x => x.DataName, chatTemplate.Name }, { x => x.DataName, chatTemplate.Name },
{ x => x.DataSystemPrompt, chatTemplate.NeedToKnow }, { x => x.DataSystemPrompt, chatTemplate.SystemPrompt },
// { x => x.DataActions, chatTemplate.Actions }, // { x => x.DataActions, chatTemplate.Actions },
{ x => x.IsEditing, true }, { x => x.IsEditing, true },
// {x => x.AdditionalMessages, chatTemplate}, TODO {x => x.AdditionalMessages, chatTemplate.AdditionalMessages},
}; };
var dialogReference = await this.DialogService.ShowAsync<ChatTemplateDialog>(T("Edit Chat Template"), dialogParameters, DialogOptions.FULLSCREEN); var dialogReference = await this.DialogService.ShowAsync<ChatTemplateDialog>(T("Edit Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);

View File

@ -1,3 +1,5 @@
@using AIStudio.Chat
@using MudBlazor.Extensions
@inherits MSGComponentBase @inherits MSGComponentBase
@inject ISnackbar Snackbar @inject ISnackbar Snackbar
@ -51,37 +53,41 @@
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?")"/>
<MudTable Items="@additionalMessagesEntries" RowEditPreview="BackupItem" RowEditCancel="ResetItemToOriginalValues" RowEditCommit="ItemHasBeenCommitted" CanCancelEdit="true" CommitEditTooltip="Commit Changes" Elevation="10" Outlined="true"> <MudText> What should you know about the additional messages? TODO</MudText>
<MudTable FixedHeader="true" Items="@AdditionalMessages" RowEditPreview="BackupItem" RowEditCancel="ResetItemToOriginalValues" RowEditCommit="ItemHasBeenCommitted" CanCancelEdit="true" CommitEditTooltip="@T("Commit Changes")" Elevation="10" Outlined="true" >
<ToolBarContent> <ToolBarContent>
<MudText Typo="Typo.h6">Additional messages</MudText> <MudText Typo="Typo.h6">Additional messages</MudText>
<MudSpacer />
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="AddInitialMessage" StartIcon="@Icons.Material.Filled.Add" Disabled="@initialAddButtonDisabled">Start using messages</MudButton>
</ToolBarContent> </ToolBarContent>
<ColGroup>
<col style="width: 20%;" />
<col style="width: 65%;" />
<col style="width: 15%;" />
</ColGroup>
<HeaderContent> <HeaderContent>
<MudTh>Role</MudTh> <MudTh>Role</MudTh>
<MudTh>Entry</MudTh> <MudTh>Entry</MudTh>
<MudTh>Actions</MudTh> <MudTh Style="text-align:center">Actions</MudTh>
</HeaderContent> </HeaderContent>
<RowTemplate> <RowTemplate>
<MudTd DataLabel="Role">@context.Role</MudTd> <MudTd DataLabel="@T("Role")">@context.Role</MudTd>
<MudTd DataLabel="Message">@context.Entry</MudTd> <MudTd DataLabel="@T("Message")">@context.Content</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.Success"
Size="Size.Small" Size="Size.Small"
OnClick="@(() => AddNewMessageBelow(context))" OnClick="@(() => AddNewMessageBelow(context))"/>
/>
<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))"/>
/>
</MudTd> </MudTd>
</RowTemplate> </RowTemplate>
<RowEditingTemplate> <RowEditingTemplate>
<MudTd DataLabel="Role"> <MudTd DataLabel="Role">
<MudSelect @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</MudSelectItem>
@ -89,7 +95,7 @@
</MudSelect> </MudSelect>
</MudTd> </MudTd>
<MudTd DataLabel="Message"> <MudTd DataLabel="Message">
<MudTextField @bind-Value="context.Entry" Required /> <MudTextField Label="Your message" AutoGrow="true" MaxLines="10" @bind-Value="context.Content.As<ContentText>()!.Text" Required />
</MudTd> </MudTd>
<MudTd style="text-align: center"> <MudTd style="text-align: center">
<MudIconButton Icon="@Icons.Material.Filled.Add" <MudIconButton Icon="@Icons.Material.Filled.Add"
@ -105,11 +111,13 @@
</MudTd> </MudTd>
</RowEditingTemplate> </RowEditingTemplate>
<PagerContent> <PagerContent>
<MudTablePager /> <MudTablePager RowsPerPageString="Messages per page" PageSizeOptions="[20,50,100]"/>
</PagerContent> </PagerContent>
</MudTable> </MudTable>
</MudForm> </MudForm>
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="AddNewMessageToEnd" StartIcon="@Icons.Material.Filled.Add">@T("Add additional message")</MudButton>
<Issues IssuesData="@this.dataIssues"/> <Issues IssuesData="@this.dataIssues"/>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>

View File

@ -42,7 +42,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
public bool IsEditing { get; init; } public bool IsEditing { get; init; }
[Parameter] [Parameter]
public List<EntryItem> AdditionalMessages { get; set; } = []; public List<ContentBlock> AdditionalMessages { get; set; } = [];
[Inject] [Inject]
private ILogger<ProviderDialog> Logger { get; init; } = null!; private ILogger<ProviderDialog> Logger { get; init; } = null!;
@ -58,10 +58,11 @@ public partial class ChatTemplateDialog : MSGComponentBase
private string[] dataIssues = []; private string[] dataIssues = [];
private string dataEditingPreviousName = string.Empty; private string dataEditingPreviousName = string.Empty;
private EntryItem messageEntryBeforeEdit; private ContentBlock messageEntryBeforeEdit;
private readonly List<EntryItem> additionalMessagesEntries = []; // private readonly List<ContentBlock> additionalMessagesEntries = [];
private readonly List<string> availableRoles = ["User", "Assistant"]; // private readonly List<string> availableRoles = ["User", "Assistant"];
private bool initialAddButtonDisabled = false; private readonly IEnumerable<ChatRole> availableRoles = ChatRoles.ChatTemplateRoles().ToArray();
private bool allowProfile = 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!;
@ -72,83 +73,77 @@ public partial class ChatTemplateDialog : MSGComponentBase
Id = this.DataId, Id = this.DataId,
Name = this.DataName, Name = this.DataName,
NeedToKnow = this.DataSystemPrompt, SystemPrompt = this.DataSystemPrompt,
// AdditionalMessages = this.additionalMessagesEntries, AdditionalMessages = this.AdditionalMessages,
Actions = string.Empty,
}; };
private void RemoveMessage(EntryItem item) private void RemoveMessage(ContentBlock item)
{ {
this.additionalMessagesEntries.Remove(item); this.AdditionalMessages.Remove(item);
this.Snackbar.Add("Entry removed", Severity.Info); // this.Snackbar.Add("Entry removed", Severity.Info);
this.initialAddButtonDisabled = this.additionalMessagesEntries.Count > 0;
// ChatRoles.ChatTemplateRoles() // TODO: -> darauf foreach für alle Rollen in der Tabelle
} }
private void AddInitialMessage() private void AddNewMessageToEnd()
{ {
var newEntry = new EntryItem var newEntry = new ContentBlock
{ {
Role = availableRoles[0], // Default to first role ("User") Role = ChatRole.USER, // Default to User
Entry = "Your message" Content = new ContentText(),
ContentType = ContentType.TEXT,
HideFromUser = true,
Time = DateTimeOffset.Now,
}; };
this.additionalMessagesEntries.Add(newEntry); this.AdditionalMessages.Add(newEntry);
this.Snackbar.Add("Initial entry added", Severity.Success); // this.Snackbar.Add("Initial entry added", Severity.Success);
this.initialAddButtonDisabled = this.additionalMessagesEntries.Count > 0;
} }
private void AddNewMessageBelow(EntryItem currentItem) private void AddNewMessageBelow(ContentBlock currentItem)
{ {
// Create new entry with a valid role // Create new entry with a valid role
var newEntry = new EntryItem var newEntry = new ContentBlock
{ {
Role = availableRoles.FirstOrDefault(role => role != currentItem.Role) ?? availableRoles[0], // Default to role not used in the previous entry Role = ChatRole.USER, // Default to User
Entry = "Your message" Content = new ContentText(),
ContentType = ContentType.TEXT,
HideFromUser = true,
Time = DateTimeOffset.Now,
}; };
// Rest of the method remains the same // Rest of the method remains the same
var index = this.additionalMessagesEntries.IndexOf(currentItem); var index = this.AdditionalMessages.IndexOf(currentItem);
if (index >= 0) if (index >= 0)
{ {
this.additionalMessagesEntries.Insert(index + 1, newEntry); this.AdditionalMessages.Insert(index + 1, newEntry);
this.Snackbar.Add("New entry added", Severity.Success); // this.Snackbar.Add("New entry added", Severity.Success);
} }
else else
{ {
this.additionalMessagesEntries.Add(newEntry); this.AdditionalMessages.Add(newEntry);
this.Snackbar.Add("New entry added", Severity.Success); // this.Snackbar.Add("New entry added", Severity.Success);
} }
this.initialAddButtonDisabled = this.additionalMessagesEntries.Count > 0;
} }
private void BackupItem(object element) private void BackupItem(object element)
{ {
this.messageEntryBeforeEdit = new() this.messageEntryBeforeEdit = new ContentBlock
{ {
Role = ((EntryItem)element).Role, Role = ((ContentBlock)element).Role,
Entry = ((EntryItem)element).Entry Content = ((ContentBlock)element).Content,
}; };
} }
private void ItemHasBeenCommitted(object element) private void ItemHasBeenCommitted(object element)
{ {
this.Snackbar.Add("Changes saved", Severity.Success); // this.Snackbar.Add("Changes saved", Severity.Success);
} }
private void ResetItemToOriginalValues(object element) private void ResetItemToOriginalValues(object element)
{ {
((EntryItem)element).Role = this.messageEntryBeforeEdit.Role; ((ContentBlock)element).Role = this.messageEntryBeforeEdit.Role;
((EntryItem)element).Entry = this.messageEntryBeforeEdit.Entry; ((ContentBlock)element).Content = this.messageEntryBeforeEdit.Content;
}
public class EntryItem
{
public required string Role { get; set; }
public required string Entry { get; set; }
} }
#region Overrides of ComponentBase #region Overrides of ComponentBase

View File

@ -1,19 +1,19 @@
using AIStudio.Chat;
using AIStudio.Tools.PluginSystem; using AIStudio.Tools.PluginSystem;
namespace AIStudio.Settings; namespace AIStudio.Settings;
public readonly record struct ChatTemplate(uint Num, string Id, string Name, string NeedToKnow, string Actions) //, CategoryTypes.List AdditionalMessages) public readonly record struct ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, List<ContentBlock> AdditionalMessages)
{ {
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(Profile).Namespace, nameof(Profile)); private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ChatTemplate).Namespace, nameof(ChatTemplate));
public static readonly Profile NO_PROFILE = new() public static readonly ChatTemplate NO_CHATTEMPLATE = new()
{ {
Name = TB("Use no profile"), Name = TB("Use no chat template"),
NeedToKnow = string.Empty, SystemPrompt = string.Empty,
Actions = string.Empty,
Id = Guid.Empty.ToString(), Id = Guid.Empty.ToString(),
Num = uint.MaxValue, Num = uint.MaxValue,
// AdditionalMessages = [], AdditionalMessages = [],
}; };
#region Overrides of ValueType #region Overrides of ValueType
@ -31,34 +31,16 @@ public readonly record struct ChatTemplate(uint Num, string Id, string Name, str
if(this.Num == uint.MaxValue) if(this.Num == uint.MaxValue)
return string.Empty; return string.Empty;
var needToKnow = var systemPrompt =
$""" $"""
What should you know about the user?
``` ```
{this.NeedToKnow} {this.SystemPrompt}
``` ```
"""; """;
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 $""" return $"""
{needToKnow} {systemPrompt}
{actions}
"""; """;
} }
} }