mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-04-28 15:39:46 +00:00
Added a data source selection component
This commit is contained in:
parent
3414286afa
commit
f95348e6e3
93
app/MindWork AI Studio/Components/DataSourceSelection.razor
Normal file
93
app/MindWork AI Studio/Components/DataSourceSelection.razor
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
@using AIStudio.Settings
|
||||||
|
|
||||||
|
@if (this.SelectionMode is DataSourceSelectionMode.SELECTION_MODE)
|
||||||
|
{
|
||||||
|
<div class="d-flex">
|
||||||
|
<MudTooltip Text="Select the data sources you want to use here." Placement="Placement.Top">
|
||||||
|
@if (this.PopoverTriggerMode is PopoverTriggerMode.ICON)
|
||||||
|
{
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Source" Class="@this.PopoverButtonClasses" OnClick="@(() => this.ToggleDataSourceSelection())"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Source" Class="@this.PopoverButtonClasses" OnClick="@(() => this.ToggleDataSourceSelection())">
|
||||||
|
Select data sources
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
</MudTooltip>
|
||||||
|
|
||||||
|
<MudPopover Open="@this.showDataSourceSelection" AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft" DropShadow="@true" Class="border-solid border-4 rounded-lg">
|
||||||
|
<MudCard>
|
||||||
|
<MudCardHeader>
|
||||||
|
<CardHeaderContent>
|
||||||
|
<PreviewPrototype/>
|
||||||
|
<MudText Typo="Typo.h5">Data Source Selection</MudText>
|
||||||
|
</CardHeaderContent>
|
||||||
|
</MudCardHeader>
|
||||||
|
<MudCardContent Style="max-height: 60vh; overflow: auto;">
|
||||||
|
@if (this.waitingForDataSources)
|
||||||
|
{
|
||||||
|
<MudSkeleton Width="30%" Height="42px;"/>
|
||||||
|
<MudSkeleton Width="80%"/>
|
||||||
|
<MudSkeleton Width="100%"/>
|
||||||
|
}
|
||||||
|
else if (this.showDataSourceSelection)
|
||||||
|
{
|
||||||
|
<MudTextSwitch Label="Are data sources enabled?" Value="@this.areDataSourcesEnabled" LabelOn="Yes, I want to use data sources." LabelOff="No, I don't want to use data sources." ValueChanged="@this.EnabledChanged"/>
|
||||||
|
@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 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"/>
|
||||||
|
<MudField Label="Available Data Sources" Variant="Variant.Outlined" Class="mb-3" Disabled="@this.aiBasedSourceSelection">
|
||||||
|
<MudList T="IDataSource" SelectionMode="MudBlazor.SelectionMode.MultiSelection" @bind-SelectedValues:get="@this.selectedDataSources" @bind-SelectedValues:set="@(x => this.SelectionChanged(x))" Style="max-height: 14em;">
|
||||||
|
@foreach (var source in this.availableDataSources)
|
||||||
|
{
|
||||||
|
<MudListItem Value="@source">
|
||||||
|
@source.Name
|
||||||
|
</MudListItem>
|
||||||
|
}
|
||||||
|
</MudList>
|
||||||
|
</MudField>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</MudCardContent>
|
||||||
|
<MudCardActions>
|
||||||
|
<MudButton Variant="Variant.Filled" OnClick="@(() => this.HideDataSourceSelection())">
|
||||||
|
Close
|
||||||
|
</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</MudPopover>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else if (this.SelectionMode is DataSourceSelectionMode.CONFIGURATION_MODE)
|
||||||
|
{
|
||||||
|
<MudPaper Class="pa-3 mb-8 mt-3 border-dashed border rounded-lg">
|
||||||
|
<PreviewPrototype/>
|
||||||
|
<MudText Typo="Typo.h5">Data Source Selection</MudText>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(this.ConfigurationHeaderMessage))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body1">
|
||||||
|
@this.ConfigurationHeaderMessage
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudTextSwitch Label="Are data sources enabled?" Value="@this.areDataSourcesEnabled" LabelOn="Yes, I want to use data sources." LabelOff="No, I don't want to use data sources." ValueChanged="@this.EnabledChanged"/>
|
||||||
|
@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 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"/>
|
||||||
|
<MudField Label="Available Data Sources" Variant="Variant.Outlined" Class="mb-3" Disabled="@this.aiBasedSourceSelection">
|
||||||
|
<MudList T="IDataSource" SelectionMode="MudBlazor.SelectionMode.MultiSelection" @bind-SelectedValues:get="@this.selectedDataSources" @bind-SelectedValues:set="@(x => this.SelectionChanged(x))">
|
||||||
|
@foreach (var source in this.availableDataSources)
|
||||||
|
{
|
||||||
|
<MudListItem Value="@source">
|
||||||
|
@source.Name
|
||||||
|
</MudListItem>
|
||||||
|
}
|
||||||
|
</MudList>
|
||||||
|
</MudField>
|
||||||
|
}
|
||||||
|
</MudPaper>
|
||||||
|
}
|
251
app/MindWork AI Studio/Components/DataSourceSelection.razor.cs
Normal file
251
app/MindWork AI Studio/Components/DataSourceSelection.razor.cs
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
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; }
|
||||||
|
|
||||||
|
[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);
|
||||||
|
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
public void ChangeOptionWithoutSaving(DataSourceOptions options)
|
||||||
|
{
|
||||||
|
this.DataSourceOptions = options;
|
||||||
|
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();
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
23
app/MindWork AI Studio/Components/DataSourceSelectionMode.cs
Normal file
23
app/MindWork AI Studio/Components/DataSourceSelectionMode.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public enum DataSourceSelectionMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The user is selecting data sources for, e.g., the chat.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// In this case, we have to filter the data sources based on the
|
||||||
|
/// selected provider and check security requirements.
|
||||||
|
/// </remarks>
|
||||||
|
SELECTION_MODE,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user is configuring the default data sources, e.g., for the chat.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// In this case, all data sources are available for selection.
|
||||||
|
/// They get filtered later based on the selected provider and
|
||||||
|
/// security requirements.
|
||||||
|
/// </remarks>
|
||||||
|
CONFIGURATION_MODE,
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user