Improved data security by enforcing provider filtering (#290)

This commit is contained in:
Thorsten Sommer 2025-02-23 15:05:29 +01:00 committed by GitHub
parent bfc9f2ea1d
commit 9bd79bd3a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 370 additions and 41 deletions

View File

@ -2,6 +2,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MindWork AI Studio", "MindWork AI Studio\MindWork AI Studio.csproj", "{059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MindWork AI Studio", "MindWork AI Studio\MindWork AI Studio.csproj", "{059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceCodeRules", "SourceCodeRules\SourceCodeRules\SourceCodeRules.csproj", "{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -12,6 +14,10 @@ Global
{059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}.Debug|Any CPU.Build.0 = Debug|Any CPU {059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}.Release|Any CPU.ActiveCfg = Release|Any CPU {059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}.Release|Any CPU.Build.0 = Release|Any CPU {059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}.Release|Any CPU.Build.0 = Release|Any CPU
{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
EndGlobalSection EndGlobalSection

View File

@ -9,5 +9,6 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=agentic/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=agentic/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=groq/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=groq/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=mwais/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ollama/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=ollama/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -140,17 +140,7 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
// //
// We start with the provider currently selected by the user: // We start with the provider currently selected by the user:
var agentProvider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == provider.Id); var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_DATA_SOURCE_SELECTION, provider.Id, true);
// If the user preselected an agent provider, we try to use this one:
if (this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions)
{
var configuredAgentProvider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectedAgentProvider);
// If the configured agent provider is available, we use it:
if (configuredAgentProvider != default)
agentProvider = configuredAgentProvider;
}
// Assign the provider settings to the agent: // Assign the provider settings to the agent:
logger.LogInformation($"The agent for the data source selection uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()})."); logger.LogInformation($"The agent for the data source selection uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");

View File

@ -132,17 +132,7 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
public void SetLLMProvider(IProvider provider) public void SetLLMProvider(IProvider provider)
{ {
// We start with the provider currently selected by the user: // We start with the provider currently selected by the user:
var agentProvider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == provider.Id); var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION, provider.Id, true);
// If the user preselected an agent provider, we try to use this one:
if (this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions)
{
var configuredAgentProvider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectedAgentProvider);
// If the configured agent provider is available, we use it:
if (configuredAgentProvider != default)
agentProvider = configuredAgentProvider;
}
// Assign the provider settings to the agent: // Assign the provider settings to the agent:
logger.LogInformation($"The agent for the retrieval context validation uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()})."); logger.LogInformation($"The agent for the retrieval context validation uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");
@ -163,11 +153,16 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
if (!this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation) if (!this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation)
return []; return [];
logger.LogInformation($"Validating {retrievalContexts.Count:###,###,###,###} retrieval contexts.");
// Prepare the list of validation tasks: // Prepare the list of validation tasks:
var validationTasks = new List<Task<RetrievalContextValidationResult>>(retrievalContexts.Count); var validationTasks = new List<Task<RetrievalContextValidationResult>>(retrievalContexts.Count);
// Read the number of parallel validations: // Read the number of parallel validations:
var numParallelValidations = this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.NumParallelValidations; var numParallelValidations = 3;
if(this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions)
numParallelValidations = this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.NumParallelValidations;
numParallelValidations = Math.Max(1, numParallelValidations); numParallelValidations = Math.Max(1, numParallelValidations);
// Use a semaphore to limit the number of parallel validations: // Use a semaphore to limit the number of parallel validations:

View File

@ -1,3 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using AIStudio.Provider; using AIStudio.Provider;
using AIStudio.Settings; using AIStudio.Settings;
@ -47,6 +49,7 @@ public partial class ConfigurationProviderSelection : ComponentBase, IMessageBus
#endregion #endregion
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
private IEnumerable<ConfigurationSelectData<string>> FilteredData() private IEnumerable<ConfigurationSelectData<string>> FilteredData()
{ {
if(this.Component is not Tools.Components.NONE and not Tools.Components.APP_SETTINGS) if(this.Component is not Tools.Components.NONE and not Tools.Components.APP_SETTINGS)

View File

@ -37,7 +37,11 @@
@if (this.areDataSourcesEnabled) @if (this.areDataSourcesEnabled)
{ {
<MudTextSwitch Label="AI-based data source selection" Value="@this.aiBasedSourceSelection" LabelOn="Yes, let the AI decide which data sources are needed." LabelOff="No, I manually decide which data source to use." ValueChanged="@this.AutoModeChanged"/> <MudTextSwitch Label="AI-based data source selection" Value="@this.aiBasedSourceSelection" LabelOn="Yes, let the AI decide which data sources are needed." LabelOff="No, I manually decide which data source to use." ValueChanged="@this.AutoModeChanged"/>
<MudTextSwitch Label="AI-based data validation" Value="@this.aiBasedValidation" LabelOn="Yes, let the AI validate & filter the retrieved data." LabelOff="No, use all data retrieved from the data sources." ValueChanged="@this.ValidationModeChanged"/>
@if (this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation)
{
<MudTextSwitch Label="AI-based data validation" Value="@this.aiBasedValidation" LabelOn="Yes, let the AI validate & filter the retrieved data." LabelOff="No, use all data retrieved from the data sources." ValueChanged="@this.ValidationModeChanged"/>
}
@if (this.aiBasedSourceSelection is false || this.DataSourcesAISelected.Count == 0) @if (this.aiBasedSourceSelection is false || this.DataSourcesAISelected.Count == 0)
{ {

View File

@ -1,3 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using AIStudio.Assistants; using AIStudio.Assistants;
using AIStudio.Provider; using AIStudio.Provider;
using AIStudio.Settings; using AIStudio.Settings;
@ -29,6 +31,7 @@ public partial class ProviderSelection : ComponentBase
await this.ProviderSettingsChanged.InvokeAsync(provider); await this.ProviderSettingsChanged.InvokeAsync(provider);
} }
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
private IEnumerable<AIStudio.Settings.Provider> GetAvailableProviders() private IEnumerable<AIStudio.Settings.Provider> GetAvailableProviders()
{ {
var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.AssistantBase?.Component ?? Tools.Components.NONE); var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.AssistantBase?.Component ?? Tools.Components.NONE);

View File

@ -59,11 +59,7 @@ public partial class ReadWebContent : ComponentBase
if(this.PreselectContentCleanerAgent) if(this.PreselectContentCleanerAgent)
this.useContentCleanerAgent = true; this.useContentCleanerAgent = true;
if (this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions) this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_TEXT_CONTENT_CLEANER, this.ProviderSettings.Id, true);
this.providerSettings = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider);
else
this.providerSettings = this.ProviderSettings;
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }

View File

@ -1,3 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using AIStudio.Dialogs; using AIStudio.Dialogs;
using AIStudio.Provider; using AIStudio.Provider;
using AIStudio.Settings; using AIStudio.Settings;
@ -26,6 +28,7 @@ public partial class SettingsPanelProviders : SettingsPanelBase
#endregion #endregion
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
private async Task AddLLMProvider() private async Task AddLLMProvider()
{ {
var dialogParameters = new DialogParameters<ProviderDialog> var dialogParameters = new DialogParameters<ProviderDialog>
@ -48,6 +51,7 @@ public partial class SettingsPanelProviders : SettingsPanelBase
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED); await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
} }
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
private async Task EditLLMProvider(AIStudio.Settings.Provider provider) private async Task EditLLMProvider(AIStudio.Settings.Provider provider)
{ {
var dialogParameters = new DialogParameters<ProviderDialog> var dialogParameters = new DialogParameters<ProviderDialog>
@ -82,6 +86,7 @@ public partial class SettingsPanelProviders : SettingsPanelBase
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED); await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
} }
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
private async Task DeleteLLMProvider(AIStudio.Settings.Provider provider) private async Task DeleteLLMProvider(AIStudio.Settings.Provider provider)
{ {
var dialogParameters = new DialogParameters var dialogParameters = new DialogParameters
@ -112,6 +117,7 @@ public partial class SettingsPanelProviders : SettingsPanelBase
return modelName.Length > MAX_LENGTH ? "[...] " + modelName[^Math.Min(MAX_LENGTH, modelName.Length)..] : modelName; return modelName.Length > MAX_LENGTH ? "[...] " + modelName[^Math.Min(MAX_LENGTH, modelName.Length)..] : modelName;
} }
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
private async Task UpdateProviders() private async Task UpdateProviders()
{ {
this.AvailableLLMProviders.Clear(); this.AvailableLLMProviders.Clear();

View File

@ -137,7 +137,9 @@ public partial class ProviderDialog : ComponentBase, ISecretId
this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES); this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES);
// Load the used instance names: // Load the used instance names:
#pragma warning disable MWAIS0001
this.UsedInstanceNames = this.SettingsManager.ConfigurationData.Providers.Select(x => x.InstanceName.ToLowerInvariant()).ToList(); this.UsedInstanceNames = this.SettingsManager.ConfigurationData.Providers.Select(x => x.InstanceName.ToLowerInvariant()).ToList();
#pragma warning restore MWAIS0001
// When editing, we need to load the data: // When editing, we need to load the data:
if(this.IsEditing) if(this.IsEditing)

