Pick the right date from an email
Supplier emails mention many dates: when the order was placed, the date that slipped, an invoice number that looks like a date, and somewhere the new delivery date. Select, don't generate: code finds every date with a regular expression and normalizes it, Valen picks which one is the new delivery date, and code copies the normalized value into the order. The model never writes a date, so it can never write a malformed one.
How it works
| Question | Type | Purpose |
|---|---|---|
has_new_date: the supplier gives a new delivery date | predicate | the null case: no date at all, or the old date confirmed |
delivery_date: which date is the new delivery date? | choice over the dates code found | picks the span |
Each candidate's description quotes the text around it, so the model judges the date in context. The truth table:
has_new_date | delivery_date | Action |
|---|---|---|
| below 0.3, or no dates found | — | no_new_date |
| 0.7 or above | top ≥ 0.8 and gap ≥ 0.2 | update_order with the normalized date |
| otherwise | otherwise | review |
Run it
Download the bundle once. The recorded answers in caches/value_extraction.json let the script run without an API key; --replay off calls the free hosted API instead.
curl -fLO https://valen-team.github.io/cookbook/valen-cookbook.zip
python -m zipfile -e valen-cookbook.zip .
cd valen-cookbook && python -m pip install -r requirements.txt
python value_extraction.py
python value_extraction.py --replay off --base-url https://api.valen-team.workers.dev/v1
Code
"""Select instead of generate: code finds the dates, Valen picks the one the business needs. Apache-2.0."""
from datetime import date
import json
import re
from common import HERE, choice_status, client, emit, parser, request
MONTHS = {m: i + 1 for i, m in enumerate(["january", "february", "march", "april", "may", "june", "july",
"august", "september", "october", "november", "december"])}
PATTERN = re.compile(r"\b(\d{4})-(\d{2})-(\d{2})\b|\b(\d{1,2}) (" + "|".join(MONTHS) + r")\b|\b("
+ "|".join(MONTHS) + r") (\d{1,2})\b", re.IGNORECASE)
def candidates(text, year):
"""Every date written in the message, normalized by code; the model never writes a date."""
found = []
for m in PATTERN.finditer(text):
if m.group(1):
value = date(int(m.group(1)), int(m.group(2)), int(m.group(3)))
else:
day, month = (m.group(4), m.group(5)) if m.group(4) else (m.group(7), m.group(6))
value = date(year, MONTHS[month.lower()], int(day))
found.append({"span": m.group(0), "iso": value.isoformat(),
"context": text[max(0, m.start() - 60):m.end() + 40].replace("\n", " ")})
return found
def run(predict, email):
found = candidates(email["text"], email["year"])
asked = [{"name": "has_new_date", "type": "predicate",
"instructions": "The supplier gives a new delivery date in this email."}]
if found:
asked.append({"name": "delivery_date", "type": "choice",
"instructions": "Which date is the new date the order will be delivered?",
"choices": [{"value": c["span"], "description": f"The date in: “{c['context']}”"}
for c in found]})
answers = request(predict, email["text"], asked)
has_new = answers["has_new_date"]["probability"]
result = {"id": email["id"], "candidates": [c["span"] for c in found], "has_new_date": has_new}
if has_new < 0.3 or not found:
return {**result, "status": "no_new_date"}
picked = answers["delivery_date"]
confidence, certain = choice_status(picked)
status = "update_order" if certain and has_new >= 0.7 else "review"
iso = next(c["iso"] for c in found if c["span"] == picked["choice"])
return {**result, "status": status, "delivery_date": iso, "span": picked["choice"], "confidence": confidence}
def main():
args = parser(__doc__).parse_args()
emails = json.loads((HERE / "value_extraction_data.json").read_text())
with client(args) as predict:
emit([run(predict, email) for email in emails], args)
if __name__ == "__main__":
main()
[
{"id": "po-1182", "year": 2026, "text": "Hi Mei,\n\nThanks for your order placed on 2 March. Unfortunately the glaze kiln is down, so we can't ship on March 9 as promised. The new delivery date is 16 March. Invoice 2026-03-02 stays the same.\n\nBest,\nTomas"},
{"id": "po-1190", "year": 2026, "text": "Hello,\n\nQuick update on PO 1190: everything is on track and will arrive on 2026-03-20 as originally agreed. Our office is closed on 18 March for a holiday.\n\nRegards,\nPriya"},
{"id": "po-1204", "year": 2026, "text": "Hi team,\n\nThe courier lost the first pallet. We are re-shipping today; you should have it by April 7 instead of 31 March. Sorry about this. We'll credit the freight on the 15 April statement.\n\nAnna"},
{"id": "po-1211", "year": 2026, "text": "Dear Mei,\n\nWe received your payment, thank you. Delivery is being planned and we'll confirm a date next week.\n\nKind regards,\nTomas"}
]
Output
PO 1182 has four candidates, including the invoice number 2026-03-02; Valen picks "16 March". PO 1190 confirms the original date, so has_new_date is 0.004 and the order stays as it is.
[
{
"id": "po-1182",
"candidates": [
"2 March",
"March 9",
"16 March",
"2026-03-02"
],
"has_new_date": 0.9980370964696507,
"status": "update_order",
"delivery_date": "2026-03-16",
"span": "16 March",
"confidence": 0.970832077353691
},
{
"id": "po-1190",
"candidates": [
"2026-03-20",
"18 March"
],
"has_new_date": 0.00443016482795233,
"status": "no_new_date"
},
{
"id": "po-1204",
"candidates": [
"April 7",
"31 March",
"15 April"
],
"has_new_date": 0.9780033681094145,
"status": "update_order",
"delivery_date": "2026-04-07",
"span": "April 7",
"confidence": 0.9725751976466991
},
{
"id": "po-1211",
"candidates": [],
"has_new_date": 0.19682010421693014,
"status": "no_new_date"
}
]
Make it yours
- The same shape works for amounts, order numbers, names and addresses: find candidates with code, ask Valen which one the business means, normalize in code.
- Add the year, the time zone or the sender's locale in code; the model only chooses.