Skip to main content
A regex finds the candidate values, TypeSafe picks the one the question asks for, and code copies it verbatim. By the end you will have a find and pick pair you can point at your own documents, plus three worked cases: the address a sender wants their receipt sent to, a phone number as +14155550177, and an invoice total as 1315.50 USD flagged as a charge. TypeSafe picks one of the options you hand it, so the candidates have to be found first. The recipe runs in three steps:
  1. A regex finds the candidate values in the text. Tune it to over-find.
  2. TypeSafe picks which candidate the question is asking for, and reads off any attribute the code needs downstream (currency, country, whether an amount is a credit or a charge).
  3. The code copies the picked value and normalizes it.
Because TypeSafe only ever chooses among the spans the regex found, the value you get back is a verbatim copy of one of them - nothing invented, no digits transposed. Overview diagram The regex finds candidate values in the document, TypeSafe picks one, and downstream code normalizes it and acts on it.

Setup

then set TYPESAFE_API_KEY.

Helpers

find runs a regex tuned to over-find and dedupes the matches. pick is a Choice whose options are the spans find returns, so its answer is one of those spans copied exactly, or none when no candidate fits. classify is a Choice over a fixed set of labels, used here for the currency and the country. is_true is a Noul, used here to ask whether an amount is a credit. Every call is cached to json_cache.json, so re-rendering makes no API calls.

Email: pick the right address by role

Four addresses in the headers. The body asks for the receipt to go to a personal address instead of the To: billing alias, so the answer depends on reading the body. Two questions here: which address gets the receipt, and which one sent the message.
receipt is the personal Gmail address on the Reply-To: line, which is what the body asks for; sender is the one on the From line. Both are copies of regex matches, lowercased in code.

Phone: pick the mobile, normalize to E.164

Three numbers, none of them carrying a country code. TypeSafe picks the mobile and reads the country from the text; phonenumbers combines those two answers into E.164, the international format that starts with a + and the country code.
Nothing in the digits says which number is the mobile or what country it is in - the words around them do. TypeSafe reads those words, and phonenumbers formats the picked number as +14155550177.

Money: pick the amount, classify the currency, flag credit vs charge

An invoice with four amounts on it. TypeSafe picks the total due and the credit, reads the currency, and flags each picked amount as a charge or a credit. The code copies each picked string and parses it into a Decimal.
The total due is $1,315.50 and the credit is $50.00, both in USD. The credit-or-charge Noul answers 0.01 on the total and 0.99 on the credit, so the code knows the sign of each Decimal it parses.
Note - to_decimal assumes the comma groups thousands and the dot is the decimal point. That holds for $1,315.50; in €1.315,50 it is the other way round. Ask a Noul which convention the document uses, and branch on it in code.

Open it in the TypeSafe playground

A share link that opens the email thread in the browser, with the receipt question on it and the four addresses the regex found among its options.
Open this thread + selection in the TypeSafe playground →

Two limits

  • A Choice allows at most 255 options. With more candidates than that, narrow in two stages: pick the section first, then the span inside it.
  • Finding the candidates is the part that takes work. Emails, phone numbers and amounts have regexes that cover them; a name does not, so its candidates have to come from a roster you already have, or from a named-entity recognizer or an LLM that proposes them. TypeSafe then picks the one the question asks for.