Enhanced data source options handling and selection logic

This commit is contained in:
Thorsten Sommer 2026-07-05 14:44:51 +02:00
parent 9891788ecb
commit b8e104c0d3
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
3 changed files with 64 additions and 16 deletions

View File

@ -59,6 +59,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
private DataSourceSelection? dataSourceSelectionComponent; private DataSourceSelection? dataSourceSelectionComponent;
private DataSourceOptions earlyDataSourceOptions = new(); private DataSourceOptions earlyDataSourceOptions = new();
private DataSourceOptions lastAppliedStandardDataSourceOptions = new();
private Profile currentProfile = Profile.NO_PROFILE; private Profile currentProfile = Profile.NO_PROFILE;
private ChatTemplate currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE; private ChatTemplate currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
private bool hasUnsavedChanges; private bool hasUnsavedChanges;
@ -118,6 +119,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
if (!this.ComposerState.HasUserDraft && !this.ComposerState.HasComposerContent) if (!this.ComposerState.HasUserDraft && !this.ComposerState.HasComposerContent)
this.ComposerState.ApplyTemplate(this.currentChatTemplate); this.ComposerState.ApplyTemplate(this.currentChatTemplate);
this.lastAppliedStandardDataSourceOptions = this.SettingsManager.ConfigurationData.Chat.PreselectedDataSourceOptions.CreateCopy();
var deferredInput = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_CHAT_INPUT).FirstOrDefault(); var deferredInput = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_CHAT_INPUT).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(deferredInput)) if (!string.IsNullOrWhiteSpace(deferredInput))
this.ComposerState.SetUserInput(deferredInput); this.ComposerState.SetUserInput(deferredInput);
@ -458,12 +461,42 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
private void ApplyStandardDataSourceOptions() private void ApplyStandardDataSourceOptions()
{ {
var chatDefaultOptions = this.SettingsManager.ConfigurationData.Chat.PreselectedDataSourceOptions.CreateCopy(); var chatDefaultOptions = this.SettingsManager.ConfigurationData.Chat.PreselectedDataSourceOptions.CreateCopy();
this.lastAppliedStandardDataSourceOptions = chatDefaultOptions.CreateCopy();
this.earlyDataSourceOptions = chatDefaultOptions; this.earlyDataSourceOptions = chatDefaultOptions;
if(this.ChatThread is not null) if(this.ChatThread is not null)
this.ChatThread.DataSourceOptions = chatDefaultOptions; this.ChatThread.DataSourceOptions = chatDefaultOptions;
this.dataSourceSelectionComponent?.ChangeOptionWithoutSaving(chatDefaultOptions); this.dataSourceSelectionComponent?.ChangeOptionWithoutSaving(chatDefaultOptions);
} }
private async Task ApplyUpdatedStandardDataSourceOptionsAfterConfigurationChange()
{
var updatedStandardOptions = this.SettingsManager.ConfigurationData.Chat.PreselectedDataSourceOptions.CreateCopy();
var previousStandardOptions = this.lastAppliedStandardDataSourceOptions;
this.lastAppliedStandardDataSourceOptions = updatedStandardOptions.CreateCopy();
if (this.ChatThread is null)
{
this.earlyDataSourceOptions = updatedStandardOptions;
this.dataSourceSelectionComponent?.ChangeOptionWithoutSaving(updatedStandardOptions);
return;
}
if (!DataSourceOptionsAreEqual(this.ChatThread.DataSourceOptions, previousStandardOptions))
return;
await this.SetCurrentDataSourceOptions(updatedStandardOptions);
this.dataSourceSelectionComponent?.ChangeOptionWithoutSaving(updatedStandardOptions, this.ChatThread.AISelectedDataSources);
await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
}
private static bool DataSourceOptionsAreEqual(DataSourceOptions left, DataSourceOptions right)
{
return left.DisableDataSources == right.DisableDataSources
&& left.AutomaticDataSourceSelection == right.AutomaticDataSourceSelection
&& left.AutomaticValidation == right.AutomaticValidation
&& left.PreselectedDataSourceIds.ToHashSet(StringComparer.Ordinal).SetEquals(right.PreselectedDataSourceIds);
}
private string ExtractThreadName(string firstUserInput) private string ExtractThreadName(string firstUserInput)
{ {
@ -547,6 +580,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
if (!this.ComposerState.HasUserDraft && previousChatTemplate != this.currentChatTemplate) if (!this.ComposerState.HasUserDraft && previousChatTemplate != this.currentChatTemplate)
this.ComposerState.ApplyTemplate(this.currentChatTemplate); this.ComposerState.ApplyTemplate(this.currentChatTemplate);
await this.ApplyUpdatedStandardDataSourceOptionsAfterConfigurationChange();
} }
private IReadOnlyList<DataSourceAgentSelected> GetAgentSelectedDataSources() private IReadOnlyList<DataSourceAgentSelected> GetAgentSelectedDataSources()