View File

@ -53,6 +53,10 @@
<PackageReference Include="ReverseMarkdown" Version="4.6.0" /> <PackageReference Include="ReverseMarkdown" Version="4.6.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SourceCodeRules\SourceCodeRules\SourceCodeRules.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup>
<!-- Read the meta data file --> <!-- Read the meta data file -->
<Target Name="ReadMetaData" BeforeTargets="BeforeBuild"> <Target Name="ReadMetaData" BeforeTargets="BeforeBuild">
<Error Text="The ../../metadata.txt file was not found!" Condition="!Exists('../../metadata.txt')" /> <Error Text="The ../../metadata.txt file was not found!" Condition="!Exists('../../metadata.txt')" />

View File

@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@ -141,7 +142,8 @@ public sealed class SettingsManager(ILogger<SettingsManager> logger)
return minimumLevel; return minimumLevel;
} }
public Provider GetPreselectedProvider(Tools.Components component, string? chatProviderId = null) [SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
public Provider GetPreselectedProvider(Tools.Components component, string? currentProviderId = null, bool usePreselectionBeforeCurrentProvider = false)
{ {
var minimumLevel = this.GetMinimumConfidenceLevel(component); var minimumLevel = this.GetMinimumConfidenceLevel(component);
@ -149,17 +151,43 @@ public sealed class SettingsManager(ILogger<SettingsManager> logger)
if (this.ConfigurationData.Providers.Count == 1 && this.ConfigurationData.Providers[0].UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) if (this.ConfigurationData.Providers.Count == 1 && this.ConfigurationData.Providers[0].UsedLLMProvider.GetConfidence(this).Level >= minimumLevel)
return this.ConfigurationData.Providers[0]; return this.ConfigurationData.Providers[0];
// When there is a chat provider, and it has a confidence level that is high enough, we return it: // Is there a current provider with a sufficiently high confidence level?
if (chatProviderId is not null && !string.IsNullOrWhiteSpace(chatProviderId)) Provider currentProvider = default;
if (currentProviderId is not null && !string.IsNullOrWhiteSpace(currentProviderId))
{ {
var chatProvider = this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == chatProviderId); var currentProviderProbe = this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == currentProviderId);
if (chatProvider.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) if (currentProviderProbe.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel)
return chatProvider; currentProvider = currentProviderProbe;
} }
// When there is a component-preselected provider, and it has a confidence level that is high enough, we return it: // Is there a component-preselected provider with a sufficiently high confidence level?
var preselectedProvider = component.PreselectedProvider(this); Provider preselectedProvider = default;
if(preselectedProvider != default && preselectedProvider.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) var preselectedProviderProbe = component.PreselectedProvider(this);
if(preselectedProviderProbe != default && preselectedProviderProbe.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel)
preselectedProvider = preselectedProviderProbe;
//
// Case: The preselected provider should be used before the current provider,
// and the preselected provider is available and has a confidence level
// that is high enough.
//
if(usePreselectionBeforeCurrentProvider && preselectedProvider != default)
return preselectedProvider;
//
// Case: The current provider is available and has a confidence level that is
// high enough.
//
if(currentProvider != default)
return currentProvider;
//
// Case: The current provider should be used before the preselected provider,
// but the current provider is not available or does not have a confidence
// level that is high enough. The preselected provider is available and
// has a confidence level that is high enough.
//
if(preselectedProvider != default)
return preselectedProvider; return preselectedProvider;
// When there is an app-wide preselected provider, and it has a confidence level that is high enough, we return it: // When there is an app-wide preselected provider, and it has a confidence level that is high enough, we return it:

View File

@ -21,4 +21,8 @@ public enum Components
CHAT, CHAT,
APP_SETTINGS, APP_SETTINGS,
AGENT_TEXT_CONTENT_CLEANER,
AGENT_DATA_SOURCE_SELECTION,
AGENT_RETRIEVAL_CONTEXT_VALIDATION,
} }

