Convert

Convert#

The convert tool reformats files offline using system converters, falling back gracefully when a specific one is unavailable. Choose the destination format by the output_path extension (.png, .jpg, .txt, .md), and pass dry_run to see the converter plan without executing. Image-to-image output paths are literal filenames: asset%03d.jpg writes that exact name, not a numbered image sequence. It has no markdown-fence block type, so ask for it in natural language and let the model call it with structured arguments.

Conversions stage their single output beside the destination and replace the requested file only after success. A failed conversion leaves an existing file unchanged and does not publish a partial new file. Existing regular-file symlinks are followed without replacing the link; the requested extension still selects the format. Existing permission bits are retained, but replacement creates a new inode (other hardlinks keep the old contents); ownership, ACLs and extended attributes are not preserved. The destination directory must be writable even when the old output file itself is writable. This is not crash durability or protection against concurrent pathname changes. A cleanup problem after successful publication is reported as a warning, not as failed conversion. Custom Converter subclasses can implement _convert to use this staging boundary. Existing subclasses that override convert remain supported and retain their own publication behavior; overriding convert bypasses staging.

For example:

gptme "convert report.pdf to report-page-1.png at high quality, dry run first"

When converting images to PNG or JPEG (.png, .jpg, .jpeg), animated input produces its first frame at the exact requested output path, not a numbered image sequence. Animation-capable GIF and WebP targets are not subject to this first-frame selection.

TIFF-to-TIFF conversion preserves document pages in their original order using ImageMagick. If a TIFF has multiple pages and ImageMagick is unavailable, the conversion fails explicitly without writing the destination; FFmpeg alone cannot preserve these pages. Single-page TIFF conversions can still use FFmpeg.

Image and video sources passed to FFmpeg are local filesystem paths, not protocol URLs. A relative filename such as file:photo.png selects that literal file, not a different file named photo.png.

Percent-bearing image source paths are literal too: scan%03d.png reads that file, not scan001.png. For FFmpeg, these inputs are copied into a private temporary directory under a safe basename, preserving normal content probing (including animated PNG and GIF). This uses additional temporary disk space equal to the source size; the copy is removed after conversion.

File format conversion tool for gptme.

Provides offline, multi-format file conversion with auto-detection and graceful degradation when specific converters are unavailable.

Supported conversions:

  • PDF → PNG/JPEG (Poppler/pdftoppm, ImageMagick fallback)

  • Image → Image: PNG ↔ JPEG ↔ WebP (FFmpeg primary, ImageMagick fallback). PNG/JPEG output selects the first frame of animated input.

  • DOCX → text/markdown (python-docx, LibreOffice headless fallback)

  • MP4/video → JPEG thumbnail (FFmpeg)

  • PDF → text (pypdf, Poppler/pdftotext fallback)

class gptme.tools.convert.ConversionResult

ConversionResult(success: ‘bool’, output_path: ‘Path | None’, converter_used: ‘str’, warnings: ‘list[str]’ = <factory>, error: ‘str | None’ = None, lossy: ‘bool’ = False, metadata: ‘dict’ = <factory>)

__init__(success: bool, output_path: ~pathlib.Path | None, converter_used: str, warnings: list[str] = <factory>, error: str | None = None, lossy: bool = False, metadata: dict = <factory>) → None
class gptme.tools.convert.Converter
convert(src: Path, dest: Path, quality: str = 'medium', **kwargs) → ConversionResult

Publish a completed single output, never a backend’s partial writes.

Resolve the destination separately from its staging filename: existing symlinks keep pointing to their target, while the requested extension still selects the output format. Replacement is on the same filesystem. This is not a durability or concurrent-path-mutation guarantee.

class gptme.tools.convert.DocumentToTextConverter

DOCX/ODT/etc. → plain text or markdown via python-docx or LibreOffice.

class gptme.tools.convert.ImageConverter

Image ↔ image format conversion via FFmpeg (primary) or ImageMagick.

gptme.tools.convert.MAX_DOCX_EXPANDED_BYTES = 16777216

Maximum total expanded package size before document conversion refuses it. A DOCX is a zip, so a small attachment can expand into a large working set: measured 2026-11-05, 110 KB of input declaring 25 MB of body XML cost 421 MB peak RSS and 9.2 s, while an ordinary 37 KB document cost 44 MB and 0.09 s. The bound is on expanded work, because input bytes do not predict it: the same expanded XML compressed into 46 KB or stored as 4 MB costs the same. Use the whole package, not a fixed body path: DOCX relationships can relocate the body, and backends also load styles, headers, media and other parts. This deliberately counts embedded media too; a small body is not proof that the package is cheap to load. Non-ZIP documents retain backend-owned handling.

class gptme.tools.convert.PDFToImageConverter

PDF first page → one raster image (PNG/JPEG) via pdftoppm or ImageMagick.

class gptme.tools.convert.PDFToTextConverter

PDF → plain text via pypdf or pdftotext (Poppler).

class gptme.tools.convert.ToolAvailability

ToolAvailability(ffmpeg: ‘bool’ = <factory>, imagemagick: ‘bool’ = <factory>, pdftoppm: ‘bool’ = <factory>, pdftotext: ‘bool’ = <factory>, libreoffice: ‘bool’ = <factory>, tesseract: ‘bool’ = <factory>, python_magic: ‘bool’ = <factory>, python_docx: ‘bool’ = <factory>, pypdf: ‘bool’ = <factory>)

__init__(ffmpeg: bool = <factory>, imagemagick: bool = <factory>, pdftoppm: bool = <factory>, pdftotext: bool = <factory>, libreoffice: bool = <factory>, tesseract: bool = <factory>, python_magic: bool = <factory>, python_docx: bool = <factory>, pypdf: bool = <factory>) → None
class gptme.tools.convert.VideoThumbnailConverter

Video → JPEG thumbnail via FFmpeg.

gptme.tools.convert.convert_file(src: Path, dest: Path, quality: str = 'medium', dry_run: bool = False, **kwargs) → ConversionResult

Main entry point for file conversion.

Parameters:
  • src – Source file path.

  • dest – Destination file path (extension determines target format).

  • quality – One of ‘low’, ‘medium’, ‘high’.

  • dry_run – If True, return converter plan without executing.

gptme.tools.convert.find_converter(src: Path, dest_ext: str, avail: ToolAvailability | None = None) → Converter | None

Select a converter for a regular source; raise ValueError for invalid input.