From e775d65ae3bd8e15924565283d4f9ff899fdbdc5 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 28 Dec 2025 18:55:45 +0100 Subject: [PATCH] Allow images for attachments when provider supports it --- .../DocumentAnalysisAssistant.razor | 2 +- .../Components/AttachDocuments.razor.cs | 7 ++- .../Components/ChatComponent.razor | 2 +- .../Components/ReadFileContent.razor.cs | 2 +- .../Validation/FileExtensionValidation.cs | 61 +++++++++++++++++-- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor index 4b68618a..ec801e84 100644 --- a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor +++ b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor @@ -103,7 +103,7 @@ else @T("Documents for the analysis") - + diff --git a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs index a647876e..bddcbe43 100644 --- a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs +++ b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs @@ -36,6 +36,9 @@ public partial class AttachDocuments : MSGComponentBase [Parameter] public bool UseSmallForm { get; set; } + [Parameter] + public AIStudio.Settings.Provider? Provider { get; set; } + [Inject] private ILogger Logger { get; set; } = null!; @@ -114,7 +117,7 @@ public partial class AttachDocuments : MSGComponentBase foreach (var path in paths) { - if(!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(path)) + if(!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, path, this.Provider)) continue; this.DocumentPaths.Add(FileAttachment.FromPath(path)); @@ -158,7 +161,7 @@ public partial class AttachDocuments : MSGComponentBase if (!File.Exists(selectedFilePath)) continue; - if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(selectedFilePath)) + if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, selectedFilePath, this.Provider)) continue; this.DocumentPaths.Add(FileAttachment.FromPath(selectedFilePath)); diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor b/app/MindWork AI Studio/Components/ChatComponent.razor index 8f4eca23..1eb28244 100644 --- a/app/MindWork AI Studio/Components/ChatComponent.razor +++ b/app/MindWork AI Studio/Components/ChatComponent.razor @@ -83,7 +83,7 @@ - + @if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY) { diff --git a/app/MindWork AI Studio/Components/ReadFileContent.razor.cs b/app/MindWork AI Studio/Components/ReadFileContent.razor.cs index cd970cd4..5db85e21 100644 --- a/app/MindWork AI Studio/Components/ReadFileContent.razor.cs +++ b/app/MindWork AI Studio/Components/ReadFileContent.razor.cs @@ -55,7 +55,7 @@ public partial class ReadFileContent : MSGComponentBase return; } - if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(selectedFile.SelectedFilePath)) + if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.DIRECTLY_LOADING_CONTENT, selectedFile.SelectedFilePath)) { this.Logger.LogWarning("User attempted to load unsupported file: {FilePath}", selectedFile.SelectedFilePath); return; diff --git a/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs b/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs index 8be34eca..991dcbdf 100644 --- a/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs +++ b/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs @@ -1,3 +1,5 @@ +using AIStudio.Provider; +using AIStudio.Settings; using AIStudio.Tools.PluginSystem; using AIStudio.Tools.Rust; @@ -10,12 +12,35 @@ public static class FileExtensionValidation { private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(FileExtensionValidation).Namespace, nameof(FileExtensionValidation)); + /// + /// Defines the use cases for file extension validation. + /// + public enum UseCase + { + /// + /// No specific use case; general validation. + /// + NONE, + + /// + /// Validating for directly loading content into the UI. In this state, there might be no provider selected yet. + /// + DIRECTLY_LOADING_CONTENT, + + /// + /// Validating for attaching content to a message or prompt. + /// + ATTACHING_CONTENT, + } + /// /// Validates the file extension and sends appropriate MessageBus notifications when invalid. /// + /// The validation use case. /// The file path to validate. + /// The selected provider. /// True if valid, false if invalid (error/warning already sent via MessageBus). - public static async Task IsExtensionValidWithNotifyAsync(string filePath) + public static async Task IsExtensionValidWithNotifyAsync(UseCase useCae, string filePath, Settings.Provider? provider = null) { var ext = Path.GetExtension(filePath).TrimStart('.').ToLowerInvariant(); if(FileTypeFilter.Executables.FilterExtensions.Contains(ext)) @@ -26,12 +51,36 @@ public static class FileExtensionValidation return false; } - if(FileTypeFilter.AllImages.FilterExtensions.Contains(ext)) + var capabilities = provider?.GetModelCapabilities() ?? new(); + if (FileTypeFilter.AllImages.FilterExtensions.Contains(ext)) { - await MessageBus.INSTANCE.SendWarning(new( - Icons.Material.Filled.ImageNotSupported, - TB("Images are not supported yet"))); - return false; + switch (useCae) + { + // In this use case, we cannot guarantee that a provider is selected yet: + case UseCase.DIRECTLY_LOADING_CONTENT: + await MessageBus.INSTANCE.SendWarning(new( + Icons.Material.Filled.ImageNotSupported, + TB("Images are not supported at this place"))); + return false; + + // In this use case, we can check the provider capabilities: + case UseCase.ATTACHING_CONTENT when capabilities.Contains(Capability.SINGLE_IMAGE_INPUT) || + capabilities.Contains(Capability.MULTIPLE_IMAGE_INPUT): + return true; + + // We know that images are not supported: + case UseCase.ATTACHING_CONTENT: + await MessageBus.INSTANCE.SendWarning(new( + Icons.Material.Filled.ImageNotSupported, + TB("Images are not supported by the selected provider and model"))); + return false; + + default: + await MessageBus.INSTANCE.SendWarning(new( + Icons.Material.Filled.ImageNotSupported, + TB("Images are not supported yet"))); + return false; + } } if(FileTypeFilter.AllVideos.FilterExtensions.Contains(ext))