Add normalize_recipe skill
CI / ci (push) Successful in 1m37s
CI / ci (pull_request) Successful in 1m38s

normalize_recipe v1 turns a recipe page's title, raw ingredient lines,
steps and JSON-LD times into {ingredients: [{name, qty, unit, note}],
method, difficulty, minutes_total}. skills.NormalizeRecipe always runs it
at temperature 0. Validation requires one named ingredient per input line,
in order, so a mismatch gets the harness's repair turn. Code-derived
values win over the model's: JSON-LD total minutes, crockpot for a slow
cooker or crock pot mention, and a derived difficulty when the model gives
none. Golden tests with the Fake provider cover a typical recipe, a
repaired line-count mismatch, a slow-cooker recipe forced to crockpot and
JSON-LD minutes overriding the model.

Maestro task 9b4db226-9d8f-4251-a5de-adc45713c7bc

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 14:28:41 -04:00
co-authored by Claude Opus 5.5
parent 58e8f30f0e
commit 925eddfa24
7 changed files with 823 additions and 0 deletions
+12
View File
@@ -267,6 +267,18 @@ automatic** (D-021).
`ErrNoUsableRecipe`. Either is joined with each unranked candidate's URL
and reason (fetch failure, no valid recipe, missing ingredients or
image), so the caller decides per item what a failure means.
- Ingredient lines are normalized by the `normalize_recipe` v1 skill
(`skills.NormalizeRecipe`), always at temperature 0 whatever the
household's setting. From the title, raw ingredient lines, steps and
JSON-LD times it returns `{ingredients: [{name, qty, unit, note}], method,
difficulty, minutes_total}`. Validation requires exactly one ingredient
per input line, in order (a name matching another line but not its own is
out of order), each with a name; a mismatch gets the harness's repair
turn. Code-derived values win: a positive JSON-LD total is
`minutes_total`; "slow cooker" or "crock pot" in the title or a step makes
the method `crockpot`; a difficulty the model leaves null is derived —
hard over 10 steps or 90 minutes, easy at 5 steps or fewer and 45 minutes
or less, else medium, a crockpot's minutes not counting.
- Photos are cached locally (`photos`) and processed with
`golang.org/x/image`. `recipes.PhotoCache.CachePhoto` downloads through
the Fetcher's `FetchImage` (JPEG, PNG, WebP or GIF content types, 8 MB