using AIStudio.Settings; using AIStudio.Tools.Services; using Microsoft.AspNetCore.Components; namespace AIStudio.Components; /// /// Tells that no embedding provider exists, which every local data source needs, and offers to add /// one right there. /// /// /// Shown where a local data source is set up: above the data source table, and in the dialogs for /// adding or editing one. Without it, the missing provider only came up when the user saved the /// dialog, after filling in everything else, and leaving the dialog to add one lost all of it. /// public partial class DataSourceMissingEmbeddingWarning : MSGComponentBase { /// /// Whether the warning sits in the dialog of a data source rather than above the data source table. /// [Parameter] public bool IsInDataSourceDialog { get; set; } /// /// Receives the embedding provider the user just added, so that a dialog can select it. /// [Parameter] public EventCallback EmbeddingProviderAdded { get; set; } [Inject] private IDialogService DialogService { get; init; } = null!; [Inject] private DataSourceEmbeddingService DataSourceEmbeddingService { get; init; } = null!; /// /// Whether the dialog for adding a provider is open, so that a second click does not open another one. /// private bool isAdding; private bool IsMissing => EmbeddingProviderSetup.IsMissing(this.SettingsManager); private bool MayAdd => EmbeddingProviderSetup.MayAdd(this.SettingsManager); private bool MayImport => EmbeddingProviderSetup.MayImport(this.SettingsManager); private string Text { get { var problem = this.IsInDataSourceDialog ? T("No embedding provider is configured yet, but this data source needs one.") : T("No embedding provider is configured yet. Local data sources, such as your files and folders, need one: it prepares your data so that the AI can search it."); if (!this.MayAdd) return $"{problem} {T("Your organization manages the embedding providers and has not provided one yet. Please ask your IT department.")}"; // Above the table, the buttons speak for themselves. In a dialog, the worry is about what was entered so far: return this.IsInDataSourceDialog ? $"{problem} {T("You can add one right here; everything you entered in this dialog stays as it is.")}" : problem; } } #region Overrides of ComponentBase protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); // The warning goes away as soon as a provider exists, also one which a configuration plugin brings along: this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED ]); } #endregion #region Overrides of MSGComponentBase protected override Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default { if (triggeredEvent is Event.CONFIGURATION_CHANGED) this.StateHasChanged(); return base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data); } #endregion private Task AddEmbeddingProvider() => this.AddOnceAsync(() => EmbeddingProviderSetup.AddAsync(this.DialogService, this.SettingsManager, this.DataSourceEmbeddingService)); private Task ImportEmbeddingProvider() => this.AddOnceAsync(() => EmbeddingProviderSetup.ImportAsync(this.DialogService, this.SettingsManager, this.DataSourceEmbeddingService)); private async Task AddOnceAsync(Func> addProvider) { if (this.isAdding) return; this.isAdding = true; try { var addedProvider = await addProvider(); if (addedProvider is null) return; await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED); await this.EmbeddingProviderAdded.InvokeAsync(addedProvider); } finally { this.isAdding = false; } } }