mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-13 21:53:47 +00:00
Added mailboxes as local data sources (#1022)
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
This commit is contained in:
1 parent
93be539e50
commit
c4400c0ff6
343 files changed
+26051
-3122
No files matched your search
@@ -1,6 +1,7 @@
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.Databases;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Services;
|
||||
@@ -26,8 +27,21 @@ public partial class DataSourceManagement : MSGComponentBase
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private DatabaseClientProvider DatabaseClientProvider { get; init; } = null!;
|
||||
|
||||
private readonly List<ConfigurationSelectData<string>> availableEmbeddingProviders = new();
|
||||
|
||||
/// <summary>
|
||||
/// The data sources and then the mailboxes, which the table shows together.
|
||||
/// </summary>
|
||||
private IEnumerable<IDataSourceBase> ConfiguredDataSources => this.SettingsManager.ConfigurationData.DataSources
|
||||
.Concat(this.SettingsManager.ConfigurationData.Mailboxes.Cast<IDataSourceBase>());
|
||||
|
||||
private bool AreMailboxesEnabled => PreviewFeatures.PRE_MAILBOXES_2026.IsEnabled(this.SettingsManager);
|
||||
|
||||
private bool MayAddMailbox => this.SettingsManager.ConfigurationData.App.AllowUserToAddMailbox;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@@ -77,7 +91,7 @@ public partial class DataSourceManagement : MSGComponentBase
|
||||
if (status is null || status.State is DataSourceEmbeddingState.IDLE or DataSourceEmbeddingState.QUEUED or DataSourceEmbeddingState.RUNNING)
|
||||
return Color.Warning;
|
||||
|
||||
return status.State is DataSourceEmbeddingState.FAILED || status.FailedFiles > 0
|
||||
return status.State is DataSourceEmbeddingState.FAILED || status.FailedDocuments > 0
|
||||
? Color.Error
|
||||
: Color.Success;
|
||||
}
|
||||
@@ -95,17 +109,18 @@ public partial class DataSourceManagement : MSGComponentBase
|
||||
if (status is null)
|
||||
return T("Waiting for indexing status");
|
||||
|
||||
if (status.PermanentlySkippedFiles == 0)
|
||||
// Only files are skipped for want of readable text. A mail is read from the server, never from a file:
|
||||
if (status.PermanentlySkippedDocuments == 0 || status.DataSourceType is DataSourceType.MAILBOX)
|
||||
return status.StateLabel;
|
||||
|
||||
return $"{status.StateLabel} — {string.Format(T("{0} files were skipped because they contain no readable text. AI Studio reads them again once they change."), status.PermanentlySkippedFiles)}";
|
||||
return $"{status.StateLabel} — {string.Format(T("{0} files were skipped because they contain no readable text. AI Studio reads them again once they change."), status.PermanentlySkippedDocuments)}";
|
||||
}
|
||||
|
||||
private string GetEmbeddingName(IDataSource dataSource)
|
||||
private string GetEmbeddingName(IDataSourceBase dataSource)
|
||||
{
|
||||
if(dataSource is IInternalDataSource internalDataSource)
|
||||
if(dataSource is IIndexedDataSource indexedDataSource)
|
||||
{
|
||||
var matchedEmbedding = this.SettingsManager.ConfigurationData.EmbeddingProviders.FirstOrDefault(x => x.Id == internalDataSource.EmbeddingId);
|
||||
var matchedEmbedding = this.SettingsManager.ConfigurationData.EmbeddingProviders.FirstOrDefault(x => x.Id == indexedDataSource.EmbeddingId);
|
||||
if(matchedEmbedding == default)
|
||||
return T("No valid embedding");
|
||||
|
||||
@@ -118,14 +133,14 @@ public partial class DataSourceManagement : MSGComponentBase
|
||||
return T("Unknown");
|
||||
}
|
||||
|
||||
private bool CanRefreshDataSource(IDataSource dataSource)
|
||||
private bool CanRefreshDataSource(IDataSourceBase dataSource)
|
||||
{
|
||||
return this.DataSourceEmbeddingService.CanRefreshDataSource(dataSource);
|
||||
}
|
||||
|
||||
private bool HasRefreshableDataSources()
|
||||
{
|
||||
return this.SettingsManager.ConfigurationData.DataSources.Any(this.CanRefreshDataSource);
|
||||
return this.ConfiguredDataSources.Any(this.CanRefreshDataSource);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
@@ -134,12 +149,12 @@ public partial class DataSourceManagement : MSGComponentBase
|
||||
/// every internal data source regardless of state, and singling this one out would say more
|
||||
/// about the state than that button ever has.
|
||||
/// </remarks>
|
||||
private bool CanRepairDataSource(IDataSource dataSource)
|
||||
private bool CanRepairDataSource(IDataSourceBase dataSource)
|
||||
{
|
||||
return this.DataSourceEmbeddingService.NeedsIndexRepair(dataSource);
|
||||
}
|
||||
|
||||
private async Task RepairDataSource(IDataSource dataSource)
|
||||
private async Task RepairDataSource(IDataSourceBase dataSource)
|
||||
{
|
||||
if (!this.CanRepairDataSource(dataSource))
|
||||
return;
|
||||
@@ -161,7 +176,7 @@ public partial class DataSourceManagement : MSGComponentBase
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
private async Task RefreshDataSource(IDataSource dataSource)
|
||||
private async Task RefreshDataSource(IDataSourceBase dataSource)
|
||||
{
|
||||
if (!this.CanRefreshDataSource(dataSource))
|
||||
return;
|
||||
@@ -235,7 +250,7 @@ public partial class DataSourceManagement : MSGComponentBase
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
private async Task ExportDataSource(IDataSource dataSource)
|
||||
private async Task ExportDataSource(IDataSourceBase dataSource)
|
||||
{
|
||||
if (!this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
|
||||
return;
|
||||
@@ -442,6 +457,112 @@ public partial class DataSourceManagement : MSGComponentBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edits a row of the table, which is either a data source or a mailbox.
|
||||
/// </summary>
|
||||
private Task EditEntry(IDataSourceBase entry) => entry switch
|
||||
{
|
||||
DataSourceMailbox mailbox => this.EditMailbox(mailbox),
|
||||
IDataSource dataSource => this.EditDataSource(dataSource),
|
||||
|
||||
_ => Task.CompletedTask,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a row of the table, which is either a data source or a mailbox.
|
||||
/// </summary>
|
||||
private Task DeleteEntry(IDataSourceBase entry) => entry switch
|
||||
{
|
||||
DataSourceMailbox mailbox => this.DeleteMailbox(mailbox),
|
||||
IDataSource dataSource => this.DeleteDataSource(dataSource),
|
||||
|
||||
_ => Task.CompletedTask,
|
||||
};
|
||||
|
||||
private async Task AddMailbox()
|
||||
{
|
||||
if (!this.MayAddMailbox)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<DataSourceMailboxDialog>
|
||||
{
|
||||
{ x => x.IsEditing, false },
|
||||
{ x => x.AvailableEmbeddings, this.availableEmbeddingProviders }
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<DataSourceMailboxDialog>(T("Add Mailbox"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
var dialogResult = await dialogReference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
return;
|
||||
|
||||
var mailbox = (DataSourceMailbox)dialogResult.Data!;
|
||||
|
||||
//
|
||||
// The dialog refuses to add a mailbox once the organization rules them out, but the rule
|
||||
// may change while it stores the password. Then the password goes again, as far as possible:
|
||||
//
|
||||
if (!this.MayAddMailbox)
|
||||
{
|
||||
await this.RustService.DeleteSecret(mailbox, SecretStoreType.DATA_SOURCE);
|
||||
return;
|
||||
}
|
||||
|
||||
mailbox = mailbox with { Num = this.SettingsManager.ConfigurationData.NextDataSourceNum++ };
|
||||
|
||||
this.SettingsManager.ConfigurationData.Mailboxes.Add(mailbox);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.DataSourceEmbeddingService.QueueDataSourceAsync(mailbox);
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
private async Task EditMailbox(DataSourceMailbox mailbox)
|
||||
{
|
||||
if (await MailboxEditing.EditAsync(this.DialogService, this.SettingsManager, this.DataSourceEmbeddingService, mailbox.Id))
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a mailbox from AI Studio: its settings, its password, its index, and its recorded sign-in failure.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The sign-in failure is deleted on its own, since it outlives the index on purpose: repairing
|
||||
/// an index must not give the server a new attempt with a password it already refused.
|
||||
/// </remarks>
|
||||
private async Task DeleteMailbox(DataSourceMailbox mailbox)
|
||||
{
|
||||
if (mailbox.IsEnterpriseConfiguration)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the mailbox '{0}'? Your mails stay on the server as they are. AI Studio only deletes its index of them and the stored password."), mailbox.Name) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Mailbox"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
var dialogResult = await dialogReference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
return;
|
||||
|
||||
var deleteSecretResponse = await this.RustService.DeleteSecret(mailbox, SecretStoreType.DATA_SOURCE);
|
||||
if (!deleteSecretResponse.Success)
|
||||
{
|
||||
await this.DialogService.ShowMessageBox(
|
||||
T("Delete Mailbox"),
|
||||
string.Format(T("The password of this mailbox could not be deleted from the operating system, so the mailbox was kept. The issue was: {0}"), deleteSecretResponse.Issue),
|
||||
T("Close"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.SettingsManager.ConfigurationData.Mailboxes.Remove(mailbox);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.DataSourceEmbeddingService.RemoveDataSourceAsync(mailbox);
|
||||
|
||||
var indexStore = await this.DatabaseClientProvider.GetIndexStoreAsync();
|
||||
await indexStore.ClearMailboxAuthFailureAsync(mailbox.Id, CancellationToken.None);
|
||||
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
}
|
||||
|
||||
private async Task ShowInformation(IDataSource dataSource)
|
||||
{
|
||||
switch (dataSource)
|
||||
|
||||
Reference in new issue
Block a user