Inspiration

Friends often spend more time agreeing on what to do than actually doing it. Everyone has different tastes, recommendations tend to favor the loudest voice, and a group chat makes it hard to see where people's tastes overlap. We wanted a lightweight way for a group to say what they like and get a plan that shows why each option fits, with no accounts and no sign-up.

What it does

Taste Bridge turns the confirmed favorites of 3 to 6 friends into group plans using Qloo's taste graph.

  1. A host creates a room, picks categories, and adds a city or area for dining.
  2. Friends join from a shared link with a nickname. No account is needed.
  3. Members chat in the room, search Qloo, and confirm 2 to 4 favorites each. Nothing is ever saved automatically.
  4. Once at least three members are ready, the host generates a plan.
  5. Each pick shows Qloo's overlap evidence and the AI explanation separately. Members vote, and the host can ask for new picks. Earlier picks are never repeated.

A Suggest from chat button reads only the requesting member's recent messages and proposes up to three Qloo entities, which the member can accept or ignore. A Try a demo mode seeds two labeled demo profiles from live Qloo searches so a judge can see a full plan in one click.

How we built it

  • TypeScript monorepo with npm workspaces (shared, server, web)
  • Fastify API that also serves the built React + Vite app from one Node process
  • SQLite (better-sqlite3, WAL mode) for rooms, members, favorites, chat, plans and votes
  • Qloo entity search over the hackathon API, and recommendations through the Qloo MCP harness running as a child process of the API
  • Gemini (optional) for chat keyword extraction and per-friend explanations, with deterministic fallback text when it is unavailable or returns invalid output
  • Docker image deployed on Fly.io as a single machine with a persistent volume

How picks are ranked

Each candidate gets a group overlap score from its affinity values across members:

$$\text{score} = \operatorname{round}\Big(100 \times \big(0.5\,\bar{a} + 0.5\,a_{\min}\big)\Big)$$

where ( \bar{a} ) is the mean affinity and ( a_{\min} ) is the lowest affinity in the group. Using the minimum as well as the mean stops a pick from winning just because three people love it and one person dislikes it. When a member has both chat-derived and other favorites, chat-derived signals get 70% of the combined affinity and the rest get 30%.

Challenges we ran into

  • Keeping evidence honest. Qloo's score and the AI's explanation are labeled separately in the UI, so the AI's wording is never presented as data.
  • Privacy without accounts. Rooms use anonymous, room-scoped cookies (the database stores only a token hash). Nicknames are never sent to Qloo or Gemini, browser coordinates are never stored, and rooms are deleted after 72 hours by default.
  • Reliable plans when services fail. Gemini can be slow or return invalid output, so every plan has deterministic fallback reasons.
  • Rate limits. API requests, Qloo searches and plan regeneration are all limited so one room cannot use up the API budget.
  • Deployment. SQLite and the local Qloo MCP process mean the app must run as one instance with persistent storage, so serverless hosting was not an option. We deployed on Fly.io with a mounted volume.

What we learned

Grounding an AI explanation in real taste data makes recommendations more trustworthy than letting a model guess. Designing fallbacks early, and labeling clearly what comes from Qloo and what comes from AI, kept the app useful even when an outside service failed.

What's next

  • A persistent database option so the app can run on more than one instance
  • Better handling when Qloo is rate limited or unavailable
  • More UI tests covering room creation, favorites, the demo, and plan regeneration

Built With

Share this project:

Updates

Submission history