mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-02-12 02:01:36 +00:00
Refactored file attachments (#608)
Some checks failed
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Has been cancelled
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) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
Some checks failed
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Has been cancelled
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) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
This commit is contained in:
parent
4be5002088
commit
ed4c7d215a
@ -185,7 +185,7 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<SettingsDialo
|
||||
private string policyOutputRules = string.Empty;
|
||||
#warning Use deferred content for document analysis
|
||||
private string deferredContent = string.Empty;
|
||||
private HashSet<string> loadedDocumentPaths = [];
|
||||
private HashSet<FileAttachment> loadedDocumentPaths = [];
|
||||
|
||||
private bool IsNoPolicySelectedOrProtected => this.selectedPolicy is null || this.selectedPolicy.IsProtected;
|
||||
|
||||
@ -330,13 +330,19 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<SettingsDialo
|
||||
var documentSections = new List<string>();
|
||||
var count = 1;
|
||||
|
||||
foreach (var documentPath in this.loadedDocumentPaths)
|
||||
foreach (var fileAttachment in this.loadedDocumentPaths)
|
||||
{
|
||||
var fileContent = await this.RustService.ReadArbitraryFileData(documentPath, int.MaxValue);
|
||||
|
||||
if (fileAttachment.IsForbidden)
|
||||
{
|
||||
this.Logger.LogWarning($"Skipping forbidden file: '{fileAttachment.FilePath}'.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var fileContent = await this.RustService.ReadArbitraryFileData(fileAttachment.FilePath, int.MaxValue);
|
||||
|
||||
documentSections.Add($"""
|
||||
## DOCUMENT {count}:
|
||||
File path: {documentPath}
|
||||
File path: {fileAttachment.FilePath}
|
||||
Content:
|
||||
```
|
||||
{fileContent}
|
||||
|
||||
@ -32,7 +32,7 @@ public sealed class ContentImage : IContent, IImageSource
|
||||
public List<Source> Sources { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<string> FileAttachments { get; set; } = [];
|
||||
public List<FileAttachment> FileAttachments { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatChatThread, CancellationToken token = default)
|
||||
|
||||
@ -42,7 +42,7 @@ public sealed class ContentText : IContent
|
||||
public List<Source> Sources { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<string> FileAttachments { get; set; } = [];
|
||||
public List<FileAttachment> FileAttachments { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatThread, CancellationToken token = default)
|
||||
@ -149,24 +149,24 @@ public sealed class ContentText : IContent
|
||||
|
||||
#endregion
|
||||
|
||||
public async Task<string> PrepareContentForAI()
|
||||
public async Task<string> PrepareTextContentForAI()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(this.Text);
|
||||
|
||||
if(this.FileAttachments.Count > 0)
|
||||
{
|
||||
// Filter out files that no longer exist
|
||||
var existingFiles = this.FileAttachments.Where(File.Exists).ToList();
|
||||
// Get the list of existing documents:
|
||||
var existingDocuments = this.FileAttachments.Where(x => x.Type is FileAttachmentType.DOCUMENT && x.Exists).ToList();
|
||||
|
||||
// Log warning for missing files
|
||||
var missingFiles = this.FileAttachments.Except(existingFiles).ToList();
|
||||
if (missingFiles.Count > 0)
|
||||
foreach (var missingFile in missingFiles)
|
||||
LOGGER.LogWarning("File attachment no longer exists and will be skipped: '{MissingFile}'", missingFile);
|
||||
|
||||
// Only proceed if there are existing files
|
||||
if (existingFiles.Count > 0)
|
||||
// Log warning for missing files:
|
||||
var missingDocuments = this.FileAttachments.Except(existingDocuments).Where(x => x.Type is FileAttachmentType.DOCUMENT).ToList();
|
||||
if (missingDocuments.Count > 0)
|
||||
foreach (var missingDocument in missingDocuments)
|
||||
LOGGER.LogWarning("File attachment no longer exists and will be skipped: '{MissingDocument}'.", missingDocument.FilePath);
|
||||
|
||||
// Only proceed if there are existing, allowed documents:
|
||||
if (existingDocuments.Count > 0)
|
||||
{
|
||||
// Check Pandoc availability once before processing file attachments
|
||||
var pandocState = await Pandoc.CheckAvailabilityAsync(Program.RUST_SERVICE, showMessages: true, showSuccessMessage: false);
|
||||
@ -179,14 +179,20 @@ public sealed class ContentText : IContent
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("The following files are attached to this message:");
|
||||
foreach(var file in existingFiles)
|
||||
foreach(var document in existingDocuments)
|
||||
{
|
||||
if (document.IsForbidden)
|
||||
{
|
||||
LOGGER.LogWarning("File attachment '{FilePath}' has a forbidden file type and will be skipped.", document.FilePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("---------------------------------------");
|
||||
sb.AppendLine($"File path: {file}");
|
||||
sb.AppendLine($"File path: {document.FilePath}");
|
||||
sb.AppendLine("File content:");
|
||||
sb.AppendLine("````");
|
||||
sb.AppendLine(await Program.RUST_SERVICE.ReadArbitraryFileData(file, int.MaxValue));
|
||||
sb.AppendLine(await Program.RUST_SERVICE.ReadArbitraryFileData(document.FilePath, int.MaxValue));
|
||||
sb.AppendLine("````");
|
||||
}
|
||||
}
|
||||
|
||||
71
app/MindWork AI Studio/Chat/FileAttachment.cs
Normal file
71
app/MindWork AI Studio/Chat/FileAttachment.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using AIStudio.Tools.Rust;
|
||||
|
||||
namespace AIStudio.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an immutable file attachment with details about its type, name, path, and size.
|
||||
/// </summary>
|
||||
/// <param name="Type">The type of the file attachment.</param>
|
||||
/// <param name="FileName">The name of the file, including extension.</param>
|
||||
/// <param name="FilePath">The full path to the file, including the filename and extension.</param>
|
||||
/// <param name="FileSizeBytes">The size of the file in bytes.</param>
|
||||
public readonly record struct FileAttachment(FileAttachmentType Type, string FileName, string FilePath, long FileSizeBytes)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file still exists on the file system.
|
||||
/// </summary>
|
||||
public bool Exists => File.Exists(this.FilePath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file type is forbidden and should not be attached.
|
||||
/// </summary>
|
||||
public bool IsForbidden => this.Type == FileAttachmentType.FORBIDDEN;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file type is valid and allowed to be attached.
|
||||
/// </summary>
|
||||
public bool IsValid => this.Type != FileAttachmentType.FORBIDDEN;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a FileAttachment from a file path by automatically determining the type,
|
||||
/// extracting the filename, and reading the file size.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The full path to the file.</param>
|
||||
/// <returns>A FileAttachment instance with populated properties.</returns>
|
||||
public static FileAttachment FromPath(string filePath)
|
||||
{
|
||||
var fileName = Path.GetFileName(filePath);
|
||||
var fileSize = File.Exists(filePath) ? new FileInfo(filePath).Length : 0;
|
||||
var type = DetermineFileType(filePath);
|
||||
|
||||
return new FileAttachment(type, fileName, filePath, fileSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the file attachment type based on the file extension.
|
||||
/// Uses centrally defined file type filters from <see cref="FileTypeFilter"/>.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file path to analyze.</param>
|
||||
/// <returns>The corresponding FileAttachmentType.</returns>
|
||||
private static FileAttachmentType DetermineFileType(string filePath)
|
||||
{
|
||||
var extension = Path.GetExtension(filePath).TrimStart('.').ToLowerInvariant();
|
||||
|
||||
// Check if it's an image file:
|
||||
if (FileTypeFilter.AllImages.FilterExtensions.Contains(extension))
|
||||
return FileAttachmentType.IMAGE;
|
||||
|
||||
// Check if it's an audio file:
|
||||
if (FileTypeFilter.AllAudio.FilterExtensions.Contains(extension))
|
||||
return FileAttachmentType.AUDIO;
|
||||
|
||||
// Check if it's an allowed document file (PDF, Text, or Office):
|
||||
if (FileTypeFilter.PDF.FilterExtensions.Contains(extension) ||
|
||||
FileTypeFilter.Text.FilterExtensions.Contains(extension) ||
|
||||
FileTypeFilter.AllOffice.FilterExtensions.Contains(extension))
|
||||
return FileAttachmentType.DOCUMENT;
|
||||
|
||||
// All other file types are forbidden:
|
||||
return FileAttachmentType.FORBIDDEN;
|
||||
}
|
||||
}
|
||||
27
app/MindWork AI Studio/Chat/FileAttachmentType.cs
Normal file
27
app/MindWork AI Studio/Chat/FileAttachmentType.cs
Normal file
@ -0,0 +1,27 @@
|
||||
namespace AIStudio.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// Represents different types of file attachments.
|
||||
/// </summary>
|
||||
public enum FileAttachmentType
|
||||
{
|
||||
/// <summary>
|
||||
/// Document file types, such as .pdf, .docx, .txt, etc.
|
||||
/// </summary>
|
||||
DOCUMENT,
|
||||
|
||||
/// <summary>
|
||||
/// All image file types, such as .jpg, .png, .gif, etc.
|
||||
/// </summary>
|
||||
IMAGE,
|
||||
|
||||
/// <summary>
|
||||
/// All audio file types, such as .mp3, .wav, .aac, etc.
|
||||
/// </summary>
|
||||
AUDIO,
|
||||
|
||||
/// <summary>
|
||||
/// Forbidden file types that should not be attached, such as executables.
|
||||
/// </summary>
|
||||
FORBIDDEN,
|
||||
}
|
||||
@ -50,10 +50,10 @@ public interface IContent
|
||||
|
||||
/// <summary>
|
||||
/// Represents a collection of file attachments associated with the content.
|
||||
/// This property contains a list of file paths that are appended
|
||||
/// This property contains a list of file attachments that are appended
|
||||
/// to the content to provide additional context or resources.
|
||||
/// </summary>
|
||||
public List<string> FileAttachments { get; set; }
|
||||
public List<FileAttachment> FileAttachments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Uses the provider to create the content.
|
||||
|
||||
@ -68,9 +68,9 @@ else
|
||||
</MudStack>
|
||||
<div @onmouseenter="@this.OnMouseEnter" @onmouseleave="@this.OnMouseLeave">
|
||||
<MudPaper Height="20em" Outlined="true" Class="@this.dragClass" Style="overflow-y: auto;">
|
||||
@foreach (var filePath in this.DocumentPaths)
|
||||
@foreach (var fileAttachment in this.DocumentPaths)
|
||||
{
|
||||
<MudChip T="string" Color="Color.Dark" Text="@Path.GetFileName(filePath)" tabindex="-1" Icon="@Icons.Material.Filled.Search" OnClick="@(() => this.InvestigateFile(filePath))" OnClose="@(() => this.RemoveDocument(filePath))"/>
|
||||
<MudChip T="string" Color="Color.Dark" Text="@fileAttachment.FileName" tabindex="-1" Icon="@Icons.Material.Filled.Search" OnClick="@(() => this.InvestigateFile(fileAttachment))" OnClose="@(() => this.RemoveDocument(fileAttachment))"/>
|
||||
}
|
||||
</MudPaper>
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Rust;
|
||||
@ -16,15 +17,15 @@ public partial class AttachDocuments : MSGComponentBase
|
||||
|
||||
[Parameter]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
|
||||
[Parameter]
|
||||
public HashSet<string> DocumentPaths { get; set; } = [];
|
||||
|
||||
public HashSet<FileAttachment> DocumentPaths { get; set; } = [];
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<HashSet<string>> DocumentPathsChanged { get; set; }
|
||||
|
||||
public EventCallback<HashSet<FileAttachment>> DocumentPathsChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<HashSet<string>, Task> OnChange { get; set; } = _ => Task.CompletedTask;
|
||||
public Func<HashSet<FileAttachment>, Task> OnChange { get; set; } = _ => Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Catch all documents that are hovered over the AI Studio window and not only over the drop zone.
|
||||
@ -116,7 +117,7 @@ public partial class AttachDocuments : MSGComponentBase
|
||||
if(!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(path))
|
||||
continue;
|
||||
|
||||
this.DocumentPaths.Add(path);
|
||||
this.DocumentPaths.Add(FileAttachment.FromPath(path));
|
||||
}
|
||||
|
||||
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
|
||||
@ -160,7 +161,7 @@ public partial class AttachDocuments : MSGComponentBase
|
||||
if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(selectedFilePath))
|
||||
continue;
|
||||
|
||||
this.DocumentPaths.Add(selectedFilePath);
|
||||
this.DocumentPaths.Add(FileAttachment.FromPath(selectedFilePath));
|
||||
}
|
||||
|
||||
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
|
||||
@ -199,23 +200,23 @@ public partial class AttachDocuments : MSGComponentBase
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task RemoveDocument(string filePath)
|
||||
private async Task RemoveDocument(FileAttachment fileAttachment)
|
||||
{
|
||||
this.DocumentPaths.Remove(filePath);
|
||||
|
||||
this.DocumentPaths.Remove(fileAttachment);
|
||||
|
||||
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
|
||||
await this.OnChange(this.DocumentPaths);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The user might want to check what we actually extract from his file and therefore give the LLM as an input.
|
||||
/// The user might want to check what we actually extract from his file and therefore give the LLM as an input.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file to check.</param>
|
||||
private async Task InvestigateFile(string filePath)
|
||||
/// <param name="fileAttachment">The file to check.</param>
|
||||
private async Task InvestigateFile(FileAttachment fileAttachment)
|
||||
{
|
||||
var dialogParameters = new DialogParameters<DocumentCheckDialog>
|
||||
{
|
||||
{ x => x.FilePath, filePath },
|
||||
{ x => x.FilePath, fileAttachment.FilePath },
|
||||
};
|
||||
|
||||
await this.DialogService.ShowAsync<DocumentCheckDialog>(T("Document Preview"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@ -57,7 +57,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
private string currentWorkspaceName = string.Empty;
|
||||
private Guid currentWorkspaceId = Guid.Empty;
|
||||
private CancellationTokenSource? cancellationTokenSource;
|
||||
private HashSet<string> chatDocumentPaths = [];
|
||||
private HashSet<FileAttachment> chatDocumentPaths = [];
|
||||
|
||||
// Unfortunately, we need the input field reference to blur the focus away. Without
|
||||
// this, we cannot clear the input field.
|
||||
@ -464,7 +464,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
lastUserPrompt = new ContentText
|
||||
{
|
||||
Text = this.userInput,
|
||||
FileAttachments = this.chatDocumentPaths.ToList(),
|
||||
FileAttachments = [..this.chatDocumentPaths.Where(x => x.IsValid)],
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
@ -18,9 +18,9 @@
|
||||
|
||||
@{
|
||||
var currentFolder = string.Empty;
|
||||
foreach (var filePath in this.DocumentPaths)
|
||||
foreach (var fileAttachment in this.DocumentPaths)
|
||||
{
|
||||
var folderPath = Path.GetDirectoryName(filePath);
|
||||
var folderPath = Path.GetDirectoryName(fileAttachment.FilePath);
|
||||
if (folderPath != currentFolder)
|
||||
{
|
||||
currentFolder = folderPath;
|
||||
@ -31,8 +31,8 @@
|
||||
</MudText>
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
@if (File.Exists(filePath))
|
||||
|
||||
@if (fileAttachment.Exists)
|
||||
{
|
||||
<MudStack Row Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="ms-3 mb-2">
|
||||
<div style="min-width: 0; flex: 1; overflow: hidden;">
|
||||
@ -40,7 +40,7 @@
|
||||
<span class="d-inline-flex align-items-center" style="overflow: hidden; width: 100%;">
|
||||
<MudIcon Icon="@Icons.Material.Filled.AttachFile" Class="mr-2" Style="flex-shrink: 0;"/>
|
||||
<MudText Style="white-space: nowrap;">
|
||||
@Path.GetFileName(filePath)
|
||||
@fileAttachment.FileName
|
||||
</MudText>
|
||||
</span>
|
||||
</MudTooltip>
|
||||
@ -51,7 +51,7 @@
|
||||
Color="Color.Error"
|
||||
Class="ml-2"
|
||||
Style="flex-shrink: 0;"
|
||||
OnClick="@(() => this.DeleteAttachment(filePath))"/>
|
||||
OnClick="@(() => this.DeleteAttachment(fileAttachment))"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
<span class="d-inline-flex align-items-center" style="overflow: hidden; width: 100%;">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Report" Color="Color.Error" Class="mr-2" Style="flex-shrink: 0;"/>
|
||||
<MudText Style="white-space: nowrap;">
|
||||
<s>@Path.GetFileName(filePath)</s>
|
||||
<s>@fileAttachment.FileName</s>
|
||||
</MudText>
|
||||
</span>
|
||||
</MudTooltip>
|
||||
@ -75,7 +75,7 @@
|
||||
Color="Color.Error"
|
||||
Class="ml-2"
|
||||
Style="flex-shrink: 0;"
|
||||
OnClick="@(() => this.DeleteAttachment(filePath))"/>
|
||||
OnClick="@(() => this.DeleteAttachment(fileAttachment))"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
@ -13,20 +14,20 @@ public partial class ReviewAttachmentsDialog : MSGComponentBase
|
||||
private IMudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public HashSet<string> DocumentPaths { get; set; } = new();
|
||||
|
||||
public HashSet<FileAttachment> DocumentPaths { get; set; } = new();
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; set; } = null!;
|
||||
|
||||
|
||||
private void Close() => this.MudDialog.Close(DialogResult.Ok(this.DocumentPaths));
|
||||
|
||||
public static async Task<HashSet<string>> OpenDialogAsync(IDialogService dialogService, params HashSet<string> documentPaths)
|
||||
|
||||
public static async Task<HashSet<FileAttachment>> OpenDialogAsync(IDialogService dialogService, params HashSet<FileAttachment> documentPaths)
|
||||
{
|
||||
var dialogParameters = new DialogParameters<ReviewAttachmentsDialog>
|
||||
{
|
||||
{ x => x.DocumentPaths, documentPaths }
|
||||
{ x => x.DocumentPaths, documentPaths }
|
||||
};
|
||||
|
||||
|
||||
var dialogReference = await dialogService.ShowAsync<ReviewAttachmentsDialog>(TB("Your attached files"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
var dialogResult = await dialogReference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
@ -34,13 +35,13 @@ public partial class ReviewAttachmentsDialog : MSGComponentBase
|
||||
|
||||
if (dialogResult.Data is null)
|
||||
return documentPaths;
|
||||
|
||||
return dialogResult.Data as HashSet<string> ?? documentPaths;
|
||||
|
||||
return dialogResult.Data as HashSet<FileAttachment> ?? documentPaths;
|
||||
}
|
||||
|
||||
private void DeleteAttachment(string filePath)
|
||||
private void DeleteAttachment(FileAttachment fileAttachment)
|
||||
{
|
||||
if (this.DocumentPaths.Remove(filePath))
|
||||
if (this.DocumentPaths.Remove(fileAttachment))
|
||||
{
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ public sealed class ProviderAlibabaCloud() : BaseProvider("https://dashscope-int
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -44,7 +44,7 @@ public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.co
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -54,7 +54,7 @@ public sealed class ProviderDeepSeek() : BaseProvider("https://api.deepseek.com/
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -54,7 +54,7 @@ public class ProviderFireworks() : BaseProvider("https://api.fireworks.ai/infere
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -54,7 +54,7 @@ public sealed class ProviderGWDG() : BaseProvider("https://chat-ai.academiccloud
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -54,7 +54,7 @@ public class ProviderGoogle() : BaseProvider("https://generativelanguage.googlea
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -54,7 +54,7 @@ public class ProviderGroq() : BaseProvider("https://api.groq.com/openai/v1/", LO
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -54,7 +54,7 @@ public sealed class ProviderHelmholtz() : BaseProvider("https://api.helmholtz-bl
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -59,7 +59,7 @@ public sealed class ProviderHuggingFace : BaseProvider
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -52,7 +52,7 @@ public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -104,7 +104,7 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -57,7 +57,7 @@ public sealed class ProviderOpenRouter() : BaseProvider("https://openrouter.ai/a
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -63,7 +63,7 @@ public sealed class ProviderPerplexity() : BaseProvider("https://api.perplexity.
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -50,7 +50,7 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
@ -54,7 +54,7 @@ public sealed class ProviderX() : BaseProvider("https://api.x.ai/v1/", LOGGER)
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => await text.PrepareContentForAI(),
|
||||
ContentText text => await text.PrepareTextContentForAI(),
|
||||
_ => string.Empty,
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user