Allow chat templates coming from a config plugin

This commit is contained in:
Thorsten Sommer 2025-08-16 22:21:32 +02:00
parent afefc651a5
commit 26e597b159
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
10 changed files with 134 additions and 12 deletions

View File

@ -109,7 +109,7 @@ public sealed record ChatThread
else else
{ {
var chatTemplate = settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatThread.SelectedChatTemplate); var chatTemplate = settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatThread.SelectedChatTemplate);
if(chatTemplate == default) if(chatTemplate == null)
systemPromptTextWithChatTemplate = chatThread.SystemPrompt; systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
else else
{ {

View File

@ -327,7 +327,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
private async Task ChatTemplateWasChanged(ChatTemplate chatTemplate) private async Task ChatTemplateWasChanged(ChatTemplate chatTemplate)
{ {
this.currentChatTemplate = chatTemplate; this.currentChatTemplate = chatTemplate;
this.userInput = this.currentChatTemplate.PredefinedUserPrompt; if(!string.IsNullOrWhiteSpace(this.currentChatTemplate.PredefinedUserPrompt))
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
if(this.ChatThread is null) if(this.ChatThread is null)
return; return;
@ -435,7 +437,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
DataSourceOptions = this.earlyDataSourceOptions, DataSourceOptions = this.earlyDataSourceOptions,
Name = this.ExtractThreadName(this.userInput), Name = this.ExtractThreadName(this.userInput),
Seed = this.RNG.Next(), Seed = this.RNG.Next(),
Blocks = this.currentChatTemplate == default ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(), Blocks = this.currentChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
}; };
await this.ChatThreadChanged.InvokeAsync(this.ChatThread); await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
@ -673,7 +675,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
ChatId = Guid.NewGuid(), ChatId = Guid.NewGuid(),
Name = string.Empty, Name = string.Empty,
Seed = this.RNG.Next(), Seed = this.RNG.Next(),
Blocks = this.currentChatTemplate == default ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(), Blocks = this.currentChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
}; };
} }
@ -813,9 +815,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
// Try to select the chat template: // Try to select the chat template:
if (!string.IsNullOrWhiteSpace(chatChatTemplate)) if (!string.IsNullOrWhiteSpace(chatChatTemplate))
{ {
this.currentChatTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate); var selectedTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
if(this.currentChatTemplate == default) this.currentChatTemplate = selectedTemplate ?? ChatTemplate.NO_CHAT_TEMPLATE;
this.currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
} }
} }

View File

