Skip to main content
A regex finds the candidate values, TypeSafe picks the one the question asks for, and we copy it verbatim. We pull three values out of text: 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. Each starts as a span copied unchanged from the document. TypeSafe only chooses among spans a regex already found, so it cannot invent a value or transpose a digit. You can point the same find and pick pair at your own documents. There are three steps:
  1. A regex finds the candidate values in the text. Tune it to catch every real value, even if it also catches some wrong ones.
  2. TypeSafe picks which candidate the question is asking for, and reads off any attribute we need downstream (currency, country, whether an amount is a credit or a charge).
  3. We copy the picked value and normalize it.
Date extraction splits the work the same way: TypeSafe reads a date’s parts off the text, and we do the calendar math in code. A regex finds candidate values, TypeSafe picks one, and code normalizes it The regex finds candidate values in the document, TypeSafe picks one, and we normalize it and act on it.

Setup

then set TYPESAFE_API_KEY. To replay the published numbers without an API key, download json_cache.json from typesafe-public-examples into the folder you run the script from.

Helpers

We find candidates with a regex that catches every real value, and drop duplicate matches (find). We pick one with a Choice question whose options are those spans (pick), so the answer is one of them copied exactly, or none when no candidate fits. The currency and the country come from a Choice question over a fixed set of labels (classify), and whether an amount is a credit from a Noul question (is_true). Every call is cached to json_cache.json, so re-rendering makes no API calls.

Email: pick the right address by role

The headers hold four addresses. 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. We ask two questions: which address gets the receipt, and which one sent the message.
The receipt goes to the personal address on the Reply-To: line, as the body asks. The sender is the address on the From line. Both are copies of regex matches, which we lowercase.

Phone: pick the mobile, normalize to E.164

The text has three numbers, and none of them carries 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

The invoice has 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. We copy each picked string and parse 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 question gives 0.03 for the total and 0.98 for the credit, so we know the sign of each Decimal we parse.
We parse amounts on the assumption that 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 question such as “Does this document use a comma as the decimal point?” and branch on the answer.

Open it in the TypeSafe playground

To try the receipt question yourself, open the email thread in the playground. The four addresses the regex found are the question’s options.
Open this thread + selection in the TypeSafe playground →

Two limits

  • A Choice question allows at most 255 options. With more candidates than that, narrow in two stages: pick the section first, then the span inside it. Line-by-line search shows the first stage: tag each line with an ID and let a Choice point to the lines that match.
  • 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.