mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-04-28 10:39:47 +00:00
Improved data security by preventing the use of cloud LLMs after confidential data has been retrieved previously
This commit is contained in:
parent
371731ac13
commit
b2328716ce
@ -45,6 +45,11 @@ public sealed record ChatThread
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string AugmentedData { get; set; } = string.Empty;
|
public string AugmentedData { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The data security to use, derived from the data sources used so far.
|
||||||
|
/// </summary>
|
||||||
|
public DataSourceSecurity DataSecurity { get; set; } = DataSourceSecurity.NOT_SPECIFIED;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the chat thread. Usually generated by an AI model or manually edited by the user.
|
/// The name of the chat thread. Usually generated by an AI model or manually edited by the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
58
app/MindWork AI Studio/Chat/ChatThreadExtensions.cs
Normal file
58
app/MindWork AI Studio/Chat/ChatThreadExtensions.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using AIStudio.Provider.SelfHosted;
|
||||||
|
using AIStudio.Settings.DataModel;
|
||||||
|
|
||||||
|
namespace AIStudio.Chat;
|
||||||
|
|
||||||
|
public static class ChatThreadExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the specified provider is allowed for the chat thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// We don't check if the provider is allowed to use the data sources of the chat thread.
|
||||||
|
/// That kind of check is done in the RAG process itself.<br/><br/>
|
||||||
|
///
|
||||||
|
/// One thing which is not so obvious: after RAG was used on this thread, the entire chat
|
||||||
|
/// thread is kind of a data source by itself. Why? Because the augmentation data collected
|
||||||
|
/// from the data sources is stored in the chat thread. This means we must check if the
|
||||||
|
/// selected provider is allowed to use this thread's data.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="chatThread">The chat thread to check.</param>
|
||||||
|
/// <param name="provider">The provider to check.</param>
|
||||||
|
/// <returns>True, when the provider is allowed for the chat thread. False, otherwise.</returns>
|
||||||
|
public static bool IsLLMProviderAllowed<T>(this ChatThread? chatThread, T provider)
|
||||||
|
{
|
||||||
|
// No chat thread available means we have a new chat. That's fine:
|
||||||
|
if (chatThread is null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// The chat thread is available, but the data security is not specified.
|
||||||
|
// Means, we never used RAG or RAG was enabled, but no data sources were selected.
|
||||||
|
// That's fine as well:
|
||||||
|
if (chatThread.DataSecurity is DataSourceSecurity.NOT_SPECIFIED)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Is the provider self-hosted?
|
||||||
|
//
|
||||||
|
var isSelfHostedProvider = provider switch
|
||||||
|
{
|
||||||
|
ProviderSelfHosted => true,
|
||||||
|
AIStudio.Settings.Provider p => p.IsSelfHosted,
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check the chat data security against the selected provider:
|
||||||
|
//
|
||||||
|
return isSelfHostedProvider switch
|
||||||
|
{
|
||||||
|
// The provider is self-hosted -- we can use any data source:
|
||||||
|
true => true,
|
||||||
|
|
||||||
|
// The provider is not self-hosted -- it depends on the data security of the chat thread:
|
||||||
|
false => chatThread.DataSecurity is not DataSourceSecurity.SELF_HOSTED,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -41,6 +41,13 @@ public sealed class ContentText : IContent
|
|||||||
if(chatThread is null)
|
if(chatThread is null)
|
||||||
return new();
|
return new();
|
||||||
|
|
||||||
|
if(!chatThread.IsLLMProviderAllowed(provider))
|
||||||
|
{
|
||||||
|
var logger = Program.SERVICE_PROVIDER.GetService<ILogger<ContentText>>()!;
|
||||||
|
logger.LogError("The provider is not allowed for this chat thread due to data security reasons. Skipping the AI process.");
|
||||||
|
return chatThread;
|
||||||
|
}
|
||||||
|
|
||||||
// Call the RAG process. Right now, we only have one RAG process:
|
// Call the RAG process. Right now, we only have one RAG process:
|
||||||
if (lastPrompt is not null)
|
if (lastPrompt is not null)
|
||||||
{
|
{
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
Adornment="Adornment.End"
|
Adornment="Adornment.End"
|
||||||
AdornmentIcon="@Icons.Material.Filled.Send"
|
AdornmentIcon="@Icons.Material.Filled.Send"
|
||||||
OnAdornmentClick="() => this.SendMessage()"
|
OnAdornmentClick="() => this.SendMessage()"
|
||||||
ReadOnly="!this.IsProviderSelected || this.isStreaming"
|
Disabled="@this.IsInputForbidden()"
|
||||||
Immediate="@true"
|
Immediate="@true"
|
||||||
OnKeyUp="this.InputKeyEvent"
|
OnKeyUp="this.InputKeyEvent"
|
||||||
UserAttributes="@USER_INPUT_ATTRIBUTES"
|
UserAttributes="@USER_INPUT_ATTRIBUTES"
|
||||||
@ -113,6 +113,14 @@
|
|||||||
{
|
{
|
||||||
<DataSourceSelection @ref="@this.dataSourceSelectionComponent" PopoverTriggerMode="PopoverTriggerMode.BUTTON" PopoverButtonClasses="ma-3" LLMProvider="@this.Provider" DataSourceOptions="@this.GetCurrentDataSourceOptions()" DataSourceOptionsChanged="@(async options => await this.SetCurrentDataSourceOptions(options))" DataSourcesAISelected="@this.GetAgentSelectedDataSources()"/>
|
<DataSourceSelection @ref="@this.dataSourceSelectionComponent" PopoverTriggerMode="PopoverTriggerMode.BUTTON" PopoverButtonClasses="ma-3" LLMProvider="@this.Provider" DataSourceOptions="@this.GetCurrentDataSourceOptions()" DataSourceOptionsChanged="@(async options => await this.SetCurrentDataSourceOptions(options))" DataSourcesAISelected="@this.GetAgentSelectedDataSources()"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (!this.ChatThread.IsLLMProviderAllowed(this.Provider))
|
||||||
|
{
|
||||||
|
<MudTooltip Text="The selected provider is not allowed in this chat due to data security reasons." Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Error" Color="Color.Error"/>
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
<MudIconButton />
|
||||||
</MudToolBar>
|
</MudToolBar>
|
||||||
</FooterContent>
|
</FooterContent>
|
||||||
</InnerScrolling>
|
</InnerScrolling>
|
@ -340,6 +340,20 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
this.earlyDataSourceOptions = updatedOptions;
|
this.earlyDataSourceOptions = updatedOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsInputForbidden()
|
||||||
|
{
|
||||||
|
if (!this.IsProviderSelected)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(this.isStreaming)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(!this.ChatThread.IsLLMProviderAllowed(this.Provider))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task InputKeyEvent(KeyboardEventArgs keyEvent)
|
private async Task InputKeyEvent(KeyboardEventArgs keyEvent)
|
||||||
{
|
{
|
||||||
if(this.dataSourceSelectionComponent?.IsVisible ?? false)
|
if(this.dataSourceSelectionComponent?.IsVisible ?? false)
|
||||||
@ -374,6 +388,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
if (!this.IsProviderSelected)
|
if (!this.IsProviderSelected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(!this.ChatThread.IsLLMProviderAllowed(this.Provider))
|
||||||
|
return;
|
||||||
|
|
||||||
// We need to blur the focus away from the input field
|
// We need to blur the focus away from the input field
|
||||||
// to be able to clear the field:
|
// to be able to clear the field:
|
||||||
await this.inputField.BlurAsync();
|
await this.inputField.BlurAsync();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Settings.DataModel;
|
||||||
using AIStudio.Tools.RAG.AugmentationProcesses;
|
using AIStudio.Tools.RAG.AugmentationProcesses;
|
||||||
using AIStudio.Tools.RAG.DataSourceSelectionProcesses;
|
using AIStudio.Tools.RAG.DataSourceSelectionProcesses;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
@ -96,6 +97,56 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
|
|||||||
logger.LogWarning("No data sources are selected. The RAG process is skipped.");
|
logger.LogWarning("No data sources are selected. The RAG process is skipped.");
|
||||||
proceedWithRAG = false;
|
proceedWithRAG = false;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var previousDataSecurity = chatThread.DataSecurity;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update the data security of the chat thread. We consider the current data security
|
||||||
|
// of the chat thread and the data security of the selected data sources:
|
||||||
|
//
|
||||||
|
var dataSecurityRestrictedToSelfHosted = selectedDataSources.Any(x => x.SecurityPolicy is DataSourceSecurity.SELF_HOSTED);
|
||||||
|
chatThread.DataSecurity = dataSecurityRestrictedToSelfHosted switch
|
||||||
|
{
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Case: the data sources which are selected have a security policy
|
||||||
|
// of SELF_HOSTED (at least one data source).
|
||||||
|
//
|
||||||
|
// When the policy was already set to ALLOW_ANY, we restrict it
|
||||||
|
// to SELF_HOSTED.
|
||||||
|
//
|
||||||
|
true => DataSourceSecurity.SELF_HOSTED,
|
||||||
|
|
||||||
|
//
|
||||||
|
// Case: the data sources which are selected have a security policy
|
||||||
|
// of ALLOW_ANY (none of the data sources has a SELF_HOSTED policy).
|
||||||
|
//
|
||||||
|
// When the policy was already set to SELF_HOSTED, we must keep that.
|
||||||
|
//
|
||||||
|
false => chatThread.DataSecurity switch
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// When the policy was not specified yet, we set it to ALLOW_ANY.
|
||||||
|
//
|
||||||
|
DataSourceSecurity.NOT_SPECIFIED => DataSourceSecurity.ALLOW_ANY,
|
||||||
|
DataSourceSecurity.ALLOW_ANY => DataSourceSecurity.ALLOW_ANY,
|
||||||
|
|
||||||
|
//
|
||||||
|
// When the policy was already set to SELF_HOSTED, we must keep that.
|
||||||
|
// This is important since the thread might already contain data
|
||||||
|
// from a data source with a SELF_HOSTED policy.
|
||||||
|
//
|
||||||
|
DataSourceSecurity.SELF_HOSTED => DataSourceSecurity.SELF_HOSTED,
|
||||||
|
|
||||||
|
// Default case: we use the current data security of the chat thread.
|
||||||
|
_ => chatThread.DataSecurity,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (previousDataSecurity != chatThread.DataSecurity)
|
||||||
|
logger.LogInformation($"The data security of the chat thread was updated from '{previousDataSecurity}' to '{chatThread.DataSecurity}'.");
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Trigger the retrieval part of the (R)AG process:
|
// Trigger the retrieval part of the (R)AG process:
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# v0.9.32, build 207 (2025-03-xx xx:xx UTC)
|
# v0.9.32, build 207 (2025-03-xx xx:xx UTC)
|
||||||
- Added the "Community & Code" section to the about page. It includes links to the GitHub repositories and the project website.
|
- Added the "Community & Code" section to the about page. It includes links to the GitHub repositories and the project website.
|
||||||
|
- Improved data security by preventing the use of cloud LLMs after confidential data has been retrieved previously.
|
||||||
- Improved the ERI client to expect JSON responses and send JSON requests using camel case.
|
- Improved the ERI client to expect JSON responses and send JSON requests using camel case.
|
||||||
- Improved the ERI client to raise an error when the server responds with additional JSON data that is not expected.
|
- Improved the ERI client to raise an error when the server responds with additional JSON data that is not expected.
|
||||||
- Improved the error handling in the ERI data source info dialog in cases where servers respond with an invalid message.
|
- Improved the error handling in the ERI data source info dialog in cases where servers respond with an invalid message.
|
||||||
|
Loading…
Reference in New Issue
Block a user