mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-12 18:53:48 +00:00
Refactored configuration objects (#538)
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
This commit is contained in:
1 parent
be11efed67
commit
c1826426d9
31 files changed
+723
-472
No files matched your search
@@ -0,0 +1,23 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public abstract record ConfigurationBaseObject : IConfigurationObject
|
||||
{
|
||||
#region Implementation of IConfigurationObject
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string Id { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract uint Num { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string Name { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract bool IsEnterpriseConfiguration { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract Guid EnterpriseConfigurationPluginId { get; init; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a configuration object, such as a chat template or a LLM provider.
|
||||
/// </summary>
|
||||
public interface IConfigurationObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique ID of the configuration object.
|
||||
/// </summary>
|
||||
public string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The continuous number of the configuration object.
|
||||
/// </summary>
|
||||
public uint Num { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the configuration object.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this configuration object an enterprise configuration?
|
||||
/// </summary>
|
||||
public bool IsEnterpriseConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the enterprise configuration plugin.
|
||||
/// </summary>
|
||||
public Guid EnterpriseConfigurationPluginId { get; }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public sealed record NoConfigurationObject : ConfigurationBaseObject
|
||||
{
|
||||
public static readonly NoConfigurationObject INSTANCE = new();
|
||||
|
||||
private NoConfigurationObject()
|
||||
{
|
||||
this.Id = Guid.Empty.ToString();
|
||||
this.Name = "No Configuration";
|
||||
}
|
||||
|
||||
#region Overrides of ConfigurationBaseObject
|
||||
|
||||
public override string Id { get; init; }
|
||||
|
||||
public override uint Num { get; init; }
|
||||
|
||||
public override string Name { get; init; }
|
||||
|
||||
public override bool IsEnterpriseConfiguration { get; init; }
|
||||
|
||||
public override Guid EnterpriseConfigurationPluginId { get; init; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,21 +1,15 @@
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Chat;
|
||||
|
||||
using Lua;
|
||||
|
||||
using Host = AIStudio.Provider.SelfHosted.Host;
|
||||
using Model = AIStudio.Provider.Model;
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginType type) : PluginBase(isInternal, state, type)
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginConfiguration).Namespace, nameof(PluginConfiguration));
|
||||
private static readonly ILogger<PluginConfiguration> LOGGER = Program.LOGGER_FACTORY.CreateLogger<PluginConfiguration>();
|
||||
private static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||
|
||||
private readonly List<PluginConfigurationObject> configObjects = [];
|
||||
private List<PluginConfigurationObject> configObjects = [];
|
||||
|
||||
/// <summary>
|
||||
/// The list of configuration objects. Configuration objects are, e.g., providers or chat templates.
|
||||
@@ -51,317 +45,34 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// ===========================================
|
||||
// Configured settings
|
||||
// ===========================================
|
||||
//
|
||||
// Check for the main SETTINGS table:
|
||||
if (!mainTable.TryGetValue("SETTINGS", out var settingsValue) || !settingsValue.TryRead<LuaTable>(out var settingsTable))
|
||||
{
|
||||
message = TB("The SETTINGS table does not exist or is not a valid table.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for updates, and if so, how often?
|
||||
// Config: check for updates, and if so, how often?
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.UpdateBehavior, this.Id, settingsTable, dryRun);
|
||||
|
||||
// Allow the user to add providers?
|
||||
// Config: allow the user to add providers?
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToAddProvider, this.Id, settingsTable, dryRun);
|
||||
|
||||
//
|
||||
// Configured providers:
|
||||
//
|
||||
if (!mainTable.TryGetValue("LLM_PROVIDERS", out var providersValue) || !providersValue.TryRead<LuaTable>(out var providersTable))
|
||||
|
||||
// Handle configured LLM providers:
|
||||
if (!PluginConfigurationObject.TryParse(PluginConfigurationObjectType.LLM_PROVIDER, x => x.Providers, x => x.NextProviderNum, mainTable, this.Id, ref this.configObjects, dryRun))
|
||||
{
|
||||
message = TB("The LLM_PROVIDERS table does not exist or is not a valid table.");
|
||||
message = TB("At least one configured LLM provider is not valid or could not be parsed, or the LLM_PROVIDERS table does not exist.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Handle configured chat templates:
|
||||
if (!PluginConfigurationObject.TryParse(PluginConfigurationObjectType.CHAT_TEMPLATE, x => x.ChatTemplates, x => x.NextChatTemplateNum, mainTable, this.Id, ref this.configObjects, dryRun))
|
||||
{
|
||||
message = TB("At least one configured chat template is not valid or could not be parsed, or the CHAT_TEMPLATES table does not exist.");
|
||||
return false;
|
||||
}
|
||||
|
||||
message = string.Empty;
|
||||
var numberProviders = providersTable.ArrayLength;
|
||||
var configuredProviders = new List<Settings.Provider>(numberProviders);
|
||||
for (var i = 1; i <= numberProviders; i++)
|
||||
{
|
||||
var providerLuaTableValue = providersTable[i];
|
||||
if (!providerLuaTableValue.TryRead<LuaTable>(out var providerLuaTable))
|
||||
{
|
||||
LOGGER.LogWarning($"The LLM_PROVIDERS table at index {i} is not a valid table.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(this.TryReadProviderTable(i, providerLuaTable, out var provider))
|
||||
configuredProviders.Add(provider);
|
||||
else
|
||||
LOGGER.LogWarning($"The LLM_PROVIDERS table at index {i} does not contain a valid provider configuration.");
|
||||
}
|
||||
|
||||
//
|
||||
// Apply the configured providers to the system settings:
|
||||
//
|
||||
#pragma warning disable MWAIS0001
|
||||
foreach (var configuredProvider in configuredProviders)
|
||||
{
|
||||
// The iterating variable is immutable, so we need to create a local copy:
|
||||
var provider = configuredProvider;
|
||||
|
||||
// Store this provider in the config object list:
|
||||
this.configObjects.Add(new()
|
||||
{
|
||||
ConfigPluginId = this.Id,
|
||||
Id = Guid.Parse(provider.Id),
|
||||
Type = PluginConfigurationObjectType.LLM_PROVIDER,
|
||||
});
|
||||
|
||||
if (dryRun)
|
||||
continue;
|
||||
|
||||
var providerIndex = SETTINGS_MANAGER.ConfigurationData.Providers.FindIndex(p => p.Id == provider.Id);
|
||||
if (providerIndex > -1)
|
||||
{
|
||||
// Case: The provider already exists, we update it:
|
||||
var existingProvider = SETTINGS_MANAGER.ConfigurationData.Providers[providerIndex];
|
||||
provider = provider with { Num = existingProvider.Num }; // Keep the original number
|
||||
SETTINGS_MANAGER.ConfigurationData.Providers[providerIndex] = provider;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Case: The provider does not exist, we add it:
|
||||
provider = provider with { Num = SETTINGS_MANAGER.ConfigurationData.NextProviderNum++ };
|
||||
SETTINGS_MANAGER.ConfigurationData.Providers.Add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
// The iterating variable is immutable, so we need to create a local copy:
|
||||
var template = configuredTemplate;
|
||||
|
||||
// Store this provider in the config object list:
|
||||
this.configObjects.Add(new()
|
||||
{
|
||||
ConfigPluginId = this.Id,
|
||||
Id = Guid.Parse(template.Id),
|
||||
Type = PluginConfigurationObjectType.CHAT_TEMPLATE,
|
||||
});
|
||||
|
||||
if (dryRun)
|
||||
continue;
|
||||
|
||||
var tplIndex = SETTINGS_MANAGER.ConfigurationData.ChatTemplates.FindIndex(t => t.Id == template.Id);
|
||||
if (tplIndex > -1)
|
||||
{
|
||||
// Case: The template already exists, we update it:
|
||||
var existingTemplate = SETTINGS_MANAGER.ConfigurationData.ChatTemplates[tplIndex];
|
||||
template = template with { Num = existingTemplate.Num };
|
||||
SETTINGS_MANAGER.ConfigurationData.ChatTemplates[tplIndex] = template;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Case: The template does not exist, we add it:
|
||||
template = template with { Num = SETTINGS_MANAGER.ConfigurationData.NextChatTemplateNum++ };
|
||||
SETTINGS_MANAGER.ConfigurationData.ChatTemplates.Add(template);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryReadProviderTable(int idx, LuaTable table, out Settings.Provider provider)
|
||||
{
|
||||
provider = default;
|
||||
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
|
||||
{
|
||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid ID. The ID must be a valid GUID.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table.TryGetValue("InstanceName", out var instanceNameValue) || !instanceNameValue.TryRead<string>(out var instanceName))
|
||||
{
|
||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid instance name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table.TryGetValue("UsedLLMProvider", out var usedLLMProviderValue) || !usedLLMProviderValue.TryRead<string>(out var usedLLMProviderText) || !Enum.TryParse<LLMProviders>(usedLLMProviderText, true, out var usedLLMProvider))
|
||||
{
|
||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid LLM provider enum value.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table.TryGetValue("Host", out var hostValue) || !hostValue.TryRead<string>(out var hostText) || !Enum.TryParse<Host>(hostText, true, out var host))
|
||||
{
|
||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid host enum value.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table.TryGetValue("Hostname", out var hostnameValue) || !hostnameValue.TryRead<string>(out var hostname))
|
||||
{
|
||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid hostname.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table.TryGetValue("Model", out var modelValue) || !modelValue.TryRead<LuaTable>(out var modelTable))
|
||||
{
|
||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model table.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.TryReadModelTable(idx, modelTable, out var model))
|
||||
{
|
||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model configuration.");
|
||||
return false;
|
||||
}
|
||||
|
||||
provider = new()
|
||||
{
|
||||
Num = 0,
|
||||
Id = id.ToString(),
|
||||
InstanceName = instanceName,
|
||||
UsedLLMProvider = usedLLMProvider,
|
||||
Model = model,
|
||||
IsSelfHosted = usedLLMProvider is LLMProviders.SELF_HOSTED,
|
||||
IsEnterpriseConfiguration = true,
|
||||
EnterpriseConfigurationPluginId = this.Id,
|
||||
Hostname = hostname,
|
||||
Host = host
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryReadModelTable(int idx, LuaTable table, out Model model)
|
||||
{
|
||||
model = default;
|
||||
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var id))
|
||||
{
|
||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model ID.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table.TryGetValue("DisplayName", out var displayNameValue) || !displayNameValue.TryRead<string>(out var displayName))
|
||||
{
|
||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model display name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
template = new()
|
||||
{
|
||||
Num = 0,
|
||||
Id = id.ToString(),
|
||||
Name = name,
|
||||
SystemPrompt = systemPrompt,
|
||||
PredefinedUserPrompt = predefinedUserPrompt,
|
||||
ExampleConversation = ParseExampleConversation(idx, table),
|
||||
AllowProfileUsage = allowProfileUsage,
|
||||
IsEnterpriseConfiguration = true,
|
||||
EnterpriseConfigurationPluginId = this.Id
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static List<ContentBlock> ParseExampleConversation(int idx, LuaTable table)
|
||||
{
|
||||
var exampleConversation = new List<ContentBlock>();
|
||||
if (!table.TryGetValue("ExampleConversation", out var exConvValue) || !exConvValue.TryRead<LuaTable>(out var exConvTable))
|
||||
return exampleConversation;
|
||||
|
||||
var numBlocks = exConvTable.ArrayLength;
|
||||
for (var j = 1; j <= numBlocks; j++)
|
||||
{
|
||||
var blockValue = exConvTable[j];
|
||||
if (!blockValue.TryRead<LuaTable>(out var blockTable))
|
||||
{
|
||||
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} is not a valid table.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!blockTable.TryGetValue("Role", out var roleValue) || !roleValue.TryRead<string>(out var roleText) || !Enum.TryParse<ChatRole>(roleText, true, out var parsedRole))
|
||||
{
|
||||
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} does not contain a valid role.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!blockTable.TryGetValue("Content", out var contentValue) || !contentValue.TryRead<string>(out var content))
|
||||
{
|
||||
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} does not contain a valid content message.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} contains an empty content message.");
|
||||
continue;
|
||||
}
|
||||
|
||||
exampleConversation.Add(new ContentBlock
|
||||
{
|
||||
Time = DateTimeOffset.UtcNow,
|
||||
Role = parsedRole,
|
||||
Content = new ContentText { Text = content },
|
||||
ContentType = ContentType.TEXT,
|
||||
HideFromUser = true,
|
||||
});
|
||||
}
|
||||
|
||||
return exampleConversation;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
@@ -6,6 +13,9 @@ namespace AIStudio.Tools.PluginSystem;
|
||||
/// </summary>
|
||||
public sealed record PluginConfigurationObject
|
||||
{
|
||||
private static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger<PluginConfigurationObject>();
|
||||
|
||||
/// <summary>
|
||||
/// The id of the configuration plugin to which this configuration object belongs.
|
||||
/// </summary>
|
||||
@@ -20,4 +30,178 @@ public sealed record PluginConfigurationObject
|
||||
/// The type of the configuration object.
|
||||
/// </summary>
|
||||
public required PluginConfigurationObjectType Type { get; init; } = PluginConfigurationObjectType.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// Parses Lua table entries into configuration objects of the specified type, populating the
|
||||
/// provided list with results.
|
||||
/// </summary>
|
||||
/// <typeparam name="TClass">The type of configuration object to parse, which must
|
||||
/// inherit from <see cref="ConfigurationBaseObject"/>.</typeparam>
|
||||
/// <param name="configObjectType">The type of configuration object to process, as specified
|
||||
/// in <see cref="PluginConfigurationObjectType"/>.</param>
|
||||
/// <param name="configObjectSelection">An expression to retrieve existing configuration objects from
|
||||
/// the main configuration data.</param>
|
||||
/// <param name="nextConfigObjectNumSelection">An expression to retrieve the next available configuration
|
||||
/// object number from the main configuration data.</param>
|
||||
/// <param name="mainTable">The Lua table containing entries to parse into configuration objects.</param>
|
||||
/// <param name="configPluginId">The unique identifier of the plugin associated with the configuration
|
||||
/// objects being parsed.</param>
|
||||
/// <param name="configObjects">The list to populate with the parsed configuration objects.
|
||||
/// This parameter is passed by reference.</param>
|
||||
/// <param name="dryRun">Specifies whether to perform the operation as a dry run, where changes
|
||||
/// are not persisted.</param>
|
||||
/// <returns>Returns true if parsing succeeds and configuration objects are added
|
||||
/// to the list; otherwise, false.</returns>
|
||||
public static bool TryParse<TClass>(
|
||||
PluginConfigurationObjectType configObjectType,
|
||||
Expression<Func<Data, List<TClass>>> configObjectSelection,
|
||||
Expression<Func<Data, uint>> nextConfigObjectNumSelection,
|
||||
LuaTable mainTable,
|
||||
Guid configPluginId,
|
||||
ref List<PluginConfigurationObject> configObjects,
|
||||
bool dryRun
|
||||
) where TClass : ConfigurationBaseObject
|
||||
{
|
||||
var luaTableName = configObjectType switch
|
||||
{
|
||||
PluginConfigurationObjectType.LLM_PROVIDER => "LLM_PROVIDERS",
|
||||
PluginConfigurationObjectType.CHAT_TEMPLATE => "CHAT_TEMPLATES",
|
||||
PluginConfigurationObjectType.DATA_SOURCE => "DATA_SOURCES",
|
||||
PluginConfigurationObjectType.EMBEDDING_PROVIDER => "EMBEDDING_PROVIDERS",
|
||||
PluginConfigurationObjectType.PROFILE => "PROFILES",
|
||||
|
||||
_ => null,
|
||||
};
|
||||
|
||||
if (luaTableName is null)
|
||||
{
|
||||
LOG.LogError($"The configuration object type '{configObjectType}' is not supported yet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mainTable.TryGetValue(luaTableName, out var luaValue) || !luaValue.TryRead<LuaTable>(out var luaTable))
|
||||
{
|
||||
LOG.LogWarning($"The {luaTableName} table does not exist or is not a valid table.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var storedObjects = configObjectSelection.Compile()(SETTINGS_MANAGER.ConfigurationData);
|
||||
var numberObjects = luaTable.ArrayLength;
|
||||
ThreadSafeRandom? random = null;
|
||||
for (var i = 1; i <= numberObjects; i++)
|
||||
{
|
||||
var luaObjectTableValue = luaTable[i];
|
||||
if (!luaObjectTableValue.TryRead<LuaTable>(out var luaObjectTable))
|
||||
{
|
||||
LOG.LogWarning($"The {luaObjectTable} table at index {i} is not a valid table.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var (wasParsingSuccessful, configObject) = configObjectType switch
|
||||
{
|
||||
PluginConfigurationObjectType.LLM_PROVIDER => (Settings.Provider.TryParseProviderTable(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject != Settings.Provider.NONE, configurationObject),
|
||||
PluginConfigurationObjectType.CHAT_TEMPLATE => (ChatTemplate.TryParseChatTemplateTable(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject != ChatTemplate.NO_CHAT_TEMPLATE, configurationObject),
|
||||
|
||||
_ => (false, NoConfigurationObject.INSTANCE)
|
||||
};
|
||||
|
||||
if (wasParsingSuccessful)
|
||||
{
|
||||
// Store it in the config object list:
|
||||
configObjects.Add(new()
|
||||
{
|
||||
ConfigPluginId = configPluginId,
|
||||
Id = Guid.Parse(configObject.Id),
|
||||
Type = configObjectType,
|
||||
});
|
||||
|
||||
if (dryRun)
|
||||
continue;
|
||||
|
||||
var objectIndex = storedObjects.FindIndex(t => t.Id == configObject.Id);
|
||||
|
||||
// Case: The object already exists, we update it:
|
||||
if (objectIndex > -1)
|
||||
{
|
||||
var existingObject = storedObjects[objectIndex];
|
||||
configObject = configObject with { Num = existingObject.Num };
|
||||
storedObjects[objectIndex] = (TClass)configObject;
|
||||
}
|
||||
|
||||
// Case: The object does not exist, we have to add it
|
||||
else
|
||||
{
|
||||
if (nextConfigObjectNumSelection.TryIncrement(SETTINGS_MANAGER.ConfigurationData, IncrementType.POST) is { Success: true, UpdatedValue: var nextNum })
|
||||
{
|
||||
// Case: Increment the next number was successful
|
||||
configObject = configObject with { Num = nextNum };
|
||||
storedObjects.Add((TClass)configObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Case: The next number could not be incremented, we use a random number
|
||||
random ??= new ThreadSafeRandom();
|
||||
configObject = configObject with { Num = (uint)random.Next(500_000, 1_000_000) };
|
||||
storedObjects.Add((TClass)configObject);
|
||||
LOG.LogWarning($"The next number for the configuration object '{configObject.Name}' (id={configObject.Id}) could not be incremented. Using a random number instead.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
LOG.LogWarning($"The {luaObjectTable} table at index {i} does not contain a valid chat template configuration.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up configuration objects of a specified type that are no longer associated with any available plugin.
|
||||
/// </summary>
|
||||
/// <typeparam name="TClass">The type of configuration object to clean up.</typeparam>
|
||||
/// <param name="configObjectType">The type of configuration object to process.</param>
|
||||
/// <param name="configObjectSelection">A selection expression to retrieve the configuration objects from the main configuration.</param>
|
||||
/// <param name="availablePlugins">A list of currently available plugins.</param>
|
||||
/// <param name="configObjectList">A list of all existing configuration objects.</param>
|
||||
/// <returns>Returns true if the configuration was altered during cleanup; otherwise, false.</returns>
|
||||
public static bool CleanLeftOverConfigurationObjects<TClass>(
|
||||
PluginConfigurationObjectType configObjectType,
|
||||
Expression<Func<Data, List<TClass>>> configObjectSelection,
|
||||
IList<IAvailablePlugin> availablePlugins,
|
||||
IList<PluginConfigurationObject> configObjectList) where TClass : IConfigurationObject
|
||||
{
|
||||
var configuredObjects = configObjectSelection.Compile()(SETTINGS_MANAGER.ConfigurationData);
|
||||
var leftOverObjects = new List<TClass>();
|
||||
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.");
|
||||
leftOverObjects.Add(configuredObject);
|
||||
}
|
||||
|
||||
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.");
|
||||
leftOverObjects.Add(configuredObject);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove collected items after enumeration to avoid modifying the collection during iteration:
|
||||
var wasConfigurationChanged = leftOverObjects.Count > 0;
|
||||
foreach (var item in leftOverObjects.Distinct())
|
||||
configuredObjects.Remove(item);
|
||||
|
||||
return wasConfigurationChanged;
|
||||
}
|
||||
}
|
||||
@@ -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<DataApp, UpdateBehavior>(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<DataApp, bool>(x => x.App, x => x.AllowUserToAddProvider, AVAILABLE_PLUGINS))
|
||||
wasConfigurationChanged = true;
|
||||
|
||||
|
||||
Reference in new issue
Block a user