@ -54,6 +54,9 @@ public partial class SettingsPanelProviders : SettingsPanelBase
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")] [SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
private async Task EditLLMProvider(AIStudio.Settings.Provider provider) private async Task EditLLMProvider(AIStudio.Settings.Provider provider)
{ {
if (provider.IsEnterpriseConfiguration)
return;
var dialogParameters = new DialogParameters<ProviderDialog> var dialogParameters = new DialogParameters<ProviderDialog>
{ {
{ x => x.DataNum, provider.Num }, { x => x.DataNum, provider.Num },

View File

@ -129,6 +129,9 @@ public partial class ChatTemplateDialog : MSGComponentBase
PredefinedUserPrompt = this.PredefinedUserPrompt, PredefinedUserPrompt = this.PredefinedUserPrompt,
ExampleConversation = this.dataExampleConversation, ExampleConversation = this.dataExampleConversation,
AllowProfileUsage = this.AllowProfileUsage, AllowProfileUsage = this.AllowProfileUsage,
EnterpriseConfigurationPluginId = Guid.Empty,
IsEnterpriseConfiguration = false,
}; };
private void RemoveMessage(ContentBlock item) private void RemoveMessage(ContentBlock item)

View File

@ -53,6 +53,9 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
private async Task EditChatTemplate(ChatTemplate chatTemplate) private async Task EditChatTemplate(ChatTemplate chatTemplate)
{ {
if (chatTemplate == ChatTemplate.NO_CHAT_TEMPLATE || chatTemplate.IsEnterpriseConfiguration)
return;
var dialogParameters = new DialogParameters<ChatTemplateDialog> var dialogParameters = new DialogParameters<ChatTemplateDialog>
{ {
{ x => x.DataNum, chatTemplate.Num }, { x => x.DataNum, chatTemplate.Num },

View File

@ -3,7 +3,7 @@ using AIStudio.Tools.PluginSystem;
namespace AIStudio.Settings; namespace AIStudio.Settings;
public record ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, string PredefinedUserPrompt, List<ContentBlock> ExampleConversation, bool AllowProfileUsage) public record ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, string PredefinedUserPrompt, List<ContentBlock> ExampleConversation, bool AllowProfileUsage, bool IsEnterpriseConfiguration = false, Guid EnterpriseConfigurationPluginId = default)
{ {
public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], false) public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], false)
{ {
@ -20,6 +20,8 @@ public record ChatTemplate(uint Num, string Id, string Name, string SystemPrompt
Num = uint.MaxValue, Num = uint.MaxValue,
ExampleConversation = [], ExampleConversation = [],
AllowProfileUsage = true, AllowProfileUsage = true,
EnterpriseConfigurationPluginId = Guid.Empty,
IsEnterpriseConfiguration = false,
}; };
#region Overrides of ValueType #region Overrides of ValueType

View File

@ -270,11 +270,11 @@ public sealed class SettingsManager
public ChatTemplate GetPreselectedChatTemplate(Tools.Components component) public ChatTemplate GetPreselectedChatTemplate(Tools.Components component)
{ {
var preselection = component.PreselectedChatTemplate(this); var preselection = component.PreselectedChatTemplate(this);
if (preselection != default) if (preselection != ChatTemplate.NO_CHAT_TEMPLATE)
return preselection; return preselection;
preselection = this.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedChatTemplate); preselection = this.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedChatTemplate);
return preselection != default ? preselection : ChatTemplate.NO_CHAT_TEMPLATE; return preselection ?? ChatTemplate.NO_CHAT_TEMPLATE;
} }
public ConfidenceLevel GetConfiguredConfidenceLevel(LLMProviders llmProvider) public ConfidenceLevel GetConfiguredConfidenceLevel(LLMProviders llmProvider)

View File

@ -133,8 +133,8 @@ public static class ComponentsExtensions
public static ChatTemplate PreselectedChatTemplate(this Components component, SettingsManager settingsManager) => component switch public static ChatTemplate PreselectedChatTemplate(this Components component, SettingsManager settingsManager) => component switch
{ {
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedChatTemplate) : default, Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedChatTemplate) ?? ChatTemplate.NO_CHAT_TEMPLATE : ChatTemplate.NO_CHAT_TEMPLATE,
_ => default, _ => ChatTemplate.NO_CHAT_TEMPLATE,
}; };
} }

View File