View File

@ -1,3 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using AIStudio.Provider; using AIStudio.Provider;
using AIStudio.Settings; using AIStudio.Settings;
@ -12,6 +14,10 @@ public static class ComponentsExtensions
Components.BIAS_DAY_ASSISTANT => false, Components.BIAS_DAY_ASSISTANT => false,
Components.APP_SETTINGS => false, Components.APP_SETTINGS => false,
Components.AGENT_TEXT_CONTENT_CLEANER => false,
Components.AGENT_DATA_SOURCE_SELECTION => false,
Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION => false,
_ => true, _ => true,
}; };
@ -76,6 +82,7 @@ public static class ComponentsExtensions
_ => default, _ => default,
}; };
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
public static AIStudio.Settings.Provider PreselectedProvider(this Components component, SettingsManager settingsManager) => component switch public static AIStudio.Settings.Provider PreselectedProvider(this Components component, SettingsManager settingsManager) => component switch
{ {
Components.GRAMMAR_SPELLING_ASSISTANT => settingsManager.ConfigurationData.GrammarSpelling.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider) : default, Components.GRAMMAR_SPELLING_ASSISTANT => settingsManager.ConfigurationData.GrammarSpelling.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider) : default,
@ -95,6 +102,10 @@ public static class ComponentsExtensions
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedProvider) : default, Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedProvider) : default,
Components.AGENT_TEXT_CONTENT_CLEANER => settingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider) : default,
Components.AGENT_DATA_SOURCE_SELECTION => settingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.AgentDataSourceSelection.PreselectedAgentProvider) : default,
Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION => settingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectedAgentProvider) : default,
_ => default, _ => default,
}; };

