Inspiration
What it does
How we built it
Inspiration
Every group chat has the same question: where should we go? Ratings and "top 10" lists tell you what is popular, not what suits these particular people, so the loudest voice picks and somebody sits through a dinner that was never meant for them.
People are bad at describing food taste in the abstract, but they can name what they love in a heartbeat: a film, a show, an artist. Qloo's taste graph connects those two worlds, which made it the obvious engine for a group decision.
What it does
- Seat the table. Up to six people, each with up to three loves (films, shows, artists, books, podcasts, games). Mark anyone vegetarian, pick coffee, a meal or dessert, and type a neighbourhood. Or paste a sentence like "Riya: BTS, Squid Game" and it seats everyone for you.
- The agent works in the open. It resolves every love to a Qloo entity (and asks when a name is genuinely ambiguous, like Dune the film, the book or the band), reads each person's taste, searches real venues nearby and scores every venue for every person. Each step streams to the screen with what it found.
- One place for the whole table. Venues are ranked weakest person first:
0.60 × the weakest match + 0.25 × the average match + 0.15 × Qloo's affinity for the venue. A place Qloo loves cannot win if one friend barely fits it. The fairness bar shows everyone's match. - An order for each person, from that venue's own Qloo menu highlights and specialty dishes, matched to their cuisine lean. Vegetarians only get dishes whose names carry no meat, fish or egg. Nobody is defaulted to alcohol.
- Everything you need to actually go: rating, price range (and a labelled estimate for the table), today's hours in the venue's time zone, distance (from you if you opt in, computed on your device), booking, access and parking facts, what reviews keep mentioning, Directions, Call, Send to the group, and links to the venue's real photos.
How I built it
- Next.js 16 (App Router), React 19, TypeScript, CSS Modules, deployed on Vercel. Every Qloo call runs in server routes; keys never reach the browser.
- Qloo calls:
GET /searchto resolve each love to an entity as you type it, with ambiguity checks.GET /v2/insightswithfilter.type=urn:tagandsignal.interests.entities, once per person, for cuisine and ambience affinities. In the sample run, fans of BTS and Squid Game lean Korean (0.87), Thai (0.86) and Chinese (0.81) and like hip rooms (0.91); fans of A. R. Rahman and Pushpa lean Indian (0.96) and comfortable rooms (0.89).GET /v2/insightswithfilter.type=urn:entity:place,filter.location.query,filter.tags(venue genre), everyone's loves and the table's shared tags as signals, andfeature.explainability=trueso each venue says how much each person's loves pulled on it.GET /entitiesfor the three picks, to get Qloo's Google Maps ids so "Their photos" and Directions open the exact place.- Planning a three-person table takes five Qloo calls in total, cached for six hours.
- Scoring and safeguards are small pure modules with 22 unit tests (
npm test): the weakest-person-first ranking (including the counterexample that broke an earlier formula), vegetarian and alcohol rules on real menu names, shared-dish labelling and open-ended prices ("₹2,000 or above" stays a floor, never a fixed price). - Language layer: Groq (
gpt-oss-120b) writes one line per person from the evidence alone, with Gemini Flash-Lite as fallback and templates as the last resort. A validator rejects lines that address the reader, invent praise or leak a seat letter. - Privacy: names never leave the browser. The server only sees seats A to F; Qloo only receives entity ids, tag ids and a place name.
- Design: a Bauhaus system (flat primary blocks, geometric seat shapes, hard shadows), real credited food photos from Unsplash, Clash Display and Satoshi.
Challenges I ran into
- Fairness that actually holds. My first formula multiplied by Qloo's affinity, so a venue Qloo loved could win with one guest at 35%. My first fix rescaled affinity across candidates and broke again on a live run (Roast CCX beat Terra Cafe despite a worse weakest match). Both cases are now regression tests.
- Honesty about data. Qloo gives a price range but not whether it is per person, review keyword counts but not reviews, menu highlights but not a live menu. The UI labels each one for what it is.
- Photos. Restaurant and delivery-app photos belong to the venues and platforms, so Sapor links out to them instead of copying them, and uses credited stock photos (labelled "a close match" when not exact) on the dish cards.
- Coverage varies by city. Qloo's cafe filter can return a viewpoint or a museum cafe, so venues with no cuisine or menu data are set aside and the search widens step by step when results are thin.
Accomplishments that I'm proud of
- A full product, not a proof of concept: from "who's coming" to directions, in one screen.
- Cross-domain taste that people can see: the bao on Riya's card traces back to what BTS and Squid Game fans like to eat.
- Every number on screen comes from Qloo or is labelled as Sapor's estimate.
What I learned
- Qloo's strength is cross-domain: the cuisine and room profiles that come out of films and music are specific enough to pick dishes.
- Group recommendation is a fairness problem first and a ranking problem second.
- Tests on the scoring caught a bug that looked fine in the UI.
What's next for Sapor
- Calibrate match scores against real outings (did the table actually enjoy it?).
- A shared link where each friend adds their own loves from their phone.
- More occasions (drinks, brunch, a gift) and more cities as Qloo's place coverage grows. ## Challenges we ran into
Accomplishments that we're proud of
What we learned
What's next for Sapor
Built With
- css3
- gemini
- groq
- next.js
- playwright
- qloo
- react
- typescript
- unsplash
- vercel
Log in or sign up for Devpost to join the conversation.