mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-17 16:58:00 +00:00
Merge branch 'main' into fix/Darkmode-switch
This commit is contained in:
1036 files changed
+342361
-8176
No files matched your search
@@ -5,6 +5,7 @@ using AIStudio.Tools.AIJobs;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using AIStudio.Tools.Media;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Security;
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
@@ -53,6 +54,12 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
|
||||
[Inject]
|
||||
private MudTheme ColorTheme { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private DataSourceEmbeddingService DataSourceEmbeddingService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private CircuitStateService CircuitState { get; init; } = null!;
|
||||
|
||||
private ILanguagePlugin Lang { get; set; } = PluginFactory.BaseLanguage;
|
||||
|
||||
@@ -71,8 +78,12 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
private bool startupCompleted;
|
||||
private bool settingsWriteProtectionWarningShown;
|
||||
private readonly SemaphoreSlim mandatoryInfoDialogSemaphore = new(1, 1);
|
||||
private readonly SemaphoreSlim promptInjectionDialogSemaphore = new(1, 1);
|
||||
|
||||
private DataSourceEmbeddingOverview embeddingOverview = new(DataSourceEmbeddingState.COMPLETED, 0, 0, 0);
|
||||
private IReadOnlyCollection<NavBarItem> navItems = [];
|
||||
private NavBarItem embeddingItem = new (string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, false);
|
||||
private bool showEmbeddingStatusIcon;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
@@ -106,15 +117,17 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
|
||||
// Ensure that all settings are loaded:
|
||||
await this.SettingsManager.LoadSettings();
|
||||
await this.DataSourceEmbeddingService.QueueAllInternalDataSourcesIfAutomaticRefreshAsync();
|
||||
|
||||
// Register this component with the message bus:
|
||||
this.MessageBus.RegisterComponent(this);
|
||||
this.MessageBus.RegisterComponent(this, this.CircuitState);
|
||||
this.MessageBus.ApplyFilters(this, [],
|
||||
[
|
||||
Event.UPDATE_AVAILABLE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED, Event.SHOW_ERROR,
|
||||
Event.SHOW_WARNING, Event.SHOW_SUCCESS, Event.SHOW_INFO, Event.STARTUP_PLUGIN_SYSTEM, Event.PLUGINS_RELOADED,
|
||||
Event.SHOW_WARNING, Event.SHOW_SUCCESS, Event.SHOW_INFO, Event.SHOW_PROMPT_INJECTION_ALERT, Event.STARTUP_PLUGIN_SYSTEM, Event.PLUGINS_RELOADED,
|
||||
Event.INSTALL_UPDATE, Event.STARTUP_COMPLETED, Event.AI_JOB_CHANGED, Event.AI_JOB_FINISHED,
|
||||
Event.CHAT_GENERATION_CHANGED, Event.ASSISTANT_SESSION_CHANGED, Event.ASSISTANT_SESSION_FINISHED,
|
||||
Event.CHAT_GENERATION_CHANGED, Event.RAG_EMBEDDING_STATUS_CHANGED,Event.ASSISTANT_SESSION_CHANGED,
|
||||
Event.ASSISTANT_SESSION_FINISHED,
|
||||
]);
|
||||
|
||||
// Set the snackbar for the update service:
|
||||
@@ -134,6 +147,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
await this.themeProvider.WatchSystemDarkModeAsync(this.SystemeThemeChanged);
|
||||
await this.UpdateThemeConfiguration();
|
||||
this.LoadNavItems();
|
||||
this.LoadEmbeddingItem();
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
@@ -230,9 +244,10 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
|
||||
await this.UpdateThemeConfiguration();
|
||||
this.LoadNavItems();
|
||||
this.LoadEmbeddingItem();
|
||||
this.StateHasChanged();
|
||||
if (this.startupCompleted)
|
||||
_ = this.EnsureMandatoryInfosAcceptedAsync();
|
||||
this.EnsureMandatoryInfosAcceptedAsync().Observe($"{nameof(MainLayout)}: mandatory infos after a configuration change");
|
||||
break;
|
||||
|
||||
case Event.COLOR_THEME_CHANGED:
|
||||
@@ -254,6 +269,12 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
|
||||
break;
|
||||
|
||||
case Event.SHOW_PROMPT_INJECTION_ALERT:
|
||||
if (data is PromptInjectionAlertMessage promptInjectionAlert)
|
||||
await this.ShowPromptInjectionAlertAsync(promptInjectionAlert);
|
||||
|
||||
break;
|
||||
|
||||
case Event.SHOW_ERROR:
|
||||
if (data is DataErrorMessage error)
|
||||
error.Show(this.Snackbar);
|
||||
@@ -273,7 +294,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
break;
|
||||
|
||||
case Event.STARTUP_PLUGIN_SYSTEM:
|
||||
_ = Task.Run(async () =>
|
||||
Task.Run(async () =>
|
||||
{
|
||||
// Set up the plugin system:
|
||||
if (PluginFactory.Setup())
|
||||
@@ -284,8 +305,10 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
//
|
||||
// Check if there is an enterprise configuration plugin to download:
|
||||
//
|
||||
// Every deferred environment matters here: each one is a configuration
|
||||
// to download, so this is the one place which uses all of them.
|
||||
var enterpriseEnvironments = this.MessageBus
|
||||
.CheckDeferredMessages<EnterpriseEnvironment>(Event.STARTUP_ENTERPRISE_ENVIRONMENT)
|
||||
.TakeDeferredMessages<EnterpriseEnvironment>(Event.STARTUP_ENTERPRISE_ENVIRONMENT)
|
||||
.Where(env => env != default)
|
||||
.ToList();
|
||||
|
||||
@@ -326,7 +349,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
PluginFactory.SetUpHotReloading();
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.STARTUP_COMPLETED);
|
||||
}
|
||||
});
|
||||
}).Observe($"{nameof(MainLayout)}: setting up the plugin system");
|
||||
break;
|
||||
|
||||
case Event.PLUGINS_RELOADED:
|
||||
@@ -334,20 +357,53 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
I18N.Init(this.Lang);
|
||||
this.ShowSettingsWriteProtectionWarning();
|
||||
this.LoadNavItems();
|
||||
this.LoadEmbeddingItem();
|
||||
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
if (this.startupCompleted)
|
||||
_ = this.EnsureMandatoryInfosAcceptedAsync();
|
||||
this.EnsureMandatoryInfosAcceptedAsync().Observe($"{nameof(MainLayout)}: mandatory infos after a plugin reload");
|
||||
break;
|
||||
|
||||
case Event.STARTUP_COMPLETED:
|
||||
this.startupCompleted = true;
|
||||
_ = this.EnsureMandatoryInfosAcceptedAsync();
|
||||
this.EnsureMandatoryInfosAcceptedAsync().Observe($"{nameof(MainLayout)}: mandatory infos after the startup");
|
||||
break;
|
||||
|
||||
case Event.RAG_EMBEDDING_STATUS_CHANGED:
|
||||
this.LoadNavItems();
|
||||
this.LoadEmbeddingItem();
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task ShowPromptInjectionAlertAsync(PromptInjectionAlertMessage alert)
|
||||
{
|
||||
await this.promptInjectionDialogSemaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (!this.SettingsManager.ConfigurationData.App.ShowPromptInjectionAlert)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<PromptInjectionAlertDialog>
|
||||
{
|
||||
{ x => x.Alert, alert },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<PromptInjectionAlertDialog>(
|
||||
T("Security notice"),
|
||||
dialogParameters,
|
||||
DialogOptions.FULLSCREEN);
|
||||
|
||||
await dialogReference.Result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.promptInjectionDialogSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||
{
|
||||
return Task.FromResult<TResult?>(default);
|
||||
@@ -363,11 +419,11 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
/// <summary>Refreshes navigation activity colors when a media import changes state.</summary>
|
||||
private void OnMediaImportStateChanged(MediaImportOwner owner)
|
||||
{
|
||||
_ = this.InvokeAsync(() =>
|
||||
this.InvokeAsync(() =>
|
||||
{
|
||||
this.LoadNavItems();
|
||||
this.StateHasChanged();
|
||||
});
|
||||
}).Observe($"{nameof(MainLayout)}: refreshing the navigation after a media import change");
|
||||
}
|
||||
|
||||
private IEnumerable<NavBarItem> GetNavItems()
|
||||
@@ -400,6 +456,52 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
yield return new(T("Settings"), Icons.Material.Filled.Settings, defaultLightColor, defaultDarkColor, Routes.SETTINGS, false);
|
||||
}
|
||||
|
||||
private void LoadEmbeddingItem()
|
||||
{
|
||||
this.embeddingOverview = this.DataSourceEmbeddingService.GetOverview();
|
||||
|
||||
//
|
||||
// The entry is shown whenever local RAG is available, in every state. Hiding it while
|
||||
// nothing was running looked tidier, but a data source which was just added has no status
|
||||
// yet: the service creates one when the run begins. The icon was therefore missing during
|
||||
// the very moment the user was waiting for it. What the entry does communicate is its
|
||||
// state, through the icon below.
|
||||
//
|
||||
// The preview feature is what gates it now. The route itself is not gated, so without this
|
||||
// check, users who have no RAG at all would get a navigation entry for it.
|
||||
//
|
||||
this.showEmbeddingStatusIcon = PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager);
|
||||
|
||||
var palette = this.ColorTheme.GetCurrentPalette(this.SettingsManager);
|
||||
(string icon, string lightcolor, string darkcolor) embeddingIcon = this.embeddingOverview.State switch
|
||||
{
|
||||
DataSourceEmbeddingState.FAILED => (Icons.Material.Filled.Warning, palette.Error.Value, "#d32f2f"),
|
||||
DataSourceEmbeddingState.QUEUED => (Icons.Material.Filled.Sync, palette.Info.Value, "#1976d2"),
|
||||
DataSourceEmbeddingState.RUNNING => (Icons.Material.Filled.Sync, palette.Warning.Value, "#d29f00"),
|
||||
|
||||
// Nothing to do: the entry keeps the colors of its neighbors, so a permanently visible
|
||||
// icon does not draw attention while there is nothing to attend to:
|
||||
_ => (Icons.Material.Filled.CloudDone, palette.DarkLighten, palette.GrayLight),
|
||||
};
|
||||
this.embeddingItem = new NavBarItem(T("Embeddings"), embeddingIcon.icon, embeddingIcon.lightcolor, embeddingIcon.darkcolor, Routes.EMBEDDINGS, false);
|
||||
}
|
||||
|
||||
private string EmbeddingNavigationTooltip => this.embeddingOverview.State switch
|
||||
{
|
||||
DataSourceEmbeddingState.QUEUED => T("Embeddings are waiting to be processed."),
|
||||
DataSourceEmbeddingState.RUNNING => string.Format(
|
||||
T("Embeddings are running: {0} of {1} files are indexed."),
|
||||
this.embeddingOverview.IndexedFiles,
|
||||
this.embeddingOverview.TotalFiles),
|
||||
DataSourceEmbeddingState.FAILED => this.embeddingOverview.FailedFiles > 0
|
||||
? string.Format(T("Some embeddings failed. {0} file(s) need attention."), this.embeddingOverview.FailedFiles)
|
||||
: T("Some embeddings failed and need attention."),
|
||||
|
||||
// The entry is always visible, so its resting state needs words as well. An empty tooltip
|
||||
// would leave the user guessing what the icon is there for:
|
||||
_ => T("All data sources are up to date.")
|
||||
};
|
||||
|
||||
private async Task ShowUpdateDialog()
|
||||
{
|
||||
if (!this.UpdatePolicy.AllowsInstallations)
|
||||
|
||||
Reference in new issue
Block a user