PhoenixKitAgent.Attachments.Extract (PhoenixKitAgent v0.1.0)

Copy Markdown View Source

Turns the bytes of an attachment into what the model reads (spec §9): the text of a text file, a CSV file or a PDF; CSV text of a workbook; JPEG or PNG pictures of an image or of the first pages of a scan.

Every function takes the path of a file already read from Storage and answers {:ok, result} or {:error, code}; the codes are the ones the attachment row keeps in error (unreadable, too_large, pdf_tools_missing, image_tools_missing) and say nothing of the content. A file that makes a parser raise is unreadable; what the parser said is dropped, as it can quote the file.

What is not shown

A text that holds less than the file is truncated, and says so where it ends, in a line the model reads with it (continues/1): [... the file continues: 12 more rows not shown in full], [... the sheet continues: 1240 of 1300 rows not shown in full], [... the workbook continues: 2 more sheets not shown: Q3, Q4], [... the document continues: 38 of 50 pages not shown in full] — so that a total or a count is not taken from part of the data without a word. These lines are not counted in text_chars.

A workbook's sheets share text_chars: a sheet that needs less than an equal share leaves the rest to the others, and each is cut between rows, so one large sheet does not push the others out.

Summary

Types

The text of a PDF, with how many pages it has.

Text for the model, cut to the limit; truncated when the file had more.

Functions

The first rows of a CSV file, as written in it (any delimiter).

The text of a PDF (pdftotext) with its page count. {:ok, {:scan, pages}} when it holds (almost) no text: a scan, to be read as pictures.

The first pages of a PDF as PNG binaries (at most scan_pages, 1600 px on the long side) and the page count.

A picture as a JPEG whose long side is at most 1600 px (never enlarged), upright, on white where it was transparent, without its camera metadata (EXIF with the place and time of a photo, XMP, IPTC, comments: PhoenixKitAgent.Attachments.Jpeg.strip_metadata/1) — what goes to a model provider is the picture, not where it was taken.

The start of a text file as text (a byte order mark and NUL bytes dropped). A byte that is not UTF-8 shows as U+FFFD, it is not silently left out.

A workbook as CSV: for each sheet (hidden ones left out) a ## Name line and its first rows, blank rows skipped, each sheet within its share of text_chars (see the moduledoc). The parts of the workbook are inflated by PhoenixKitAgent.Attachments.Unzip, which counts the bytes it produces and does not believe the sizes the file declares: one that unpacks to more than the limit is :too_large, however it was forged, and is not opened.

Types

pdf_text()

@type pdf_text() :: %{text: String.t(), pages: pos_integer(), truncated: boolean()}

The text of a PDF, with how many pages it has.

text()

@type text() :: %{text: String.t(), truncated: boolean()}

Text for the model, cut to the limit; truncated when the file had more.

Functions

csv(path)

@spec csv(Path.t()) :: {:ok, text()} | {:error, :unreadable}

The first rows of a CSV file, as written in it (any delimiter).

pdf(path)

@spec pdf(Path.t()) ::
  {:ok, pdf_text()}
  | {:ok, {:scan, pos_integer()}}
  | {:error, :unreadable | :pdf_tools_missing}

The text of a PDF (pdftotext) with its page count. {:ok, {:scan, pages}} when it holds (almost) no text: a scan, to be read as pictures.

pdf_pages(path)

@spec pdf_pages(Path.t()) ::
  {:ok, [binary()], pos_integer()} | {:error, :unreadable | :pdf_tools_missing}

The first pages of a PDF as PNG binaries (at most scan_pages, 1600 px on the long side) and the page count.

picture(path)

@spec picture(Path.t()) ::
  {:ok, binary()} | {:error, :unreadable | :image_tools_missing}

A picture as a JPEG whose long side is at most 1600 px (never enlarged), upright, on white where it was transparent, without its camera metadata (EXIF with the place and time of a photo, XMP, IPTC, comments: PhoenixKitAgent.Attachments.Jpeg.strip_metadata/1) — what goes to a model provider is the picture, not where it was taken.

text(path)

@spec text(Path.t()) :: {:ok, text()} | {:error, :unreadable}

The start of a text file as text (a byte order mark and NUL bytes dropped). A byte that is not UTF-8 shows as U+FFFD, it is not silently left out.

xlsx(path)

@spec xlsx(Path.t()) :: {:ok, text()} | {:error, :unreadable | :too_large}

A workbook as CSV: for each sheet (hidden ones left out) a ## Name line and its first rows, blank rows skipped, each sheet within its share of text_chars (see the moduledoc). The parts of the workbook are inflated by PhoenixKitAgent.Attachments.Unzip, which counts the bytes it produces and does not believe the sizes the file declares: one that unpacks to more than the limit is :too_large, however it was forged, and is not opened.