Photograph a menu in any language and say your allergies and diet. Menu Decoder translates each dish and tags it for cuisine, the 14 EU allergens, spice and vegetarian or vegan. The tagger is Qwen2.5-1.5B with a LoRA adapter that learned the whole JSON schema, so its prompt is one line: Dish: Pad Thai.
On 303 held-out dishes it returns valid tags every time and catches 69.3% of the allergens a dish contains. Claude does better on allergens (79.2%) but only when the full schema is written into every prompt; without it, Claude answers a different question. Because the tagger misses some allergens, the app never marks one as safe.
There is no green check. Dishes tagged contains or may for your allergens are removed in code before the model recommends anything.
| System | Recall | False-safe |
|---|---|---|
| Base 1.5B | 0.0% | 100.0% |
| 1.5B + LoRA | 69.3% | 7.3% |
| Claude, bare prompt | 0.0% | 100.0% |
| Claude, schema in prompt | 79.2% | 1.7% |
Recall on dishes labeled contains; false-safe is a contains dish tagged not indicated. Base and LoRA on 303 rows, Claude on 100.
| Allergen | Contains (N) | LoRA recall | LoRA false-safe | Claude recall | Claude false-safe |
|---|---|---|---|---|---|
| Gluten | 130 | 91.5% | 0.0% | 84.4% | 0.0% |
| Milk | 103 | 90.3% | 1.9% | 95.2% | 0.0% |
| Fish | 71 | 94.4% | 2.8% | 96.0% | 4.0% |
| Nuts | 68 | 92.7% | 5.9% | 94.1% | 0.0% |
| Molluscs | 23 | 56.5% | 39.1% | 66.7% | 0.0% |
| Peanuts | 22 | 72.7% | 18.2% | 85.7% | 14.3% |
Six of 14 allergens; all 14 are in eval/benchmark.md. Every label is synthetic and was generated by Claude; the human spot-check the spec calls for has not been done.
filter_safe_dishes drops every dish marked contains or may for your allergens, or failing your diet.