Fixed PDFs and other formats requiring Pandoc although they do not use it

This commit is contained in:
Thorsten Sommer 2026-08-10 09:09:37 +02:00
parent cf4661d1ad
commit 2cc27859ce
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
3 changed files with 103 additions and 63 deletions

View File

@ -5,6 +5,7 @@ using AIStudio.Provider;
using AIStudio.Settings; using AIStudio.Settings;
using AIStudio.Tools.PluginSystem; using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.RAG.RAGProcesses; using AIStudio.Tools.RAG.RAGProcesses;
using AIStudio.Tools.Rust;
namespace AIStudio.Chat; namespace AIStudio.Chat;
@ -282,15 +283,23 @@ public sealed class ContentText : IContent
// Only proceed if there are existing, allowed documents: // Only proceed if there are existing, allowed documents:
if (existingDocuments.Count > 0) if (existingDocuments.Count > 0)
{ {
// Check Pandoc availability once before processing file attachments //
// Pandoc is only needed for the few formats we convert with it. PDFs, text files,
// spreadsheets, and presentations are read by the runtime itself, so a missing
// Pandoc installation must not stop them.
//
var pandocIsUsable = true;
if (existingDocuments.Any(document => FileTypes.RequiresPandoc(document.FilePath)))
{
var pandocState = await Pandoc.CheckAvailabilityAsync(Program.RUST_SERVICE, showMessages: true, showSuccessMessage: false); var pandocState = await Pandoc.CheckAvailabilityAsync(Program.RUST_SERVICE, showMessages: true, showSuccessMessage: false);
pandocIsUsable = pandocState is { IsAvailable: true, CheckWasSuccessful: true };
if (!pandocState.IsAvailable) if (!pandocState.IsAvailable)
LOGGER.LogWarning("File attachments could not be processed because Pandoc is not available."); LOGGER.LogWarning("File attachments which need Pandoc could not be processed because Pandoc is not available.");
else if (!pandocState.CheckWasSuccessful) else if (!pandocState.CheckWasSuccessful)
LOGGER.LogWarning("File attachments could not be processed because the Pandoc version check failed."); LOGGER.LogWarning("File attachments which need Pandoc could not be processed because the Pandoc version check failed.");
else }
{
// //
// The document blocks are collected separately, so we only announce attached // The document blocks are collected separately, so we only announce attached
// files when at least one of them could actually be read. Announcing files we // files when at least one of them could actually be read. Announcing files we
@ -305,6 +314,13 @@ public sealed class ContentText : IContent
continue; continue;
} }
if (!pandocIsUsable && FileTypes.RequiresPandoc(document.FilePath))
{
LOGGER.LogWarning("The file attachment '{FilePath}' needs Pandoc and will be skipped.", document.FilePath);
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Description, string.Format(TB("The file '{0}' needs Pandoc to be read and was not sent."), document.FileName)));
continue;
}
var extraction = await Program.RUST_SERVICE.ReadArbitraryFileData(document.FilePath, int.MaxValue); var extraction = await Program.RUST_SERVICE.ReadArbitraryFileData(document.FilePath, int.MaxValue);
if (!extraction.HasUsableContent) if (!extraction.HasUsableContent)
{ {
@ -348,7 +364,6 @@ public sealed class ContentText : IContent
} }
} }
} }
}
return sb.ToString(); return sb.ToString();
} }

View File

@ -443,19 +443,27 @@ public partial class AttachDocuments : MSGComponentBase
var mediaPaths = existingPaths.Where(IsTranscribableMedia).ToList(); var mediaPaths = existingPaths.Where(IsTranscribableMedia).ToList();
var regularPaths = existingPaths.Except(mediaPaths).ToList(); var regularPaths = existingPaths.Except(mediaPaths).ToList();
var canAddRegularFiles = true; //
if (regularPaths.Count > 0) // Only the formats we convert with Pandoc depend on a Pandoc installation. Everything
// else, PDFs in particular, is read by the Rust runtime itself, so those files must stay
// attachable without Pandoc.
//
var canAddPandocFiles = true;
if (regularPaths.Any(FileTypes.RequiresPandoc))
{ {
var pandocState = await this.PandocAvailabilityService.EnsureAvailabilityAsync( var pandocState = await this.PandocAvailabilityService.EnsureAvailabilityAsync(
showSuccessMessage: false, showSuccessMessage: false,
showDialog: true); showDialog: true);
canAddRegularFiles = pandocState.IsAvailable; canAddPandocFiles = pandocState.IsAvailable;
} }
foreach (var path in regularPaths) foreach (var path in regularPaths)
{ {
if (!canAddRegularFiles) if (!canAddPandocFiles && FileTypes.RequiresPandoc(path))
break; {
this.Logger.LogWarning("The file '{Path}' needs Pandoc and was not attached.", path);
continue;
}
if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, path, this.ValidateMediaFileTypes, this.Provider)) if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, path, this.ValidateMediaFileTypes, this.Provider))
continue; continue;

View File

@ -84,6 +84,23 @@ public static class FileTypes
public static readonly FileTypeFilter EXECUTABLES = FileTypeFilter.Leaf(TB("Executable"), "exe", "app", "bin", "appimage"); public static readonly FileTypeFilter EXECUTABLES = FileTypeFilter.Leaf(TB("Executable"), "exe", "app", "bin", "appimage");
public static readonly FileTypeFilter PLUGIN_ARCHIVE = FileTypeFilter.Leaf(TB("Plugin archive"), PluginArchive.PLUGIN_FILE_EXTENSION.TrimStart('.'), "zip"); public static readonly FileTypeFilter PLUGIN_ARCHIVE = FileTypeFilter.Leaf(TB("Plugin archive"), PluginArchive.PLUGIN_FILE_EXTENSION.TrimStart('.'), "zip");
/// <summary>
/// The file types AI Studio converts using Pandoc.
/// </summary>
/// <remarks>
/// This is not a user-selectable type, it mirrors the formats the Rust runtime hands to
/// Pandoc. Every other document type is read by the runtime itself, so it must never depend
/// on a Pandoc installation. The name is not localized because it is never shown.
/// </remarks>
private static readonly FileTypeFilter PANDOC_CONVERTED = FileTypeFilter.Leaf("Pandoc conversion", "docx", "odt", "html", "htm");
/// <summary>
/// Determines whether reading the given file needs Pandoc.
/// </summary>
/// <param name="filePath">The path of the file to check.</param>
/// <returns>True, when reading the file needs Pandoc.</returns>
public static bool RequiresPandoc(string filePath) => IsAllowedPath(filePath, PANDOC_CONVERTED);
public static FileTypeFilter? AsOneFileType(params FileTypeFilter[]? types) public static FileTypeFilter? AsOneFileType(params FileTypeFilter[]? types)
{ {
if (types == null || types.Length == 0) if (types == null || types.Length == 0)