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
{
var chatTemplate = settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatThread.SelectedChatTemplate);
if(chatTemplate == default)
if(chatTemplate == null)
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
else
{

View File

@ -327,7 +327,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
private async Task ChatTemplateWasChanged(ChatTemplate 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)
return;
@ -435,7 +437,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
DataSourceOptions = this.earlyDataSourceOptions,
Name = this.ExtractThreadName(this.userInput),
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);
@ -673,7 +675,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
ChatId = Guid.NewGuid(),
Name = string.Empty,
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:
if (!string.IsNullOrWhiteSpace(chatChatTemplate))
{
this.currentChatTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
if(this.currentChatTemplate == default)
this.currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
var selectedTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
this.currentChatTemplate = selectedTemplate ?? 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")]
private async Task EditLLMProvider(AIStudio.Settings.Provider provider)
{
if (provider.IsEnterpriseConfiguration)
return;
var dialogParameters = new DialogParameters<ProviderDialog>
{
{ x => x.DataNum, provider.Num },

View File

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

View File

@ -53,6 +53,9 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
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 },

View File

@ -3,7 +3,7 @@ using AIStudio.Tools.PluginSystem;
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)
{
@ -20,6 +20,8 @@ public record ChatTemplate(uint Num, string Id, string Name, string SystemPrompt
Num = uint.MaxValue,
ExampleConversation = [],
AllowProfileUsage = true,
EnterpriseConfigurationPluginId = Guid.Empty,
IsEnterpriseConfiguration = false,
};
#region Overrides of ValueType

View File

@ -270,11 +270,11 @@ public sealed class SettingsManager
public ChatTemplate GetPreselectedChatTemplate(Tools.Components component)
{
var preselection = component.PreselectedChatTemplate(this);
if (preselection != default)
if (preselection != ChatTemplate.NO_CHAT_TEMPLATE)
return preselection;
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)

View File

@ -133,8 +133,8 @@ public static class ComponentsExtensions
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
//
// 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;
}
@ -194,4 +235,51 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
model = new(id, displayName);
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
//
// 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: