refactored confidence levels and ensured checks work

This commit is contained in:
PaulKoudelka committed 2026-08-14 12:03:16 +02:00
1 parent 786b106096
commit 88999d985c
44 files changed
+324 -335

No files matched your search

@@ -43,8 +43,8 @@ public sealed record EmbeddingStateFile(
DateTimeOffset LastWriteUtc,
DateTimeOffset EmbeddedAtUtc,
int ChunkCount,
string ComplianceLevel,
int ComplianceLevelRank);
string ConfidenceLevel,
int ConfidenceLevelRank);
public sealed record EmbeddingStateChunk(
string ChunkId,
@@ -74,5 +74,5 @@ public sealed record EmbeddingStateSearchResult(
DateTimeOffset LastWriteUtc,
DateTimeOffset EmbeddedAtUtc,
int ChunkCount,
string ComplianceLevel,
int ComplianceLevelRank);
string ConfidenceLevel,
int ConfidenceLevelRank);
@@ -62,13 +62,13 @@ internal sealed class EmbeddingStateDbContext(DbContextOptions<EmbeddingStateDbC
entity.Property(file => file.LastWriteUtc).HasColumnName("last_write_utc").HasConversion(utcDateTimeOffsetConverter).IsRequired();
entity.Property(file => file.EmbeddedAtUtc).HasColumnName("embedded_at_utc").HasConversion(utcDateTimeOffsetConverter).IsRequired();
entity.Property(file => file.ChunkCount).HasColumnName("chunk_count");
entity.Property(file => file.ComplianceLevel).HasColumnName("compliance_level").IsRequired();
entity.Property(file => file.ComplianceLevelRank).HasColumnName("compliance_level_rank");
entity.Property(file => file.ConfidenceLevel).HasColumnName("confidence_level").IsRequired();
entity.Property(file => file.ConfidenceLevelRank).HasColumnName("confidence_level_rank");
entity.HasIndex(file => file.DataSourceId).HasDatabaseName("idx_embedded_files_data_source");
entity.HasIndex(file => file.AbsolutePath).HasDatabaseName("idx_embedded_files_absolute_path");
entity.HasIndex(file => file.FileType).HasDatabaseName("idx_embedded_files_file_type");
entity.HasIndex(file => file.ComplianceLevelRank).HasDatabaseName("idx_embedded_files_compliance");
entity.HasIndex(file => file.ConfidenceLevelRank).HasDatabaseName("idx_embedded_files_confidence");
entity.HasIndex(file => new { file.DataSourceId, file.AbsolutePath }).HasDatabaseName("idx_embedded_files_data_source_absolute_path").IsUnique();
entity
@@ -165,9 +165,9 @@ internal sealed class EmbeddingStateFileEntity
public int ChunkCount { get; set; }
public string ComplianceLevel { get; set; } = string.Empty;
public string ConfidenceLevel { get; set; } = string.Empty;
public int ComplianceLevelRank { get; set; }
public int ConfidenceLevelRank { get; set; }
public EmbeddingStateDataSourceEntity? DataSource { get; set; }
@@ -233,9 +233,9 @@ internal sealed class EmbeddingStateSearchResultEntity
public int ChunkCount { get; set; }
public string ComplianceLevel { get; set; } = string.Empty;
public string ConfidenceLevel { get; set; } = string.Empty;
public int ComplianceLevelRank { get; set; }
public int ConfidenceLevelRank { get; set; }
}
internal sealed class EmbeddingStateDateTimeOffsetConverter() : ValueConverter<DateTimeOffset, string>(
@@ -45,8 +45,8 @@ public partial class InitialRagIndex : Migration
last_write_utc = table.Column<string>(type: "TEXT", nullable: false),
embedded_at_utc = table.Column<string>(type: "TEXT", nullable: false),
chunk_count = table.Column<int>(type: "INTEGER", nullable: false),
compliance_level = table.Column<string>(type: "TEXT", nullable: false),
compliance_level_rank = table.Column<int>(type: "INTEGER", nullable: false),
confidence_level = table.Column<string>(type: "TEXT", nullable: false),
confidence_level_rank = table.Column<int>(type: "INTEGER", nullable: false),
},
constraints: table =>
{
@@ -89,9 +89,9 @@ public partial class InitialRagIndex : Migration
column: "absolute_path");
migrationBuilder.CreateIndex(
name: "idx_embedded_files_compliance",
name: "idx_embedded_files_confidence",
table: "embedded_files",
column: "compliance_level_rank");
column: "confidence_level_rank");
migrationBuilder.CreateIndex(
name: "idx_embedded_files_data_source",
@@ -77,14 +77,14 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot
.HasColumnType("INTEGER")
.HasColumnName("chunk_count");
entity.Property<string>("ComplianceLevel")
entity.Property<string>("ConfidenceLevel")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("compliance_level");
.HasColumnName("confidence_level");
entity.Property<int>("ComplianceLevelRank")
entity.Property<int>("ConfidenceLevelRank")
.HasColumnType("INTEGER")
.HasColumnName("compliance_level_rank");
.HasColumnName("confidence_level_rank");
entity.Property<DateTimeOffset>("CreationUtc")
.HasConversion(utcDateTimeOffsetConverter)
@@ -135,8 +135,8 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot
entity.HasIndex("AbsolutePath")
.HasDatabaseName("idx_embedded_files_absolute_path");
entity.HasIndex("ComplianceLevelRank")
.HasDatabaseName("idx_embedded_files_compliance");
entity.HasIndex("ConfidenceLevelRank")
.HasDatabaseName("idx_embedded_files_confidence");
entity.HasIndex("DataSourceId")
.HasDatabaseName("idx_embedded_files_data_source");
@@ -226,11 +226,11 @@ partial class EmbeddingStateDbContextModelSnapshot : ModelSnapshot
.IsRequired()
.HasColumnType("TEXT");
entity.Property<string>("ComplianceLevel")
entity.Property<string>("ConfidenceLevel")
.IsRequired()
.HasColumnType("TEXT");
entity.Property<int>("ComplianceLevelRank")
entity.Property<int>("ConfidenceLevelRank")
.HasColumnType("INTEGER");
entity.Property<DateTimeOffset>("CreationUtc")
@@ -268,8 +268,8 @@ public sealed class SqliteEmbeddingStateClientImplementation(
f.last_write_utc AS LastWriteUtc,
c.embedded_at_utc AS EmbeddedAtUtc,
f.chunk_count AS ChunkCount,
f.compliance_level AS ComplianceLevel,
f.compliance_level_rank AS ComplianceLevelRank
f.confidence_level AS ConfidenceLevel,
f.confidence_level_rank AS ConfidenceLevelRank
FROM embedding_chunks_fts
JOIN embedding_chunks c ON c.id = embedding_chunks_fts.rowid
JOIN embedded_files f ON f.parent_file_id = c.parent_file_id
@@ -359,8 +359,8 @@ public sealed class SqliteEmbeddingStateClientImplementation(
fileEntity.LastWriteUtc = file.LastWriteUtc;
fileEntity.EmbeddedAtUtc = file.EmbeddedAtUtc;
fileEntity.ChunkCount = file.ChunkCount;
fileEntity.ComplianceLevel = file.ComplianceLevel;
fileEntity.ComplianceLevelRank = file.ComplianceLevelRank;
fileEntity.ConfidenceLevel = file.ConfidenceLevel;
fileEntity.ConfidenceLevelRank = file.ConfidenceLevelRank;
}
private static void ApplyChunk(EmbeddingStateChunkEntity chunkEntity, EmbeddingStateChunk chunk)
@@ -393,8 +393,8 @@ public sealed class SqliteEmbeddingStateClientImplementation(
result.LastWriteUtc,
result.EmbeddedAtUtc,
result.ChunkCount,
result.ComplianceLevel,
result.ComplianceLevelRank);
result.ConfidenceLevel,
result.ConfidenceLevelRank);
private static string BuildFtsQuery(string query)
{
@@ -20,5 +20,5 @@ public sealed record VectorSearchResult(
string CreationUtc,
string LastWriteUtc,
string EmbeddedAtUtc,
string ComplianceLevel,
int ComplianceLevelRank);
string ConfidenceLevel,
int ConfidenceLevelRank);
@@ -20,5 +20,5 @@ public sealed record VectorStoragePoint(
DateTimeOffset CreationUtc,
DateTimeOffset LastWriteUtc,
DateTimeOffset EmbeddedAtUtc,
string ComplianceLevel,
int ComplianceLevelRank);
string ConfidenceLevel,
int ConfidenceLevelRank);
@@ -43,7 +43,7 @@ public sealed class AugmentationOne : IAugmentationProcess
{
// Let's get the validation agent & set up its provider:
var validationAgent = Program.SERVICE_PROVIDER.GetService<AgentRetrievalContextValidation>()!;
if (validationAgent.SetLLMProvider(provider, chatThread.DataSecurity, chatThread.DataComplianceLevel))
if (validationAgent.SetLLMProvider(provider, chatThread.DataSecurity, chatThread.DataConfidenceLevel))
{
// Let's validate all retrieval contexts:
var validationResults = await validationAgent.ValidateRetrievalContextsAsync(lastUserPrompt, chatThread, retrievalContexts, token);
@@ -58,7 +58,7 @@ public sealed class AugmentationOne : IAugmentationProcess
retrievalContexts = validationResults.Where(x => x.RetrievalContext is not null && x.Confidence >= threshold).Select(x => x.RetrievalContext!).ToList();
}
else
LOGGER.LogWarning("Skipping retrieval context validation because no compliant validation agent provider is available.");
LOGGER.LogWarning("Skipping retrieval context validation because no sufficiently trusted validation agent provider is available.");
}
LOGGER.LogInformation($"Starting the augmentation process over {numTotalRetrievalContexts:###,###,###,###} retrieval contexts.");
@@ -74,7 +74,7 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
// data sources changed its security requirements.
//
List<IDataSource> preselectedDataSources = chatThread.DataSourceOptions.PreselectedDataSourceIds.Select(id => settings.ConfigurationData.DataSources.FirstOrDefault(ds => ds.Id == id)).Where(ds => ds is not null).ToList()!;
var dataSources = await dataSourceService.GetDataSources(provider, preselectedDataSources);
var dataSources = await dataSourceService.GetDataSources(provider, chatThread.DataSourceOptions, preselectedDataSources);
var selectedDataSources = dataSources.SelectedDataSources;
//
@@ -104,13 +104,15 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
else
{
var previousDataSecurity = chatThread.DataSecurity;
var previousDataComplianceLevel = chatThread.DataComplianceLevel;
var previousDataConfidenceLevel = chatThread.DataConfidenceLevel;
//
// 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 is not IInternalDataSource && x.SecurityPolicy is DataSourceSecurity.SELF_HOSTED);
var dataSecurityRestrictedToSelfHosted = selectedDataSources
.OfType<IExternalDataSource>()
.Any(dataSource => dataSource.SecurityPolicy is DataSourceSecurity.SELF_HOSTED);
chatThread.DataSecurity = dataSecurityRestrictedToSelfHosted switch
{
//
@@ -152,12 +154,12 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
if (previousDataSecurity != chatThread.DataSecurity)
LOGGER.LogInformation($"The data security of the chat thread was updated from '{previousDataSecurity}' to '{chatThread.DataSecurity}'.");
foreach (var dataSource in selectedDataSources)
if (dataSource.ComplianceLevel > chatThread.DataComplianceLevel)
chatThread.DataComplianceLevel = dataSource.ComplianceLevel;
foreach (var dataSource in selectedDataSources.OfType<IInternalDataSource>())
if (dataSource.ConfidenceLevel > chatThread.DataConfidenceLevel)
chatThread.DataConfidenceLevel = dataSource.ConfidenceLevel;
if (previousDataComplianceLevel != chatThread.DataComplianceLevel)
LOGGER.LogInformation($"The data compliance level of the chat thread was updated from '{previousDataComplianceLevel.GetName()}' to '{chatThread.DataComplianceLevel.GetName()}'.");
if (previousDataConfidenceLevel != chatThread.DataConfidenceLevel)
LOGGER.LogInformation($"The data confidence level of the chat thread was updated from '{previousDataConfidenceLevel.GetName()}' to '{chatThread.DataConfidenceLevel.GetName()}'.");
}
//
@@ -946,7 +946,7 @@ public sealed partial class DataSourceEmbeddingService
embeddingProvider.Hostname,
embeddingProvider.TokenizerPath,
embeddingProvider.EffectiveTokenLimit,
GetDataSourceComplianceLevel(dataSource).ToString(),
GetDataSourceConfidenceLevel(dataSource).ToString(),
dataSource is IInternalDataSource internalDataSource ? internalDataSource.MaxChunkTokenLength : 0,
dataSource is IInternalDataSource overlapDataSource ? overlapDataSource.ChunkOverlapTokenLength : 0,
chunkingOptions.MaxChunkTokenLength,
@@ -1050,7 +1050,7 @@ public sealed partial class DataSourceEmbeddingService
{
file.Refresh();
var absolutePath = Path.GetFullPath(file.FullName);
var complianceLevel = GetDataSourceComplianceLevel(dataSource);
var confidenceLevel = GetDataSourceConfidenceLevel(dataSource);
return new(
this.CreateParentFileId(dataSource.Id, absolutePath),
absolutePath,
@@ -1063,8 +1063,8 @@ public sealed partial class DataSourceEmbeddingService
file.Exists ? new DateTimeOffset(file.LastWriteTimeUtc) : DateTimeOffset.UnixEpoch,
embeddedAtUtc,
chunkCount,
complianceLevel.ToString(),
(int)complianceLevel);
confidenceLevel.ToString(),
(int)confidenceLevel);
}
private IReadOnlyList<EmbeddingStateChunk> CreateEmbeddingStateChunks(EmbeddingStateFile parentFile, IReadOnlyList<EmbeddingChunkDraft> batch, DateTimeOffset embeddedAtUtc)
@@ -1080,10 +1080,10 @@ public sealed partial class DataSourceEmbeddingService
.ToList();
}
private static ConfidenceLevel GetDataSourceComplianceLevel(IDataSource dataSource) =>
dataSource.ComplianceLevel is ConfidenceLevel.NONE
private static ConfidenceLevel GetDataSourceConfidenceLevel(IDataSource dataSource) =>
dataSource is not IInternalDataSource internalDataSource || internalDataSource.ConfidenceLevel is ConfidenceLevel.NONE
? ConfidenceLevel.UNKNOWN
: dataSource.ComplianceLevel;
: internalDataSource.ConfidenceLevel;
private static string GetFileType(FileInfo file)
{
@@ -393,6 +393,15 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM
private async Task ProcessDataSourceAsync(IDataSource dataSource, DataSourceEmbeddingRefreshMode refreshMode, CancellationToken token)
{
if (dataSource is not IInternalDataSource internalDataSource)
{
logger.LogWarning(
"Skipping background embeddings for non-internal data source '{DataSourceName}' ({DataSourceId}).",
dataSource.Name,
dataSource.Id);
return;
}
logger.LogInformation(
"Starting background embedding hash check for data source '{DataSourceName}' ({DataSourceId}). RefreshMode={RefreshMode}.",
dataSource.Name,
@@ -451,17 +460,16 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM
return;
}
if (!embeddingProvider.AllowsDataSourceAccess(settingsManager, dataSource.SecurityPolicy, dataSource.ComplianceLevel))
if (!embeddingProvider.GetConfidenceLevel(settingsManager).AllowsDataSourceConfidenceLevel(internalDataSource.ConfidenceLevel))
{
var errorMessage = $"The selected embedding provider is not allowed to embed this data source. The data source requires provider confidence '{dataSource.ComplianceLevel.GetName()}'. The embedding provider has confidence '{embeddingProvider.GetConfidenceLevel(settingsManager).GetName()}'.";
var errorMessage = $"The selected embedding provider is not allowed to embed this data source. The data source requires provider confidence '{internalDataSource.ConfidenceLevel.GetName()}'. The embedding provider has confidence '{embeddingProvider.GetConfidenceLevel(settingsManager).GetName()}'.";
logger.LogWarning(
"Skipping background embeddings for data source '{DataSourceName}' ({DataSourceId}) because embedding provider '{EmbeddingProviderName}' ({EmbeddingProviderId}) is not allowed. RequiredDataSecurity={RequiredDataSecurity}, RequiredCompliance={RequiredCompliance}, EmbeddingProviderConfidence={EmbeddingProviderConfidence}.",
"Skipping background embeddings for data source '{DataSourceName}' ({DataSourceId}) because embedding provider '{EmbeddingProviderName}' ({EmbeddingProviderId}) does not meet the required confidence. RequiredConfidence={RequiredConfidence}, EmbeddingProviderConfidence={EmbeddingProviderConfidence}.",
dataSource.Name,
dataSource.Id,
embeddingProvider.Name,
embeddingProvider.Id,
dataSource.SecurityPolicy,
dataSource.ComplianceLevel.GetName(),
internalDataSource.ConfidenceLevel.GetName(),
embeddingProvider.GetConfidenceLevel(settingsManager).GetName());
token.ThrowIfCancellationRequested();
@@ -877,8 +885,8 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM
parentFile.CreationUtc,
parentFile.LastWriteUtc,
embeddedAtUtc,
parentFile.ComplianceLevel,
parentFile.ComplianceLevelRank)).ToList();
parentFile.ConfidenceLevel,
parentFile.ConfidenceLevelRank)).ToList();
await vectorStore.InsertEmbedding(collectionName, points, token);
}
@@ -38,8 +38,8 @@ public sealed class DataSourceLocalRetrievalService(
string Text,
double Score,
int Rank,
string ComplianceLevel,
int ComplianceLevelRank);
string ConfidenceLevel,
int ConfidenceLevelRank);
public Task<IReadOnlyList<IRetrievalContext>> RetrieveDataAsync(DataSourceLocalFile dataSource, IContent lastUserPrompt, ChatThread thread, CancellationToken token = default) =>
this.RetrieveDataAsync((IInternalDataSource)dataSource, lastUserPrompt, token);
@@ -296,8 +296,8 @@ public sealed class DataSourceLocalRetrievalService(
result.Text,
result.Score,
rank,
result.ComplianceLevel,
result.ComplianceLevelRank);
result.ConfidenceLevel,
result.ConfidenceLevelRank);
private static LocalRetrievalHit FromBm25Result(EmbeddingStateSearchResult result, int rank) =>
new(
@@ -316,8 +316,8 @@ public sealed class DataSourceLocalRetrievalService(
result.ChunkText,
result.Score,
rank,
result.ComplianceLevel,
result.ComplianceLevelRank);
result.ConfidenceLevel,
result.ConfidenceLevelRank);
private static RetrievalTextContext ToRetrievalContext(LocalRetrievalHit hit)
{
@@ -9,6 +9,8 @@ namespace AIStudio.Tools.Services;
public sealed class DataSourceService
{
private readonly record struct ParticipatingProvider(string Role, bool IsTrusted, ConfidenceLevel ConfidenceLevel);
private readonly RustService rustService;
private readonly SettingsManager settingsManager;
private readonly ILogger<DataSourceService> logger;
@@ -27,9 +29,10 @@ public sealed class DataSourceService
/// It also returns the data sources selected before when they are still allowed.
/// </summary>
/// <param name="selectedLLMProvider">The selected LLM provider.</param>
/// <param name="dataSourceOptions">The active data source options, which determine which agent providers participate.</param>
/// <param name="previousSelectedDataSources">The data sources selected before.</param>
/// <returns>The allowed data sources and the data sources selected before -- when they are still allowed.</returns>
public async Task<AllowedSelectedDataSources> GetDataSources(AIStudio.Settings.Provider selectedLLMProvider, IReadOnlyCollection<IDataSource>? previousSelectedDataSources = null)
public async Task<AllowedSelectedDataSources> GetDataSources(AIStudio.Settings.Provider selectedLLMProvider, DataSourceOptions dataSourceOptions, IReadOnlyCollection<IDataSource>? previousSelectedDataSources = null)
{
//
// Case: Somehow the selected LLM provider was not set. The default provider
@@ -42,10 +45,10 @@ public sealed class DataSourceService
return new([], []);
}
return await this.GetDataSources(
selectedLLMProvider.IsTrustedForDataSourceSecurityChecks(this.settingsManager),
selectedLLMProvider.GetConfidenceLevel(this.settingsManager),
previousSelectedDataSources);
var usingTrustedProvider = selectedLLMProvider.IsTrustedForDataSourceSecurityChecks(this.settingsManager);
var participatingProviders = this.GetParticipatingProviders(selectedLLMProvider.Id, dataSourceOptions,
new("chat provider", usingTrustedProvider, selectedLLMProvider.GetConfidenceLevel(this.settingsManager)));
return await this.GetDataSources(usingTrustedProvider, participatingProviders, previousSelectedDataSources);
}
/// <summary>
@@ -53,9 +56,10 @@ public sealed class DataSourceService
/// It also returns the data sources selected before when they are still allowed.
/// </summary>
/// <param name="selectedLLMProvider">The selected LLM provider.</param>
/// <param name="dataSourceOptions">The active data source options, which determine which agent providers participate.</param>
/// <param name="previousSelectedDataSources">The data sources selected before.</param>
/// <returns>The allowed data sources and the data sources selected before -- when they are still allowed.</returns>
public async Task<AllowedSelectedDataSources> GetDataSources(IProvider selectedLLMProvider, IReadOnlyCollection<IDataSource>? previousSelectedDataSources = null)
public async Task<AllowedSelectedDataSources> GetDataSources(IProvider selectedLLMProvider, DataSourceOptions dataSourceOptions, IReadOnlyCollection<IDataSource>? previousSelectedDataSources = null)
{
//
// Case: Somehow the selected LLM provider was not set. The default provider
@@ -68,13 +72,42 @@ public sealed class DataSourceService
return new([], []);
}
return await this.GetDataSources(
selectedLLMProvider.IsTrustedForDataSourceSecurityChecks(this.settingsManager),
selectedLLMProvider.GetConfidenceLevel(this.settingsManager),
previousSelectedDataSources);
var usingTrustedProvider = selectedLLMProvider.IsTrustedForDataSourceSecurityChecks(this.settingsManager);
var participatingProviders = this.GetParticipatingProviders(selectedLLMProvider.ConfiguredProviderId, dataSourceOptions,
new("chat provider", usingTrustedProvider, selectedLLMProvider.GetConfidenceLevel(this.settingsManager)));
return await this.GetDataSources(usingTrustedProvider, participatingProviders, previousSelectedDataSources);
}
private async Task<AllowedSelectedDataSources> GetDataSources(bool usingTrustedProvider, ConfidenceLevel providerConfidenceLevel, IReadOnlyCollection<IDataSource>? previousSelectedDataSources = null)
private IReadOnlyList<ParticipatingProvider> GetParticipatingProviders(string currentProviderId, DataSourceOptions dataSourceOptions, ParticipatingProvider currentProvider)
{
var providers = new List<ParticipatingProvider> { currentProvider };
if (dataSourceOptions.AutomaticDataSourceSelection)
this.AddAgentProvider(providers, Components.AGENT_DATA_SOURCE_SELECTION, currentProviderId, "data source selection agent");
if (dataSourceOptions.AutomaticValidation && this.settingsManager.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation)
this.AddAgentProvider(providers, Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION, currentProviderId, "retrieval context validation agent");
return providers;
}
private void AddAgentProvider(List<ParticipatingProvider> providers, Components component, string currentProviderId, string role)
{
var provider = this.settingsManager.GetPreselectedProvider(component, currentProviderId, true);
if (provider == Settings.Provider.NONE)
{
this.logger.LogWarning($"No provider is available for the {role}. Data sources cannot be made available while this agent is enabled.");
providers.Add(new(role, false, ConfidenceLevel.NONE));
return;
}
providers.Add(new(
role,
provider.IsTrustedForDataSourceSecurityChecks(this.settingsManager),
provider.GetConfidenceLevel(this.settingsManager)));
}
private async Task<AllowedSelectedDataSources> GetDataSources(bool usingTrustedProvider, IReadOnlyList<ParticipatingProvider> participatingProviders, IReadOnlyCollection<IDataSource>? previousSelectedDataSources = null)
{
var allDataSources = this.settingsManager.ConfigurationData.DataSources.ToList();
var previousSelectedDataSourceIds = previousSelectedDataSources?.Select(source => source.Id).ToHashSet(StringComparer.Ordinal) ?? [];
@@ -84,7 +117,7 @@ public sealed class DataSourceService
// Start all checks in parallel:
foreach (var source in allDataSources)
tasks.Add(this.CheckOneDataSource(source, usingTrustedProvider, providerConfidenceLevel));
tasks.Add(this.CheckOneDataSource(source, usingTrustedProvider, participatingProviders));
// Wait for all checks and collect the results:
foreach (var task in tasks)
@@ -101,16 +134,34 @@ public sealed class DataSourceService
return new(filteredDataSources, filteredSelectedDataSources);
}
private async Task<IDataSource?> CheckOneDataSource(IDataSource source, bool usingTrustedProvider, ConfidenceLevel providerConfidenceLevel)
private async Task<IDataSource?> CheckOneDataSource(IDataSource source, bool usingTrustedProvider, IReadOnlyList<ParticipatingProvider> participatingProviders)
{
if (!providerConfidenceLevel.AllowsDataSourceComplianceLevel(source.ComplianceLevel))
if (source is IInternalDataSource internalSource)
{
this.logger.LogWarning($"The data source '{source.Name}' (id={source.Id}) requires provider confidence '{source.ComplianceLevel.GetName()}'. The selected provider only has confidence '{providerConfidenceLevel.GetName()}'. We skip this source.");
return null;
}
foreach (var provider in participatingProviders)
{
if (!provider.ConfidenceLevel.AllowsDataSourceConfidenceLevel(internalSource.ConfidenceLevel))
{
this.logger.LogWarning($"The internal data source '{source.Name}' (id={source.Id}) requires provider confidence '{internalSource.ConfidenceLevel.GetName()}'. The {provider.Role} only has confidence '{provider.ConfidenceLevel.GetName()}'. We skip this source.");
return null;
}
}
if (!DataSourceEmbeddingProviders.TryResolve(this.settingsManager, source, out var embeddingProvider))
{
this.logger.LogWarning($"The internal data source '{source.Name}' (id={source.Id}) has no usable embedding provider. We skip this source.");
return null;
}
var embeddingProviderConfidence = embeddingProvider.GetConfidenceLevel(this.settingsManager);
if (!embeddingProviderConfidence.AllowsDataSourceConfidenceLevel(internalSource.ConfidenceLevel))
{
this.logger.LogWarning($"The internal data source '{source.Name}' (id={source.Id}) requires provider confidence '{internalSource.ConfidenceLevel.GetName()}'. Its embedding provider '{embeddingProvider.Name}' only has confidence '{embeddingProviderConfidence.GetName()}'. We skip this source.");
return null;
}
if (source is IInternalDataSource)
return source;
}
//
// Unfortunately, we have to live-check any ERI source for its security requirements.
@@ -146,8 +197,11 @@ public sealed class DataSourceService
eriSourceRequirements = securityRequest.Data;
this.logger.LogInformation($"Security requirements for ERI source '{source.Name}' (id={source.Id}) retrieved successfully.");
}
switch (source.SecurityPolicy)
if (source is not IExternalDataSource externalSource)
return source;
switch (externalSource.SecurityPolicy)
{
case DataSourceSecurity.ALLOW_ANY:
@@ -23,14 +23,12 @@ public sealed class DataSourceValidation
public Func<AuthMethod> GetAuthMethod { get; init; } = () => AuthMethod.NONE;
public Func<SecurityRequirements?> GetSecurityRequirements { get; init; } = () => null;
public Func<bool> GetSelectedCloudEmbedding { get; init; } = () => false;
public Func<EmbeddingProvider?> GetSelectedEmbeddingProvider { get; init; } = () => null;
public Func<DataSourceSecurity> GetSecurityPolicy { get; init; } = () => DataSourceSecurity.ALLOW_ANY;
public Func<ConfidenceLevel> GetComplianceLevel { get; init; } = () => ConfidenceLevel.NONE;
public Func<ConfidenceLevel> GetConfidenceLevel { get; init; } = () => ConfidenceLevel.NONE;
public Func<SettingsManager?> GetSettingsManager { get; init; } = () => null;
@@ -61,19 +59,19 @@ public sealed class DataSourceValidation
return null;
}
public string? ValidateSecurityPolicy(DataSourceSecurity securityPolicy)
{
if(securityPolicy is DataSourceSecurity.NOT_SPECIFIED)
return TB("Please select your security policy.");
var dataSourceSecurity = this.GetSecurityRequirements();
if (dataSourceSecurity is null)
return null;
if(dataSourceSecurity.Value.AllowedProviderType is ProviderType.SELF_HOSTED && securityPolicy is not DataSourceSecurity.SELF_HOSTED)
return TB("This data source can only be used with a self-hosted LLM provider. Please change the security policy.");
return null;
}
@@ -172,10 +170,10 @@ public sealed class DataSourceValidation
return embeddingIssue ?? this.ValidateSelectedEmbeddingProviderAccess();
}
public string? ValidateDataSourceComplianceLevel(ConfidenceLevel complianceLevel)
public string? ValidateDataSourceConfidenceLevel(ConfidenceLevel confidenceLevel)
{
if(complianceLevel is ConfidenceLevel.NONE)
return TB("Please select a compliance level.");
if(confidenceLevel is ConfidenceLevel.NONE)
return TB("Please select a required provider confidence level.");
return this.ValidateSelectedEmbeddingProviderAccess();
}
@@ -214,17 +212,13 @@ public sealed class DataSourceValidation
if(selectedEmbedding is null || settingsManager is null)
return null;
var dataSecurity = this.GetSecurityPolicy();
var complianceLevel = this.GetComplianceLevel();
if(selectedEmbedding.AllowsDataSourceAccess(settingsManager, dataSecurity, complianceLevel))
var confidenceLevel = this.GetConfidenceLevel();
if(selectedEmbedding.GetConfidenceLevel(settingsManager).AllowsDataSourceConfidenceLevel(confidenceLevel))
return null;
if(!selectedEmbedding.AllowsDataSourceSecurity(dataSecurity, settingsManager))
return TB("The selected embedding provider is not allowed to process this data source due to its data security policy. Select a self-hosted or organization-trusted embedding provider.");
return string.Format(
TB("The selected embedding provider has confidence '{0}', but this data source requires provider confidence '{1}'. Select an embedding provider with equal or higher confidence or lower the compliance level."),
TB("The selected embedding provider has confidence '{0}', but this data source requires provider confidence '{1}'. Select an embedding provider with equal or higher confidence or lower the required confidence level."),
selectedEmbedding.GetConfidenceLevel(settingsManager).GetName(),
complianceLevel.GetName());
confidenceLevel.GetName());
}
}