mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-05 04:47:21 +00:00
Added a way to read what the AI received from your sources (#1100)
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
1 parent
e0d8e18f34
commit
cbc4ce5413
229 files changed
+12739
-1269
No files matched your search
@@ -1,4 +1,5 @@
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Tools.RAG;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@@ -29,14 +30,20 @@ public partial class SourcesList : MSGComponentBase
|
||||
[Parameter]
|
||||
public IList<Source> Sources { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The passages the AI received from the sources, which the user can read below each of them.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public IReadOnlyList<SourcePassage> Passages { get; set; } = [];
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IJSRuntime JsRuntime { get; init; } = null!;
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ILogger<SourcesList> Logger { get; init; } = null!;
|
||||
private IJSRuntime JsRuntime { get; init; } = null!;
|
||||
|
||||
private readonly List<SourceEntryGroup> groups = [];
|
||||
|
||||
@@ -73,77 +80,57 @@ public partial class SourcesList : MSGComponentBase
|
||||
private void RebuildGroups()
|
||||
{
|
||||
this.groups.Clear();
|
||||
var passagesBySource = this.Passages.SortBySource();
|
||||
foreach (var group in this.Sources.GroupSources())
|
||||
{
|
||||
var entries = new List<SourceEntry>(group.Sources.Count);
|
||||
foreach (var numberedSource in group.Sources)
|
||||
{
|
||||
var document = numberedSource.Source.TryGetDocumentLocation(out var location) ? location : (SourceDocumentLocation?)null;
|
||||
entries.Add(new(numberedSource.Number, numberedSource.Source.Title, numberedSource.Source.URL, document, numberedSource.Source.IsMailSource()));
|
||||
var passages = passagesBySource.GetPassagesOf(numberedSource.Source);
|
||||
|
||||
//
|
||||
// Pages are links only into a document, and only under the name the title of the
|
||||
// source was made with, see SourcePassageExtensions.NamePassagesInTitles. Parts of a
|
||||
// document without pages are nothing a program can open, so their title stays as it is:
|
||||
//
|
||||
var name = passages.Count > 0 ? passages[0].DataSourceName : string.Empty;
|
||||
var pages = document is not null && !string.IsNullOrWhiteSpace(name) && passages.GetLocation() is { ArePages: true } pageLocation ? pageLocation : null;
|
||||
|
||||
entries.Add(new(numberedSource.Number, numberedSource.Source.Title, numberedSource.Source.URL, document, numberedSource.Source.HasNoLink(), passages, name, pages));
|
||||
}
|
||||
|
||||
this.groups.Add(new(group.Heading, entries));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a document in the program the system uses for it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Whether the program can be sent to a page is the runtime's business, and it says afterwards
|
||||
/// whether it managed to. Nothing is shown about that here: the document is open, and the title
|
||||
/// of the source names the page anyway.
|
||||
/// </remarks>
|
||||
/// <param name="document">The document to open, and the page to show.</param>
|
||||
private async Task OpenDocument(SourceDocumentLocation document)
|
||||
{
|
||||
OpenDocumentResponse response;
|
||||
try
|
||||
{
|
||||
response = await this.RustService.TryOpenDocumentInSystemViewer(document.Path, document.PageNumber);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.Logger.LogWarning(e, "Could not open a source document.");
|
||||
await this.MessageBus.SendError(new(Icons.Material.Filled.Description, T("Could not open the document.")));
|
||||
return;
|
||||
}
|
||||
private string PassagesTooltip(SourceEntry entry) => entry.Passages.Count is 1
|
||||
? T("Show the passage the AI received from this source")
|
||||
: string.Format(T("Show the {0} passages the AI received from this source"), entry.Passages.Count.CompactCount());
|
||||
|
||||
if (response.Success)
|
||||
return;
|
||||
private async Task ShowPassages(SourceEntry entry) => await SourcePassagesDialog.OpenDialogAsync(this.DialogService, entry.Passages, entry.Document);
|
||||
|
||||
var issue = string.IsNullOrWhiteSpace(response.Issue) ? T("Unknown error") : response.Issue;
|
||||
await this.MessageBus.SendError(new(Icons.Material.Filled.Description, string.Format(T("Could not open the document: {0}"), issue)));
|
||||
}
|
||||
private async Task OpenDocument(SourceDocumentLocation document) => await document.OpenAsync(this.RustService);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the file browser of the system and selects the document in it.
|
||||
/// The files of sources which are no longer at their place, by their paths, as FileActions found out.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The second way out of the list: a document which the system opens in the wrong program, or
|
||||
/// which the user wants to move or send on instead of read, is reached from here without being
|
||||
/// opened. This is the same way out the embeddings page offers for a file it could not read.
|
||||
/// </remarks>
|
||||
/// <param name="document">The document to show.</param>
|
||||
private async Task ShowInFileManager(SourceDocumentLocation document)
|
||||
private readonly HashSet<string> missingFiles = new(PathTools.COMPARER);
|
||||
|
||||
private bool IsMissing(SourceEntry entry) => entry.Document is { } document && this.missingFiles.Contains(document.Path);
|
||||
|
||||
/// <summary>
|
||||
/// Keeps whether the file of a source is at its place, so that its title stops being a link when it is not.
|
||||
/// </summary>
|
||||
private void NoteExistence(SourceEntry entry, bool exists)
|
||||
{
|
||||
OpenPathResponse response;
|
||||
try
|
||||
{
|
||||
response = await this.RustService.TryOpenPathInRuntimeFileManager(document.Path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.Logger.LogWarning(e, "Could not show a source document in the file manager.");
|
||||
await this.MessageBus.SendError(new(Icons.Material.Filled.FolderOpen, T("Could not open the file location.")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.Success)
|
||||
if (entry.Document is not { } document)
|
||||
return;
|
||||
|
||||
var issue = string.IsNullOrWhiteSpace(response.Issue) ? T("Unknown error") : response.Issue;
|
||||
await this.MessageBus.SendError(new(Icons.Material.Filled.FolderOpen, string.Format(T("Could not open the file location: {0}"), issue)));
|
||||
if (exists)
|
||||
this.missingFiles.Remove(document.Path);
|
||||
else
|
||||
this.missingFiles.Add(document.Path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -160,6 +147,9 @@ public partial class SourcesList : MSGComponentBase
|
||||
/// <param name="Title">The title of the source.</param>
|
||||
/// <param name="Link">The address of the source, which a web source is opened by.</param>
|
||||
/// <param name="Document">The document the source names, or null when it names none.</param>
|
||||
/// <param name="IsMail">Whether the source names a mail, which is shown as text.</param>
|
||||
private readonly record struct SourceEntry(int Number, string Title, string Link, SourceDocumentLocation? Document, bool IsMail);
|
||||
/// <param name="HasNoLink">Whether nothing can open the source by its address, e.g., a mail, so that it is shown as text.</param>
|
||||
/// <param name="Passages">The passages the AI received from the source; empty when it received none, or the chat is older than these.</param>
|
||||
/// <param name="Name">The name of the source without its location, or empty when it has no passages.</param>
|
||||
/// <param name="Location">The pages of the passages in the document, each to be opened on its own, or null when there are none.</param>
|
||||
private readonly record struct SourceEntry(int Number, string Title, string Link, SourceDocumentLocation? Document, bool HasNoLink, IReadOnlyList<SourcePassage> Passages, string Name, PassageLocation? Location);
|
||||
}
|
||||
Reference in new issue
Block a user