Read the full writeup →
Menu Decoder Local LoRA fine-tune / benchmarked against Claude

A 1.5B model that tags menu dishes for 14 allergens from the dish name alone

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.

Allergen badges

contains
a cross mark
may
ask the staff
not_indicated
not indicated

There is no green check. Dishes tagged contains or may for your allergens are removed in code before the model recommends anything.

Held-out benchmark

SystemRecallFalse-safe
Base 1.5B0.0%100.0%
1.5B + LoRA69.3%7.3%
Claude, bare prompt0.0%100.0%
Claude, schema in prompt79.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.

Per allergen, LoRA against Claude with the schema

AllergenContains (N)LoRA recallLoRA false-safeClaude recallClaude false-safe
Gluten13091.5%0.0%84.4%0.0%
Milk10390.3%1.9%95.2%0.0%
Fish7194.4%2.8%96.0%4.0%
Nuts6892.7%5.9%94.1%0.0%
Molluscs2356.5%39.1%66.7%0.0%
Peanuts2272.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.

How it works

Extract
One vision call reads the whole menu into a structured list of dishes, translations and prices. Lines it can't read stay in the list, flagged as unreadable.
Brief
Your spoken or typed constraints become a structured diet brief.
Tag
The local LoRA model tags each dish, prompted to answer may whenever an allergen is plausible.
Filter
filter_safe_dishes drops every dish marked contains or may for your allergens, or failing your diet.
Pick
The LLM recommends two or three dishes from what is left. Tap a dish to hear its name in the menu's language.