View File

@ -35,7 +35,7 @@ public sealed class AugmentationOne : IAugmentationProcess
var numTotalRetrievalContexts = retrievalContexts.Count; var numTotalRetrievalContexts = retrievalContexts.Count;
// Want the user to validate all retrieval contexts? // Want the user to validate all retrieval contexts?
if (settings.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation) if (settings.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation && chatThread.DataSourceOptions.AutomaticValidation)
{ {
// Let's get the validation agent & set up its provider: // Let's get the validation agent & set up its provider:
var validationAgent = Program.SERVICE_PROVIDER.GetService<AgentRetrievalContextValidation>()!; var validationAgent = Program.SERVICE_PROVIDER.GetService<AgentRetrievalContextValidation>()!;

View File

@ -77,6 +77,11 @@
"resolved": "31.0.3", "resolved": "31.0.3",
"contentHash": "ygck8DR4mG/VDA/LgIVVGpEtXXPDVaaNZNJGrOAJ4pckVw4MbAQ3n/u6YFDv3bwlQhlxTmPhCyk5E4hxe96Crg==" "contentHash": "ygck8DR4mG/VDA/LgIVVGpEtXXPDVaaNZNJGrOAJ4pckVw4MbAQ3n/u6YFDv3bwlQhlxTmPhCyk5E4hxe96Crg=="
}, },
"Humanizer.Core": {
"type": "Transitive",
"resolved": "2.14.1",
"contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw=="
},
"Markdig": { "Markdig": {
"type": "Transitive", "type": "Transitive",
"resolved": "0.37.0", "resolved": "0.37.0",
@ -132,6 +137,56 @@
"resolved": "8.0.11", "resolved": "8.0.11",
"contentHash": "cy04xnMSTXTkRPjEwseRz57R5zjR/CWsdEOHH6NhWbNl97k+U1w6dSjqIOC7kv08tyzmM30FzIilSDtE5HdL/A==" "contentHash": "cy04xnMSTXTkRPjEwseRz57R5zjR/CWsdEOHH6NhWbNl97k+U1w6dSjqIOC7kv08tyzmM30FzIilSDtE5HdL/A=="
}, },
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg=="
},
"Microsoft.CodeAnalysis.Common": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "Hhaw6DKZHiR+vgOdIqvndfUntJhmDR7MjylUJ55EvWtDyJFLDf2eij8r9tcwXP35FLD+bVNNCO0+KIYuvJjNnA==",
"dependencies": {
"Microsoft.CodeAnalysis.Analyzers": "3.3.3",
"System.Collections.Immutable": "6.0.0",
"System.Memory": "4.5.4",
"System.Reflection.Metadata": "5.0.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
"System.Text.Encoding.CodePages": "6.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.CodeAnalysis.CSharp": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "0PU4a2h7L6N9SlF/oNHwj2A/+n0LK/7n6PEGvXyIZq8hc7r/TztB+47mhVLvapT6bWSV7nMT78cNxbQuC6tk6g==",
"dependencies": {
"Microsoft.CodeAnalysis.Common": "[4.3.0]"
}
},
"Microsoft.CodeAnalysis.CSharp.Workspaces": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "CH2kezeYUD9/+nGs7QZ6xpOqD6OH6000YkAyU8LbhqLp7W65Xb+HerBzDX9yfprmRwArlF887QhZd8nW6enHIg==",
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.CodeAnalysis.CSharp": "[4.3.0]",
"Microsoft.CodeAnalysis.Common": "[4.3.0]",
"Microsoft.CodeAnalysis.Workspaces.Common": "[4.3.0]"
}
},
"Microsoft.CodeAnalysis.Workspaces.Common": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "oFF7lU3dQIeEbAVF6Qq/YEA4jvys3oQKkDezP8NcQTFgD8uRUiIA4NimRxANaWxK2n2PT4DAQspIOtrAE2hSdA==",
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.Bcl.AsyncInterfaces": "6.0.0",
"Microsoft.CodeAnalysis.Common": "[4.3.0]",
"System.Composition": "6.0.0",
"System.IO.Pipelines": "6.0.3"
}
},
"Microsoft.Extensions.DependencyInjection": { "Microsoft.Extensions.DependencyInjection": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.1", "resolved": "8.0.1",
@ -196,17 +251,117 @@
"resolved": "8.0.11", "resolved": "8.0.11",
"contentHash": "UYSbAkNGTWVUne3I04/9IRQel3Bt1Ww6Y5cjvZEZ89rWhBD1yWu7YDotvQS62V6mgSfFaXXPGrCUm1VG824QXw==" "contentHash": "UYSbAkNGTWVUne3I04/9IRQel3Bt1Ww6Y5cjvZEZ89rWhBD1yWu7YDotvQS62V6mgSfFaXXPGrCUm1VG824QXw=="
}, },
"System.Collections.Immutable": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Composition": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==",
"dependencies": {
"System.Composition.AttributedModel": "6.0.0",
"System.Composition.Convention": "6.0.0",
"System.Composition.Hosting": "6.0.0",
"System.Composition.Runtime": "6.0.0",
"System.Composition.TypedParts": "6.0.0"
}
},
"System.Composition.AttributedModel": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w=="
},
"System.Composition.Convention": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==",
"dependencies": {
"System.Composition.AttributedModel": "6.0.0"
}
},
"System.Composition.Hosting": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==",
"dependencies": {
"System.Composition.Runtime": "6.0.0"
}
},
"System.Composition.Runtime": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg=="
},
"System.Composition.TypedParts": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==",
"dependencies": {
"System.Composition.AttributedModel": "6.0.0",
"System.Composition.Hosting": "6.0.0",
"System.Composition.Runtime": "6.0.0"
}
},
"System.IO.Pipelines": { "System.IO.Pipelines": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.0", "resolved": "8.0.0",
"contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==" "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA=="
}, },
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw=="
},
"System.Reflection.Metadata": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Text.Encoding.CodePages": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg=="
},
"ZXing.Net": { "ZXing.Net": {
"type": "Transitive", "type": "Transitive",
"resolved": "0.16.9", "resolved": "0.16.9",
"contentHash": "7WaVMHklpT3Ye2ragqRIwlFRsb6kOk63BOGADV0fan3ulVfGLUYkDi5yNUsZS/7FVNkWbtHAlDLmu4WnHGfqvQ==" "contentHash": "7WaVMHklpT3Ye2ragqRIwlFRsb6kOk63BOGADV0fan3ulVfGLUYkDi5yNUsZS/7FVNkWbtHAlDLmu4WnHGfqvQ=="
},
"sourcecoderules": {
"type": "Project",
"dependencies": {
"Microsoft.CodeAnalysis.CSharp": "[4.3.0, )",
"Microsoft.CodeAnalysis.CSharp.Workspaces": "[4.3.0, )"
}
} }
}, },
"net8.0/osx-arm64": {} "net8.0/osx-arm64": {
"System.Text.Encoding.CodePages": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
}
}
} }
} }

View File

@ -6,4 +6,5 @@
- Added an agent to validate whether a retrieval context makes sense for the given prompt. This preview feature is hidden behind the RAG feature flag. - Added an agent to validate whether a retrieval context makes sense for the given prompt. This preview feature is hidden behind the RAG feature flag.
- Added a generic RAG process to integrate possibly any data in your chats. Although the generic RAG process is now implemented, the retrieval part is working only with external data sources using the ERI interface. That means that you could integrate your company's data from the corporate network by now. The retrieval process for your local data is still under development and will take several weeks to be released. In order to use data through ERI, you (or your company) have to develop an ERI server. You might use the ERI server assistant within AI Studio to do so. This preview feature is hidden behind the RAG feature flag. - Added a generic RAG process to integrate possibly any data in your chats. Although the generic RAG process is now implemented, the retrieval part is working only with external data sources using the ERI interface. That means that you could integrate your company's data from the corporate network by now. The retrieval process for your local data is still under development and will take several weeks to be released. In order to use data through ERI, you (or your company) have to develop an ERI server. You might use the ERI server assistant within AI Studio to do so. This preview feature is hidden behind the RAG feature flag.
- Improved confidence card for small spaces. - Improved confidence card for small spaces.
- Improved data security by enforcing provider filtering based on the chosen confidence level. To ensure this in the future, source code analyzers have been added to warn developers about insecure code.
- Fixed a bug in which 'APP_SETTINGS' appeared as a valid destination in the "send to" menu. - Fixed a bug in which 'APP_SETTINGS' appeared as a valid destination in the "send to" menu.