View File

@ -52,6 +52,7 @@ public partial class DataSourceSelection : MSGComponentBase
private bool aiBasedSourceSelection; private bool aiBasedSourceSelection;
private bool aiBasedValidation; private bool aiBasedValidation;
private bool areDataSourcesEnabled; private bool areDataSourcesEnabled;
private uint loadAndApplyFiltersGeneration;
#region Overrides of ComponentBase #region Overrides of ComponentBase
@ -75,15 +76,7 @@ public partial class DataSourceSelection : MSGComponentBase
// Right before the preselection would be used to kick off the // Right before the preselection would be used to kick off the
// RAG process, we will filter the data sources as well. // RAG process, we will filter the data sources as well.
// //
var preselectedSources = new List<IDataSource>(this.DataSourceOptions.PreselectedDataSourceIds.Count); this.selectedDataSources = this.GetDataSourcesFromConfiguredIds();
foreach (var preselectedDataSourceId in this.DataSourceOptions.PreselectedDataSourceIds)
{
var dataSource = this.SettingsManager.ConfigurationData.DataSources.FirstOrDefault(ds => ds.Id == preselectedDataSourceId);
if (dataSource is not null)
preselectedSources.Add(dataSource);
}
this.selectedDataSources = preselectedSources;
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }
@ -94,7 +87,7 @@ public partial class DataSourceSelection : MSGComponentBase
this.aiBasedSourceSelection = this.DataSourceOptions.AutomaticDataSourceSelection; this.aiBasedSourceSelection = this.DataSourceOptions.AutomaticDataSourceSelection;
this.aiBasedValidation = this.DataSourceOptions.AutomaticValidation; this.aiBasedValidation = this.DataSourceOptions.AutomaticValidation;
this.areDataSourcesEnabled = !this.DataSourceOptions.DisableDataSources; this.areDataSourcesEnabled = !this.DataSourceOptions.DisableDataSources;
this.selectedDataSources = this.SettingsManager.ConfigurationData.DataSources.Where(ds => this.DataSourceOptions.PreselectedDataSourceIds.Contains(ds.Id)).ToList(); this.selectedDataSources = this.GetDataSourcesFromConfiguredIds();
} }
switch (this.SelectionMode) switch (this.SelectionMode)
@ -120,7 +113,7 @@ public partial class DataSourceSelection : MSGComponentBase
// In configuration mode, we have to load all data sources: // In configuration mode, we have to load all data sources:
// //
case DataSourceSelectionMode.CONFIGURATION_MODE: case DataSourceSelectionMode.CONFIGURATION_MODE:
this.availableDataSources = this.SettingsManager.ConfigurationData.DataSources; this.availableDataSources = this.GetConfiguredDataSourcesSnapshot();
break; break;
} }
@ -157,7 +150,7 @@ public partial class DataSourceSelection : MSGComponentBase
this.aiBasedSourceSelection = this.DataSourceOptions.AutomaticDataSourceSelection; this.aiBasedSourceSelection = this.DataSourceOptions.AutomaticDataSourceSelection;
this.aiBasedValidation = this.DataSourceOptions.AutomaticValidation; this.aiBasedValidation = this.DataSourceOptions.AutomaticValidation;
this.areDataSourcesEnabled = !this.DataSourceOptions.DisableDataSources; this.areDataSourcesEnabled = !this.DataSourceOptions.DisableDataSources;
this.selectedDataSources = this.SettingsManager.ConfigurationData.DataSources.Where(ds => this.DataSourceOptions.PreselectedDataSourceIds.Contains(ds.Id)).ToList(); this.selectedDataSources = this.GetDataSourcesFromConfiguredIds();
this.waitingForDataSources = false; this.waitingForDataSources = false;
// //
@ -177,20 +170,38 @@ public partial class DataSourceSelection : MSGComponentBase
this.showDataSourceSelection = false; this.showDataSourceSelection = false;
this.StateHasChanged(); this.StateHasChanged();
} }
private IReadOnlyList<IDataSource> GetConfiguredDataSourcesSnapshot() => this.SettingsManager.ConfigurationData.DataSources.ToList();
private IReadOnlyCollection<IDataSource> GetDataSourcesFromConfiguredIds()
{
var preselectedDataSourceIds = this.DataSourceOptions.PreselectedDataSourceIds.ToHashSet(StringComparer.Ordinal);
return this.GetConfiguredDataSourcesSnapshot().Where(ds => preselectedDataSourceIds.Contains(ds.Id)).ToList();
}
private async Task LoadAndApplyFilters() private async Task LoadAndApplyFilters()
{ {
if(this.DataSourceOptions.DisableDataSources) if(this.DataSourceOptions.DisableDataSources)
{
this.loadAndApplyFiltersGeneration++;
return; return;
}
if(this.SelectionMode is DataSourceSelectionMode.CONFIGURATION_MODE) if(this.SelectionMode is DataSourceSelectionMode.CONFIGURATION_MODE)
{
this.loadAndApplyFiltersGeneration++;
return; return;
}
var generation = ++this.loadAndApplyFiltersGeneration;
this.waitingForDataSources = true; this.waitingForDataSources = true;
this.StateHasChanged(); this.StateHasChanged();
// Load the data sources: // Load the data sources:
var sources = await this.DataSourceService.GetDataSources(this.LLMProvider, this.selectedDataSources); var sources = await this.DataSourceService.GetDataSources(this.LLMProvider, this.selectedDataSources);
if (generation != this.loadAndApplyFiltersGeneration)
return;
this.availableDataSources = sources.AllowedDataSources; this.availableDataSources = sources.AllowedDataSources;
this.selectedDataSources = sources.SelectedDataSources; this.selectedDataSources = sources.SelectedDataSources;
this.waitingForDataSources = false; this.waitingForDataSources = false;
@ -262,6 +273,7 @@ public partial class DataSourceSelection : MSGComponentBase
private async Task OptionsChanged() private async Task OptionsChanged()
{ {
this.internalChange = true; this.internalChange = true;
this.loadAndApplyFiltersGeneration++;
await this.DataSourceOptionsChanged.InvokeAsync(this.DataSourceOptions); await this.DataSourceOptionsChanged.InvokeAsync(this.DataSourceOptions);

View File

@ -70,9 +70,10 @@ public sealed class DataSourceService
private async Task<AllowedSelectedDataSources> GetDataSources(bool usingTrustedProvider, 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.ToList();
var previousSelectedDataSourceIds = previousSelectedDataSources?.Select(source => source.Id).ToHashSet(StringComparer.Ordinal) ?? [];
var filteredDataSources = new List<IDataSource>(allDataSources.Count); var filteredDataSources = new List<IDataSource>(allDataSources.Count);
var filteredSelectedDataSources = new List<IDataSource>(previousSelectedDataSources?.Count ?? 0); var filteredSelectedDataSources = new List<IDataSource>(previousSelectedDataSourceIds.Count);
var tasks = new List<Task<IDataSource?>>(allDataSources.Count); var tasks = new List<Task<IDataSource?>>(allDataSources.Count);
// Start all checks in parallel: // Start all checks in parallel:
@ -86,7 +87,7 @@ public sealed class DataSourceService
if (source is not null) if (source is not null)
{ {
filteredDataSources.Add(source); filteredDataSources.Add(source);
if (previousSelectedDataSources is not null && previousSelectedDataSources.Contains(source)) if (previousSelectedDataSourceIds.Contains(source.Id))
filteredSelectedDataSources.Add(source); filteredSelectedDataSources.Add(source);
} }
} }
@ -205,4 +206,4 @@ public sealed class DataSourceService
return null; return null;
} }
} }
} }