Allow images for attachments when provider supports it

This commit is contained in:
Thorsten Sommer 2025-12-28 18:55:45 +01:00
parent 6cac072ffd
commit e775d65ae3
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
5 changed files with 63 additions and 11 deletions

View File

@ -103,7 +103,7 @@ else
@T("Documents for the analysis") @T("Documents for the analysis")
</MudText> </MudText>
<AttachDocuments Name="Document Analysis Files" @bind-DocumentPaths="@this.loadedDocumentPaths" CatchAllDocuments="true" UseSmallForm="false"/> <AttachDocuments Name="Document Analysis Files" @bind-DocumentPaths="@this.loadedDocumentPaths" CatchAllDocuments="true" UseSmallForm="false" Provider="@this.providerSettings"/>
</ExpansionPanel> </ExpansionPanel>
</MudExpansionPanels> </MudExpansionPanels>

View File

@ -36,6 +36,9 @@ public partial class AttachDocuments : MSGComponentBase
[Parameter] [Parameter]
public bool UseSmallForm { get; set; } public bool UseSmallForm { get; set; }
[Parameter]
public AIStudio.Settings.Provider? Provider { get; set; }
[Inject] [Inject]
private ILogger<AttachDocuments> Logger { get; set; } = null!; private ILogger<AttachDocuments> Logger { get; set; } = null!;
@ -114,7 +117,7 @@ public partial class AttachDocuments : MSGComponentBase
foreach (var path in paths) foreach (var path in paths)
{ {
if(!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(path)) if(!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, path, this.Provider))
continue; continue;
this.DocumentPaths.Add(FileAttachment.FromPath(path)); this.DocumentPaths.Add(FileAttachment.FromPath(path));
@ -158,7 +161,7 @@ public partial class AttachDocuments : MSGComponentBase
if (!File.Exists(selectedFilePath)) if (!File.Exists(selectedFilePath))
continue; continue;
if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(selectedFilePath)) if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, selectedFilePath, this.Provider))
continue; continue;
this.DocumentPaths.Add(FileAttachment.FromPath(selectedFilePath)); this.DocumentPaths.Add(FileAttachment.FromPath(selectedFilePath));

View File

@ -83,7 +83,7 @@
<ChatTemplateSelection CanChatThreadBeUsedForTemplate="@this.CanThreadBeSaved" CurrentChatThread="@this.ChatThread" CurrentChatTemplate="@this.currentChatTemplate" CurrentChatTemplateChanged="@this.ChatTemplateWasChanged"/> <ChatTemplateSelection CanChatThreadBeUsedForTemplate="@this.CanThreadBeSaved" CurrentChatThread="@this.ChatThread" CurrentChatTemplate="@this.currentChatTemplate" CurrentChatTemplateChanged="@this.ChatTemplateWasChanged"/>
<AttachDocuments Name="File Attachments" @bind-DocumentPaths="@this.chatDocumentPaths" CatchAllDocuments="true" UseSmallForm="true"/> <AttachDocuments Name="File Attachments" @bind-DocumentPaths="@this.chatDocumentPaths" CatchAllDocuments="true" UseSmallForm="true" Provider="@this.Provider"/>
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY) @if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
{ {

View File

@ -55,7 +55,7 @@ public partial class ReadFileContent : MSGComponentBase
return; 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); this.Logger.LogWarning("User attempted to load unsupported file: {FilePath}", selectedFile.SelectedFilePath);
return; return;

View File

@ -1,3 +1,5 @@
using AIStudio.Provider;
using AIStudio.Settings;
using AIStudio.Tools.PluginSystem; using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Rust; 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)); private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(FileExtensionValidation).Namespace, nameof(FileExtensionValidation));
/// <summary>
/// Defines the use cases for file extension validation.
/// </summary>
public enum UseCase
{
/// <summary>
/// No specific use case; general validation.
/// </summary>
NONE,
/// <summary>
/// Validating for directly loading content into the UI. In this state, there might be no provider selected yet.
/// </summary>
DIRECTLY_LOADING_CONTENT,
/// <summary>
/// Validating for attaching content to a message or prompt.
/// </summary>
ATTACHING_CONTENT,
}
/// <summary> /// <summary>
/// Validates the file extension and sends appropriate MessageBus notifications when invalid. /// Validates the file extension and sends appropriate MessageBus notifications when invalid.
/// </summary> /// </summary>
/// <param name="useCae">The validation use case.</param>
/// <param name="filePath">The file path to validate.</param> /// <param name="filePath">The file path to validate.</param>
/// <param name="provider">The selected provider.</param>
/// <returns>True if valid, false if invalid (error/warning already sent via MessageBus).</returns> /// <returns>True if valid, false if invalid (error/warning already sent via MessageBus).</returns>
public static async Task<bool> IsExtensionValidWithNotifyAsync(string filePath) public static async Task<bool> IsExtensionValidWithNotifyAsync(UseCase useCae, string filePath, Settings.Provider? provider = null)
{ {
var ext = Path.GetExtension(filePath).TrimStart('.').ToLowerInvariant(); var ext = Path.GetExtension(filePath).TrimStart('.').ToLowerInvariant();
if(FileTypeFilter.Executables.FilterExtensions.Contains(ext)) if(FileTypeFilter.Executables.FilterExtensions.Contains(ext))
@ -26,12 +51,36 @@ public static class FileExtensionValidation
return false; return false;
} }
if(FileTypeFilter.AllImages.FilterExtensions.Contains(ext)) var capabilities = provider?.GetModelCapabilities() ?? new();
if (FileTypeFilter.AllImages.FilterExtensions.Contains(ext))
{ {
await MessageBus.INSTANCE.SendWarning(new( switch (useCae)
Icons.Material.Filled.ImageNotSupported, {
TB("Images are not supported yet"))); // In this use case, we cannot guarantee that a provider is selected yet:
return false; 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)) if(FileTypeFilter.AllVideos.FilterExtensions.Contains(ext))