mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 13:07:21 +00:00
339 lines
25 KiB
Plaintext
339 lines
25 KiB
Plaintext
@attribute [Route(Routes.EMBEDDINGS)]
|
|
@using AIStudio.Tools.Services
|
|
@inherits MSGComponentBase
|
|
|
|
<MudStack Spacing="3" Class="pr-2 pb-4" Style="height: 100%; min-height: 0; overflow-y: auto;">
|
|
<MudPaper Class="pa-4 border-dashed border rounded-lg" Elevation="0">
|
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
|
<MudText Typo="Typo.h5">@T("Background embeddings")</MudText>
|
|
<MudSpacer/>
|
|
<MudTooltip Text="@T("Manage your data sources")" Placement="Placement.Top">
|
|
<MudIconButton Icon="@Icons.Material.Filled.Settings" OnClick="@this.OpenDataSourceSettings"/>
|
|
</MudTooltip>
|
|
</MudStack>
|
|
<MudText Typo="Typo.body1" Class="mt-2">
|
|
@T("AI Studio indexes local RAG data sources in the background. Finished files stay recorded so unchanged files can be skipped after a restart, while added or deleted files are detected during the next run. The same applies to documents without readable text, such as scanned pages: AI Studio remembers them and reads them again only once they change.")
|
|
</MudText>
|
|
<MudText Typo="Typo.body1" Class="mt-2">
|
|
@T("AI Studio indexes large data sources in steps of a few minutes, so that your other data sources get their turn in between.")
|
|
@this.PauseBetweenStepsText
|
|
</MudText>
|
|
@if (this.AreMailboxesEnabled)
|
|
{
|
|
<MudText Typo="Typo.body1" Class="mt-2">
|
|
@T("Mailboxes are synced every 16 minutes while the automatic refresh is on. Each mail is embedded only once: when it moves to another folder, AI Studio merely notes where it lies now.")
|
|
</MudText>
|
|
}
|
|
<MudStack Row="true" Class="mt-3" Wrap="Wrap.Wrap" Spacing="2">
|
|
<MudChip T="string" Color="Color.Success" Variant="Variant.Filled">@string.Format(T("Indexed: {0}"), this.FormatNumber(this.TotalIndexedDocuments))</MudChip>
|
|
<MudChip T="string" Color="Color.Info" Variant="Variant.Filled">@string.Format(T("Pending: {0}"), this.FormatNumber(this.TotalPendingDocuments))</MudChip>
|
|
<MudChip T="string" Color="Color.Default" Variant="Variant.Filled">@string.Format(T("Skipped: {0}"), this.FormatNumber(this.TotalPermanentlySkippedDocuments))</MudChip>
|
|
<MudChip T="string" Color="Color.Error" Variant="Variant.Filled">@string.Format(T("Failed: {0}"), this.FormatNumber(this.TotalFailedDocuments))</MudChip>
|
|
</MudStack>
|
|
@if (this.IsWorkingThroughDataSources)
|
|
{
|
|
<MudText Typo="Typo.body2" Class="mt-2">
|
|
@string.Format(T("Data source {0} of {1} is being worked on. The others are waiting their turn."), this.CurrentDataSourceNumber, this.Statuses.Count)
|
|
</MudText>
|
|
}
|
|
</MudPaper>
|
|
|
|
@if (this.Statuses.Count == 0)
|
|
{
|
|
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined">
|
|
@T("No local data source has been queued for embedding yet.")
|
|
</MudAlert>
|
|
}
|
|
else
|
|
{
|
|
@*
|
|
One panel per data source, and only one of them open: a folder with thousands of files
|
|
fills the page by itself. What a closed panel still has to say stays in its header, so
|
|
nobody has to open every one of them to see how their data sources are doing.
|
|
*@
|
|
<MudExpansionPanels>
|
|
@foreach (var status in this.Statuses)
|
|
{
|
|
<MudExpansionPanel Class="border-solid border rounded-lg" Expanded="@(this.expandedDataSourceId == status.DataSourceId)" ExpandedChanged="@(isExpanded => this.DataSourcePanelExpandedChanged(status, isExpanded))">
|
|
<TitleContent>
|
|
<div class="d-flex align-center">
|
|
<MudText Typo="Typo.h6">@string.Format(T("Data source: {0}"), status.DataSourceName)</MudText>
|
|
<MudSpacer/>
|
|
<MudChip T="string" Color="@GetStatusColor(status)" Variant="Variant.Filled">@status.StateLabel</MudChip>
|
|
@if (CanRefresh(status))
|
|
{
|
|
@*
|
|
This opens or closes the panel along the way. The header is what
|
|
toggles it, and neither stopping the event nor swallowing the
|
|
click works in this project — see the end button of our own
|
|
ExpansionPanel component, which behaves the same way.
|
|
*@
|
|
<MudTooltip Text="@T("Refresh this data source")">
|
|
<MudIconButton Icon="@Icons.Material.Filled.Sync" Color="Color.Info" Size="Size.Small" OnClick="@(async () => await this.RefreshDataSource(status))" />
|
|
</MudTooltip>
|
|
}
|
|
@if (this.CanRepair(status))
|
|
{
|
|
<MudTooltip Text="@T("Repair this data source by indexing it anew")">
|
|
<MudIconButton Icon="@Icons.Material.Filled.Build" Color="Color.Warning" Size="Size.Small" OnClick="@(async () => await this.RepairDataSource(status))" />
|
|
</MudTooltip>
|
|
}
|
|
</div>
|
|
</TitleContent>
|
|
<ChildContent>
|
|
<MudStack Spacing="2">
|
|
<MudProgressLinear Value="@status.ProgressPercent" Rounded="@true" Color="@GetStatusColor(status)" />
|
|
|
|
<MudText Typo="Typo.body2">
|
|
@this.GetProgressText(status)
|
|
</MudText>
|
|
|
|
@if (IsMailbox(status))
|
|
{
|
|
<MudText Typo="Typo.body2">
|
|
@this.GetLastSyncText(status)
|
|
</MudText>
|
|
}
|
|
|
|
@if (status.SkippedDocuments > 0)
|
|
{
|
|
<MudText Typo="Typo.body2">
|
|
@this.GetSkippedText(status)
|
|
</MudText>
|
|
}
|
|
|
|
@if (status.FailedDocuments > 0)
|
|
{
|
|
<MudText Typo="Typo.body2">
|
|
@this.GetFailedText(status)
|
|
</MudText>
|
|
}
|
|
|
|
@if (!string.IsNullOrWhiteSpace(status.CurrentDocument))
|
|
{
|
|
<MudText Typo="Typo.body2">
|
|
@this.GetCurrentDocumentText(status)
|
|
</MudText>
|
|
}
|
|
|
|
@*
|
|
One panel per cause, the most pressing one first. A folder in which nine
|
|
hundred scanned documents were skipped otherwise buries the handful of
|
|
files somebody has to look at.
|
|
*@
|
|
@if (status.Failures.Count > 0 || status.IgnoredFiles.Count > 0)
|
|
{
|
|
<MudExpansionPanels MultiExpansion="@true" Class="mt-2">
|
|
@foreach (var group in this.GetFailureGroups(status))
|
|
{
|
|
<ExpansionPanel HeaderIcon="@group.Cause.Icon" IconColor="@group.Cause.Color" IconSize="Size.Small" HeaderTypo="Typo.subtitle1" HeaderClass="expansion-panel-header-compact" HeaderText="@GetGroupHeader(group)">
|
|
<MudStack Spacing="3">
|
|
<MudStack Row="@true" Wrap="Wrap.Wrap" Spacing="2" AlignItems="AlignItems.Center">
|
|
<MudChip T="string" Size="Size.Small" Color="@group.Cause.Color" Variant="Variant.Outlined">
|
|
@(group.Cause.IsPermanent ? (IsMailbox(status) ? T("Skipped") : T("Skipped until the file changes")) : T("Tried again during the next run"))
|
|
</MudChip>
|
|
@if (!string.IsNullOrWhiteSpace(group.EmbeddingProviderName))
|
|
{
|
|
<MudText Typo="Typo.caption">@string.Format(T("Embedding provider: {0}"), group.EmbeddingProviderName)</MudText>
|
|
}
|
|
@if (group.Cause.NeedsProviderSettings)
|
|
{
|
|
<MudButton Variant="Variant.Text" Size="Size.Small" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Settings" OnClick="@this.OpenProviderSettings">
|
|
@T("Open the settings")
|
|
</MudButton>
|
|
}
|
|
</MudStack>
|
|
<MudTable Items="@group.Failures" Dense="@true" Hover="@true" Elevation="0" Breakpoint="Breakpoint.None" RowsPerPage="10" Class="border-dashed border rounded-lg">
|
|
<ColGroup>
|
|
<col/>
|
|
<col style="width: 11em;"/>
|
|
<col style="width: 7em;"/>
|
|
</ColGroup>
|
|
<HeaderContent>
|
|
<MudTh>@(IsMailbox(status) ? T("Mail") : T("File"))</MudTh>
|
|
<MudTh>@T("Noticed")</MudTh>
|
|
<MudTh>@T("Actions")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd Style="white-space: normal; overflow-wrap: anywhere;">
|
|
@* Captions render as spans, so each of them needs to be told to take its own line. *@
|
|
@if (IsMailbox(status))
|
|
{
|
|
<MudText Typo="Typo.body2">@GetMailName(context)</MudText>
|
|
}
|
|
else
|
|
{
|
|
<MudText Typo="Typo.body2">@GetFileName(context.DocumentKey)</MudText>
|
|
<MudText Typo="Typo.caption" Class="mud-text-secondary d-block">@context.DocumentKey</MudText>
|
|
}
|
|
@if (group.Cause.ShowsMessagePerFile)
|
|
{
|
|
<MudText Typo="Typo.caption" Color="Color.Warning" Class="d-block">@context.Reason</MudText>
|
|
}
|
|
</MudTd>
|
|
<MudTd Style="white-space: nowrap;">
|
|
<MudText Typo="Typo.caption">@GetOccurrenceText(context)</MudText>
|
|
</MudTd>
|
|
<MudTd>
|
|
@if (CanShowInFileManager(status, context))
|
|
{
|
|
<MudTooltip Text="@T("Show this file in the file browser of your system")">
|
|
<MudIconButton Icon="@Icons.Material.Filled.FolderOpen" Color="Color.Info" Size="Size.Small" OnClick="@(async () => await this.ShowInFileManager(context))"/>
|
|
</MudTooltip>
|
|
}
|
|
</MudTd>
|
|
</RowTemplate>
|
|
<PagerContent>
|
|
<MudTablePager PageSizeOptions="@PAGE_SIZE_OPTIONS"/>
|
|
</PagerContent>
|
|
</MudTable>
|
|
</MudStack>
|
|
</ExpansionPanel>
|
|
}
|
|
@foreach (var group in this.GetIgnoredFileGroups(status))
|
|
{
|
|
<ExpansionPanel HeaderIcon="@group.Cause.Icon" IconColor="@group.Cause.Color" IconSize="Size.Small" HeaderTypo="Typo.subtitle1" HeaderClass="expansion-panel-header-compact" HeaderText="@GetIgnoredGroupHeader(group)">
|
|
<MudStack Spacing="3">
|
|
<MudText Typo="Typo.body2">@group.Cause.Description</MudText>
|
|
<MudStack Row="@true" Wrap="Wrap.Wrap" Spacing="2" AlignItems="AlignItems.Center">
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Default" Variant="Variant.Outlined">
|
|
@T("Not added to the index")
|
|
</MudChip>
|
|
@if (group.Cause.NeedsDataSourceSettings || group.Cause.NeedsTranscriptionSettings)
|
|
{
|
|
<MudSpacer/>
|
|
}
|
|
@if (group.Cause.NeedsTranscriptionSettings)
|
|
{
|
|
<MudButton Variant="Variant.Text" Size="Size.Small" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Settings" OnClick="@this.OpenProviderSettings">
|
|
@T("Open the transcription settings")
|
|
</MudButton>
|
|
}
|
|
@if (group.Cause.NeedsDataSourceSettings)
|
|
{
|
|
<MudButton Variant="Variant.Text" Size="Size.Small" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Settings" OnClick="@this.OpenDataSourceSettings">
|
|
@T("Manage the data sources")
|
|
</MudButton>
|
|
}
|
|
</MudStack>
|
|
<MudTable Items="@group.Files" Dense="@true" Hover="@true" Elevation="0" Breakpoint="Breakpoint.None" RowsPerPage="10" Class="border-dashed border rounded-lg">
|
|
<ColGroup>
|
|
<col/>
|
|
<col style="width: 7em;"/>
|
|
</ColGroup>
|
|
<HeaderContent>
|
|
<MudTh>@T("File")</MudTh>
|
|
<MudTh>@T("Actions")</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd Style="white-space: normal; overflow-wrap: anywhere;">
|
|
<MudText Typo="Typo.body2">@GetFileName(context.FilePath)</MudText>
|
|
<MudText Typo="Typo.caption" Class="mud-text-secondary d-block">@context.FilePath</MudText>
|
|
</MudTd>
|
|
<MudTd>
|
|
@if (CanShowInFileManager(status, context.FilePath))
|
|
{
|
|
<MudTooltip Text="@T("Show this file in the file browser of your system")">
|
|
<MudIconButton Icon="@Icons.Material.Filled.FolderOpen" Color="Color.Info" Size="Size.Small" OnClick="@(async () => await this.ShowInFileManager(context.FilePath))"/>
|
|
</MudTooltip>
|
|
}
|
|
</MudTd>
|
|
</RowTemplate>
|
|
<PagerContent>
|
|
<MudTablePager PageSizeOptions="@PAGE_SIZE_OPTIONS"/>
|
|
</PagerContent>
|
|
</MudTable>
|
|
</MudStack>
|
|
</ExpansionPanel>
|
|
}
|
|
</MudExpansionPanels>
|
|
}
|
|
|
|
@if (status.FilePatternExclusions.Count > 0 || status.ExcludedFolders.Count > 0)
|
|
{
|
|
<MudPaper Class="pa-3 border-dashed border rounded-lg mt-2" Elevation="0">
|
|
<MudStack Spacing="2">
|
|
<MudStack Row="@true" AlignItems="AlignItems.Center" Spacing="2">
|
|
<MudIcon Icon="@Icons.Material.Filled.FilterAlt" Color="Color.Default" Size="Size.Small"/>
|
|
<MudText Typo="Typo.subtitle1">@T("Outside the selection")</MudText>
|
|
<MudSpacer/>
|
|
<MudButton Variant="Variant.Text" Size="Size.Small" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Settings" OnClick="@this.OpenDataSourceSettings">
|
|
@T("Manage the data sources")
|
|
</MudButton>
|
|
</MudStack>
|
|
<MudText Typo="Typo.body2">
|
|
@T("These file types and folders were found in the folder of the data source, but its file patterns or its list of excluded files and folders leave them out. They are intentionally excluded and do not affect the indexing status. Excluded folders are not searched at all.")
|
|
</MudText>
|
|
<MudStack Row="@true" Wrap="Wrap.Wrap" Spacing="1">
|
|
@foreach (var group in status.ExcludedFolders)
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Default" Variant="Variant.Outlined" Icon="@Icons.Material.Filled.FolderOff">
|
|
@this.GetExcludedFolderGroupText(group)
|
|
</MudChip>
|
|
}
|
|
@foreach (var group in status.FilePatternExclusions)
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Default" Variant="Variant.Outlined">
|
|
@this.GetFilePatternExclusionGroupText(group)
|
|
</MudChip>
|
|
}
|
|
</MudStack>
|
|
</MudStack>
|
|
</MudPaper>
|
|
}
|
|
|
|
@*
|
|
Shown next to the list, not instead of it: the list says which files failed,
|
|
while this is the one sentence about the data source as a whole. Hiding it
|
|
as soon as a single file failed is what made it invisible in practice.
|
|
*@
|
|
@if (!string.IsNullOrWhiteSpace(status.LastError))
|
|
{
|
|
<MudAlert Severity="Severity.Warning" Variant="Variant.Text">
|
|
@status.LastError
|
|
</MudAlert>
|
|
}
|
|
|
|
@*
|
|
What the user has to decide comes with the buttons to decide it. The
|
|
sentence above says what is at stake; reading it and then having to find
|
|
the settings of the mailbox or the data source is where people give up.
|
|
*@
|
|
@if (status.Attention is DataSourceAttention.AUTH_FAILED)
|
|
{
|
|
<MudStack Row="@true" Wrap="Wrap.Wrap" Spacing="2">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Key" OnClick="@(async () => await this.ChangeMailboxSettings(status))">
|
|
@T("Change the password")
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Sync" OnClick="@(async () => await this.RefreshDataSource(status))">
|
|
@T("Try to sign in again")
|
|
</MudButton>
|
|
</MudStack>
|
|
}
|
|
else if (status is { Attention: DataSourceAttention.MASS_REMOVAL_PENDING, PendingRemovalCount: not null })
|
|
{
|
|
<MudStack Row="@true" Wrap="Wrap.Wrap" Spacing="2">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Warning" StartIcon="@Icons.Material.Filled.PlaylistRemove" OnClick="@(async () => await this.ApproveMailRemoval(status))">
|
|
@T("Remove them from the index")
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Settings" OnClick="@(async () => await this.ChangeMailboxSettings(status))">
|
|
@T("Change the settings of the mailbox")
|
|
</MudButton>
|
|
</MudStack>
|
|
}
|
|
else if (status.Attention is DataSourceAttention.LOCATION_UNREACHABLE && this.CanEditDataSource(status))
|
|
{
|
|
<MudStack Row="@true" Wrap="Wrap.Wrap" Spacing="2">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Edit" OnClick="@(async () => await this.EditDataSource(status))">
|
|
@T("Edit this data source")
|
|
</MudButton>
|
|
</MudStack>
|
|
}
|
|
</MudStack>
|
|
</ChildContent>
|
|
</MudExpansionPanel>
|
|
}
|
|
</MudExpansionPanels>
|
|
}
|
|
</MudStack>
|