View File

@ -0,0 +1,7 @@
## Release 1.0
### New Rules
Rule ID | Category | Severity | Notes
-----------|----------|----------|------------------------
MWAIS0001 | Usage | Error | ProviderAccessAnalyzer

View File

@ -0,0 +1,10 @@
### New Rules
Rule ID | Category | Severity | Notes
---------|----------|----------|-------
### Changed Rules
Rule ID | New Category | New Severity | Old Category | Old Severity | Notes
---------|--------------|--------------|--------------|--------------|-------

View File

@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace SourceCodeRules;
#pragma warning disable RS1038
[DiagnosticAnalyzer(LanguageNames.CSharp)]
#pragma warning restore RS1038
public class ProviderAccessAnalyzer : DiagnosticAnalyzer
{
private const string DIAGNOSTIC_ID = $"{Tools.ID_PREFIX}0001";
private static readonly string TITLE = "Direct access to `Providers` is not allowed";
private static readonly string MESSAGE_FORMAT = "Direct access to `SettingsManager.ConfigurationData.Providers` is not allowed. Instead, use APIs like `SettingsManager.GetPreselectedProvider`, etc.";
private static readonly string DESCRIPTION = MESSAGE_FORMAT;
private const string CATEGORY = "Usage";
private static readonly DiagnosticDescriptor RULE = new(DIAGNOSTIC_ID, TITLE, MESSAGE_FORMAT, CATEGORY, DiagnosticSeverity.Error, isEnabledByDefault: true, description: DESCRIPTION);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(RULE);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(this.AnalyzeMemberAccess, SyntaxKind.SimpleMemberAccessExpression);
}
private void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context)
{
var memberAccess = (MemberAccessExpressionSyntax)context.Node;
// Prüfen, ob wir eine Kette von Zugriffen haben, die auf "Providers" endet
if (memberAccess.Name.Identifier.Text != "Providers")
return;
// Den kompletten Zugriffspfad aufbauen
var fullPath = this.GetFullMemberAccessPath(memberAccess);
// Prüfen, ob der Pfad unserem verbotenen Muster entspricht
if (fullPath.EndsWith("ConfigurationData.Providers"))
{
var diagnostic = Diagnostic.Create(RULE, memberAccess.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
private string GetFullMemberAccessPath(ExpressionSyntax expression)
{
var parts = new List<string>();
while (expression is MemberAccessExpressionSyntax memberAccess)
{
parts.Add(memberAccess.Name.Identifier.Text);
expression = memberAccess.Expression;
}
if (expression is IdentifierNameSyntax identifier)
parts.Add(identifier.Identifier.Text);
parts.Reverse();
return string.Join(".", parts);
}
}

View File

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent>true</IsRoslynComponent>
<RootNamespace>SourceCodeRules</RootNamespace>
<AssemblyName>SourceCodeRules</AssemblyName>
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
namespace SourceCodeRules;
public static class Tools
{
public const string ID_PREFIX = "MWAIS";
}