mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-06-27 15:56:28 +00:00
Added support for organization-trusted providers
This commit is contained in:
parent
e65110a142
commit
78670b310b
@ -1,4 +1,5 @@
|
|||||||
using AIStudio.Provider.SelfHosted;
|
using AIStudio.Provider;
|
||||||
|
using AIStudio.Settings;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
|
|
||||||
namespace AIStudio.Chat;
|
namespace AIStudio.Chat;
|
||||||
@ -33,12 +34,13 @@ public static class ChatThreadExtensions
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Is the provider self-hosted?
|
// Is the provider trusted for data-source security checks?
|
||||||
//
|
//
|
||||||
var isSelfHostedProvider = provider switch
|
var settingsManager = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||||
|
var isTrustedProvider = provider switch
|
||||||
{
|
{
|
||||||
ProviderSelfHosted => true,
|
IProvider p => p.IsTrustedForDataSourceSecurityChecks(settingsManager),
|
||||||
AIStudio.Settings.Provider p => p.IsSelfHosted,
|
AIStudio.Settings.Provider p => p.IsTrustedForDataSourceSecurityChecks(settingsManager),
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
@ -46,12 +48,12 @@ public static class ChatThreadExtensions
|
|||||||
//
|
//
|
||||||
// Check the chat data security against the selected provider:
|
// Check the chat data security against the selected provider:
|
||||||
//
|
//
|
||||||
return isSelfHostedProvider switch
|
return isTrustedProvider switch
|
||||||
{
|
{
|
||||||
// The provider is self-hosted -- we can use any data source:
|
// The provider is trusted -- we can use any data source:
|
||||||
true => true,
|
true => true,
|
||||||
|
|
||||||
// The provider is not self-hosted -- it depends on the data security of the chat thread:
|
// The provider is not trusted -- it depends on the data security of the chat thread:
|
||||||
false => chatThread.DataSecurity is not DataSourceSecurity.SELF_HOSTED,
|
false => chatThread.DataSecurity is not DataSourceSecurity.SELF_HOSTED,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,7 +96,7 @@ public partial class DataSourceLocalDirectoryDialog : MSGComponentBase
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private bool SelectedCloudEmbedding => !this.SettingsManager.ConfigurationData.EmbeddingProviders.FirstOrDefault(x => x.Id == this.dataEmbeddingId)?.IsSelfHosted ?? false;
|
private bool SelectedCloudEmbedding => !(this.SettingsManager.ConfigurationData.EmbeddingProviders.FirstOrDefault(x => x.Id == this.dataEmbeddingId)?.IsTrustedForDataSourceSecurityChecks(this.SettingsManager) ?? false);
|
||||||
|
|
||||||
private DataSourceLocalDirectory CreateDataSource() => new()
|
private DataSourceLocalDirectory CreateDataSource() => new()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -56,7 +56,7 @@ public partial class DataSourceLocalDirectoryInfoDialog : MSGComponentBase, IAsy
|
|||||||
|
|
||||||
private bool IsOperationInProgress { get; set; } = true;
|
private bool IsOperationInProgress { get; set; } = true;
|
||||||
|
|
||||||
private bool IsCloudEmbedding => !this.embeddingProvider.IsSelfHosted;
|
private bool IsCloudEmbedding => !this.embeddingProvider.IsTrustedForDataSourceSecurityChecks(this.SettingsManager);
|
||||||
|
|
||||||
private bool IsDirectoryAvailable => this.directoryInfo.Exists;
|
private bool IsDirectoryAvailable => this.directoryInfo.Exists;
|
||||||
|
|
||||||
|
|||||||
@ -96,7 +96,7 @@ public partial class DataSourceLocalFileDialog : MSGComponentBase
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private bool SelectedCloudEmbedding => !this.SettingsManager.ConfigurationData.EmbeddingProviders.FirstOrDefault(x => x.Id == this.dataEmbeddingId)?.IsSelfHosted ?? false;
|
private bool SelectedCloudEmbedding => !(this.SettingsManager.ConfigurationData.EmbeddingProviders.FirstOrDefault(x => x.Id == this.dataEmbeddingId)?.IsTrustedForDataSourceSecurityChecks(this.SettingsManager) ?? false);
|
||||||
|
|
||||||
private DataSourceLocalFile CreateDataSource() => new()
|
private DataSourceLocalFile CreateDataSource() => new()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -28,7 +28,7 @@ public partial class DataSourceLocalFileInfoDialog : MSGComponentBase
|
|||||||
private EmbeddingProvider embeddingProvider = EmbeddingProvider.NONE;
|
private EmbeddingProvider embeddingProvider = EmbeddingProvider.NONE;
|
||||||
private FileInfo fileInfo = null!;
|
private FileInfo fileInfo = null!;
|
||||||
|
|
||||||
private bool IsCloudEmbedding => !this.embeddingProvider.IsSelfHosted;
|
private bool IsCloudEmbedding => !this.embeddingProvider.IsTrustedForDataSourceSecurityChecks(this.SettingsManager);
|
||||||
|
|
||||||
private bool IsFileAvailable => this.fileInfo.Exists;
|
private bool IsFileAvailable => this.fileInfo.Exists;
|
||||||
|
|
||||||
|
|||||||
@ -337,6 +337,15 @@ CONFIG["SETTINGS"] = {}
|
|||||||
--
|
--
|
||||||
-- Configure whether users can change the custom confidence scheme locally.
|
-- Configure whether users can change the custom confidence scheme locally.
|
||||||
-- CONFIG["SETTINGS"]["DataConfidence.CustomConfidenceScheme.AllowUserOverride"] = false
|
-- CONFIG["SETTINGS"]["DataConfidence.CustomConfidenceScheme.AllowUserOverride"] = false
|
||||||
|
--
|
||||||
|
-- Configure provider instances trusted by your organization for data-source security checks.
|
||||||
|
-- These IDs may refer to LLM providers, embedding providers, or transcription providers
|
||||||
|
-- defined in this configuration. Trusted providers are treated like self-hosted providers
|
||||||
|
-- only for data-source security checks and related local data warnings.
|
||||||
|
-- CONFIG["SETTINGS"]["DataSourceSecurity.TrustedProviderIds"] = {
|
||||||
|
-- "00000000-0000-0000-0000-000000000000",
|
||||||
|
-- "00000000-0000-0000-0000-000000000001",
|
||||||
|
-- }
|
||||||
|
|
||||||
-- Example chat templates for this configuration:
|
-- Example chat templates for this configuration:
|
||||||
CONFIG["CHAT_TEMPLATES"] = {}
|
CONFIG["CHAT_TEMPLATES"] = {}
|
||||||
|
|||||||
@ -78,6 +78,9 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public abstract string Id { get; }
|
public abstract string Id { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string ConfiguredProviderId { get; init; } = string.Empty;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public abstract string InstanceName { get; set; }
|
public abstract string InstanceName { get; set; }
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,11 @@ public interface IProvider
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Id { get; }
|
public string Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the configured provider instance.
|
||||||
|
/// </summary>
|
||||||
|
public string ConfiguredProviderId { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The provider's instance name. Useful for multiple instances of the same provider,
|
/// The provider's instance name. Useful for multiple instances of the same provider,
|
||||||
/// e.g., to distinguish between different OpenAI API keys.
|
/// e.g., to distinguish between different OpenAI API keys.
|
||||||
|
|||||||
@ -225,7 +225,7 @@ public static class LLMProvidersExtensions
|
|||||||
/// <returns>The provider instance.</returns>
|
/// <returns>The provider instance.</returns>
|
||||||
public static IProvider CreateProvider(this AIStudio.Settings.Provider providerSettings)
|
public static IProvider CreateProvider(this AIStudio.Settings.Provider providerSettings)
|
||||||
{
|
{
|
||||||
return providerSettings.UsedLLMProvider.CreateProvider(providerSettings.InstanceName, providerSettings.Host, providerSettings.Hostname, providerSettings.Model, providerSettings.HFInferenceProvider, providerSettings.AdditionalJsonApiParameters, providerSettings.IsEnterpriseConfiguration);
|
return providerSettings.UsedLLMProvider.CreateProvider(providerSettings.InstanceName, providerSettings.Host, providerSettings.Hostname, providerSettings.Model, providerSettings.HFInferenceProvider, providerSettings.Id, providerSettings.AdditionalJsonApiParameters, providerSettings.IsEnterpriseConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -235,7 +235,7 @@ public static class LLMProvidersExtensions
|
|||||||
/// <returns>The provider instance.</returns>
|
/// <returns>The provider instance.</returns>
|
||||||
public static IProvider CreateProvider(this EmbeddingProvider embeddingProviderSettings)
|
public static IProvider CreateProvider(this EmbeddingProvider embeddingProviderSettings)
|
||||||
{
|
{
|
||||||
return embeddingProviderSettings.UsedLLMProvider.CreateProvider(embeddingProviderSettings.Name, embeddingProviderSettings.Host, embeddingProviderSettings.Hostname, embeddingProviderSettings.Model, HFInferenceProvider.NONE, isEnterpriseConfiguration: embeddingProviderSettings.IsEnterpriseConfiguration);
|
return embeddingProviderSettings.UsedLLMProvider.CreateProvider(embeddingProviderSettings.Name, embeddingProviderSettings.Host, embeddingProviderSettings.Hostname, embeddingProviderSettings.Model, HFInferenceProvider.NONE, configuredProviderId: embeddingProviderSettings.Id, isEnterpriseConfiguration: embeddingProviderSettings.IsEnterpriseConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -245,33 +245,33 @@ public static class LLMProvidersExtensions
|
|||||||
/// <returns>The provider instance.</returns>
|
/// <returns>The provider instance.</returns>
|
||||||
public static IProvider CreateProvider(this TranscriptionProvider transcriptionProviderSettings)
|
public static IProvider CreateProvider(this TranscriptionProvider transcriptionProviderSettings)
|
||||||
{
|
{
|
||||||
return transcriptionProviderSettings.UsedLLMProvider.CreateProvider(transcriptionProviderSettings.Name, transcriptionProviderSettings.Host, transcriptionProviderSettings.Hostname, transcriptionProviderSettings.Model, HFInferenceProvider.NONE, isEnterpriseConfiguration: transcriptionProviderSettings.IsEnterpriseConfiguration);
|
return transcriptionProviderSettings.UsedLLMProvider.CreateProvider(transcriptionProviderSettings.Name, transcriptionProviderSettings.Host, transcriptionProviderSettings.Hostname, transcriptionProviderSettings.Model, HFInferenceProvider.NONE, configuredProviderId: transcriptionProviderSettings.Id, isEnterpriseConfiguration: transcriptionProviderSettings.IsEnterpriseConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IProvider CreateProvider(this LLMProviders provider, string instanceName, Host host, string hostname, Model model, HFInferenceProvider inferenceProvider, string expertProviderApiParameter = "", bool isEnterpriseConfiguration = false)
|
private static IProvider CreateProvider(this LLMProviders provider, string instanceName, Host host, string hostname, Model model, HFInferenceProvider inferenceProvider, string configuredProviderId = "", string expertProviderApiParameter = "", bool isEnterpriseConfiguration = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return provider switch
|
return provider switch
|
||||||
{
|
{
|
||||||
LLMProviders.OPEN_AI => new ProviderOpenAI { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.OPEN_AI => new ProviderOpenAI { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.ANTHROPIC => new ProviderAnthropic { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.ANTHROPIC => new ProviderAnthropic { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.MISTRAL => new ProviderMistral { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.MISTRAL => new ProviderMistral { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.GOOGLE => new ProviderGoogle { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.GOOGLE => new ProviderGoogle { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.X => new ProviderX { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.X => new ProviderX { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.DEEP_SEEK => new ProviderDeepSeek { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.DEEP_SEEK => new ProviderDeepSeek { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.ALIBABA_CLOUD => new ProviderAlibabaCloud { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.ALIBABA_CLOUD => new ProviderAlibabaCloud { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.PERPLEXITY => new ProviderPerplexity { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.PERPLEXITY => new ProviderPerplexity { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.OPEN_ROUTER => new ProviderOpenRouter { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.OPEN_ROUTER => new ProviderOpenRouter { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
|
|
||||||
LLMProviders.GROQ => new ProviderGroq { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.GROQ => new ProviderGroq { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.FIREWORKS => new ProviderFireworks { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.FIREWORKS => new ProviderFireworks { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.HUGGINGFACE => new ProviderHuggingFace(inferenceProvider, model) { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.HUGGINGFACE => new ProviderHuggingFace(inferenceProvider, model) { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
|
|
||||||
LLMProviders.SELF_HOSTED => new ProviderSelfHosted(host, hostname) { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.SELF_HOSTED => new ProviderSelfHosted(host, hostname) { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
|
|
||||||
LLMProviders.HELMHOLTZ => new ProviderHelmholtz { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.HELMHOLTZ => new ProviderHelmholtz { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.GWDG => new ProviderGWDG { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.GWDG => new ProviderGWDG { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
|
|
||||||
_ => new NoProvider(),
|
_ => new NoProvider(),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,6 +13,8 @@ public class NoProvider : IProvider
|
|||||||
|
|
||||||
public string Id => "none";
|
public string Id => "none";
|
||||||
|
|
||||||
|
public string ConfiguredProviderId => string.Empty;
|
||||||
|
|
||||||
public string InstanceName { get; set; } = "None";
|
public string InstanceName { get; set; } = "None";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@ -23,6 +23,11 @@ public sealed class Data
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DataConfidence Confidence { get; init; } = new(x => x.Confidence);
|
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>
|
/// <summary>
|
||||||
/// A collection of embedding providers configured.
|
/// A collection of embedding providers configured.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -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,46 @@
|
|||||||
|
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 || IsTrustedProviderId(provider.Id, settingsManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsTrustedForDataSourceSecurityChecks(this EmbeddingProvider provider, SettingsManager settingsManager)
|
||||||
|
{
|
||||||
|
if (provider == EmbeddingProvider.NONE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return provider.IsSelfHosted || IsTrustedProviderId(provider.Id, settingsManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsTrustedForDataSourceSecurityChecks(this TranscriptionProvider provider, SettingsManager settingsManager)
|
||||||
|
{
|
||||||
|
if (provider == TranscriptionProvider.NONE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return provider.IsSelfHosted || IsTrustedProviderId(provider.Id, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -483,6 +483,7 @@ public sealed class SettingsManager
|
|||||||
throw new ArgumentException("Expression must be a property access", nameof(propertyExpression));
|
throw new ArgumentException("Expression must be a property access", nameof(propertyExpression));
|
||||||
|
|
||||||
// Return the full name of the property, including the class name:
|
// Return the full name of the property, including the class name:
|
||||||
return $"{typeof(TIn).Name}.{memberExpr.Member.Name}";
|
var typeName = typeof(TIn) == typeof(DataSourceSecuritySettings) ? "DataSourceSecurity" : typeof(TIn).Name;
|
||||||
|
return $"{typeName}.{memberExpr.Member.Name}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -202,6 +202,9 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
|||||||
ManagedConfiguration.TryProcessConfiguration(x => x.Confidence, x => x.ConfidenceScheme, this.Id, settingsTable, dryRun);
|
ManagedConfiguration.TryProcessConfiguration(x => x.Confidence, x => x.ConfidenceScheme, this.Id, settingsTable, dryRun);
|
||||||
ManagedConfiguration.TryProcessConfiguration(x => x.Confidence, x => x.CustomConfidenceScheme, this.Id, settingsTable, dryRun);
|
ManagedConfiguration.TryProcessConfiguration(x => x.Confidence, x => x.CustomConfidenceScheme, this.Id, settingsTable, dryRun);
|
||||||
|
|
||||||
|
// Config: data source security settings
|
||||||
|
ManagedConfiguration.TryProcessConfiguration(x => x.DataSourceSecurity, x => x.TrustedProviderIds, this.Id, settingsTable, dryRun);
|
||||||
|
|
||||||
// Handle configured LLM providers:
|
// Handle configured LLM providers:
|
||||||
PluginConfigurationObject.TryParse(PluginConfigurationObjectType.LLM_PROVIDER, x => x.Providers, x => x.NextProviderNum, mainTable, this.Id, ref this.configObjects, dryRun);
|
PluginConfigurationObject.TryParse(PluginConfigurationObjectType.LLM_PROVIDER, x => x.Providers, x => x.NextProviderNum, mainTable, this.Id, ref this.configObjects, dryRun);
|
||||||
|
|
||||||
|
|||||||
@ -283,6 +283,10 @@ public static partial class PluginFactory
|
|||||||
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Confidence, x => x.CustomConfidenceScheme, AVAILABLE_PLUGINS))
|
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Confidence, x => x.CustomConfidenceScheme, AVAILABLE_PLUGINS))
|
||||||
wasConfigurationChanged = true;
|
wasConfigurationChanged = true;
|
||||||
|
|
||||||
|
// Check data source security settings:
|
||||||
|
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.DataSourceSecurity, x => x.TrustedProviderIds, AVAILABLE_PLUGINS))
|
||||||
|
wasConfigurationChanged = true;
|
||||||
|
|
||||||
// Check if audit is required before it can be activated
|
// Check if audit is required before it can be activated
|
||||||
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AssistantPluginAudit, x => x.RequireAuditBeforeActivation, AVAILABLE_PLUGINS))
|
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AssistantPluginAudit, x => x.RequireAuditBeforeActivation, AVAILABLE_PLUGINS))
|
||||||
wasConfigurationChanged = true;
|
wasConfigurationChanged = true;
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using AIStudio.Assistants.ERI;
|
using AIStudio.Assistants.ERI;
|
||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Provider.SelfHosted;
|
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
using AIStudio.Tools.ERIClient;
|
using AIStudio.Tools.ERIClient;
|
||||||
@ -43,7 +42,7 @@ public sealed class DataSourceService
|
|||||||
return new([], []);
|
return new([], []);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.GetDataSources(selectedLLMProvider.IsSelfHosted, previousSelectedDataSources);
|
return await this.GetDataSources(selectedLLMProvider.IsTrustedForDataSourceSecurityChecks(this.settingsManager), previousSelectedDataSources);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -66,10 +65,10 @@ public sealed class DataSourceService
|
|||||||
return new([], []);
|
return new([], []);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.GetDataSources(selectedLLMProvider is ProviderSelfHosted, previousSelectedDataSources);
|
return await this.GetDataSources(selectedLLMProvider.IsTrustedForDataSourceSecurityChecks(this.settingsManager), previousSelectedDataSources);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<AllowedSelectedDataSources> GetDataSources(bool usingSelfHostedProvider, IReadOnlyCollection<IDataSource>? previousSelectedDataSources = null)
|
private async Task<AllowedSelectedDataSources> GetDataSources(bool usingTrustedProvider, IReadOnlyCollection<IDataSource>? previousSelectedDataSources = null)
|
||||||
{
|
{
|
||||||
var allDataSources = this.settingsManager.ConfigurationData.DataSources;
|
var allDataSources = this.settingsManager.ConfigurationData.DataSources;
|
||||||
var filteredDataSources = new List<IDataSource>(allDataSources.Count);
|
var filteredDataSources = new List<IDataSource>(allDataSources.Count);
|
||||||
@ -78,7 +77,7 @@ public sealed class DataSourceService
|
|||||||
|
|
||||||
// Start all checks in parallel:
|
// Start all checks in parallel:
|
||||||
foreach (var source in allDataSources)
|
foreach (var source in allDataSources)
|
||||||
tasks.Add(this.CheckOneDataSource(source, usingSelfHostedProvider));
|
tasks.Add(this.CheckOneDataSource(source, usingTrustedProvider));
|
||||||
|
|
||||||
// Wait for all checks and collect the results:
|
// Wait for all checks and collect the results:
|
||||||
foreach (var task in tasks)
|
foreach (var task in tasks)
|
||||||
@ -95,7 +94,7 @@ public sealed class DataSourceService
|
|||||||
return new(filteredDataSources, filteredSelectedDataSources);
|
return new(filteredDataSources, filteredSelectedDataSources);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IDataSource?> CheckOneDataSource(IDataSource source, bool usingSelfHostedProvider)
|
private async Task<IDataSource?> CheckOneDataSource(IDataSource source, bool usingTrustedProvider)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Unfortunately, we have to live-check any ERI source for its security requirements.
|
// Unfortunately, we have to live-check any ERI source for its security requirements.
|
||||||
@ -137,10 +136,10 @@ public sealed class DataSourceService
|
|||||||
case DataSourceSecurity.ALLOW_ANY:
|
case DataSourceSecurity.ALLOW_ANY:
|
||||||
|
|
||||||
//
|
//
|
||||||
// Case: The data source allows any provider type. We want to use a self-hosted provider.
|
// Case: The data source allows any provider type. We want to use a trusted provider.
|
||||||
// There is no issue with this source. Accept it.
|
// There is no issue with this source. Accept it.
|
||||||
//
|
//
|
||||||
if(usingSelfHostedProvider)
|
if(usingTrustedProvider)
|
||||||
return source;
|
return source;
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -151,13 +150,13 @@ public sealed class DataSourceService
|
|||||||
return source;
|
return source;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Case: The ERI source requires a self-hosted provider. This misconfiguration happens
|
// Case: The ERI source requires a self-hosted or organization-trusted provider. This misconfiguration happens
|
||||||
// when the ERI server operator changes the security requirements. The ERI server
|
// when the ERI server operator changes the security requirements. The ERI server
|
||||||
// operator owns the data -- we have to respect their rules. We skip this source.
|
// operator owns the data -- we have to respect their rules. We skip this source.
|
||||||
//
|
//
|
||||||
if (eriSourceRequirements is { AllowedProviderType: ProviderType.SELF_HOSTED })
|
if (eriSourceRequirements is { AllowedProviderType: ProviderType.SELF_HOSTED })
|
||||||
{
|
{
|
||||||
this.logger.LogWarning($"The ERI source '{source.Name}' (id={source.Id}) requires a self-hosted provider. We skip this source.");
|
this.logger.LogWarning($"The ERI source '{source.Name}' (id={source.Id}) requires a self-hosted or organization-trusted provider. We skip this source.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,22 +170,22 @@ public sealed class DataSourceService
|
|||||||
//
|
//
|
||||||
// Case: Missing rules. We skip this source. Better safe than sorry.
|
// Case: Missing rules. We skip this source. Better safe than sorry.
|
||||||
//
|
//
|
||||||
this.logger.LogDebug($"The ERI source '{source.Name}' (id={source.Id}) was filtered out due to missing rules.");
|
this.logger.LogWarning($"The ERI source '{source.Name}' (id={source.Id}) was filtered out due to missing rules.");
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Case: The data source requires a self-hosted provider. We want to use a self-hosted provider.
|
// Case: The data source requires a trusted provider. We want to use a trusted provider.
|
||||||
// There is no issue with this source. Accept it.
|
// There is no issue with this source. Accept it.
|
||||||
//
|
//
|
||||||
case DataSourceSecurity.SELF_HOSTED when usingSelfHostedProvider:
|
case DataSourceSecurity.SELF_HOSTED when usingTrustedProvider:
|
||||||
return source;
|
return source;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Case: The data source requires a self-hosted provider. We want to use a cloud provider.
|
// Case: The data source requires a trusted provider. We want to use an untrusted provider.
|
||||||
// We skip this source.
|
// We skip this source.
|
||||||
//
|
//
|
||||||
case DataSourceSecurity.SELF_HOSTED when !usingSelfHostedProvider:
|
case DataSourceSecurity.SELF_HOSTED when !usingTrustedProvider:
|
||||||
this.logger.LogWarning($"The data source '{source.Name}' (id={source.Id}) requires a self-hosted provider. We skip this source.");
|
this.logger.LogWarning($"The data source '{source.Name}' (id={source.Id}) requires a self-hosted or organization-trusted provider. We skip this source.");
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
- Added a read-only view for organization-managed profiles and chat templates, so users can inspect the content while the organization remains in control of changes.
|
- Added a read-only view for organization-managed profiles and chat templates, so users can inspect the content while the organization remains in control of changes.
|
||||||
- Added support for organization-managed introduction texts on the home page. Configuration plugins can now add custom Markdown introductions and hide the built-in introduction.
|
- Added support for organization-managed introduction texts on the home page. Configuration plugins can now add custom Markdown introductions and hide the built-in introduction.
|
||||||
- Added support for organization-managed provider confidence settings. Configuration plugins can now set confidence presets, custom confidence schemes, and an app-wide minimum confidence level.
|
- Added support for organization-managed provider confidence settings. Configuration plugins can now set confidence presets, custom confidence schemes, and an app-wide minimum confidence level.
|
||||||
|
- Added support for organization-trusted providers in data source security checks. Configuration plugins can now mark specific provider instances as trusted for data source usage and local embedding warnings.
|
||||||
- Changed provider confidence settings to appear in their own settings panel, because they apply to LLM, embedding, and transcription providers.
|
- Changed provider confidence settings to appear in their own settings panel, because they apply to LLM, embedding, and transcription providers.
|
||||||
- Fixed organization-managed chat templates not showing the correct icon in the chat template selection menu.
|
- Fixed organization-managed chat templates not showing the correct icon in the chat template selection menu.
|
||||||
- Fixed self-hosted provider API keys sometimes being stored under a localized name. AI Studio now uses a stable key name, keeps correct entries working, and automatically migrates known localized entries for LLM, transcription, and embedding providers. Organizations using configuration plugins do not need to change their plugins; affected users who still see an invalid API key warning should open the provider, transcription, or embedding settings and update the API key once.
|
- Fixed self-hosted provider API keys sometimes being stored under a localized name. AI Studio now uses a stable key name, keeps correct entries working, and automatically migrates known localized entries for LLM, transcription, and embedding providers. Organizations using configuration plugins do not need to change their plugins; affected users who still see an invalid API key warning should open the provider, transcription, or embedding settings and update the API key once.
|
||||||
Loading…
Reference in New Issue
Block a user