Added support for organization-trusted providers (#816)
Some checks are pending
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions

This commit is contained in:
Thorsten Sommer 2026-06-21 15:16:37 +02:00 committed by GitHub
parent e65110a142
commit dddb40096d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 201 additions and 48 deletions

View File

@ -2893,6 +2893,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T32678
-- Close -- Close
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3448155331"] = "Close" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3448155331"] = "Close"
-- This embedding provider is trusted by your organization for data source security checks. Local data can be sent to it without security warnings.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3459188215"] = "This embedding provider is trusted by your organization for data source security checks. Local data can be sent to it without security warnings."
-- Actions -- Actions
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3865031940"] = "Actions" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3865031940"] = "Actions"
@ -2935,6 +2938,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T336
-- Export API Key? -- Export API Key?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T4010580285"] = "Export API Key?" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T4010580285"] = "Export API Key?"
-- This provider is trusted by your organization for data source security checks.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1298650849"] = "This provider is trusted by your organization for data source security checks."
-- Delete -- Delete
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1469573738"] = "Delete" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1469573738"] = "Delete"
@ -3037,6 +3043,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T42
-- With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure providers' section. -- With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure providers' section.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T584860404"] = "With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure providers' section." UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T584860404"] = "With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure providers' section."
-- This transcription provider is trusted by your organization for data source security checks.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T601264181"] = "This transcription provider is trusted by your organization for data source security checks."
-- This transcription provider is managed by your organization. -- This transcription provider is managed by your organization.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T756131076"] = "This transcription provider is managed by your organization." UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T756131076"] = "This transcription provider is managed by your organization."

View File

@ -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,
}; };
} }

View File

