mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-15 01:14:33 +00:00
Merge branch 'main' into chunk-data
This commit is contained in:
commit
2e06af13e0
133 files changed
+3704
-968
No files matched your search
@@ -151,7 +151,7 @@ public record ConfigMeta<TClass, TValue> : ConfigMetaBase
|
||||
/// </summary>
|
||||
private void Reset()
|
||||
{
|
||||
var configInstance = this.ConfigSelection.Compile().Invoke(SETTINGS_MANAGER.ConfigurationData);
|
||||
var configInstance = this.ConfigSelection.Compile().Invoke(SettingsManagerAccess.ConfigurationData);
|
||||
var memberExpression = this.PropertyExpression.GetMemberExpression();
|
||||
if (memberExpression.Member is System.Reflection.PropertyInfo propertyInfo)
|
||||
propertyInfo.SetValue(configInstance, this.Default);
|
||||
@@ -163,7 +163,7 @@ public record ConfigMeta<TClass, TValue> : ConfigMetaBase
|
||||
/// <param name="value">The value to set for the configuration property.</param>
|
||||
public void SetValue(TValue value)
|
||||
{
|
||||
var configInstance = this.ConfigSelection.Compile().Invoke(SETTINGS_MANAGER.ConfigurationData);
|
||||
var configInstance = this.ConfigSelection.Compile().Invoke(SettingsManagerAccess.ConfigurationData);
|
||||
var memberExpression = this.PropertyExpression.GetMemberExpression();
|
||||
if (memberExpression.Member is System.Reflection.PropertyInfo propertyInfo)
|
||||
propertyInfo.SetValue(configInstance, value);
|
||||
@@ -174,7 +174,7 @@ public record ConfigMeta<TClass, TValue> : ConfigMetaBase
|
||||
/// </summary>
|
||||
public TValue GetValue()
|
||||
{
|
||||
var configInstance = this.ConfigSelection.Compile().Invoke(SETTINGS_MANAGER.ConfigurationData);
|
||||
var configInstance = this.ConfigSelection.Compile().Invoke(SettingsManagerAccess.ConfigurationData);
|
||||
var memberExpression = this.PropertyExpression.GetMemberExpression();
|
||||
if (memberExpression.Member is System.Reflection.PropertyInfo propertyInfo && propertyInfo.GetValue(configInstance) is TValue value)
|
||||
return value;
|
||||
|
||||
@@ -2,5 +2,5 @@ namespace AIStudio.Settings;
|
||||
|
||||
public abstract record ConfigMetaBase : IConfig
|
||||
{
|
||||
protected static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||
protected static SettingsManager SettingsManagerAccess => Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||
}
|
||||
@@ -271,8 +271,8 @@ public static class ConfigurationSelectDataFactory
|
||||
public static IEnumerable<ConfigurationSelectData<ConfidenceLevel>> GetConfidenceLevelsData(SettingsManager settingsManager, bool restrictToGlobalMinimum = false)
|
||||
{
|
||||
var minimumLevel = ConfidenceLevel.NONE;
|
||||
if(restrictToGlobalMinimum && settingsManager.ConfigurationData.LLMProviders is { EnforceGlobalMinimumConfidence: true, GlobalMinimumConfidence: not ConfidenceLevel.NONE and not ConfidenceLevel.UNKNOWN })
|
||||
minimumLevel = settingsManager.ConfigurationData.LLMProviders.GlobalMinimumConfidence;
|
||||
if(restrictToGlobalMinimum && settingsManager.ConfigurationData.Confidence is { EnforceGlobalMinimumConfidence: true, GlobalMinimumConfidence: not ConfidenceLevel.NONE and not ConfidenceLevel.UNKNOWN })
|
||||
minimumLevel = settingsManager.ConfigurationData.Confidence.GlobalMinimumConfidence;
|
||||
|
||||
foreach (var level in Enum.GetValues<ConfidenceLevel>())
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ public sealed class Data
|
||||
/// The version of the settings file. Allows us to upgrade the settings
|
||||
/// when a new version is available.
|
||||
/// </summary>
|
||||
public Version Version { get; init; } = Version.V5;
|
||||
public Version Version { get; init; } = Version.V6;
|
||||
|
||||
/// <summary>
|
||||
/// List of configured providers.
|
||||
@@ -19,9 +19,14 @@ public sealed class Data
|
||||
public List<Provider> Providers { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Settings concerning the LLM providers.
|
||||
/// Settings concerning confidence levels.
|
||||
/// </summary>
|
||||
public DataLLMProviders LLMProviders { get; init; } = new();
|
||||
public DataConfidence Confidence { get; init; } = new(x => x.Confidence);
|
||||
|
||||
/// <summary>
|
||||
/// Settings concerning data source security checks.
|
||||
/// </summary>
|
||||
public DataSourceSecuritySettings DataSourceSecurity { get; init; } = new(x => x.DataSourceSecurity);
|
||||
|
||||
/// <summary>
|
||||
/// A collection of embedding providers configured.
|
||||
@@ -100,7 +105,7 @@ public sealed class Data
|
||||
|
||||
public DataApp App { get; init; } = new(x => x.App);
|
||||
|
||||
public DataChat Chat { get; init; } = new();
|
||||
public DataChat Chat { get; init; } = new(x => x.Chat);
|
||||
|
||||
public DataWorkspace Workspace { get; init; } = new();
|
||||
|
||||
|
||||
@@ -57,6 +57,11 @@ public sealed class DataApp(Expression<Func<Data, DataApp>>? configSelection = n
|
||||
/// </summary>
|
||||
public StartPage StartPage { get; set; } = ManagedConfiguration.Register(configSelection, n => n.StartPage, StartPage.HOME);
|
||||
|
||||
/// <summary>
|
||||
/// Should the built-in introduction be visible on the home page?
|
||||
/// </summary>
|
||||
public bool ShowIntroduction { get; set; } = ManagedConfiguration.Register(configSelection, n => n.ShowIntroduction, true);
|
||||
|
||||
/// <summary>
|
||||
/// Should the quick start guide be visible on the home page?
|
||||
/// </summary>
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public sealed class DataChat
|
||||
public sealed class DataChat(Expression<Func<Data, DataChat>>? configSelection = null)
|
||||
{
|
||||
/// <summary>
|
||||
/// The default constructor for the JSON deserializer.
|
||||
/// </summary>
|
||||
public DataChat() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shortcuts to send the input to the AI.
|
||||
/// </summary>
|
||||
@@ -25,22 +34,22 @@ public sealed class DataChat
|
||||
/// <summary>
|
||||
/// Preselect any chat options?
|
||||
/// </summary>
|
||||
public bool PreselectOptions { get; set; }
|
||||
public bool PreselectOptions { get; set; } = ManagedConfiguration.Register(configSelection, n => n.PreselectOptions, false);
|
||||
|
||||
/// <summary>
|
||||
/// Should we preselect a provider for the chat?
|
||||
/// </summary>
|
||||
public string PreselectedProvider { get; set; } = string.Empty;
|
||||
public string PreselectedProvider { get; set; } = ManagedConfiguration.Register(configSelection, n => n.PreselectedProvider, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Preselect a profile?
|
||||
/// </summary>
|
||||
public string PreselectedProfile { get; set; } = string.Empty;
|
||||
public string PreselectedProfile { get; set; } = ManagedConfiguration.Register(configSelection, n => n.PreselectedProfile, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Preselect a chat template?
|
||||
/// </summary>
|
||||
public string PreselectedChatTemplate { get; set; } = string.Empty;
|
||||
public string PreselectedChatTemplate { get; set; } = ManagedConfiguration.Register(configSelection, n => n.PreselectedChatTemplate, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Should we preselect data sources options for a created chat?
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public sealed class DataConfidence(Expression<Func<Data, DataConfidence>>? configSelection = null)
|
||||
{
|
||||
/// <summary>
|
||||
/// The default constructor for the JSON deserializer.
|
||||
/// </summary>
|
||||
public DataConfidence() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should we enforce a global minimum confidence level?
|
||||
/// </summary>
|
||||
public bool EnforceGlobalMinimumConfidence { get; set; } = ManagedConfiguration.Register(configSelection, n => n.EnforceGlobalMinimumConfidence, false);
|
||||
|
||||
/// <summary>
|
||||
/// The global minimum confidence level to enforce.
|
||||
/// </summary>
|
||||
public ConfidenceLevel GlobalMinimumConfidence { get; set; } = ManagedConfiguration.Register(configSelection, n => n.GlobalMinimumConfidence, ConfidenceLevel.NONE);
|
||||
|
||||
/// <summary>
|
||||
/// Should we show the provider confidence level?
|
||||
/// </summary>
|
||||
public bool ShowProviderConfidence { get; set; } = ManagedConfiguration.Register(configSelection, n => n.ShowProviderConfidence, true);
|
||||
|
||||
/// <summary>
|
||||
/// Which confidence scheme to use.
|
||||
/// </summary>
|
||||
public ConfidenceSchemes ConfidenceScheme { get; set; } = ManagedConfiguration.Register(configSelection, n => n.ConfidenceScheme, ConfidenceSchemes.TRUST_ALL);
|
||||
|
||||
/// <summary>
|
||||
/// Provide custom confidence levels for each provider family.
|
||||
/// </summary>
|
||||
public Dictionary<LLMProviders, ConfidenceLevel> CustomConfidenceScheme { get; set; } = ManagedConfiguration.Register(configSelection, n => n.CustomConfidenceScheme, []);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public sealed record DataIntroduction : ILivePluginContent
|
||||
{
|
||||
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger<DataIntroduction>();
|
||||
|
||||
/// <summary>
|
||||
/// The stable ID of the introduction.
|
||||
/// </summary>
|
||||
public string Id { get; private init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the enterprise configuration plugin that provides this introduction.
|
||||
/// </summary>
|
||||
public Guid EnterpriseConfigurationPluginId { get; private init; } = Guid.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The title shown to the user.
|
||||
/// </summary>
|
||||
public string Title { get; private init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The configured version string shown to the user.
|
||||
/// </summary>
|
||||
public string VersionText { get; private init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The sort index used on the home page.
|
||||
/// </summary>
|
||||
public int Index { get; private init; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The Markdown content shown to the user.
|
||||
/// </summary>
|
||||
public string Markdown { get; private init; } = string.Empty;
|
||||
|
||||
public static bool TryParseConfiguration(int idx, LuaTable table, Guid configPluginId, out DataIntroduction introduction)
|
||||
{
|
||||
introduction = new DataIntroduction();
|
||||
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
|
||||
{
|
||||
LOG.LogWarning("The configured introduction {IntroductionIndex} does not contain a valid ID. The ID must be a valid GUID.", idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table.TryGetValue("Title", out var titleValue) || !titleValue.TryRead<string>(out var title) || string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
LOG.LogWarning("The configured introduction {IntroductionIndex} does not contain a valid Title field.", idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table.TryGetValue("Version", out var versionValue) || !versionValue.TryRead<string>(out var versionText) || string.IsNullOrWhiteSpace(versionText))
|
||||
{
|
||||
LOG.LogWarning("The configured introduction {IntroductionIndex} does not contain a valid Version field.", idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table.TryGetValue("Markdown", out var markdownValue) || !markdownValue.TryRead<string>(out var markdown) || string.IsNullOrWhiteSpace(markdown))
|
||||
{
|
||||
LOG.LogWarning("The configured introduction {IntroductionIndex} does not contain a valid Markdown field.", idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
var index = 1;
|
||||
if (table.TryGetValue("Index", out var indexValue) && !indexValue.TryRead(out index))
|
||||
{
|
||||
LOG.LogWarning("The configured introduction {IntroductionIndex} does not contain a valid Index field. The Index must be an integer.", idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
introduction = new DataIntroduction
|
||||
{
|
||||
Id = id.ToString(),
|
||||
Title = title,
|
||||
VersionText = versionText,
|
||||
Index = index,
|
||||
Markdown = AIStudio.Tools.Markdown.RemoveSharedIndentation(markdown),
|
||||
EnterpriseConfigurationPluginId = configPluginId,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public sealed record DataMandatoryInfo
|
||||
public sealed record DataMandatoryInfo : ILivePluginContent
|
||||
{
|
||||
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger<DataMandatoryInfo>();
|
||||
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
namespace AIStudio.Settings.DataModel.PreviousModels;
|
||||
|
||||
public sealed class DataLLMProviders
|
||||
public sealed class DataLLMProvidersV5
|
||||
{
|
||||
/// <summary>
|
||||
/// Should we enforce a global minimum confidence level?
|
||||
@@ -25,7 +25,7 @@ public sealed class DataLLMProviders
|
||||
public ConfidenceSchemes ConfidenceScheme { get; set; } = ConfidenceSchemes.TRUST_ALL;
|
||||
|
||||
/// <summary>
|
||||
/// Provide custom confidence levels for each LLM provider.
|
||||
/// Provide custom confidence levels for each provider family.
|
||||
/// </summary>
|
||||
public Dictionary<LLMProviders, ConfidenceLevel> CustomConfidenceScheme { get; set; } = new();
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public sealed class DataV4
|
||||
/// <summary>
|
||||
/// Settings concerning the LLM providers.
|
||||
/// </summary>
|
||||
public DataLLMProviders LLMProviders { get; init; } = new();
|
||||
public DataLLMProvidersV5 LLMProviders { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of configured profiles.
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
using AIStudio.Tools.PluginSystem.Assistants;
|
||||
|
||||
namespace AIStudio.Settings.DataModel.PreviousModels;
|
||||
|
||||
public sealed class DataV5
|
||||
{
|
||||
/// <summary>
|
||||
/// The version of the settings file. Allows us to upgrade the settings
|
||||
/// when a new version is available.
|
||||
/// </summary>
|
||||
public Version Version { get; init; } = Version.V5;
|
||||
|
||||
/// <summary>
|
||||
/// List of configured providers.
|
||||
/// </summary>
|
||||
public List<AIStudio.Settings.Provider> Providers { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Settings concerning the LLM providers.
|
||||
/// </summary>
|
||||
public DataLLMProvidersV5 LLMProviders { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// A collection of embedding providers configured.
|
||||
/// </summary>
|
||||
public List<EmbeddingProvider> EmbeddingProviders { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// A collection of speech providers configured.
|
||||
/// </summary>
|
||||
public List<TranscriptionProvider> TranscriptionProviders { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// A collection of data sources configured.
|
||||
/// </summary>
|
||||
public List<IDataSource> DataSources { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// List of configured profiles.
|
||||
/// </summary>
|
||||
public List<Profile> Profiles { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// List of configured chat templates.
|
||||
/// </summary>
|
||||
public List<ChatTemplate> ChatTemplates { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// List of enabled plugins.
|
||||
/// </summary>
|
||||
public List<Guid> EnabledPlugins { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Metadata for managed settings that use a plugin-provided editable default.
|
||||
/// </summary>
|
||||
public Dictionary<string, ManagedEditableDefaultState> ManagedEditableDefaults { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Cached audit results for assistant plugins.
|
||||
/// </summary>
|
||||
public List<PluginAssistantAudit> AssistantPluginAudits { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The next provider number to use.
|
||||
/// </summary>
|
||||
public uint NextProviderNum { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The next embedding provider number to use.
|
||||
/// </summary>
|
||||
public uint NextEmbeddingNum { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The next transcription provider number to use.
|
||||
/// </summary>
|
||||
public uint NextTranscriptionNum { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The next data source number to use.
|
||||
/// </summary>
|
||||
public uint NextDataSourceNum { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The next profile number to use.
|
||||
/// </summary>
|
||||
public uint NextProfileNum { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The next chat template number to use.
|
||||
/// </summary>
|
||||
public uint NextChatTemplateNum { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The next document analysis policy number to use.
|
||||
/// </summary>
|
||||
public uint NextDocumentAnalysisPolicyNum { get; set; } = 1;
|
||||
|
||||
public DataApp App { get; init; } = new(x => x.App);
|
||||
|
||||
public DataChat Chat { get; init; } = new();
|
||||
|
||||
public DataWorkspace Workspace { get; init; } = new();
|
||||
|
||||
public DataIconFinder IconFinder { get; init; } = new();
|
||||
|
||||
public DataTranslation Translation { get; init; } = new();
|
||||
|
||||
public DataCoding Coding { get; init; } = new();
|
||||
|
||||
public DataERI ERI { get; init; } = new();
|
||||
|
||||
public DataDocumentAnalysis DocumentAnalysis { get; init; } = new();
|
||||
|
||||
public DataMandatoryInformation MandatoryInformation { get; init; } = new();
|
||||
|
||||
public DataTextSummarizer TextSummarizer { get; init; } = new();
|
||||
|
||||
public DataTextContentCleaner TextContentCleaner { get; init; } = new();
|
||||
|
||||
public DataAgentDataSourceSelection AgentDataSourceSelection { get; init; } = new();
|
||||
|
||||
public DataAgentRetrievalContextValidation AgentRetrievalContextValidation { get; init; } = new();
|
||||
|
||||
public DataAssistantPluginAudit AssistantPluginAudit { get; init; } = new(x => x.AssistantPluginAudit);
|
||||
|
||||
public DataAgenda Agenda { get; init; } = new();
|
||||
|
||||
public DataGrammarSpelling GrammarSpelling { get; init; } = new();
|
||||
|
||||
public DataRewriteImprove RewriteImprove { get; init; } = new();
|
||||
|
||||
public DataPromptOptimizer PromptOptimizer { get; init; } = new();
|
||||
|
||||
public DataEMail EMail { get; init; } = new();
|
||||
|
||||
public DataSlideBuilder SlideBuilder { get; init; } = new();
|
||||
|
||||
public DataLegalCheck LegalCheck { get; init; } = new();
|
||||
|
||||
public DataSynonyms Synonyms { get; init; } = new();
|
||||
|
||||
public DataMyTasks MyTasks { get; init; } = new();
|
||||
|
||||
public DataJobPostings JobPostings { get; init; } = new();
|
||||
|
||||
public DataBiasOfTheDay BiasOfTheDay { get; init; } = new();
|
||||
|
||||
public DataI18N I18N { get; init; } = new();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public sealed class DataSourceSecuritySettings(Expression<Func<Data, DataSourceSecuritySettings>>? configSelection = null)
|
||||
{
|
||||
/// <summary>
|
||||
/// The default constructor for the JSON deserializer.
|
||||
/// </summary>
|
||||
public DataSourceSecuritySettings() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provider instance IDs trusted by an organization for data-source security checks.
|
||||
/// </summary>
|
||||
public HashSet<string> TrustedProviderIds { get; set; } = ManagedConfiguration.Register(configSelection, n => n.TrustedProviderIds, []);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public static class DataSourceSecurityTrustExtensions
|
||||
{
|
||||
public static bool IsTrustedForDataSourceSecurityChecks(this Provider provider, SettingsManager settingsManager)
|
||||
{
|
||||
if (provider == Provider.NONE)
|
||||
return false;
|
||||
|
||||
return provider.IsSelfHosted || provider.IsTrustedByConfiguration(settingsManager);
|
||||
}
|
||||
|
||||
public static bool IsTrustedForDataSourceSecurityChecks(this EmbeddingProvider provider, SettingsManager settingsManager)
|
||||
{
|
||||
if (provider == EmbeddingProvider.NONE)
|
||||
return false;
|
||||
|
||||
return provider.IsSelfHosted || provider.IsTrustedByConfiguration(settingsManager);
|
||||
}
|
||||
|
||||
public static bool IsTrustedForDataSourceSecurityChecks(this TranscriptionProvider provider, SettingsManager settingsManager)
|
||||
{
|
||||
if (provider == TranscriptionProvider.NONE)
|
||||
return false;
|
||||
|
||||
return provider.IsSelfHosted || provider.IsTrustedByConfiguration(settingsManager);
|
||||
}
|
||||
|
||||
public static bool IsTrustedForDataSourceSecurityChecks(this IProvider provider, SettingsManager settingsManager)
|
||||
{
|
||||
if (provider is NoProvider)
|
||||
return false;
|
||||
|
||||
return provider.Provider is LLMProviders.SELF_HOSTED || IsTrustedProviderId(provider.ConfiguredProviderId, settingsManager);
|
||||
}
|
||||
|
||||
public static bool IsTrustedByConfiguration(this Provider provider, SettingsManager settingsManager) => IsTrustedProviderId(provider.Id, settingsManager);
|
||||
|
||||
public static bool IsTrustedByConfiguration(this EmbeddingProvider provider, SettingsManager settingsManager) => IsTrustedProviderId(provider.Id, settingsManager);
|
||||
|
||||
public static bool IsTrustedByConfiguration(this TranscriptionProvider provider, SettingsManager settingsManager) => IsTrustedProviderId(provider.Id, settingsManager);
|
||||
|
||||
private static bool IsTrustedProviderId(string providerId, SettingsManager settingsManager)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(providerId))
|
||||
return false;
|
||||
|
||||
return settingsManager.ConfigurationData.DataSourceSecurity.TrustedProviderIds.Any(id => string.Equals(id, providerId, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public sealed record EmbeddingProvider(
|
||||
|
||||
/// <inheritdoc />
|
||||
[JsonIgnore]
|
||||
public string SecretId => this.IsEnterpriseConfiguration ? $"{ISecretId.ENTERPRISE_KEY_PREFIX}::{this.UsedLLMProvider.ToName()}" : this.UsedLLMProvider.ToName();
|
||||
public string SecretId => this.IsEnterpriseConfiguration ? $"{ISecretId.ENTERPRISE_KEY_PREFIX}::{this.UsedLLMProvider.ToSecretId()}" : this.UsedLLMProvider.ToSecretId();
|
||||
|
||||
/// <inheritdoc />
|
||||
[JsonIgnore]
|
||||
@@ -134,7 +134,7 @@ public sealed record EmbeddingProvider(
|
||||
{
|
||||
// Queue the API key for storage in the OS keyring:
|
||||
PendingEnterpriseApiKeys.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToName()}",
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToSecretId()}",
|
||||
name,
|
||||
decryptedApiKey,
|
||||
SecretStoreType.EMBEDDING_PROVIDER));
|
||||
|
||||
@@ -38,14 +38,14 @@ public static partial class ManagedConfiguration
|
||||
//
|
||||
// Handle configured enum values
|
||||
//
|
||||
|
||||
|
||||
// Check if that configuration was registered:
|
||||
if(!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
var successful = false;
|
||||
var configuredValue = configMeta.Default;
|
||||
|
||||
|
||||
// Step 1 -- try to read the Lua value out of the Lua table:
|
||||
if(settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredEnumValue))
|
||||
{
|
||||
@@ -60,7 +60,7 @@ public static partial class ManagedConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(dryRun)
|
||||
return successful;
|
||||
|
||||
@@ -98,14 +98,14 @@ public static partial class ManagedConfiguration
|
||||
//
|
||||
// Handle configured ISpanParsable values
|
||||
//
|
||||
|
||||
|
||||
// Check if that configuration was registered:
|
||||
if(!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
|
||||
var successful = false;
|
||||
var configuredValue = configMeta.Default;
|
||||
|
||||
|
||||
// Step 1 -- try to read the Lua value out of the Lua table:
|
||||
if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaValue))
|
||||
{
|
||||
@@ -119,7 +119,7 @@ public static partial class ManagedConfiguration
|
||||
successful = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Step 2b -- try to read the Lua value:
|
||||
if(configuredLuaValue.TryRead<TValue>(out var configuredLuaValueInstance))
|
||||
{
|
||||
@@ -135,7 +135,7 @@ public static partial class ManagedConfiguration
|
||||
var managedMode = ReadManagedConfigurationMode(propertyExpression, settings);
|
||||
return HandleParsedScalarValue(configPluginId, dryRun, successful, configMeta, configuredValue, managedMode, settingName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to process the configuration settings from a Lua table for string values.
|
||||
/// </summary>
|
||||
@@ -189,14 +189,14 @@ public static partial class ManagedConfiguration
|
||||
//
|
||||
// Handle configured string values
|
||||
//
|
||||
|
||||
|
||||
// Check if that configuration was registered:
|
||||
if(!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
|
||||
var successful = false;
|
||||
var configuredValue = configMeta.Default;
|
||||
|
||||
|
||||
// Step 1 -- try to read the Lua value out of the Lua table:
|
||||
if(settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredTextValue))
|
||||
{
|
||||
@@ -210,7 +210,7 @@ public static partial class ManagedConfiguration
|
||||
successful = Guid.TryParse(configuredText, out var id);
|
||||
configuredValue = successful ? id.ToString().ToLowerInvariant() : configuredText;
|
||||
break;
|
||||
|
||||
|
||||
// Case: the read string is just a string:
|
||||
case string:
|
||||
configuredValue = configuredText;
|
||||
@@ -219,7 +219,7 @@ public static partial class ManagedConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var settingName = SettingName(propertyExpression);
|
||||
var managedMode = ReadManagedConfigurationMode(propertyExpression, settings);
|
||||
return HandleParsedScalarValue(configPluginId, dryRun, successful, configMeta, configuredValue, managedMode, settingName);
|
||||
@@ -273,13 +273,13 @@ public static partial class ManagedConfiguration
|
||||
// Determine the length of the Lua table and prepare a list to hold the parsed values:
|
||||
var len = valueTable.ArrayLength;
|
||||
var list = new List<TValue>(len);
|
||||
|
||||
|
||||
// Iterate over each entry in the Lua table:
|
||||
for (var index = 1; index <= len; index++)
|
||||
{
|
||||
// Retrieve the Lua value at the current index:
|
||||
var value = valueTable[index];
|
||||
|
||||
|
||||
// Step 2a -- try to read the Lua value as a string:
|
||||
if (value.Type is LuaValueType.String && value.TryRead<string>(out var configuredLuaValueText))
|
||||
{
|
||||
@@ -304,7 +304,7 @@ public static partial class ManagedConfiguration
|
||||
}
|
||||
|
||||
// ReSharper restore MethodOverloadWithOptionalParameter
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to process the configuration settings from a Lua table for enum list types.
|
||||
/// </summary>
|
||||
@@ -333,14 +333,14 @@ public static partial class ManagedConfiguration
|
||||
//
|
||||
// Handle configured enum lists
|
||||
//
|
||||
|
||||
|
||||
// Check if that configuration was registered:
|
||||
if(!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
|
||||
var successful = false;
|
||||
var configuredValue = configMeta.Default;
|
||||
|
||||
|
||||
// Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
|
||||
if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
|
||||
configuredLuaList.Type is LuaValueType.Table &&
|
||||
@@ -349,13 +349,13 @@ public static partial class ManagedConfiguration
|
||||
// Determine the length of the Lua table and prepare a list to hold the parsed values:
|
||||
var len = valueTable.ArrayLength;
|
||||
var list = new List<TValue>(len);
|
||||
|
||||
|
||||
// Iterate over each entry in the Lua table:
|
||||
for (var index = 1; index <= len; index++)
|
||||
{
|
||||
// Retrieve the Lua value at the current index:
|
||||
var value = valueTable[index];
|
||||
|
||||
|
||||
// Step 2 -- try to read the Lua value as a string:
|
||||
if (value.Type is LuaValueType.String && value.TryRead<string>(out var configuredLuaValueText))
|
||||
{
|
||||
@@ -364,17 +364,17 @@ public static partial class ManagedConfiguration
|
||||
list.Add((TValue)configuredEnum);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
configuredValue = list;
|
||||
successful = true;
|
||||
}
|
||||
|
||||
|
||||
if(dryRun)
|
||||
return successful;
|
||||
|
||||
|
||||
return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to process the configuration settings from a Lua table for string list types.
|
||||
/// </summary>
|
||||
@@ -400,14 +400,14 @@ public static partial class ManagedConfiguration
|
||||
//
|
||||
// Handle configured string lists
|
||||
//
|
||||
|
||||
|
||||
// Check if that configuration was registered:
|
||||
if(!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
|
||||
var successful = false;
|
||||
var configuredValue = configMeta.Default;
|
||||
|
||||
|
||||
// Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
|
||||
if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
|
||||
configuredLuaList.Type is LuaValueType.Table &&
|
||||
@@ -416,25 +416,25 @@ public static partial class ManagedConfiguration
|
||||
// Determine the length of the Lua table and prepare a list to hold the parsed values:
|
||||
var len = valueTable.ArrayLength;
|
||||
var list = new List<string>(len);
|
||||
|
||||
|
||||
// Iterate over each entry in the Lua table:
|
||||
for (var index = 1; index <= len; index++)
|
||||
{
|
||||
// Retrieve the Lua value at the current index:
|
||||
var value = valueTable[index];
|
||||
|
||||
|
||||
// Step 2 -- try to read the Lua value as a string:
|
||||
if (value.Type is LuaValueType.String && value.TryRead<string>(out var configuredLuaValueText))
|
||||
list.Add(configuredLuaValueText);
|
||||
}
|
||||
|
||||
|
||||
configuredValue = list;
|
||||
successful = true;
|
||||
}
|
||||
|
||||
|
||||
if(dryRun)
|
||||
return successful;
|
||||
|
||||
|
||||
return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
|
||||
}
|
||||
|
||||
@@ -486,13 +486,13 @@ public static partial class ManagedConfiguration
|
||||
// Determine the length of the Lua table and prepare a set to hold the parsed values:
|
||||
var len = valueTable.ArrayLength;
|
||||
var set = new HashSet<TValue>(len);
|
||||
|
||||
|
||||
// Iterate over each entry in the Lua table:
|
||||
for (var index = 1; index <= len; index++)
|
||||
{
|
||||
// Retrieve the Lua value at the current index:
|
||||
var value = valueTable[index];
|
||||
|
||||
|
||||
// Step 2a -- try to read the Lua value as a string:
|
||||
if (value.Type is LuaValueType.String && value.TryRead<string>(out var configuredLuaValueText))
|
||||
{
|
||||
@@ -517,7 +517,7 @@ public static partial class ManagedConfiguration
|
||||
}
|
||||
|
||||
// ReSharper restore MethodOverloadWithOptionalParameter
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to process the configuration settings from a Lua table for enum set types.
|
||||
/// </summary>
|
||||
@@ -546,14 +546,14 @@ public static partial class ManagedConfiguration
|
||||
//
|
||||
// Handle configured enum sets
|
||||
//
|
||||
|
||||
|
||||
// Check if that configuration was registered:
|
||||
if(!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
|
||||
var successful = false;
|
||||
var configuredValue = configMeta.Default;
|
||||
|
||||
|
||||
// Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
|
||||
if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
|
||||
configuredLuaList.Type is LuaValueType.Table &&
|
||||
@@ -562,13 +562,13 @@ public static partial class ManagedConfiguration
|
||||
// Determine the length of the Lua table and prepare a set to hold the parsed values:
|
||||
var len = valueTable.ArrayLength;
|
||||
var set = new HashSet<TValue>(len);
|
||||
|
||||
|
||||
// Iterate over each entry in the Lua table:
|
||||
for (var index = 1; index <= len; index++)
|
||||
{
|
||||
// Retrieve the Lua value at the current index:
|
||||
var value = valueTable[index];
|
||||
|
||||
|
||||
// Step 2 -- try to read the Lua value as a string:
|
||||
if (value.Type is LuaValueType.String && value.TryRead<string>(out var configuredLuaValueText))
|
||||
{
|
||||
@@ -577,14 +577,14 @@ public static partial class ManagedConfiguration
|
||||
set.Add((TValue)configuredEnum);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
configuredValue = set;
|
||||
successful = true;
|
||||
}
|
||||
|
||||
|
||||
if(dryRun)
|
||||
return successful;
|
||||
|
||||
|
||||
return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
|
||||
}
|
||||
|
||||
@@ -629,13 +629,13 @@ public static partial class ManagedConfiguration
|
||||
// Determine the length of the Lua table and prepare a set to hold the parsed values:
|
||||
var len = valueTable.ArrayLength;
|
||||
var set = new HashSet<TValue>(len);
|
||||
|
||||
|
||||
// Iterate over each entry in the Lua table:
|
||||
for (var index = 1; index <= len; index++)
|
||||
{
|
||||
// Retrieve the Lua value at the current index:
|
||||
var value = valueTable[index];
|
||||
|
||||
|
||||
// Step 2 -- try to read the Lua value as a string:
|
||||
if (value.Type is LuaValueType.String && value.TryRead<string>(out var configuredLuaValueText))
|
||||
{
|
||||
@@ -654,7 +654,7 @@ public static partial class ManagedConfiguration
|
||||
|
||||
if (successful)
|
||||
{
|
||||
var configInstance = configSelection.Compile().Invoke(SETTINGS_MANAGER.ConfigurationData);
|
||||
var configInstance = configSelection.Compile().Invoke(SettingsManagerAccess.ConfigurationData);
|
||||
var currentValue = propertyExpression.Compile().Invoke(configInstance);
|
||||
var merged = new HashSet<TValue>(currentValue);
|
||||
merged.UnionWith(configuredValue);
|
||||
@@ -671,7 +671,7 @@ public static partial class ManagedConfiguration
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to process the configuration settings from a Lua table for string set types.
|
||||
/// </summary>
|
||||
@@ -697,14 +697,14 @@ public static partial class ManagedConfiguration
|
||||
//
|
||||
// Handle configured string sets
|
||||
//
|
||||
|
||||
|
||||
// Check if that configuration was registered:
|
||||
if(!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
|
||||
var successful = false;
|
||||
var configuredValue = configMeta.Default;
|
||||
|
||||
|
||||
// Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
|
||||
if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
|
||||
configuredLuaList.Type is LuaValueType.Table &&
|
||||
@@ -713,28 +713,28 @@ public static partial class ManagedConfiguration
|
||||
// Determine the length of the Lua table and prepare a set to hold the parsed values:
|
||||
var len = valueTable.ArrayLength;
|
||||
var set = new HashSet<string>(len);
|
||||
|
||||
|
||||
// Iterate over each entry in the Lua table:
|
||||
for (var index = 1; index <= len; index++)
|
||||
{
|
||||
// Retrieve the Lua value at the current index:
|
||||
var value = valueTable[index];
|
||||
|
||||
|
||||
// Step 2 -- try to read the Lua value as a string:
|
||||
if (value.Type is LuaValueType.String && value.TryRead<string>(out var configuredLuaValueText))
|
||||
set.Add(configuredLuaValueText);
|
||||
}
|
||||
|
||||
|
||||
configuredValue = set;
|
||||
successful = true;
|
||||
}
|
||||
|
||||
|
||||
if(dryRun)
|
||||
return successful;
|
||||
|
||||
|
||||
return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to process the configuration settings from a Lua table for string dictionary types.
|
||||
/// </summary>
|
||||
@@ -760,14 +760,14 @@ public static partial class ManagedConfiguration
|
||||
//
|
||||
// Handle configured string dictionaries (both keys and values are strings)
|
||||
//
|
||||
|
||||
|
||||
// Check if that configuration was registered:
|
||||
if(!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
|
||||
var successful = false;
|
||||
var configuredValue = configMeta.Default;
|
||||
|
||||
|
||||
// Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
|
||||
if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
|
||||
configuredLuaList.Type is LuaValueType.Table &&
|
||||
@@ -778,7 +778,7 @@ public static partial class ManagedConfiguration
|
||||
var len = valueTable.HashMapCount;
|
||||
if (len > 0)
|
||||
configuredValue.Clear();
|
||||
|
||||
|
||||
// In order to iterate over all key-value pairs in the Lua table, we have to use TryGetNext.
|
||||
// Thus, we initialize the previous key variable to Nil and keep calling TryGetNext until
|
||||
// there are no more pairs:
|
||||
@@ -787,25 +787,101 @@ public static partial class ManagedConfiguration
|
||||
{
|
||||
// Update the previous key for the next iteration:
|
||||
previousKey = pair.Key;
|
||||
|
||||
|
||||
// Try to read both the key and the value as strings:
|
||||
var hadKey = pair.Key.TryRead<string>(out var key);
|
||||
var hadValue = pair.Value.TryRead<string>(out var value);
|
||||
|
||||
|
||||
// If both key and value were read successfully, add them to the dictionary:
|
||||
if (hadKey && hadValue)
|
||||
configuredValue[key] = value;
|
||||
}
|
||||
|
||||
|
||||
successful = true;
|
||||
}
|
||||
|
||||
|
||||
if(dryRun)
|
||||
return successful;
|
||||
|
||||
return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to process the configuration settings from a Lua table for enum dictionary types.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
|
||||
/// Furthermore, it applies the configured managed state to the provided configuration plugin ID.
|
||||
/// The setting's value is set to the configured value when locked or when the editable default should apply.
|
||||
/// </remarks>
|
||||
/// <param name="configPluginId">The ID of the related configuration plugin.</param>
|
||||
/// <param name="settings">The Lua table containing the settings to process.</param>
|
||||
/// <param name="configSelection">The expression to select the configuration class.</param>
|
||||
/// <param name="propertyExpression">The expression to select the property within the configuration class.</param>
|
||||
/// <param name="dryRun">When true, the method will not apply any changes but only check if the configuration can be read.</param>
|
||||
/// <typeparam name="TClass">The type of the configuration class.</typeparam>
|
||||
/// <typeparam name="TKey">The enum type of the dictionary keys.</typeparam>
|
||||
/// <typeparam name="TValue">The enum type of the dictionary values.</typeparam>
|
||||
/// <returns>True when the configuration was successfully processed, otherwise false.</returns>
|
||||
public static bool TryProcessConfiguration<TClass, TKey, TValue>(
|
||||
Expression<Func<Data, TClass>> configSelection,
|
||||
Expression<Func<TClass, Dictionary<TKey, TValue>>> propertyExpression,
|
||||
Guid configPluginId,
|
||||
LuaTable settings,
|
||||
bool dryRun)
|
||||
where TKey : struct, Enum
|
||||
where TValue : struct, Enum
|
||||
{
|
||||
//
|
||||
// Handle configured enum dictionaries (Lua keys and values are strings)
|
||||
//
|
||||
|
||||
// Check if that configuration was registered:
|
||||
if(!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
var successful = false;
|
||||
var configuredValue = new Dictionary<TKey, TValue>(configMeta.Default);
|
||||
|
||||
// Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
|
||||
if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
|
||||
configuredLuaList.Type is LuaValueType.Table &&
|
||||
configuredLuaList.TryRead<LuaTable>(out var valueTable))
|
||||
{
|
||||
configuredValue.Clear();
|
||||
|
||||
// In order to iterate over all key-value pairs in the Lua table, we have to use TryGetNext.
|
||||
// Thus, we initialize the previous key variable to Nil and keep calling TryGetNext until
|
||||
// there are no more pairs:
|
||||
var previousKey = LuaValue.Nil;
|
||||
while(valueTable.TryGetNext(previousKey, out var pair))
|
||||
{
|
||||
// Update the previous key for the next iteration:
|
||||
previousKey = pair.Key;
|
||||
|
||||
// Try to read both the key and the value as strings:
|
||||
var hadKey = pair.Key.TryRead<string>(out var keyText);
|
||||
var hadValue = pair.Value.TryRead<string>(out var valueText);
|
||||
|
||||
// If both key and value were read successfully, parse and add them to the dictionary:
|
||||
if (hadKey
|
||||
&& hadValue
|
||||
&& Enum.TryParse<TKey>(keyText, true, out var key)
|
||||
&& Enum.TryParse<TValue>(valueText, true, out var value))
|
||||
configuredValue[key] = value;
|
||||
}
|
||||
|
||||
successful = true;
|
||||
}
|
||||
|
||||
if(dryRun)
|
||||
return successful;
|
||||
|
||||
var settingName = SettingName(propertyExpression);
|
||||
var managedMode = ReadManagedConfigurationMode(propertyExpression, settings);
|
||||
return HandleParsedScalarValue(configPluginId, dryRun, successful, configMeta, configuredValue, managedMode, settingName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the parsed configuration value based on whether the parsing was successful and whether it's a dry run.
|
||||
/// </summary>
|
||||
@@ -826,14 +902,14 @@ public static partial class ManagedConfiguration
|
||||
{
|
||||
if(dryRun)
|
||||
return successful;
|
||||
|
||||
|
||||
switch (successful)
|
||||
{
|
||||
case true:
|
||||
//
|
||||
// Case: the setting was configured, and we could read the value successfully.
|
||||
//
|
||||
|
||||
|
||||
// Set the configured value and lock the configuration:
|
||||
configMeta.SetValue(configuredValue);
|
||||
configMeta.LockConfiguration(configPluginId);
|
||||
@@ -852,7 +928,7 @@ public static partial class ManagedConfiguration
|
||||
//
|
||||
configMeta.ResetLockedConfiguration();
|
||||
break;
|
||||
|
||||
|
||||
case false:
|
||||
//
|
||||
// Case: the setting was not configured, or we could not read the value successfully.
|
||||
@@ -946,8 +1022,12 @@ public static partial class ManagedConfiguration
|
||||
{
|
||||
null => string.Empty,
|
||||
string text => text,
|
||||
System.Collections.IDictionary dictionary => string.Join(";", dictionary.Keys
|
||||
.Cast<object>()
|
||||
.OrderBy(key => key.ToString(), StringComparer.Ordinal)
|
||||
.Select(key => $"{key}:{dictionary[key]}")),
|
||||
IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture),
|
||||
|
||||
|
||||
_ => value.ToString() ?? string.Empty,
|
||||
};
|
||||
}
|
||||
@@ -66,13 +66,13 @@ public static partial class ManagedConfiguration
|
||||
// we ignore the register call and return the default value:
|
||||
if(configSelection is null)
|
||||
return defaultValue;
|
||||
|
||||
|
||||
var configPath = Path(configSelection, propertyExpression);
|
||||
|
||||
// If the metadata already exists for this configuration path, we return the default value:
|
||||
if (METADATA.ContainsKey(configPath))
|
||||
return defaultValue;
|
||||
|
||||
|
||||
// Not registered yet, so we register it now:
|
||||
METADATA[configPath] = new ConfigMeta<TClass, string>(configSelection, propertyExpression)
|
||||
{
|
||||
@@ -104,13 +104,13 @@ public static partial class ManagedConfiguration
|
||||
// we ignore the register call and return the default value:
|
||||
if(configSelection is null)
|
||||
return [defaultValue];
|
||||
|
||||
|
||||
var configPath = Path(configSelection, propertyExpression);
|
||||
|
||||
// If the metadata already exists for this configuration path, we return the default value:
|
||||
if (METADATA.ContainsKey(configPath))
|
||||
return [defaultValue];
|
||||
|
||||
|
||||
// Not registered yet, so we register it now:
|
||||
METADATA[configPath] = new ConfigMeta<TClass, IList<TValue>>(configSelection, propertyExpression)
|
||||
{
|
||||
@@ -142,13 +142,13 @@ public static partial class ManagedConfiguration
|
||||
// we ignore the register call and return the default value:
|
||||
if(configSelection is null)
|
||||
return [..defaultValues];
|
||||
|
||||
|
||||
var configPath = Path(configSelection, propertyExpression);
|
||||
|
||||
// If the metadata already exists for this configuration path, we return the default value:
|
||||
if (METADATA.ContainsKey(configPath))
|
||||
return [..defaultValues];
|
||||
|
||||
|
||||
// Not registered yet, so we register it now:
|
||||
METADATA[configPath] = new ConfigMeta<TClass, IList<TValue>>(configSelection, propertyExpression)
|
||||
{
|
||||
@@ -179,13 +179,13 @@ public static partial class ManagedConfiguration
|
||||
// we ignore the register call and return the default value:
|
||||
if (configSelection is null)
|
||||
return [defaultValue];
|
||||
|
||||
|
||||
var configPath = Path(configSelection, propertyExpression);
|
||||
|
||||
// If the metadata already exists for this configuration path, we return the default value:
|
||||
if (METADATA.ContainsKey(configPath))
|
||||
return [defaultValue];
|
||||
|
||||
|
||||
// Not registered yet, so we register it now:
|
||||
METADATA[configPath] = new ConfigMeta<TClass, ISet<TValue>>(configSelection, propertyExpression)
|
||||
{
|
||||
@@ -217,13 +217,13 @@ public static partial class ManagedConfiguration
|
||||
// we ignore the register call and return the default value:
|
||||
if (configSelection is null)
|
||||
return [..defaultValues];
|
||||
|
||||
|
||||
var configPath = Path(configSelection, propertyExpression);
|
||||
|
||||
// If the metadata already exists for this configuration path, we return the default value:
|
||||
if (METADATA.ContainsKey(configPath))
|
||||
return [..defaultValues];
|
||||
|
||||
|
||||
// Not registered yet, so we register it now:
|
||||
METADATA[configPath] = new ConfigMeta<TClass, ISet<TValue>>(configSelection, propertyExpression)
|
||||
{
|
||||
@@ -268,7 +268,48 @@ public static partial class ManagedConfiguration
|
||||
{
|
||||
Default = defaultValues,
|
||||
};
|
||||
|
||||
|
||||
return defaultValues;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a configuration setting with a default dictionary of enum key-value pairs.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When the method is invoked with a null configSelection, the configuration path
|
||||
/// is ignored, and the specified default values are returned without registration.
|
||||
/// </remarks>
|
||||
/// <param name="configSelection">The expression that selects the configuration class from the root Data model.</param>
|
||||
/// <param name="propertyExpression">The expression to select the property within the configuration class.</param>
|
||||
/// <param name="defaultValues">The default dictionary of values to use when the setting is not configured.</param>
|
||||
/// <typeparam name="TClass">The type of the configuration class from which the property is selected.</typeparam>
|
||||
/// <typeparam name="TKey">The enum type of the dictionary keys.</typeparam>
|
||||
/// <typeparam name="TValue">The enum type of the dictionary values.</typeparam>
|
||||
/// <returns>A dictionary containing the default values.</returns>
|
||||
public static Dictionary<TKey, TValue> Register<TClass, TKey, TValue>(
|
||||
Expression<Func<Data, TClass>>? configSelection,
|
||||
Expression<Func<TClass, Dictionary<TKey, TValue>>> propertyExpression,
|
||||
Dictionary<TKey, TValue> defaultValues)
|
||||
where TKey : struct, Enum
|
||||
where TValue : struct, Enum
|
||||
{
|
||||
// When called from the JSON deserializer by using the standard constructor,
|
||||
// we ignore the register call and return the default value:
|
||||
if (configSelection is null)
|
||||
return new();
|
||||
|
||||
var configPath = Path(configSelection, propertyExpression);
|
||||
|
||||
// If the metadata already exists for this configuration path, we return the default value:
|
||||
if (METADATA.ContainsKey(configPath))
|
||||
return defaultValues;
|
||||
|
||||
// Not registered yet, so we register it now:
|
||||
METADATA[configPath] = new ConfigMeta<TClass, Dictionary<TKey, TValue>>(configSelection, propertyExpression)
|
||||
{
|
||||
Default = defaultValues,
|
||||
};
|
||||
|
||||
return defaultValues;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace AIStudio.Settings;
|
||||
public static partial class ManagedConfiguration
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, IConfig> METADATA = new();
|
||||
private static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||
private static SettingsManager SettingsManagerAccess => Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the configuration metadata for a given configuration selection and
|
||||
@@ -231,6 +231,44 @@ public static partial class ManagedConfiguration
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the configuration metadata for an enum dictionary-based setting.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When no configuration metadata is found, it returns a NoConfig instance with the default
|
||||
/// value set to an empty dictionary. This allows the caller to handle the absence of configuration
|
||||
/// gracefully. In such cases, the return value of the method will be false.
|
||||
/// </remarks>
|
||||
/// <param name="configSelection">The expression to select the configuration class.</param>
|
||||
/// <param name="propertyExpression">The expression to select the property within the
|
||||
/// configuration class.</param>
|
||||
/// <param name="configMeta">The output parameter that will hold the configuration metadata
|
||||
/// if found.</param>
|
||||
/// <typeparam name="TClass">The type of the configuration class.</typeparam>
|
||||
/// <typeparam name="TKey">The enum type of the dictionary keys.</typeparam>
|
||||
/// <typeparam name="TValue">The enum type of the dictionary values.</typeparam>
|
||||
/// <returns>True if the configuration metadata was found, otherwise false.</returns>
|
||||
public static bool TryGet<TClass, TKey, TValue>(
|
||||
Expression<Func<Data, TClass>> configSelection,
|
||||
Expression<Func<TClass, Dictionary<TKey, TValue>>> propertyExpression,
|
||||
out ConfigMeta<TClass, Dictionary<TKey, TValue>> configMeta)
|
||||
where TKey : struct, Enum
|
||||
where TValue : struct, Enum
|
||||
{
|
||||
var configPath = Path(configSelection, propertyExpression);
|
||||
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta<TClass, Dictionary<TKey, TValue>> meta)
|
||||
{
|
||||
configMeta = meta;
|
||||
return true;
|
||||
}
|
||||
|
||||
configMeta = new NoConfig<TClass, Dictionary<TKey, TValue>>(configSelection, propertyExpression)
|
||||
{
|
||||
Default = new Dictionary<TKey, TValue>(),
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a configuration setting is left over from a configuration plugin that is no longer available.
|
||||
/// If the configuration setting is locked and managed by a configuration plugin that is not available,
|
||||
@@ -399,6 +437,42 @@ public static partial class ManagedConfiguration
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsConfigurationLeftOver<TClass, TKey, TValue>(
|
||||
Expression<Func<Data, TClass>> configSelection,
|
||||
Expression<Func<TClass, Dictionary<TKey, TValue>>> propertyExpression,
|
||||
IEnumerable<IAvailablePlugin> availablePlugins)
|
||||
where TKey : struct, Enum
|
||||
where TValue : struct, Enum
|
||||
{
|
||||
if (!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
if (configMeta.ManagedMode is ManagedConfigurationMode.EDITABLE_DEFAULT)
|
||||
{
|
||||
var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.EditableDefaultByConfigPluginId);
|
||||
if (plugin is null)
|
||||
{
|
||||
configMeta.ClearEditableDefaultConfiguration();
|
||||
ClearEditableDefaultState(SettingName(propertyExpression));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (configMeta.LockedByConfigPluginId == Guid.Empty || !configMeta.IsLocked)
|
||||
return false;
|
||||
|
||||
var lockedPlugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId);
|
||||
if (lockedPlugin is null)
|
||||
{
|
||||
configMeta.ResetLockedConfiguration();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string Path<TClass, TValue>(Expression<Func<Data, TClass>> configSelection, Expression<Func<TClass, TValue>> propertyExpression)
|
||||
{
|
||||
@@ -418,19 +492,19 @@ public static partial class ManagedConfiguration
|
||||
|
||||
private static bool TryGetEditableDefaultState(string settingName, out ManagedEditableDefaultState editableDefaultState)
|
||||
{
|
||||
return SETTINGS_MANAGER.ConfigurationData.ManagedEditableDefaults.TryGetValue(settingName, out editableDefaultState!);
|
||||
return SettingsManagerAccess.ConfigurationData.ManagedEditableDefaults.TryGetValue(settingName, out editableDefaultState!);
|
||||
}
|
||||
|
||||
private static void SetEditableDefaultState(string settingName, Guid pluginId, string lastAppliedValue)
|
||||
{
|
||||
SETTINGS_MANAGER.ConfigurationData.ManagedEditableDefaults[settingName] = new()
|
||||
SettingsManagerAccess.ConfigurationData.ManagedEditableDefaults[settingName] = new()
|
||||
{
|
||||
ConfigPluginId = pluginId,
|
||||
LastAppliedValue = lastAppliedValue,
|
||||
};
|
||||
}
|
||||
|
||||
private static bool ClearEditableDefaultState(string settingName) => SETTINGS_MANAGER.ConfigurationData.ManagedEditableDefaults.Remove(settingName);
|
||||
private static bool ClearEditableDefaultState(string settingName) => SettingsManagerAccess.ConfigurationData.ManagedEditableDefaults.Remove(settingName);
|
||||
|
||||
private static bool CleanupEditableDefaultState<TClass, TValue>(
|
||||
ConfigMeta<TClass, TValue> configMeta,
|
||||
|
||||
@@ -73,7 +73,7 @@ public sealed record Provider(
|
||||
|
||||
/// <inheritdoc />
|
||||
[JsonIgnore]
|
||||
public string SecretId => this.IsEnterpriseConfiguration ? $"{ISecretId.ENTERPRISE_KEY_PREFIX}::{this.UsedLLMProvider.ToName()}" : this.UsedLLMProvider.ToName();
|
||||
public string SecretId => this.IsEnterpriseConfiguration ? $"{ISecretId.ENTERPRISE_KEY_PREFIX}::{this.UsedLLMProvider.ToSecretId()}" : this.UsedLLMProvider.ToSecretId();
|
||||
|
||||
/// <inheritdoc />
|
||||
[JsonIgnore]
|
||||
@@ -191,7 +191,7 @@ public sealed record Provider(
|
||||
{
|
||||
// Queue the API key for storage in the OS keyring:
|
||||
PendingEnterpriseApiKeys.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToName()}",
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToSecretId()}",
|
||||
instanceName,
|
||||
decryptedApiKey,
|
||||
SecretStoreType.LLM_PROVIDER));
|
||||
|
||||
@@ -17,6 +17,11 @@ namespace AIStudio.Settings;
|
||||
public sealed class SettingsManager
|
||||
{
|
||||
private const string SETTINGS_FILENAME = "settings.json";
|
||||
private const Version CURRENT_SETTINGS_VERSION = Version.V6;
|
||||
|
||||
private readonly record struct SettingsVersionReadResult(Version Version, SettingsWriteBlockReason FailureReason);
|
||||
|
||||
private readonly record struct CurrentSettingsReadResult(Data? SettingsData, SettingsWriteBlockReason FailureReason);
|
||||
|
||||
private static readonly JsonSerializerOptions JSON_OPTIONS = new()
|
||||
{
|
||||
@@ -62,6 +67,16 @@ public sealed class SettingsManager
|
||||
/// </summary>
|
||||
public bool HasCompletedInitialSettingsLoad { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates why settings writes are blocked for the current session.
|
||||
/// </summary>
|
||||
public SettingsWriteBlockReason SettingsWriteBlockReason { get; private set; } = SettingsWriteBlockReason.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that settings writes are blocked for the current session.
|
||||
/// </summary>
|
||||
public bool SettingsWriteBlocked => this.SettingsWriteBlockReason is not SettingsWriteBlockReason.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The configuration data.
|
||||
/// </summary>
|
||||
@@ -87,6 +102,7 @@ public sealed class SettingsManager
|
||||
/// <returns>A (migrated) settings snapshot, or null if it could not be read.</returns>
|
||||
public async Task<Data?> TryReadSettingsSnapshot()
|
||||
{
|
||||
this.SettingsWriteBlockReason = SettingsWriteBlockReason.NONE;
|
||||
if(!this.IsSetUp)
|
||||
{
|
||||
this.logger.LogWarning("Cannot load settings, because the configuration is not set up yet.");
|
||||
@@ -100,38 +116,175 @@ public sealed class SettingsManager
|
||||
return null;
|
||||
}
|
||||
|
||||
// We read the `"Version": "V3"` line to determine the version of the settings file:
|
||||
await foreach (var line in File.ReadLinesAsync(settingsPath))
|
||||
var settingsVersion = await this.TryReadSettingsVersion(settingsPath);
|
||||
if(settingsVersion.FailureReason is not SettingsWriteBlockReason.NONE)
|
||||
{
|
||||
if (!line.Contains("""
|
||||
"Version":
|
||||
"""))
|
||||
continue;
|
||||
this.BlockSettingsWrites(settingsVersion.FailureReason, "The settings file version could not be identified. Settings writes are blocked to avoid overwriting newer or unreadable settings.");
|
||||
return await this.TryReadCurrentVersionBackupSnapshotForBlockedSettings();
|
||||
}
|
||||
|
||||
// Extract the version from the line:
|
||||
var settingsVersionText = line.Split('"')[3];
|
||||
if(settingsVersion.Version > CURRENT_SETTINGS_VERSION)
|
||||
{
|
||||
this.BlockSettingsWrites(SettingsWriteBlockReason.VERSION_NEWER_THAN_APP, $"The settings file uses the newer version '{settingsVersion.Version}'. Settings writes are blocked to avoid overwriting newer settings.");
|
||||
return await this.TryReadCurrentVersionBackupSnapshotForBlockedSettings();
|
||||
}
|
||||
|
||||
// Parse the version:
|
||||
Enum.TryParse(settingsVersionText, out Version settingsVersion);
|
||||
if(settingsVersion is Version.UNKNOWN)
|
||||
Data? settingsData;
|
||||
if(settingsVersion.Version < CURRENT_SETTINGS_VERSION)
|
||||
{
|
||||
settingsData = await this.TryReadCurrentVersionBackupSnapshot();
|
||||
if(settingsData is not null)
|
||||
{
|
||||
this.logger.LogError("Unknown version of the settings file found.");
|
||||
return new();
|
||||
this.PrepareLoadedSettings(settingsData);
|
||||
await this.StoreSettingsSnapshot(settingsData, settingsPath);
|
||||
await this.StoreCurrentVersionBackup(settingsData);
|
||||
this.logger.LogInformation($"Restored settings from the '{GetBackupSettingsFilename(CURRENT_SETTINGS_VERSION)}' backup file.");
|
||||
return settingsData;
|
||||
}
|
||||
|
||||
var settingsData = SettingsMigrations.Migrate(this.logger, settingsVersion, await File.ReadAllTextAsync(settingsPath), JSON_OPTIONS);
|
||||
|
||||
//
|
||||
// We filter the enabled preview features based on the preview visibility.
|
||||
// This is necessary when the app starts up: some preview features may have
|
||||
// been disabled or released from the last time the app was started.
|
||||
//
|
||||
settingsData.App.EnabledPreviewFeatures = settingsData.App.PreviewVisibility.FilterPreviewFeatures(settingsData.App.EnabledPreviewFeatures);
|
||||
this.logger.LogInformation("No valid current-version settings backup was found. Migrating the settings file.");
|
||||
settingsData = SettingsMigrations.Migrate(this.logger, settingsVersion.Version, await File.ReadAllTextAsync(settingsPath), JSON_OPTIONS);
|
||||
this.PrepareLoadedSettings(settingsData);
|
||||
await this.StoreSettingsSnapshot(settingsData, settingsPath);
|
||||
await this.StoreCurrentVersionBackup(settingsData);
|
||||
return settingsData;
|
||||
}
|
||||
|
||||
this.logger.LogError("Failed to read the version of the settings file.");
|
||||
return new();
|
||||
var currentSettings = await this.TryDeserializeCurrentSettings(settingsPath, "settings file");
|
||||
if(currentSettings.FailureReason is not SettingsWriteBlockReason.NONE)
|
||||
{
|
||||
this.BlockSettingsWrites(currentSettings.FailureReason, "The current settings file could not be safely loaded. Settings writes are blocked to avoid overwriting recoverable settings.");
|
||||
return await this.TryReadCurrentVersionBackupSnapshotForBlockedSettings();
|
||||
}
|
||||
|
||||
settingsData = currentSettings.SettingsData!;
|
||||
this.PrepareLoadedSettings(settingsData);
|
||||
await this.StoreCurrentVersionBackup(settingsData);
|
||||
return settingsData;
|
||||
}
|
||||
|
||||
private async Task<SettingsVersionReadResult> TryReadSettingsVersion(string settingsPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var settingsStream = File.OpenRead(settingsPath);
|
||||
using var settingsDocument = await JsonDocument.ParseAsync(settingsStream);
|
||||
if(!settingsDocument.RootElement.TryGetProperty("Version", out var versionElement))
|
||||
{
|
||||
this.logger.LogError($"Failed to read the version of the settings file '{settingsPath}'.");
|
||||
return new(Version.UNKNOWN, SettingsWriteBlockReason.VERSION_MISSING);
|
||||
}
|
||||
|
||||
if(versionElement.ValueKind is JsonValueKind.String && versionElement.GetString() is { } versionText)
|
||||
{
|
||||
if(Enum.TryParse(versionText, out Version stringVersion) && Enum.IsDefined(stringVersion) && stringVersion is not Version.UNKNOWN)
|
||||
return new(stringVersion, SettingsWriteBlockReason.NONE);
|
||||
|
||||
if(versionText.StartsWith('V') && int.TryParse(versionText[1..], out var futureVersion) && futureVersion > (int)CURRENT_SETTINGS_VERSION)
|
||||
return new((Version)futureVersion, SettingsWriteBlockReason.NONE);
|
||||
|
||||
if(int.TryParse(versionText, out var numericStringVersion) && numericStringVersion > (int)CURRENT_SETTINGS_VERSION)
|
||||
return new((Version)numericStringVersion, SettingsWriteBlockReason.NONE);
|
||||
}
|
||||
|
||||
if(versionElement.ValueKind is JsonValueKind.Number && versionElement.TryGetInt32(out var numericVersion) && numericVersion > (int)Version.UNKNOWN && (Enum.IsDefined(typeof(Version), numericVersion) || numericVersion > (int)CURRENT_SETTINGS_VERSION))
|
||||
return new((Version)numericVersion, SettingsWriteBlockReason.NONE);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
this.logger.LogError(e, $"Failed to read the version of the settings file '{settingsPath}'.");
|
||||
return new(Version.UNKNOWN, SettingsWriteBlockReason.FILE_UNREADABLE);
|
||||
}
|
||||
|
||||
return new(Version.UNKNOWN, SettingsWriteBlockReason.VERSION_UNKNOWN);
|
||||
}
|
||||
|
||||
private async Task<Data?> TryReadCurrentVersionBackupSnapshot()
|
||||
{
|
||||
var backupSettingsPath = GetBackupSettingsPath(CURRENT_SETTINGS_VERSION);
|
||||
if(!File.Exists(backupSettingsPath))
|
||||
{
|
||||
this.logger.LogInformation($"The settings backup file '{backupSettingsPath}' does not exist.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var backupVersion = await this.TryReadSettingsVersion(backupSettingsPath);
|
||||
if(backupVersion.FailureReason is not SettingsWriteBlockReason.NONE)
|
||||
{
|
||||
this.logger.LogWarning($"The settings backup file '{backupSettingsPath}' could not be used because its version could not be identified. Reason: '{backupVersion.FailureReason}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(backupVersion.Version != CURRENT_SETTINGS_VERSION)
|
||||
{
|
||||
this.logger.LogWarning($"The settings backup file '{backupSettingsPath}' uses version '{backupVersion.Version}' instead of '{CURRENT_SETTINGS_VERSION}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var backupSettings = await this.TryDeserializeCurrentSettings(backupSettingsPath, "settings backup file");
|
||||
if(backupSettings.FailureReason is not SettingsWriteBlockReason.NONE)
|
||||
{
|
||||
this.logger.LogWarning($"The settings backup file '{backupSettingsPath}' could not be used. Reason: '{backupSettings.FailureReason}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return backupSettings.SettingsData;
|
||||
}
|
||||
|
||||
private async Task<Data?> TryReadCurrentVersionBackupSnapshotForBlockedSettings()
|
||||
{
|
||||
var settingsData = await this.TryReadCurrentVersionBackupSnapshot();
|
||||
if(settingsData is null)
|
||||
{
|
||||
this.logger.LogWarning($"No valid current-version settings backup was found while settings writes are blocked. Reason: '{this.SettingsWriteBlockReason}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
this.PrepareLoadedSettings(settingsData);
|
||||
this.logger.LogWarning($"Loaded settings from the '{GetBackupSettingsFilename(CURRENT_SETTINGS_VERSION)}' backup file while settings writes remain blocked. Reason: '{this.SettingsWriteBlockReason}'.");
|
||||
return settingsData;
|
||||
}
|
||||
|
||||
private async Task<CurrentSettingsReadResult> TryDeserializeCurrentSettings(string settingsPath, string sourceDescription)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settingsData = JsonSerializer.Deserialize<Data>(await File.ReadAllTextAsync(settingsPath), JSON_OPTIONS);
|
||||
if(settingsData is null)
|
||||
{
|
||||
this.logger.LogError($"Failed to parse the {sourceDescription} '{settingsPath}'.");
|
||||
return new(null, SettingsWriteBlockReason.CURRENT_VERSION_INVALID);
|
||||
}
|
||||
|
||||
if(settingsData.Version != CURRENT_SETTINGS_VERSION)
|
||||
{
|
||||
this.logger.LogError($"The {sourceDescription} '{settingsPath}' uses version '{settingsData.Version}' instead of '{CURRENT_SETTINGS_VERSION}'.");
|
||||
return new(null, SettingsWriteBlockReason.CURRENT_VERSION_INVALID);
|
||||
}
|
||||
|
||||
return new(settingsData, SettingsWriteBlockReason.NONE);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
this.logger.LogError(e, $"Failed to parse the {sourceDescription} '{settingsPath}'.");
|
||||
return new(null, SettingsWriteBlockReason.FILE_UNREADABLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void BlockSettingsWrites(SettingsWriteBlockReason reason, string message)
|
||||
{
|
||||
this.SettingsWriteBlockReason = reason;
|
||||
this.logger.LogError($"{message} Reason: '{reason}'.");
|
||||
}
|
||||
|
||||
private void PrepareLoadedSettings(Data settingsData)
|
||||
{
|
||||
//
|
||||
// We filter the enabled preview features based on the preview visibility.
|
||||
// This is necessary when the app starts up: some preview features may have
|
||||
// been disabled or released from the last time the app was started.
|
||||
//
|
||||
settingsData.App.EnabledPreviewFeatures = settingsData.App.PreviewVisibility.FilterPreviewFeatures(settingsData.App.EnabledPreviewFeatures);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -145,19 +298,48 @@ public sealed class SettingsManager
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.SettingsWriteBlocked)
|
||||
{
|
||||
this.logger.LogWarning($"Cannot store settings, because settings writes are blocked. Reason: '{this.SettingsWriteBlockReason}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
var settingsPath = Path.Combine(ConfigDirectory!, SETTINGS_FILENAME);
|
||||
await this.StoreSettingsSnapshot(this.ConfigurationData, settingsPath);
|
||||
await this.StoreCurrentVersionBackup(this.ConfigurationData);
|
||||
}
|
||||
|
||||
private static string GetBackupSettingsFilename(Version version) => $"settings.{version.ToString().ToLowerInvariant()}.json";
|
||||
|
||||
private static string GetBackupSettingsPath(Version version) => Path.Combine(ConfigDirectory!, GetBackupSettingsFilename(version));
|
||||
|
||||
private async Task StoreCurrentVersionBackup(Data settingsData)
|
||||
{
|
||||
if(settingsData.Version != CURRENT_SETTINGS_VERSION)
|
||||
{
|
||||
this.logger.LogWarning($"Skipping settings backup because the settings version '{settingsData.Version}' is not the current version '{CURRENT_SETTINGS_VERSION}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
var backupSettingsPath = GetBackupSettingsPath(CURRENT_SETTINGS_VERSION);
|
||||
await this.StoreSettingsSnapshot(settingsData, backupSettingsPath);
|
||||
this.logger.LogInformation($"Stored the settings backup file '{backupSettingsPath}'.");
|
||||
}
|
||||
|
||||
private async Task StoreSettingsSnapshot(Data settingsData, string settingsPath)
|
||||
{
|
||||
if(!Directory.Exists(ConfigDirectory))
|
||||
{
|
||||
this.logger.LogInformation("Creating the configuration directory.");
|
||||
Directory.CreateDirectory(ConfigDirectory!);
|
||||
}
|
||||
|
||||
var settingsJson = JsonSerializer.Serialize(this.ConfigurationData, JSON_OPTIONS);
|
||||
var settingsJson = JsonSerializer.Serialize(settingsData, JSON_OPTIONS);
|
||||
var tempFile = Path.GetTempFileName();
|
||||
await File.WriteAllTextAsync(tempFile, settingsJson);
|
||||
|
||||
File.Move(tempFile, settingsPath, true);
|
||||
this.logger.LogInformation("Stored the settings to the file system.");
|
||||
this.logger.LogInformation($"Stored the settings to '{settingsPath}'.");
|
||||
}
|
||||
|
||||
public void InjectSpellchecking(Dictionary<string, object?> attributes) => attributes["spellcheck"] = this.ConfigurationData.App.EnableSpellchecking ? "true" : "false";
|
||||
@@ -165,9 +347,9 @@ public sealed class SettingsManager
|
||||
public ConfidenceLevel GetMinimumConfidenceLevel(Tools.Components component)
|
||||
{
|
||||
var minimumLevel = ConfidenceLevel.NONE;
|
||||
var enforceGlobalMinimumConfidence = this.ConfigurationData.LLMProviders is { EnforceGlobalMinimumConfidence: true, GlobalMinimumConfidence: not ConfidenceLevel.NONE and not ConfidenceLevel.UNKNOWN };
|
||||
var enforceGlobalMinimumConfidence = this.ConfigurationData.Confidence is { EnforceGlobalMinimumConfidence: true, GlobalMinimumConfidence: not ConfidenceLevel.NONE and not ConfidenceLevel.UNKNOWN };
|
||||
if (enforceGlobalMinimumConfidence)
|
||||
minimumLevel = this.ConfigurationData.LLMProviders.GlobalMinimumConfidence;
|
||||
minimumLevel = this.ConfigurationData.Confidence.GlobalMinimumConfidence;
|
||||
|
||||
var componentMinimumLevel = component.MinimumConfidence(this);
|
||||
if (componentMinimumLevel > minimumLevel)
|
||||
@@ -348,17 +530,13 @@ public sealed class SettingsManager
|
||||
return Profile.NO_PROFILE;
|
||||
|
||||
if (preselection.UseSpecificProfile)
|
||||
{
|
||||
var componentProfile = this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id.Equals(preselection.SpecificProfileId, StringComparison.OrdinalIgnoreCase));
|
||||
return componentProfile ?? Profile.NO_PROFILE;
|
||||
}
|
||||
return this.GetProfileById(preselection.SpecificProfileId);
|
||||
|
||||
var appPreselection = ProfilePreselection.FromStoredValue(this.ConfigurationData.App.PreselectedProfile);
|
||||
if (appPreselection.DoNotPreselectProfile || !appPreselection.UseSpecificProfile)
|
||||
return Profile.NO_PROFILE;
|
||||
|
||||
var appProfile = this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id.Equals(appPreselection.SpecificProfileId, StringComparison.OrdinalIgnoreCase));
|
||||
return appProfile ?? Profile.NO_PROFILE;
|
||||
return this.GetProfileById(appPreselection.SpecificProfileId);
|
||||
}
|
||||
|
||||
public Profile GetAppPreselectedProfile()
|
||||
@@ -367,8 +545,7 @@ public sealed class SettingsManager
|
||||
if (appPreselection.DoNotPreselectProfile || !appPreselection.UseSpecificProfile)
|
||||
return Profile.NO_PROFILE;
|
||||
|
||||
var appProfile = this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id.Equals(appPreselection.SpecificProfileId, StringComparison.OrdinalIgnoreCase));
|
||||
return appProfile ?? Profile.NO_PROFILE;
|
||||
return this.GetProfileById(appPreselection.SpecificProfileId);
|
||||
}
|
||||
|
||||
public ChatTemplate GetPreselectedChatTemplate(Tools.Components component)
|
||||
@@ -377,8 +554,29 @@ public sealed class SettingsManager
|
||||
if (preselection != ChatTemplate.NO_CHAT_TEMPLATE)
|
||||
return preselection;
|
||||
|
||||
preselection = this.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id.Equals(this.ConfigurationData.App.PreselectedChatTemplate, StringComparison.OrdinalIgnoreCase));
|
||||
return preselection ?? ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
return this.GetChatTemplateById(this.ConfigurationData.App.PreselectedChatTemplate);
|
||||
}
|
||||
|
||||
public Profile GetProfileById(string? profileId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(profileId))
|
||||
return Profile.NO_PROFILE;
|
||||
|
||||
if (string.Equals(profileId, Profile.NO_PROFILE.Id, StringComparison.OrdinalIgnoreCase))
|
||||
return Profile.NO_PROFILE;
|
||||
|
||||
return this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id.Equals(profileId, StringComparison.OrdinalIgnoreCase)) ?? Profile.NO_PROFILE;
|
||||
}
|
||||
|
||||
public ChatTemplate GetChatTemplateById(string? chatTemplateId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(chatTemplateId))
|
||||
return ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
|
||||
if (string.Equals(chatTemplateId, ChatTemplate.NO_CHAT_TEMPLATE.Id, StringComparison.OrdinalIgnoreCase))
|
||||
return ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
|
||||
return this.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id.Equals(chatTemplateId, StringComparison.OrdinalIgnoreCase)) ?? ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
}
|
||||
|
||||
public ConfidenceLevel GetConfiguredConfidenceLevel(LLMProviders llmProvider)
|
||||
@@ -386,7 +584,7 @@ public sealed class SettingsManager
|
||||
if(llmProvider is LLMProviders.NONE)
|
||||
return ConfidenceLevel.NONE;
|
||||
|
||||
switch (this.ConfigurationData.LLMProviders.ConfidenceScheme)
|
||||
switch (this.ConfigurationData.Confidence.ConfidenceScheme)
|
||||
{
|
||||
case ConfidenceSchemes.TRUST_ALL:
|
||||
return llmProvider switch
|
||||
@@ -446,7 +644,7 @@ public sealed class SettingsManager
|
||||
};
|
||||
|
||||
case ConfidenceSchemes.CUSTOM:
|
||||
return this.ConfigurationData.LLMProviders.CustomConfidenceScheme.GetValueOrDefault(llmProvider, ConfidenceLevel.UNKNOWN);
|
||||
return this.ConfigurationData.Confidence.CustomConfidenceScheme.GetValueOrDefault(llmProvider, ConfidenceLevel.UNKNOWN);
|
||||
|
||||
default:
|
||||
return ConfidenceLevel.UNKNOWN;
|
||||
@@ -469,4 +667,4 @@ public sealed class SettingsManager
|
||||
// Return the full name of the property, including the class name:
|
||||
return $"{typeof(TIn).Name}.{memberExpr.Member.Name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,8 @@ public static class SettingsMigrations
|
||||
configV1 = MigrateV1ToV2(logger, configV1);
|
||||
configV1 = MigrateV2ToV3(logger, configV1);
|
||||
var configV14 = MigrateV3ToV4(logger, configV1);
|
||||
return MigrateV4ToV5(logger, configV14);
|
||||
var configV15 = MigrateV4ToV5(logger, configV14);
|
||||
return MigrateV5ToV6(logger, configV15);
|
||||
|
||||
case Version.V2:
|
||||
var configV2 = JsonSerializer.Deserialize<DataV1V3>(configData, jsonOptions);
|
||||
@@ -36,7 +37,8 @@ public static class SettingsMigrations
|
||||
|
||||
configV2 = MigrateV2ToV3(logger, configV2);
|
||||
var configV24 = MigrateV3ToV4(logger, configV2);
|
||||
return MigrateV4ToV5(logger, configV24);
|
||||
var configV25 = MigrateV4ToV5(logger, configV24);
|
||||
return MigrateV5ToV6(logger, configV25);
|
||||
|
||||
case Version.V3:
|
||||
var configV3 = JsonSerializer.Deserialize<DataV1V3>(configData, jsonOptions);
|
||||
@@ -47,8 +49,9 @@ public static class SettingsMigrations
|
||||
}
|
||||
|
||||
var configV34 = MigrateV3ToV4(logger, configV3);
|
||||
return MigrateV4ToV5(logger, configV34);
|
||||
|
||||
var configV35 = MigrateV4ToV5(logger, configV34);
|
||||
return MigrateV5ToV6(logger, configV35);
|
||||
|
||||
case Version.V4:
|
||||
var configV4 = JsonSerializer.Deserialize<DataV4>(configData, jsonOptions);
|
||||
if (configV4 is null)
|
||||
@@ -57,18 +60,29 @@ public static class SettingsMigrations
|
||||
return new();
|
||||
}
|
||||
|
||||
return MigrateV4ToV5(logger, configV4);
|
||||
var configV45 = MigrateV4ToV5(logger, configV4);
|
||||
return MigrateV5ToV6(logger, configV45);
|
||||
|
||||
default:
|
||||
logger.LogInformation("No configuration migration is needed.");
|
||||
var configV5 = JsonSerializer.Deserialize<Data>(configData, jsonOptions);
|
||||
case Version.V5:
|
||||
var configV5 = JsonSerializer.Deserialize<DataV5>(configData, jsonOptions);
|
||||
if (configV5 is null)
|
||||
{
|
||||
logger.LogError("Failed to parse the v4 configuration. Using default values.");
|
||||
logger.LogError("Failed to parse the v5 configuration. Using default values.");
|
||||
return new();
|
||||
}
|
||||
|
||||
return configV5;
|
||||
return MigrateV5ToV6(logger, configV5);
|
||||
|
||||
default:
|
||||
logger.LogInformation("No configuration migration is needed.");
|
||||
var configV6 = JsonSerializer.Deserialize<Data>(configData, jsonOptions);
|
||||
if (configV6 is null)
|
||||
{
|
||||
logger.LogError("Failed to parse the v6 configuration. Using default values.");
|
||||
return new();
|
||||
}
|
||||
|
||||
return configV6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,9 +97,9 @@ public static class SettingsMigrations
|
||||
return new()
|
||||
{
|
||||
Version = Version.V2,
|
||||
|
||||
|
||||
Providers = previousData.Providers.Select(provider => provider with { IsSelfHosted = false, Hostname = string.Empty }).ToList(),
|
||||
|
||||
|
||||
EnableSpellchecking = previousData.EnableSpellchecking,
|
||||
IsSavingEnergy = previousData.IsSavingEnergy,
|
||||
NextProviderNum = previousData.NextProviderNum,
|
||||
@@ -93,7 +107,7 @@ public static class SettingsMigrations
|
||||
UpdateInterval = previousData.UpdateInterval,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static DataV1V3 MigrateV2ToV3(ILogger<SettingsManager> logger, DataV1V3 previousData)
|
||||
{
|
||||
//
|
||||
@@ -109,7 +123,7 @@ public static class SettingsMigrations
|
||||
{
|
||||
if(provider.IsSelfHosted)
|
||||
return provider with { Host = Host.LM_STUDIO };
|
||||
|
||||
|
||||
return provider with { Host = Host.NONE };
|
||||
}).ToList(),
|
||||
|
||||
@@ -129,14 +143,14 @@ public static class SettingsMigrations
|
||||
// Summary:
|
||||
// We grouped the settings into different categories.
|
||||
//
|
||||
|
||||
|
||||
logger.LogInformation("Migrating from v3 to v4...");
|
||||
return new()
|
||||
{
|
||||
Version = Version.V4,
|
||||
Providers = previousConfig.Providers,
|
||||
NextProviderNum = previousConfig.NextProviderNum,
|
||||
|
||||
|
||||
App = new(x => x.App)
|
||||
{
|
||||
EnableSpellchecking = previousConfig.EnableSpellchecking,
|
||||
@@ -144,27 +158,27 @@ public static class SettingsMigrations
|
||||
UpdateInterval = previousConfig.UpdateInterval,
|
||||
NavigationBehavior = previousConfig.NavigationBehavior,
|
||||
},
|
||||
|
||||
|
||||
Chat = new()
|
||||
{
|
||||
ShortcutSendBehavior = previousConfig.ShortcutSendBehavior,
|
||||
PreselectOptions = previousConfig.PreselectChatOptions,
|
||||
PreselectedProvider = previousConfig.PreselectedChatProvider,
|
||||
},
|
||||
|
||||
|
||||
Workspace = new()
|
||||
{
|
||||
StorageBehavior = previousConfig.WorkspaceStorageBehavior,
|
||||
StorageTemporaryMaintenancePolicy = previousConfig.WorkspaceStorageTemporaryMaintenancePolicy,
|
||||
},
|
||||
|
||||
|
||||
IconFinder = new()
|
||||
{
|
||||
PreselectOptions = previousConfig.PreselectIconOptions,
|
||||
PreselectedProvider = previousConfig.PreselectedIconProvider,
|
||||
PreselectedSource = previousConfig.PreselectedIconSource,
|
||||
},
|
||||
|
||||
|
||||
Translation = new()
|
||||
{
|
||||
PreselectLiveTranslation = previousConfig.PreselectLiveTranslation,
|
||||
@@ -177,7 +191,7 @@ public static class SettingsMigrations
|
||||
PreselectContentCleanerAgent = previousConfig.PreselectContentCleanerAgentForTranslation,
|
||||
PreselectWebContentReader = previousConfig.PreselectWebContentReaderForTranslation,
|
||||
},
|
||||
|
||||
|
||||
Coding = new()
|
||||
{
|
||||
PreselectOptions = previousConfig.PreselectCodingOptions,
|
||||
@@ -186,7 +200,7 @@ public static class SettingsMigrations
|
||||
PreselectedOtherProgrammingLanguage = previousConfig.PreselectedCodingOtherLanguage,
|
||||
PreselectCompilerMessages = previousConfig.PreselectCodingCompilerMessages,
|
||||
},
|
||||
|
||||
|
||||
TextSummarizer = new()
|
||||
{
|
||||
PreselectOptions = previousConfig.PreselectTextSummarizerOptions,
|
||||
@@ -199,7 +213,7 @@ public static class SettingsMigrations
|
||||
PreselectContentCleanerAgent = previousConfig.PreselectContentCleanerAgentForTextSummarizer,
|
||||
PreselectWebContentReader = previousConfig.PreselectWebContentReaderForTextSummarizer,
|
||||
},
|
||||
|
||||
|
||||
TextContentCleaner = new()
|
||||
{
|
||||
PreselectAgentOptions = previousConfig.PreselectAgentTextContentCleanerOptions,
|
||||
@@ -207,14 +221,14 @@ public static class SettingsMigrations
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private static Data MigrateV4ToV5(ILogger<SettingsManager> logger, DataV4 previousConfig)
|
||||
|
||||
private static DataV5 MigrateV4ToV5(ILogger<SettingsManager> logger, DataV4 previousConfig)
|
||||
{
|
||||
//
|
||||
// Summary:
|
||||
// We renamed the LLM provider enum.
|
||||
//
|
||||
|
||||
|
||||
logger.LogInformation("Migrating from v4 to v5...");
|
||||
return new()
|
||||
{
|
||||
@@ -241,4 +255,68 @@ public static class SettingsMigrations
|
||||
MyTasks = previousConfig.MyTasks,
|
||||
};
|
||||
}
|
||||
|
||||
private static Data MigrateV5ToV6(ILogger<SettingsManager> logger, DataV5 previousConfig)
|
||||
{
|
||||
//
|
||||
// Summary:
|
||||
// We moved confidence settings out of LLM provider settings.
|
||||
//
|
||||
|
||||
logger.LogInformation("Migrating from v5 to v6...");
|
||||
return new()
|
||||
{
|
||||
Version = Version.V6,
|
||||
Providers = previousConfig.Providers,
|
||||
Confidence = new(x => x.Confidence)
|
||||
{
|
||||
EnforceGlobalMinimumConfidence = previousConfig.LLMProviders.EnforceGlobalMinimumConfidence,
|
||||
GlobalMinimumConfidence = previousConfig.LLMProviders.GlobalMinimumConfidence,
|
||||
ShowProviderConfidence = previousConfig.LLMProviders.ShowProviderConfidence,
|
||||
ConfidenceScheme = previousConfig.LLMProviders.ConfidenceScheme,
|
||||
CustomConfidenceScheme = previousConfig.LLMProviders.CustomConfidenceScheme,
|
||||
},
|
||||
EmbeddingProviders = previousConfig.EmbeddingProviders,
|
||||
TranscriptionProviders = previousConfig.TranscriptionProviders,
|
||||
DataSources = previousConfig.DataSources,
|
||||
Profiles = previousConfig.Profiles,
|
||||
ChatTemplates = previousConfig.ChatTemplates,
|
||||
EnabledPlugins = previousConfig.EnabledPlugins,
|
||||
ManagedEditableDefaults = previousConfig.ManagedEditableDefaults,
|
||||
AssistantPluginAudits = previousConfig.AssistantPluginAudits,
|
||||
NextProviderNum = previousConfig.NextProviderNum,
|
||||
NextEmbeddingNum = previousConfig.NextEmbeddingNum,
|
||||
NextTranscriptionNum = previousConfig.NextTranscriptionNum,
|
||||
NextDataSourceNum = previousConfig.NextDataSourceNum,
|
||||
NextProfileNum = previousConfig.NextProfileNum,
|
||||
NextChatTemplateNum = previousConfig.NextChatTemplateNum,
|
||||
NextDocumentAnalysisPolicyNum = previousConfig.NextDocumentAnalysisPolicyNum,
|
||||
App = previousConfig.App,
|
||||
Chat = previousConfig.Chat,
|
||||
Workspace = previousConfig.Workspace,
|
||||
IconFinder = previousConfig.IconFinder,
|
||||
Translation = previousConfig.Translation,
|
||||
Coding = previousConfig.Coding,
|
||||
ERI = previousConfig.ERI,
|
||||
DocumentAnalysis = previousConfig.DocumentAnalysis,
|
||||
MandatoryInformation = previousConfig.MandatoryInformation,
|
||||
TextSummarizer = previousConfig.TextSummarizer,
|
||||
TextContentCleaner = previousConfig.TextContentCleaner,
|
||||
AgentDataSourceSelection = previousConfig.AgentDataSourceSelection,
|
||||
AgentRetrievalContextValidation = previousConfig.AgentRetrievalContextValidation,
|
||||
AssistantPluginAudit = previousConfig.AssistantPluginAudit,
|
||||
Agenda = previousConfig.Agenda,
|
||||
GrammarSpelling = previousConfig.GrammarSpelling,
|
||||
RewriteImprove = previousConfig.RewriteImprove,
|
||||
PromptOptimizer = previousConfig.PromptOptimizer,
|
||||
EMail = previousConfig.EMail,
|
||||
SlideBuilder = previousConfig.SlideBuilder,
|
||||
LegalCheck = previousConfig.LegalCheck,
|
||||
Synonyms = previousConfig.Synonyms,
|
||||
MyTasks = previousConfig.MyTasks,
|
||||
JobPostings = previousConfig.JobPostings,
|
||||
BiasOfTheDay = previousConfig.BiasOfTheDay,
|
||||
I18N = previousConfig.I18N,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public enum SettingsWriteBlockReason
|
||||
{
|
||||
NONE,
|
||||
VERSION_MISSING,
|
||||
VERSION_UNKNOWN,
|
||||
VERSION_NEWER_THAN_APP,
|
||||
FILE_UNREADABLE,
|
||||
CURRENT_VERSION_INVALID,
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public sealed record TranscriptionProvider(
|
||||
|
||||
/// <inheritdoc />
|
||||
[JsonIgnore]
|
||||
public string SecretId => this.IsEnterpriseConfiguration ? $"{ISecretId.ENTERPRISE_KEY_PREFIX}::{this.UsedLLMProvider.ToName()}" : this.UsedLLMProvider.ToName();
|
||||
public string SecretId => this.IsEnterpriseConfiguration ? $"{ISecretId.ENTERPRISE_KEY_PREFIX}::{this.UsedLLMProvider.ToSecretId()}" : this.UsedLLMProvider.ToSecretId();
|
||||
|
||||
/// <inheritdoc />
|
||||
[JsonIgnore]
|
||||
@@ -125,7 +125,7 @@ public sealed record TranscriptionProvider(
|
||||
{
|
||||
// Queue the API key for storage in the OS keyring:
|
||||
PendingEnterpriseApiKeys.Add(new(
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToName()}",
|
||||
$"{ISecretId.ENTERPRISE_KEY_PREFIX}::{usedLLMProvider.ToSecretId()}",
|
||||
name,
|
||||
decryptedApiKey,
|
||||
SecretStoreType.TRANSCRIPTION_PROVIDER));
|
||||
|
||||
@@ -13,4 +13,5 @@ public enum Version
|
||||
V3,
|
||||
V4,
|
||||
V5,
|
||||
V6,
|
||||
}
|
||||
Reference in new issue
Block a user