2025-02-15 14:41:12 +00:00
|
|
|
using AIStudio.Settings;
|
|
|
|
using AIStudio.Settings.DataModel;
|
|
|
|
using AIStudio.Tools.Services;
|
|
|
|
|
|
|
|
using Microsoft.AspNetCore.Components;
|
|
|
|
|
|
|
|
namespace AIStudio.Components;
|
|
|
|
|
|
|
|
public partial class DataSourceSelection : ComponentBase, IMessageBusReceiver, IDisposable
|
|
|
|
{
|
|
|
|
[Parameter]
|
|
|
|
public DataSourceSelectionMode SelectionMode { get; set; } = DataSourceSelectionMode.SELECTION_MODE;
|
|
|
|
|
|
|
|
[Parameter]
|
|
|
|
public PopoverTriggerMode PopoverTriggerMode { get; set; } = PopoverTriggerMode.BUTTON;
|
|
|
|
|
|
|
|
[Parameter]
|
|
|
|
public string PopoverButtonClasses { get; set; } = string.Empty;
|
|
|
|
|
|
|
|
[Parameter]
|
|
|
|
public required AIStudio.Settings.Provider LLMProvider { get; set; }
|
|
|
|
|
|
|
|
[Parameter]
|
|
|
|
public required DataSourceOptions DataSourceOptions { get; set; }
|
|
|
|
|
|
|
|
[Parameter]
|
|
|
|
public EventCallback<DataSourceOptions> DataSourceOptionsChanged { get; set; }
|
2025-02-17 11:33:34 +00:00
|
|
|
|
|
|
|
[Parameter]
|
|
|
|
public IReadOnlyList<DataSourceAgentSelected> DataSourcesAISelected { get; set; } = [];
|
2025-02-15 14:41:12 +00:00
|
|
|
|
|
|
|
[Parameter]
|
|
|
|
public string ConfigurationHeaderMessage { get; set; } = string.Empty;
|
|
|
|
|
|
|
|
[Parameter]
|
|
|
|
public bool AutoSaveAppSettings { get; set; }
|
|
|
|
|
|
|
|
[Inject]
|
|
|
|
private SettingsManager SettingsManager { get; init; } = null!;
|
|
|
|
|
|
|
|
[Inject]
|
|
|
|
private MessageBus MessageBus { get; init; } = null!;
|
|
|
|
|
|
|
|
[Inject]
|
|
|
|
private DataSourceService DataSourceService { get; init; } = null!;
|
|
|
|
|
|
|
|
[Inject]
|
|
|
|
private ILogger<DataSourceSelection> Logger { get; init; } = null!;
|
|
|
|
|
|
|
|
private bool internalChange;
|
|
|
|
private bool showDataSourceSelection;
|
|
|
|
private bool waitingForDataSources = true;
|
|
|
|
private IReadOnlyList<IDataSource> availableDataSources = [];
|
|
|
|
private IReadOnlyCollection<IDataSource> selectedDataSources = [];
|
|
|
|
private bool aiBasedSourceSelection;
|
|
|
|
private bool aiBasedValidation;
|
|
|
|
private bool areDataSourcesEnabled;
|
|
|
|
|
|
|
|
#region Overrides of ComponentBase
|
|
|
|
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
|
|
{
|
|
|
|
this.MessageBus.RegisterComponent(this);
|
2025-02-17 11:33:34 +00:00
|
|
|
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED, Event.RAG_AUTO_DATA_SOURCES_SELECTED ]);
|
2025-02-15 14:41:12 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Load the settings:
|
|
|
|
//
|
|
|
|
this.aiBasedSourceSelection = this.DataSourceOptions.AutomaticDataSourceSelection;
|
|
|
|
this.aiBasedValidation = this.DataSourceOptions.AutomaticValidation;
|
|
|
|
this.areDataSourcesEnabled = !this.DataSourceOptions.DisableDataSources;
|
|
|
|
this.waitingForDataSources = this.areDataSourcesEnabled;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Preselect the data sources. Right now, we cannot filter
|
|
|
|
// the data sources. Later, when the component is shown, we
|
|
|
|
// will filter the data sources.
|
|
|
|
//
|
|
|
|
// Right before the preselection would be used to kick off the
|
|
|
|
// RAG process, we will filter the data sources as well.
|
|
|
|
//
|
|
|
|
var preselectedSources = new List<IDataSource>(this.DataSourceOptions.PreselectedDataSourceIds.Count);
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override async Task OnParametersSetAsync()
|
|
|
|
{
|
|
|
|
if (!this.internalChange)
|
|
|
|
{
|
|
|
|
this.aiBasedSourceSelection = this.DataSourceOptions.AutomaticDataSourceSelection;
|
|
|
|
this.aiBasedValidation = this.DataSourceOptions.AutomaticValidation;
|
|
|
|
this.areDataSourcesEnabled = !this.DataSourceOptions.DisableDataSources;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (this.SelectionMode)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// In selection mode, we have to load & filter the data sources
|
|
|
|
// when the component is shown:
|
|
|
|
//
|
|
|
|
case DataSourceSelectionMode.SELECTION_MODE:
|
|
|
|
|
|
|
|
//
|
|
|
|
// For external changes, we have to reload & filter
|
|
|
|
// the data sources:
|
|
|
|
//
|
|
|
|
if (this.showDataSourceSelection && !this.internalChange)
|
|
|
|
await this.LoadAndApplyFilters();
|
|
|
|
else
|
|
|
|
this.internalChange = false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
//
|
|
|
|
// In configuration mode, we have to load all data sources:
|
|
|
|
//
|
|
|
|
case DataSourceSelectionMode.CONFIGURATION_MODE:
|
|
|
|
this.availableDataSources = this.SettingsManager.ConfigurationData.DataSources;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
await base.OnParametersSetAsync();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
2025-02-17 11:33:34 +00:00
|
|
|
private SelectionMode GetListSelectionMode() => this.aiBasedSourceSelection ? MudBlazor.SelectionMode.SingleSelection : MudBlazor.SelectionMode.MultiSelection;
|
|
|
|
|
|
|
|
private IReadOnlyCollection<DataSourceAgentSelected> GetSelectedDataSourcesWithAI() => this.DataSourcesAISelected.Where(n => n.Selected).ToList();
|
|
|
|
|
|
|
|
private string GetAIReasoning(DataSourceAgentSelected source) => $"AI reasoning (confidence {source.AIDecision.Confidence:P0}): {source.AIDecision.Reason}";
|
|
|
|
|
|
|
|
public void ChangeOptionWithoutSaving(DataSourceOptions options, IReadOnlyList<DataSourceAgentSelected>? aiSelectedDataSources = null)
|
2025-02-15 14:41:12 +00:00
|
|
|
{
|
|
|
|
this.DataSourceOptions = options;
|
2025-02-17 11:33:34 +00:00
|
|
|
this.DataSourcesAISelected = aiSelectedDataSources ?? [];
|
|
|
|
|
2025-02-15 14:41:12 +00:00
|
|
|
this.aiBasedSourceSelection = this.DataSourceOptions.AutomaticDataSourceSelection;
|
|
|
|
this.aiBasedValidation = this.DataSourceOptions.AutomaticValidation;
|
|
|
|
this.areDataSourcesEnabled = !this.DataSourceOptions.DisableDataSources;
|
|
|
|
this.selectedDataSources = this.SettingsManager.ConfigurationData.DataSources.Where(ds => this.DataSourceOptions.PreselectedDataSourceIds.Contains(ds.Id)).ToList();
|
|
|
|
this.waitingForDataSources = false;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Remark: We do not apply the filters here. This is done later
|
|
|
|
// when either the parameters are changed or just before the
|
|
|
|
// RAG process is started (outside of this component).
|
|
|
|
//
|
|
|
|
// In fact, when we apply the filters here, multiple calls
|
|
|
|
// to the filter method would be made. We would get conflicts.
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool IsVisible => this.showDataSourceSelection;
|
|
|
|
|
|
|
|
public void Hide()
|
|
|
|
{
|
|
|
|
this.showDataSourceSelection = false;
|
|
|
|
this.StateHasChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task LoadAndApplyFilters()
|
|
|
|
{
|
|
|
|
if(this.DataSourceOptions.DisableDataSources)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.waitingForDataSources = true;
|
|
|
|
this.StateHasChanged();
|
|
|
|
|
|
|
|
// Load the data sources:
|
|
|
|
var sources = await this.DataSourceService.GetDataSources(this.LLMProvider, this.selectedDataSources);
|
|
|
|
this.availableDataSources = sources.AllowedDataSources;
|
|
|
|
this.selectedDataSources = sources.SelectedDataSources;
|
|
|
|
this.waitingForDataSources = false;
|
|
|
|
this.StateHasChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task EnabledChanged(bool state)
|
|
|
|
{
|
|
|
|
this.areDataSourcesEnabled = state;
|
|
|
|
this.DataSourceOptions.DisableDataSources = !this.areDataSourcesEnabled;
|
|
|
|
|
|
|
|
await this.LoadAndApplyFilters();
|
|
|
|
await this.OptionsChanged();
|
|
|
|
this.StateHasChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task AutoModeChanged(bool state)
|
|
|
|
{
|
|
|
|
this.aiBasedSourceSelection = state;
|
|
|
|
this.DataSourceOptions.AutomaticDataSourceSelection = this.aiBasedSourceSelection;
|
|
|
|
|
|
|
|
await this.OptionsChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task ValidationModeChanged(bool state)
|
|
|
|
{
|
|
|
|
this.aiBasedValidation = state;
|
|
|
|
this.DataSourceOptions.AutomaticValidation = this.aiBasedValidation;
|
|
|
|
|
|
|
|
await this.OptionsChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task SelectionChanged(IReadOnlyCollection<IDataSource>? chosenDataSources)
|
|
|
|
{
|
|
|
|
this.selectedDataSources = chosenDataSources ?? [];
|
|
|
|
this.DataSourceOptions.PreselectedDataSourceIds = this.selectedDataSources.Select(ds => ds.Id).ToList();
|
|
|
|
|
|
|
|
await this.OptionsChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task OptionsChanged()
|
|
|
|
{
|
|
|
|
this.internalChange = true;
|
|
|
|
|
|
|
|
await this.DataSourceOptionsChanged.InvokeAsync(this.DataSourceOptions);
|
|
|
|
|
|
|
|
if(this.AutoSaveAppSettings)
|
|
|
|
await this.SettingsManager.StoreSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task ToggleDataSourceSelection()
|
|
|
|
{
|
|
|
|
this.showDataSourceSelection = !this.showDataSourceSelection;
|
|
|
|
if (this.showDataSourceSelection)
|
|
|
|
await this.LoadAndApplyFilters();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void HideDataSourceSelection() => this.showDataSourceSelection = false;
|
|
|
|
|
|
|
|
#region Implementation of IMessageBusReceiver
|
|
|
|
|
|
|
|
public string ComponentName => nameof(ConfidenceInfo);
|
|
|
|
|
|
|
|
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
|
|
|
{
|
|
|
|
switch (triggeredEvent)
|
|
|
|
{
|
|
|
|
case Event.COLOR_THEME_CHANGED:
|
|
|
|
this.showDataSourceSelection = false;
|
|
|
|
this.StateHasChanged();
|
|
|
|
break;
|
2025-02-17 11:33:34 +00:00
|
|
|
|
|
|
|
case Event.RAG_AUTO_DATA_SOURCES_SELECTED:
|
|
|
|
if(data is IReadOnlyList<DataSourceAgentSelected> aiSelectedDataSources)
|
|
|
|
this.DataSourcesAISelected = aiSelectedDataSources;
|
|
|
|
|
|
|
|
this.StateHasChanged();
|
|
|
|
break;
|
2025-02-15 14:41:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
|
|
|
{
|
|
|
|
return Task.FromResult<TResult?>(default);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Implementation of IDisposable
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
this.MessageBus.Unregister(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
}
|