@ -111,6 +111,47 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
} }
#pragma warning restore MWAIS0001 #pragma warning restore MWAIS0001
//
// Configured chat templates
//
if (mainTable.TryGetValue("CHAT_TEMPLATES", out var templatesValue) && templatesValue.TryRead<LuaTable>(out var templatesTable))
{
var numberTemplates = templatesTable.ArrayLength;
var configuredTemplates = new List<ChatTemplate>(numberTemplates);
for (var i = 1; i <= numberTemplates; i++)
{
var templateLuaTableValue = templatesTable[i];
if (!templateLuaTableValue.TryRead<LuaTable>(out var templateLuaTable))
{
LOGGER.LogWarning($"The CHAT_TEMPLATES table at index {i} is not a valid table.");
continue;
}
if(this.TryReadChatTemplateTable(i, templateLuaTable, out var template) && template != ChatTemplate.NO_CHAT_TEMPLATE)
configuredTemplates.Add(template);
else
LOGGER.LogWarning($"The CHAT_TEMPLATES table at index {i} does not contain a valid chat template configuration.");
}
// Apply configured chat templates to the system settings:
foreach (var configuredTemplate in configuredTemplates)
{
var template = configuredTemplate;
var tplIndex = SETTINGS_MANAGER.ConfigurationData.ChatTemplates.FindIndex(t => t.Id == template.Id);
if (tplIndex > -1)
{
var existingTemplate = SETTINGS_MANAGER.ConfigurationData.ChatTemplates[tplIndex];
template = template with { Num = existingTemplate.Num };
SETTINGS_MANAGER.ConfigurationData.ChatTemplates[tplIndex] = template;
}
else
{
template = template with { Num = SETTINGS_MANAGER.ConfigurationData.NextChatTemplateNum++ };
SETTINGS_MANAGER.ConfigurationData.ChatTemplates.Add(template);
}
}
}
return true; return true;
} }
@ -194,4 +235,51 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
model = new(id, displayName); model = new(id, displayName);
return true; return true;
} }
private bool TryReadChatTemplateTable(int idx, LuaTable table, out ChatTemplate template)
{
template = ChatTemplate.NO_CHAT_TEMPLATE;
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
{
LOGGER.LogWarning($"The configured chat template {idx} does not contain a valid ID. The ID must be a valid GUID.");
return false;
}
if (!table.TryGetValue("Name", out var nameValue) || !nameValue.TryRead<string>(out var name))
{
LOGGER.LogWarning($"The configured chat template {idx} does not contain a valid name.");
return false;
}
if (!table.TryGetValue("SystemPrompt", out var sysPromptValue) || !sysPromptValue.TryRead<string>(out var systemPrompt))
{
LOGGER.LogWarning($"The configured chat template {idx} does not contain a valid system prompt.");
return false;
}
var predefinedUserPrompt = string.Empty;
if (table.TryGetValue("PredefinedUserPrompt", out var preUserValue) && preUserValue.TryRead<string>(out var preUser))
predefinedUserPrompt = preUser;
var allowProfileUsage = false;
if (table.TryGetValue("AllowProfileUsage", out var allowProfileValue) && allowProfileValue.TryRead<bool>(out var allow))
allowProfileUsage = allow;
#warning Need to add support for ExampleConversation
template = new()
{
Num = 0,
Id = id.ToString(),
Name = name,
SystemPrompt = systemPrompt,
PredefinedUserPrompt = predefinedUserPrompt,
ExampleConversation = [],
AllowProfileUsage = allowProfileUsage,
IsEnterpriseConfiguration = true,
EnterpriseConfigurationPluginId = this.Id
};
return true;
}
} }

View File

@ -151,7 +151,29 @@ public static partial class PluginFactory
} }
} }
#pragma warning restore MWAIS0001 #pragma warning restore MWAIS0001
//
// Check Chat Templates:
//
var configuredTemplates = SETTINGS_MANAGER.ConfigurationData.ChatTemplates.ToList();
foreach (var configuredTemplate in configuredTemplates)
{
if(!configuredTemplate.IsEnterpriseConfiguration)
continue;
var templateSourcePluginId = configuredTemplate.EnterpriseConfigurationPluginId;
if(templateSourcePluginId == Guid.Empty)
continue;
var templateSourcePlugin = AVAILABLE_PLUGINS.FirstOrDefault(plugin => plugin.Id == templateSourcePluginId);
if(templateSourcePlugin is null)
{
LOG.LogWarning($"The configured chat template '{configuredTemplate.Name}' (id={configuredTemplate.Id}) is based on a plugin that is not available anymore. Removing the chat template from the settings.");
SETTINGS_MANAGER.ConfigurationData.ChatTemplates.Remove(configuredTemplate);
wasConfigurationChanged = true;
}
}
// //
// ========================================================== // ==========================================================
// Check all possible settings: // Check all possible settings: