Inspiration

My teammate can eat cheese made with microbial rennet, but not cheese made with animal rennet. I can eat chicken, and I can eat cheese, but never together and never on the same plate. No catering form in the world has a checkbox for either of us.

So at every club meeting, info session, and hackathon, we run the same routine. Walk the table, read the paper labels, and ask a volunteer what's in the sauce (they never know). Then either take the "vegetarian option" even though it doesn't actually fit either of us, or eat nothing. A "vegetarian" pasta can be covered in animal-rennet cheese. A meat tray next to a cheese tray is my whole problem. We spend the first twenty minutes of every event doing food forensics in the hope of maybe eating something.

We are not an edge case.

  • 10.8% of US adults (over 26 million people) have a convincing food allergy. About half have had a severe reaction, 45% are allergic to more than one food, and 38% have been to the ER for it (Gupta et al., JAMA Network Open, 2019).
  • Only 4% of Americans are vegetarian and 1% vegan (Gallup, 2023). The "vegetarian option" is built for the smallest group at the table.
  • Add celiac disease (0.7% biopsy-confirmed, 1.4% by blood test; Singh et al., 2018), US Muslims (about 1.1% of the population; Pew, 2018), kosher homes (17% of US Jews, 95% of Orthodox Jews; Pew, 2021), and Jain, Hindu, Buddhist and ethical diets that no national survey even counts.
  • At a 400-person event, that's about 43 guests with food allergies, about 22 who've had a severe reaction, and an unknown number with religious or ethical rules, while the veggie tray was planned for about 16.

Allergies finally have tools. Faith and ethics still get a checkbox.

The allergy world has real software now, and we're glad it does. Spokin crowdsources allergy-safe restaurants, AllergyEats ranks restaurants by how well they handle allergies, and Fig parses packaged-food ingredient lists for hidden allergen derivatives (Food Allergy Institute; Nutrola). Newer apps even scan restaurant menus, but you pick from a fixed list of allergens like peanuts, dairy, gluten and shellfish (Alleri).

The law works the same way. The US names 9 major allergens; the EU requires 14 to be declared in restaurants (FDA; FSAI). California's new menu law, the first in the country, covers those same 9 allergens and only chains with 20+ locations (SB 68).

Religious and ethical diets get none of that. The best catering marketplace offers checkboxes: vegetarian, vegan, gluten-free, dairy-free, halal, kosher (ezCater). A checkbox means "this restaurant has some options." It doesn't mean your guest can eat a specific dish. And every one of these tools is built for one diner choosing for themselves, not a host choosing one restaurant for 400 people.

And even for allergies, the information is thin.

  • Asking the kitchen isn't reliable either: in one survey of 100 restaurant staff, 35% thought fryer heat destroys allergens and 58% had no allergy training (Ahuja & Sicherer, 2007, via National Academies). And US restaurants outside that one California law are generally not required to disclose allergens at all (Perkins Coie).

Religious and ethical diets are harder than "vegetarian"

Allergies are mostly lists of ingredients. Faith-based and ethical diets are rules: about where an ingredient came from, how it was processed, and what it's allowed to touch.

  • Halal isn't just "no pork." Toronto Public Health's halal guide also flags alcohol (including vanilla extract), gelatin, animal rennet and pepsin in cheese, whey made with rennet, and meat not slaughtered according to Islamic law (Toronto Public Health). A "chicken" tag tells you nothing about any of it.
  • Kosher is a relationship between foods. Meat and dairy can't be combined, and many observant Jews wait between eating them (Star-K). Fish is pareve (neither meat nor dairy), so a salmon cream sauce is a different question from a chicken cream sauce. That's exactly my rule, and no "kosher" checkbox can express it for a guest who keeps some of these laws but not a certified-kosher kitchen.
  • Cheese is a trap. The OU notes that most cheese made in mainland Europe still uses animal rennet, and that Romano and blue cheese often contain animal-derived lipase (OU Kosher). So a "vegetarian" pasta with Parmesan can fail my teammate, a Muslim guest and a kosher guest at the same time, while passing every checkbox.
  • Jain diets fail the veggie tray. Beyond meat, fish and eggs, Jain practice traditionally excludes root vegetables such as onions, garlic and potatoes, grounded in the principle of non-harm (Arihanta Institute). Almost every savory vegetarian catering dish starts with onion and garlic.
  • Practice varies by person. Within Hinduism, for example, different adherents keep different dietary rules (Tanenbaum). My teammate's mushroom rule (tree-grown yes, decomposers no) is theirs alone. No fixed menu of checkboxes can represent real people; only their own words can.

Why this is a DEI problem, not a food problem

Diversity, equity and inclusion efforts ask organizations to make sure everyone can fully take part. Food is where that promise most visibly breaks.

  • People notice when they're left out. In HBR's reporting, one in three people with food allergies said they feel uncomfortable or unsafe at work, and at organizations with more inclusive food policies, employees with food allergies or sensitivities reported feeling 76% more included, with an 81% greater sense of belonging (Harvard Business Review, 2022).
  • Religious diets are part of it. At SHRM's Inclusion 2019 conference, an event-planning expert pointed out that vegans end up picking tomatoes and lettuce off the sandwich platter, and that employees observing Ramadan, Rosh Hashanah or Lent aren't fully included when there's no alternative for them (SHRM).
  • Exclusion changes behavior. Adults with food allergies describe withdrawing from events where food is involved and feeling embarrassed to ask to be catered for (Roleston et al., 2025).
  • Shared food builds trust. Strangers who eat the same food trust each other more and cooperate better than strangers eating different food (Woolley & Fishbach, 2017). The guest with the empty plate misses the part of the event that actually builds relationships.
  • The work lands on the same people every time. Religious minorities, people with allergies and ethical eaters are the ones who read labels, question volunteers, bring their own food, or go hungry. That's the definition of an equity gap: an event that's open to everyone, where some people pay a hidden tax to take part.

Dietre moves that tax from the guest to the software. Each guest explains their rules once, in their own words, including sources, processes and combinations that no checkbox covers. Dietre checks those rules against every dish at every restaurant in range, shows the host how much of the table each restaurant can safely feed, and flags anyone nobody can feed before the event, not at the buffet. And where a menu can't answer the question (whether meat is zabiha, which rennet a cheese uses, whether a kitchen is supervised), Dietre lowers its confidence instead of pretending, so the host knows which restaurants to call and what to ask.

So we built the thing that does the cross-checking.

What it does

Dietre is a software that helps event organizers plan for every single participant of a 400 person event in seconds. A host sends one link. Every guest tells a chatbot what they can't eat, in their own words. Dietre reads the actual menus of every restaurant in the radius (PDFs, JavaScript sites, whatever the restaurant published), checks every dish against every guest, and ranks the restaurants by how much of the table they can safely feed.

1. Guests describe their diet like they'd explain it to a friend. Concierge, our Gemini-powered intake chat, asks one question at a time, asks how strict kitchen handling must be only when there's an allergy, reads the list back, and asks "Is this list correct?" before saving. My rules become a hard "no pork" plus a combination rule: meat alone passes, dairy alone passes, both in one dish fails. So a cheeseburger fails for me, while grilled chicken and a cheese pizza each pass.

The server also double-checks the model. Phrases like "allergic to X" or "X is a hard no" are always promoted to hard restrictions, even if the model files them as preferences, and the chat can't submit until the guest confirms.

2. Dietre finds the restaurants and reads their menus. It pulls up to 250 restaurants inside the radius from Google Places, then sends a crawler through each website to find the real food menu: structured data, JavaScript-rendered pages, captured menu API calls, or PDFs. Gemini writes down each dish with only the ingredients the menu actually lists.

3. It matches at the ingredient level, not the cuisine tag. For every guest and every restaurant, Dietre asks one question first: is there at least one dish this person can safely eat?

  • Hard restrictions are locked doors. If nothing is safe, that guest isn't covered, and no preference score can change that.
  • "Egg" doesn't flag "eggplant." "Vegan" expands to every animal product, not one keyword.
  • Rules that don't fit a keyword, like my teammate's rennet and mushroom rules, go to Gemini, which judges dishes against them.
  • Dietre is honest about what it doesn't know. Most menus don't say which rennet a cheese uses, and a dish that's just a name tells us nothing. Those dishes are low confidence. For a guest with a high-severity restriction, a dish we know nothing about counts as unsafe unless Gemini has judged it.

4. It ranks for the whole table. Stricter needs count more: a medical restriction weighs 3×, a religious or ethical one 2×, a taste preference 1×. Preferences like "loves spicy food" can nudge a restaurant's score by at most about 20 points, only among restaurants that are already safe. Dietre also computes a "nobody left behind" ranking, flags any guest that no nearby restaurant can feed, and shows a confidence chip that drops when it has only read dish names.

5. Hosts plan together. Collaborators get email invites with in-app Accept / Decline. Everyone sees the same dashboard: a map with the search radius, a filterable participant list, each guest's saved chat, and a QR code for the guest link.

How we built it

App. Next.js 16 (App Router, Turbopack), React 19 and TypeScript in strict mode. 19 API route files plus server actions. Tailwind CSS v4, shadcn/ui on Radix primitives, Motion, and a hand-built token design system with light and dark themes. Maps are Leaflet on OpenStreetMap tiles.

Data and auth. Firestore through a REST client we wrote ourselves, signing service-account JWTs directly instead of pulling in firebase-admin. Firebase Auth for Google and email sign-in, with our own HMAC-signed session cookies. With no cloud keys at all, the app falls back to a local JSON store, mock auth and a rule-based parser, so it runs anywhere.

Beating Google's 20-result cap. Places Nearby Search returns at most 20 places per call, nearest first, so a naive search only sees the closest block. We wrote adaptive spatial tiling: when a search comes back full, we measure how far it actually reached, then cover the unsearched ring with six smaller circles, recursing until the radius is covered or we hit 40 searches, 25 seconds, or 250 restaurants.

A crawler that finds real menus. It runs in a separate child process so a slow site can't freeze the server:

  1. Check the homepage for a schema.org JSON-LD menu. If there is one, that's the menu.
  2. If the page is a single-page app, render it in headless Chrome (puppeteer-core) and capture the menu's own API responses.
  3. Otherwise, collect every same-site link, PDFs included, and let Gemini decide which is the food menu, which is worth exploring, and which is a gift-card page. It follows up to 3 links per page, 2 layers deep. If Gemini is down, a heuristic prefers PDFs, then anything named "menu."
  4. Extract text with pdf-parse for PDFs, and JSON-LD, then the Firecrawl extractor, then Cheerio for HTML.
  5. Gemini turns the text into dishes, keeping only ingredients that are written down.

Matching and math. Pure TypeScript. The primary score is severity-weighted coverage: the weighted share of guests with at least one safe dish. Guest satisfaction is a Beta–Bernoulli model: every guest starts at a 50/50 prior, preferences add pseudo-counts, and a Gemini audit of complex rules pushes it up (+2) or down harder (+3). The final score is clamp(weighted_coverage + 40 × (bayesian_score − 0.5), 0, 100), and the ranked list sorts by coverage before preference, so taste can never outrank safety.

Grounded in research. Two classic ideas from group recommender research, averaging the group and "least misery" (Masthoff, via UMUAI 2023), became our two rankings: utilitarian (severity-weighted coverage) and Rawlsian maximin (is anyone left with nothing?). Concierge follows findings that language models are strong at eliciting preferences through questions (Li et al., 2023) and that high-guidance elicitation with structured input gives better-matched recommendations (Ziegfeld et al., Computer Speech & Language, 2025).

Gemini everywhere, but never blocking. About a dozen Gemini call sites power intake, dietary parsing, preference scoring, menu-link picking, menu extraction and the complex-rule audit. Every external call has a timeout (4 to 9 seconds), the chat falls back between two models on rate limits, and slow audits run fire-and-forget into a cache.

Challenges we ran into

Restaurants don't publish ingredients. That's the whole problem. Menus live in PDFs, in JavaScript apps that render nothing until scripts run, behind "Order online" buttons, or as bare dish names. It took four extraction strategies, headless Chrome with API capture, and an LLM choosing which links to follow. Scanned photo menus still beat us; there's no OCR yet.

Our own restrictions were the hardest test cases. "Meat and dairy, but not together" isn't a keyword; it's a relationship between two ingredients in one dish. "Microbial rennet yes, animal rennet no" depends on a detail almost no menu prints. We had to build combination rules, LLM judgments for free-text rules, and a confidence system that admits when the menu doesn't say.

Free-tier Gemini hung the dashboard. The Gemini SDK has no built-in timeout or retry, and our complex-rule audit was re-running on every dashboard load, so the page froze whenever the quota ran out. We added timeouts to every external call, cached audits on the event, and moved slow model work into background upgrades.

LLMs don't always follow the format. Concierge sometimes wrapped its JSON in code fences, added trailing text, or said goodbye without emitting its SUBMIT_JSON line. We added balanced-brace parsing, a one-shot recovery request, and server-side guards so an allergy can never slip into the "preferences" bucket.

One weekend, one matching.ts. 118 commits in about three days. Two of us wrote parallel matching engines before merging on one, the location autocomplete was rewritten six times, the guest form hit three React hydration bugs, and Tailwind v4's cascade layers silently lost to our unlayered CSS. Serverless deploys broke anything that wrote to disk.

Defining "best" for a group. A restaurant that feeds 95% of the table can still leave the one guest with celiac disease with nothing. We landed on severity-weighted coverage as the main score, a separate "nobody left behind" rank, and a hard rule that taste can reorder safe restaurants but never unlock an unsafe one.

Accomplishments that we're proud of

  • It handles the restrictions we actually have, not just "vegan" and "shellfish allergy." Combination rules, category expansions and free-text rules, checked dish by dish.
  • Safety is structural. No preference, AI score or tie-breaker can rank a restaurant that feeds less of the table above one that feeds more.
  • It says what it doesn't know. Unlisted ingredients mean low confidence, and a restaurant can't earn a "high" chip if fewer than half its dishes list ingredients.
  • A crawler that reads real restaurant websites: JSON-LD, JavaScript apps, captured API calls and PDFs, with an LLM navigator and a non-AI fallback.
  • Full-radius discovery of up to 250 restaurants in a fixed budget of 40 searches, past Google's 20-result limit.
  • It degrades instead of breaking. With no API keys at all, the whole app still runs.
  • About 20,000 lines of TypeScript and 118 commits in roughly three days.

What we learned

  • Group decisions are a fairness problem. "Best for the group" has at least two defensible answers: the most good for the most people, and nobody left out. Group recommender research finds no single strategy wins everywhere (UMUAI, 2023), so we compute both and show when they disagree.
  • An allergy isn't a low rating. Most recommender math treats preferences as scores to average. Restrictions are constraints that remove options entirely, and modeling them as a gate made everything downstream simpler and safer.
  • Asking is a design problem. A text box gets "no nuts I think?" A conversation that asks one thing at a time and reads the answer back gets something a machine can act on, which matches what the elicitation research found (Li et al., 2023; Ziegfeld et al., 2025). But the rules that protect people belong in code, not in a prompt.
  • Uncertainty should be visible. Most menus say much less than you'd hope. Tracking how much we actually read, and letting that cap our confidence, is more useful than pretending a dish name tells us what's in it.
  • Build the fallback first. Rate limits, slow sites and missing keys were normal, not edge cases.

What's next for dietre

  • Let restaurants confirm. Menus rarely list full ingredients, rennet type or kitchen practices. A restaurant-side page to confirm ingredients and cross-contact handling for a specific order would turn low-confidence dishes into verified ones. With California's new disclosure law in effect (SB 68), chains are already building this data; we want to read it.
  • Same-plate rules, not just same-dish. Today Dietre catches meat and dairy in one dish. Next is serving guidance so they're packed and plated separately, the other half of my own rule.
  • Read more menus. OCR for photographed menus, and a dedicated scraping worker so it runs in production, not only where Chrome is installed.
  • From "where" to "what." Suggest the actual order, which dishes and how many, so every covered guest has something, then export it for the caterer.
  • Fix what we found. Our Rawlsian rank mixes a 0–1 scale with a 0–100 scale, and the dashboard's default sort skips guest-fit as a tie-breaker. Both are small fixes.
  • Close the loop with guests. Email anyone no nearby restaurant can feed, let guests edit their answers, and verify email ownership on collaborator invites.
  • Measure it. Track which extraction method found each menu and what share of dishes list ingredients, so we can publish real accuracy numbers.

Built With

Share this project:

Updates

Submission history