diff --git a/app/MindWork AI Studio/Chat/ChatThread.cs b/app/MindWork AI Studio/Chat/ChatThread.cs index d38ddad2..0193ce28 100644 --- a/app/MindWork AI Studio/Chat/ChatThread.cs +++ b/app/MindWork AI Studio/Chat/ChatThread.cs @@ -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 { diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor.cs b/app/MindWork AI Studio/Components/ChatComponent.razor.cs index 43f79a10..3c4a8d38 100644 --- a/app/MindWork AI Studio/Components/ChatComponent.razor.cs +++ b/app/MindWork AI Studio/Components/ChatComponent.razor.cs @@ -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; } } diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs index 16ba7727..608dec29 100644 --- a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs @@ -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 { { x => x.DataNum, provider.Num }, diff --git a/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor.cs b/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor.cs index e95b0d76..3f9378ef 100644 --- a/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor.cs +++ b/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor.cs @@ -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) diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs index 364eb49a..73ed5fa5 100644 --- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs +++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs @@ -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 { { x => x.DataNum, chatTemplate.Num }, diff --git a/app/MindWork AI Studio/Settings/ChatTemplate.cs b/app/MindWork AI Studio/Settings/ChatTemplate.cs index cd25794d..6842ae00 100644 --- a/app/MindWork AI Studio/Settings/ChatTemplate.cs +++ b/app/MindWork AI Studio/Settings/ChatTemplate.cs @@ -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 ExampleConversation, bool AllowProfileUsage) +public record ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, string PredefinedUserPrompt, List 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 diff --git a/app/MindWork AI Studio/Settings/SettingsManager.cs b/app/MindWork AI Studio/Settings/SettingsManager.cs index 059d0f12..7cad25a2 100644 --- a/app/MindWork AI Studio/Settings/SettingsManager.cs +++ b/app/MindWork AI Studio/Settings/SettingsManager.cs @@ -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) diff --git a/app/MindWork AI Studio/Tools/ComponentsExtensions.cs b/app/MindWork AI Studio/Tools/ComponentsExtensions.cs index e4bd317c..18ac4f41 100644 --- a/app/MindWork AI Studio/Tools/ComponentsExtensions.cs +++ b/app/MindWork AI Studio/Tools/ComponentsExtensions.cs @@ -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, }; } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs index 7de7137f..df6b09c2 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs @@ -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(out var templatesTable)) + { + var numberTemplates = templatesTable.ArrayLength; + var configuredTemplates = new List(numberTemplates); + for (var i = 1; i <= numberTemplates; i++) + { + var templateLuaTableValue = templatesTable[i]; + if (!templateLuaTableValue.TryRead(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(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(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(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(out var preUser)) + predefinedUserPrompt = preUser; + + var allowProfileUsage = false; + if (table.TryGetValue("AllowProfileUsage", out var allowProfileValue) && allowProfileValue.TryRead(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; + } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs index 667ed867..98dec636 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs @@ -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: