AI-Studio/app/MindWork AI Studio/Pages/Embeddings.razor
2026-09-09 11:30:45 +02:00

132 lines
8.1 KiB
Plaintext

@attribute [Route(Routes.EMBEDDINGS)]
@inherits MSGComponentBase
@using AIStudio.Provider
<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">
<MudText Typo="Typo.h5">@T("Background embeddings")</MudText>
<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>
<MudStack Row="true" Class="mt-3" Wrap="Wrap.Wrap" Spacing="2">
<MudChip T="string" Color="Color.Success" Variant="Variant.Filled">@string.Format(T("Indexed files: {0}"), this.TotalIndexedFiles)</MudChip>
<MudChip T="string" Color="Color.Info" Variant="Variant.Filled">@string.Format(T("Pending files: {0}"), this.TotalPendingFiles)</MudChip>
<MudChip T="string" Color="Color.Default" Variant="Variant.Filled">@string.Format(T("Skipped files: {0}"), this.TotalPermanentlySkippedFiles)</MudChip>
<MudChip T="string" Color="Color.Error" Variant="Variant.Filled">@string.Format(T("Failed files: {0}"), this.TotalFailedFiles)</MudChip>
</MudStack>
</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
{
@foreach (var status in this.Statuses)
{
<MudPaper Class="pa-4 border rounded-lg" Elevation="0">
<MudStack Spacing="2">
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
<MudText Typo="Typo.h6">@status.DataSourceName</MudText>
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
<MudChip T="string" Color="@GetStatusColor(status)" Variant="Variant.Filled">@status.StateLabel</MudChip>
@if (CanRefresh(status))
{
<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>
}
</MudStack>
</MudStack>
<MudProgressLinear Value="@status.ProgressPercent" Rounded="@true" Color="@GetStatusColor(status)" />
<MudText Typo="Typo.body2">
@string.Format(T("{0} of {1} files are indexed."), status.IndexedFiles, status.TotalFiles)
</MudText>
@if (status.PermanentlySkippedFiles > 0)
{
<MudText Typo="Typo.body2">
@string.Format(T("Skipped files: {0}. AI Studio reads them again once they change."), status.PermanentlySkippedFiles)
</MudText>
}
@if (status.FailedFiles > 0)
{
<MudText Typo="Typo.body2">
@string.Format(T("Failed files: {0}"), status.FailedFiles)
</MudText>
}
@if (!string.IsNullOrWhiteSpace(status.CurrentFile))
{
<MudText Typo="Typo.body2">
@string.Format(T("Current file: {0}"), status.CurrentFile)
</MudText>
}
@if (status.Failures.Count > 0)
{
<MudExpansionPanels MultiExpansion="@false" Class="mt-2">
<ExpansionPanel HeaderIcon="@GetFailureListIcon(status)" HeaderText="@this.GetFailureListHeader(status)" MaxHeight="360">
<MudStack Spacing="2">
@foreach (var failure in status.Failures)
{
<MudStack Spacing="1" Class="py-1">
<MudText Typo="Typo.body2" Style="word-break: break-word;">
@failure.FilePath
</MudText>
<MudText Typo="Typo.caption" Color="@(failure.IsPermanent ? Color.Default : Color.Warning)" Style="word-break: break-word;">
@failure.Reason
</MudText>
<MudStack Row="@true" Wrap="Wrap.Wrap" Spacing="1" AlignItems="AlignItems.Center">
@if (failure.IsPermanent)
{
<MudChip T="string" Size="Size.Small" Color="Color.Default" Variant="Variant.Outlined">@T("Skipped until the file changes")</MudChip>
}
@if (failure.FailureReason is not ProviderRequestFailureReason.NONE)
{
<MudChip T="string" Size="Size.Small" Color="Color.Warning" Variant="Variant.Outlined">@failure.FailureReason.GetName()</MudChip>
}
@if (!string.IsNullOrWhiteSpace(failure.EmbeddingProviderName))
{
<MudText Typo="Typo.caption">@string.Format(T("Embedding provider: {0}"), failure.EmbeddingProviderName)</MudText>
}
@if (failure.OccurredAtUtc > DateTimeOffset.MinValue)
{
<MudText Typo="Typo.caption">@failure.OccurredAtUtc.ToLocalTime().ToString("g")</MudText>
}
@if (failure.FailureReason.IsFixedInProviderSettings())
{
<MudButton Variant="Variant.Text" Size="Size.Small" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Settings" OnClick="@this.OpenEmbeddingProviderSettings">
@T("Open the settings")
</MudButton>
}
</MudStack>
</MudStack>
}
</MudStack>
</ExpansionPanel>
</MudExpansionPanels>
}
@*
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>
}
</MudStack>
</MudPaper>
}
}
</MudStack>