@ -1,4 +1,5 @@
@using AIStudio.Provider @using AIStudio.Provider
@using AIStudio.Settings
@using AIStudio.Settings.DataModel @using AIStudio.Settings.DataModel
@inherits SettingsPanelProviderBase @inherits SettingsPanelProviderBase
@ -39,6 +40,12 @@
<MudTd> <MudTd>
<MudStack Row="true" Class="mb-2 mt-2" Spacing="1" Wrap="Wrap.Wrap"> <MudStack Row="true" Class="mb-2 mt-2" Spacing="1" Wrap="Wrap.Wrap">
@if (context.IsTrustedByConfiguration(this.SettingsManager))
{
<MudTooltip Text="@T("This embedding provider is trusted by your organization for data source security checks. Local data can be sent to it without security warnings.")">
<MudIconButton Color="Color.Success" Icon="@Icons.Material.Filled.VerifiedUser" Disabled="true"/>
</MudTooltip>
}
@if (context.IsEnterpriseConfiguration) @if (context.IsEnterpriseConfiguration)
{ {
<MudTooltip Text="@T("This embedding provider is managed by your organization.")"> <MudTooltip Text="@T("This embedding provider is managed by your organization.")">

View File

@ -1,4 +1,5 @@
@using AIStudio.Provider @using AIStudio.Provider
@using AIStudio.Settings
@inherits SettingsPanelProviderBase @inherits SettingsPanelProviderBase
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Layers" HeaderText="@T("Configure LLM Providers")"> <ExpansionPanel HeaderIcon="@Icons.Material.Filled.Layers" HeaderText="@T("Configure LLM Providers")">
@ -30,6 +31,12 @@
<MudTd>@this.GetLLMProviderModelName(context)</MudTd> <MudTd>@this.GetLLMProviderModelName(context)</MudTd>
<MudTd> <MudTd>
<MudStack Row="true" Class="mb-2 mt-2" Spacing="1" Wrap="Wrap.Wrap"> <MudStack Row="true" Class="mb-2 mt-2" Spacing="1" Wrap="Wrap.Wrap">
@if (context.IsTrustedByConfiguration(this.SettingsManager))
{
<MudTooltip Text="@T("This provider is trusted by your organization for data source security checks.")">
<MudIconButton Color="Color.Success" Icon="@Icons.Material.Filled.VerifiedUser" Disabled="true"/>
</MudTooltip>
}
@if (context.IsEnterpriseConfiguration) @if (context.IsEnterpriseConfiguration)
{ {
<MudTooltip Text="@T("This provider is managed by your organization.")"> <MudTooltip Text="@T("This provider is managed by your organization.")">

View File

@ -1,4 +1,5 @@
@using AIStudio.Provider @using AIStudio.Provider
@using AIStudio.Settings
@using AIStudio.Settings.DataModel @using AIStudio.Settings.DataModel
@inherits SettingsPanelProviderBase @inherits SettingsPanelProviderBase
@ -35,6 +36,12 @@
<MudTd> <MudTd>
<MudStack Row="true" Class="mb-2 mt-2" Spacing="1" Wrap="Wrap.Wrap"> <MudStack Row="true" Class="mb-2 mt-2" Spacing="1" Wrap="Wrap.Wrap">
@if (context.IsTrustedByConfiguration(this.SettingsManager))
{
<MudTooltip Text="@T("This transcription provider is trusted by your organization for data source security checks.")">
<MudIconButton Color="Color.Success" Icon="@Icons.Material.Filled.VerifiedUser" Disabled="true"/>
</MudTooltip>
}
@if (context.IsEnterpriseConfiguration) @if (context.IsEnterpriseConfiguration)
{ {
<MudTooltip Text="@T("This transcription provider is managed by your organization.")"> <MudTooltip Text="@T("This transcription provider is managed by your organization.")">

View File

@ -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()
{ {

View File

@ -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;

View File

@ -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()
{ {

View File

@ -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;

View File

@ -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"]["DataSourceSecuritySettings.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"] = {}

View File

@ -2895,6 +2895,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T32678
-- Close -- Close
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3448155331"] = "Schließen" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3448155331"] = "Schließen"
-- This embedding provider is trusted by your organization for data source security checks. Local data can be sent to it without security warnings.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3459188215"] = "Ihre Organisation vertraut diesem Anbieter von Einbettungen bei der Sicherheitsprüfung von Datenquellen. Lokale Daten können ohne Sicherheitswarnungen an diesen gesendet werden."
-- Actions -- Actions
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3865031940"] = "Aktionen" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3865031940"] = "Aktionen"
@ -2937,6 +2940,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T336
-- Export API Key? -- Export API Key?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T4010580285"] = "API-Schlüssel exportieren?" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T4010580285"] = "API-Schlüssel exportieren?"
-- This provider is trusted by your organization for data source security checks.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1298650849"] = "Ihre Organisation vertraut diesem Anbieter bei der Sicherheitsprüfung von Datenquellen."
-- Delete -- Delete
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1469573738"] = "Löschen" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1469573738"] = "Löschen"
@ -3039,6 +3045,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T42
-- With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure LLM providers' section. -- With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure LLM providers' section.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T584860404"] = "Mit Unterstützung von Modellen für Transkriptionen kann MindWork AI Studio menschliche Sprache in Text umwandeln. Das ist zum Beispiel hilfreich, wenn Sie Texte diktieren möchten. Sie können aus speziellen Modellen für Transkriptionen wählen, jedoch nicht aus multimodalen LLMs (Large Language Models), die sowohl Sprache als auch Text verarbeiten können. Die Einrichtung multimodaler Modelle erfolgt im Abschnitt „Anbieter für LLMs konfigurieren“." UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T584860404"] = "Mit Unterstützung von Modellen für Transkriptionen kann MindWork AI Studio menschliche Sprache in Text umwandeln. Das ist zum Beispiel hilfreich, wenn Sie Texte diktieren möchten. Sie können aus speziellen Modellen für Transkriptionen wählen, jedoch nicht aus multimodalen LLMs (Large Language Models), die sowohl Sprache als auch Text verarbeiten können. Die Einrichtung multimodaler Modelle erfolgt im Abschnitt „Anbieter für LLMs konfigurieren“."
-- This transcription provider is trusted by your organization for data source security checks.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T601264181"] = "Ihre Organisation vertraut diesem Anbieter für Transkriptionen bei der Sicherheitsprüfung von Datenquellen."
-- This transcription provider is managed by your organization. -- This transcription provider is managed by your organization.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T756131076"] = "Dieser Anbieter für Transkriptionen wird von Ihrer Organisation verwaltet." UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T756131076"] = "Dieser Anbieter für Transkriptionen wird von Ihrer Organisation verwaltet."

View File

@ -2895,6 +2895,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T32678
-- Close -- Close
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3448155331"] = "Close" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3448155331"] = "Close"
-- This embedding provider is trusted by your organization for data source security checks. Local data can be sent to it without security warnings.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3459188215"] = "This embedding provider is trusted by your organization for data source security checks. Local data can be sent to it without security warnings."
-- Actions -- Actions
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3865031940"] = "Actions" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3865031940"] = "Actions"
@ -2937,6 +2940,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T336
-- Export API Key? -- Export API Key?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T4010580285"] = "Export API Key?" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERBASE::T4010580285"] = "Export API Key?"
-- This provider is trusted by your organization for data source security checks.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1298650849"] = "This provider is trusted by your organization for data source security checks."
-- Delete -- Delete
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1469573738"] = "Delete" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1469573738"] = "Delete"
@ -3039,6 +3045,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T42
-- With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure LLM providers' section. -- With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure LLM providers' section.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T584860404"] = "With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure LLM providers' section." UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T584860404"] = "With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure LLM providers' section."
-- This transcription provider is trusted by your organization for data source security checks.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T601264181"] = "This transcription provider is trusted by your organization for data source security checks."
-- This transcription provider is managed by your organization. -- This transcription provider is managed by your organization.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T756131076"] = "This transcription provider is managed by your organization." UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T756131076"] = "This transcription provider is managed by your organization."

View File

@ -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; }

View File

@ -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.

View File

@ -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(),
}; };

View File

@ -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 />

View File

@ -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>

View File

@ -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, []);
}

View File

@ -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));
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
// //

View File

@ -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.