From 80c1d72ca23cf2582de8e95f8574fec68fad067c Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Tue, 19 Aug 2025 09:22:33 +0200 Subject: [PATCH] Added `IConfigurationObject` interface and refactored cleanup logic --- .../Settings/ChatTemplate.cs | 11 ++- app/MindWork AI Studio/Settings/Provider.cs | 10 ++- .../PluginSystem/IConfigurationObject.cs | 32 +++++++ .../PluginSystem/PluginConfigurationObject.cs | 56 ++++++++++++ .../PluginSystem/PluginFactory.Loading.cs | 85 +++---------------- 5 files changed, 117 insertions(+), 77 deletions(-) create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/IConfigurationObject.cs diff --git a/app/MindWork AI Studio/Settings/ChatTemplate.cs b/app/MindWork AI Studio/Settings/ChatTemplate.cs index 6842ae00..de1f5e9b 100644 --- a/app/MindWork AI Studio/Settings/ChatTemplate.cs +++ b/app/MindWork AI Studio/Settings/ChatTemplate.cs @@ -3,7 +3,16 @@ using AIStudio.Tools.PluginSystem; namespace AIStudio.Settings; -public record ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, string PredefinedUserPrompt, List ExampleConversation, bool AllowProfileUsage, bool IsEnterpriseConfiguration = false, Guid EnterpriseConfigurationPluginId = default) +public record ChatTemplate( + uint Num, + string Id, + string Name, + string SystemPrompt, + string PredefinedUserPrompt, + List ExampleConversation, + bool AllowProfileUsage, + bool IsEnterpriseConfiguration = false, + Guid EnterpriseConfigurationPluginId = default) : IConfigurationObject { public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], false) { diff --git a/app/MindWork AI Studio/Settings/Provider.cs b/app/MindWork AI Studio/Settings/Provider.cs index 4cef58df..b7826b2d 100644 --- a/app/MindWork AI Studio/Settings/Provider.cs +++ b/app/MindWork AI Studio/Settings/Provider.cs @@ -2,6 +2,8 @@ using System.Text.Json.Serialization; using AIStudio.Provider; using AIStudio.Provider.HuggingFace; +using AIStudio.Tools.PluginSystem; + using Host = AIStudio.Provider.SelfHosted.Host; namespace AIStudio.Settings; @@ -27,7 +29,7 @@ public readonly record struct Provider( Guid EnterpriseConfigurationPluginId = default, string Hostname = "http://localhost:1234", Host Host = Host.NONE, - HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE) : ISecretId + HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE) : ISecretId, IConfigurationObject { #region Overrides of ValueType @@ -57,4 +59,10 @@ public readonly record struct Provider( public string SecretName => this.InstanceName; #endregion + + #region Implementation of IConfigurationObject + + public string Name => this.InstanceName; + + #endregion } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/IConfigurationObject.cs b/app/MindWork AI Studio/Tools/PluginSystem/IConfigurationObject.cs new file mode 100644 index 00000000..a46da2ef --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/IConfigurationObject.cs @@ -0,0 +1,32 @@ +namespace AIStudio.Tools.PluginSystem; + +/// +/// Represents a configuration object, such as a chat template or a LLM provider. +/// +public interface IConfigurationObject +{ + /// + /// The unique ID of the configuration object. + /// + public string Id { get; } + + /// + /// The continuous number of the configuration object. + /// + public uint Num { get; } + + /// + /// The name of the configuration object. + /// + public string Name { get; } + + /// + /// Is this configuration object an enterprise configuration? + /// + public bool IsEnterpriseConfiguration { get; } + + /// + /// The ID of the enterprise configuration plugin. + /// + public Guid EnterpriseConfigurationPluginId { get; } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObject.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObject.cs index 258e6c3c..5939c0b4 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObject.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObject.cs @@ -1,3 +1,8 @@ +using System.Linq.Expressions; + +using AIStudio.Settings; +using AIStudio.Settings.DataModel; + namespace AIStudio.Tools.PluginSystem; /// @@ -6,6 +11,9 @@ namespace AIStudio.Tools.PluginSystem; /// public sealed record PluginConfigurationObject { + private static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService(); + private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger(); + /// /// The id of the configuration plugin to which this configuration object belongs. /// @@ -20,4 +28,52 @@ public sealed record PluginConfigurationObject /// The type of the configuration object. /// public required PluginConfigurationObjectType Type { get; init; } = PluginConfigurationObjectType.NONE; + + /// + /// Cleans up configuration objects of a specified type that are no longer associated with any available plugin. + /// + /// The type of configuration object to clean up. + /// The type of configuration object to process. + /// A selection expression to retrieve the configuration objects from the main configuration. + /// A list of currently available plugins. + /// A list of all existing configuration objects. + /// Returns true if the configuration was altered during cleanup; otherwise, false. + public static bool CleanLeftOverConfigurationObjects( + PluginConfigurationObjectType configObjectType, + Expression>> configObjectSelection, + IList availablePlugins, + IList configObjectList) where TClass : IConfigurationObject + { + var wasConfigurationChanged = false; + var configuredObjects = configObjectSelection.Compile()(SETTINGS_MANAGER.ConfigurationData); + foreach (var configuredObject in configuredObjects) + { + if(!configuredObject.IsEnterpriseConfiguration) + continue; + + var configObjectSourcePluginId = configuredObject.EnterpriseConfigurationPluginId; + if(configObjectSourcePluginId == Guid.Empty) + continue; + + var templateSourcePlugin = availablePlugins.FirstOrDefault(plugin => plugin.Id == configObjectSourcePluginId); + if(templateSourcePlugin is null) + { + LOG.LogWarning($"The configured object '{configuredObject.Name}' (id={configuredObject.Id}) is based on a plugin that is not available anymore. Removing the chat template from the settings."); + configuredObjects.Remove(configuredObject); + wasConfigurationChanged = true; + } + + if(!configObjectList.Any(configObject => + configObject.Type == configObjectType && + configObject.ConfigPluginId == configObjectSourcePluginId && + configObject.Id.ToString() == configuredObject.Id)) + { + LOG.LogWarning($"The configured object '{configuredObject.Name}' (id={configuredObject.Id}) is not present in the configuration plugin anymore. Removing the chat template from the settings."); + configuredObjects.Remove(configuredObject); + wasConfigurationChanged = true; + } + } + + return wasConfigurationChanged; + } } \ 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 5972b3a4..2553b537 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs @@ -92,10 +92,10 @@ public static partial class PluginFactory case { IsValid: false }: LOG.LogError($"Was not able to load plugin '{pluginMainFile}', because the Lua code is not a valid AI Studio plugin. There are {plugin.Issues.Count()} issues to fix. First issue is: {plugin.Issues.FirstOrDefault()}"); - #if DEBUG +#if DEBUG foreach (var pluginIssue in plugin.Issues) LOG.LogError($"Plugin issue: {pluginIssue}"); - #endif +#endif continue; case { IsMaintained: false }: @@ -125,89 +125,24 @@ public static partial class PluginFactory // // ========================================================= - // Next, we have to clean up our settings. It is possible that a configuration plugin was removed. - // We have to remove the related settings as well: + // Next, we have to clean up our settings. It is possible + // that a configuration plugin was removed. We have to + // remove the related settings as well: // ========================================================= // - var wasConfigurationChanged = false; - // // Check LLM providers: - // - #pragma warning disable MWAIS0001 - var configuredProviders = SETTINGS_MANAGER.ConfigurationData.Providers.ToList(); - foreach (var configuredProvider in configuredProviders) - { - if(!configuredProvider.IsEnterpriseConfiguration) - continue; - - var providerSourcePluginId = configuredProvider.EnterpriseConfigurationPluginId; - if(providerSourcePluginId == Guid.Empty) - continue; - - var providerSourcePlugin = AVAILABLE_PLUGINS.FirstOrDefault(plugin => plugin.Id == providerSourcePluginId); - if(providerSourcePlugin is null) - { - LOG.LogWarning($"The configured LLM provider '{configuredProvider.InstanceName}' (id={configuredProvider.Id}) is based on a plugin that is not available anymore. Removing the provider from the settings."); - SETTINGS_MANAGER.ConfigurationData.Providers.Remove(configuredProvider); - wasConfigurationChanged = true; - } - - if(!configObjectList.Any(configObject => - configObject.Type is PluginConfigurationObjectType.LLM_PROVIDER && - configObject.ConfigPluginId == providerSourcePluginId && - configObject.Id.ToString() == configuredProvider.Id)) - { - LOG.LogWarning($"The configured LLM provider '{configuredProvider.InstanceName}' (id={configuredProvider.Id}) is not present in the configuration plugin anymore. Removing the provider from the settings."); - SETTINGS_MANAGER.ConfigurationData.Providers.Remove(configuredProvider); - wasConfigurationChanged = true; - } - } - #pragma warning restore MWAIS0001 + var wasConfigurationChanged = PluginConfigurationObject.CleanLeftOverConfigurationObjects(PluginConfigurationObjectType.LLM_PROVIDER, x => x.Providers, AVAILABLE_PLUGINS, configObjectList); - // // 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; - } - - if(!configObjectList.Any(configObject => - configObject.Type is PluginConfigurationObjectType.CHAT_TEMPLATE && - configObject.ConfigPluginId == templateSourcePluginId && - configObject.Id.ToString() == configuredTemplate.Id)) - { - LOG.LogWarning($"The configured chat template '{configuredTemplate.Name}' (id={configuredTemplate.Id}) is not present in the configuration plugin anymore. Removing the chat template from the settings."); - SETTINGS_MANAGER.ConfigurationData.ChatTemplates.Remove(configuredTemplate); - wasConfigurationChanged = true; - } - } + if(PluginConfigurationObject.CleanLeftOverConfigurationObjects(PluginConfigurationObjectType.CHAT_TEMPLATE, x => x.ChatTemplates, AVAILABLE_PLUGINS, configObjectList)) + wasConfigurationChanged = true; - // - // ========================================================== - // Check all possible settings: - // ========================================================== - // - - // Check for updates, and if so, how often? + // Check for update behavior: if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.UpdateBehavior, AVAILABLE_PLUGINS)) wasConfigurationChanged = true; - // Allow the user to add providers? + // Check for users allowed to added providers: if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.AllowUserToAddProvider, AVAILABLE_PLUGINS)) wasConfigurationChanged = true;