尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Tastora helps caregivers find restaurants that fit the tastes of someone they love, from their favourite dishes to the songs they still hum. Powered by Qloo's taste intelligence and an AI agent, and honest about every allergy it can't confirm.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Good food should still feel like home.
Tastora helps caregivers find restaurants near an older person they care for that the person will actually enjoy. It is best tested in New York City, and Qloo's coverage was checked in six other cities (see Where it works). You describe the person's own tastes: favourite cuisines and dishes, plus the music, films and places that feel familiar to them. You also list what they must avoid. A Claude agent plans searches against Qloo's taste intelligence, and Tastora's own tested code checks every result against the person's needs. Each suggestion says why it was picked and what to confirm with the restaurant. Tastora never presents an allergy as safe. Built for the Qloo Agentic Hackathon 2026.
Live demo: https://tastora-three.vercel.app
Try it in 30 seconds: open the site → Try an example → Italian-American in Queens (the Frank Sinatra profile) → Find restaurants. Results stream in within a few seconds. Scroll to How Qloo shaped these results to compare Qloo's ranking with and without the person's taste signals.
Everything on the landing page is built from stored real data, not mock-ups.
HeroPreview.tsx): a drawn phone cycles
through three states: the Sinatra example profile, Qloo's top 4 without and then with the Frank
Sinatra signal (from the live run in docs/phase3-smoke.md), and the real
Lenny's Clam Bar result from a live search on 2026-10-27. It pauses on hover, focus, or tap, and
stays still with reduced motion.SeeItWork.tsx): three tappable steps. Pick a
singer (Frank Sinatra, Bob Marley, Fela Kuti, or none) and Qloo's real top 10 for the same Italian
search in New York City re-orders (rerankSnapshot.json,
built by scripts/landing-rerank.ts). Then the check: Tastora's own
engine, run at build time on a committed Qloo sample for a peanut allergy, sorts dishes into
"Ask about" and "Set aside" with its real reason
(sieveData.ts).| See it work | Our rules |
|---|---|
Choosing a restaurant for someone else is hard when what matters is their taste, not yours. A caregiver might know that their dad grew up on red-sauce Italian and Sinatra, or that their mum misses home cooking from Lagos. General search tools rank places by popularity and distance, not by the person. Allergies and dietary needs are a second problem. Restaurant listings rarely include ingredient or allergen information, so a tool that sounds confident about safety is guessing.
Tastora starts from the person's own tastes, with no age field and no assumptions about what older people eat. It keeps preferences ("what they love") separate from hard constraints ("what they must avoid"), and it is explicit about what it cannot verify.
Caregivers rarely decide alone, and the restaurant still has to be asked about allergies and texture. The top 3 places start on a shortlist; "Add to shortlist" on any card changes it.
https://wa.me/?text=…). Scores and set-aside dishes are never included./results/print) is a "Before you go — questions to ask" sheet: address,
phone in large type, dishes to ask about, warnings, and every question with a ☐ box and a line
for notes. "Print or save as PDF"; paper is always black on white, one place per block. The
safety zones keep their shapes in print (thin solid, dashed, heavy solid) and pictures are hidden.| Result card | Print checklist |
|---|---|
The result card, evidence panel and print checklist come from the Sinatra example search. All
screenshots are in docs/screenshots/.
Qloo is the restaurant source and the taste engine. Tastora uses three Qloo endpoints. Details are
in docs/agent.md and docs/qloo-findings.md.
| Purpose | Qloo call |
|---|---|
| Turn "Nigerian" or "Italian-American" into cuisine tags | GET /v2/tags (filter.query). Tastora keeps urn:tag:cuisine:qloo:* and urn:tag:genre:place:restaurant:* tags |
| Turn taste memories into Qloo entities ("Frank Sinatra" → artist) | GET /search (query). Artists, films, TV shows, books and places |
| Find and rank restaurants | GET /v2/insights with filter.type=urn:entity:place, filter.location.query (the area and city), filter.tags (cuisine) and signal.interests.entities / signal.interests.tags (the taste signals) |
Cross-domain taste memories. A favourite singer or film becomes a Qloo entity, and Qloo re-ranks restaurants by affinity with it. Tastora's Phase 1 tests found that the effect is real and fits the culture involved. Within Italian restaurants, a Frank Sinatra signal brought up classic red-sauce places (Chazz Palminteri, Original Vincent's) in place of Eataly and Marea. A Bob Marley signal moved a general restaurant search toward Caribbean places. With no signals, the baseline ranking is fully deterministic, so the differences are not noise.
Evidence panel. For every search, Tastora runs the main Qloo query again without taste
signals and shows both top-10 lists. It reports how many places appear in both, how many are new
and how many moved. Results from Tastora's test searches in New York City, October 2026
(docs/phase5-profile-matrix.md,
docs/phase3-smoke.md):
| Profile | Taste signal | Same places in top 10 | Moved |
|---|---|---|---|
| Italian-American, Queens | Frank Sinatra | 4 of 10 | 10 |
| Caribbean, Bronx | Bob Marley | 0 of 10 | 10 |
| Jewish deli, Manhattan | Barbra Streisand | 6 of 10 | 9 |
| Indian, Jackson Heights | Lata Mangeshkar | 5 of 10 | 10 |
| Nigerian, Brooklyn | Fela Kuti | 10 of 10 | 7 |
The strength of the signal varies. Bob Marley replaced the whole Caribbean top 10, while Fela Kuti only re-ordered NYC's roughly 10 Nigerian places. Where Qloo finds no entity for a memory (for example Teresa Teng), Tastora says so under "About this search" instead of implying the memory was used.
Qloo data is also used for venue facts: address, borough or city (from geocode), hours, phone, website, Google Place ID, dish highlights, venue tags for accessibility, noise and diet options, and the place's image (display only; ranking and the safety checks don't read it).
Tastora was built and tested in New York City. In Phase 9c, Qloo's restaurant coverage was checked in six more cities with 23 live calls: Italian, one local cuisine, and Italian with a Frank Sinatra signal (details).
| City | Verdict | Places kept per search (of 20) | In the city or its metro |
|---|---|---|---|
| New York City | Good (main test city) | 18–20 (Phase 1, before the venue filter) | most; a few NJ/Nassau places, labelled |
| London | Good | 18–20 | 58/58 |
| Toronto | Good | 19–20 | 56/59 |
| Los Angeles | Good | 18–20 | 56/57 |
| Chicago | Good | 16–20 | 55/56 |
| Sydney | Good | 17–20 | 56/56 |
| Lagos | Thin | 6–13 | 27/27 |
The Sinatra signal reordered the Italian top 10 in every city. In Lagos it could only reshuffle the same handful of places, and 7 of 20 Nigerian results there were not restaurants (supermarkets, bars). Other cities may work, but they are untested. When a search finds fewer than 4 places, or the city is rated Thin, the results page says: "Fewer matches found around . Qloo's restaurant coverage varies by city." If Qloo can't find the city at all, Tastora asks for the nearest larger city.
Far-away venues are excluded. Qloo's location is a bias, not a boundary, and it can resolve to
the wrong place: in one live search, "Benin city" resolved to Mexico City and all 7 Caribbean results
were there. Tastora now checks each venue against the area asked for (Qloo's echoed locality, the
venue's coordinates and geocode). A venue is kept when it is in the borough, city or metro, within
50 km of the matching locality, or in the same region. Far venues are excluded with the reason
"Outside the area you chose". If nothing is left, the page says so plainly ("Qloo doesn't have
Caribbean restaurants near Benin city yet.") instead of showing places in another country. Checked
against every saved raw response (NYC, London, Toronto, LA, Chicago, Sydney, Lagos): no venue there
was excluded. Details in
docs/ranking-and-constraints.md.
flowchart TD
P["Caregiver profile"] --> S{"Split on the server"}
S -- "cuisines, dishes, dislikes, taste memories" --> C["Claude planner<br/>(claude-sonnet-5-5)"]
S -- "allergies, diet, texture, comfort" --> E
S -- "location" --> T
C -- "tool calls" --> T["SearchSession<br/>limits: 10 tool calls, 6 turns, 3 searches, 30 s<br/>ID check: only IDs Qloo returned this run<br/>location injected by the server"]
T --> F1["find_cuisine_tags → Qloo /v2/tags"]
T --> F2["find_taste_entities → Qloo /search"]
T --> F3["search_restaurants → Qloo /v2/insights"]
F1 & F2 & F3 -- "results" --> C
F3 --> V["Venue filter<br/>drops hotels, bars without a cuisine, food courts…<br/>and venues far from the chosen area"]
V --> E["Constraint engine (Phase 2)<br/>Matched / Needs confirmation / Conflict"]
E --> R["Ranking<br/>Qloo taste rank + favourites + comfort + borough/city + feedback"]
R --> X["Explanation facts<br/>reasons, dishes to ask about, removed dishes, questions"]
F3 -. "same search without signals" .-> B["Evidence panel"]
X --> U["Results UI (streamed)"]
B --> U
K["Fallback planner<br/>(if Claude is unavailable)"] -.-> T
The agent decides how to search. Tastora's tested code decides what is safe.
find_cuisine_tags, find_taste_entities and search_restaurants. It
picks the tags, resolves taste memories (in the matrix, it corrected "Nigeran" and "Fela Kutti"),
and chooses which signals to send.search_restaurants must have come from an
earlier tool result in the same run. An invented ID returns an error to the model, and no Qloo
call is made. search_restaurants has no location field: the server injects the profile's
location.docs/ranking-and-constraints.md).Qloo has no ingredient, per-dish allergen or texture data. Tastora is built around that limit instead of hiding it.
tests/ui-helpers.test.ts scans all UI source for
order/buy/checkout/purchase/cart. It also scans for "safe", "allergen-free" and "guaranteed",
including in server strings that reach the screen, unless the word is negated.| Where | What |
|---|---|
| Qloo | Cuisine and taste-memory search text (e.g. "Italian-American", "Frank Sinatra"), the Qloo tag and entity IDs chosen, and the location text (e.g. "Queens", "London"). |
| Anthropic (Claude) | Only favourite cuisines, favourite dishes, dislikes and taste memories, plus the Qloo tool results (tag and entity names, top restaurant names). A test checks every request body. |
| Never sent to Qloo or Claude | Allergies, dietary restrictions, texture, comfort needs and the display name. The display name ("Dad") is not sent to the server at all; it only appears in share text you choose to send. |
| On the device | The profile and feedback in localStorage, and the last search and shortlist in sessionStorage (so reloading doesn't run a new search). Clearing the profile clears all of them. Share text and the print checklist are built in the browser. |
| On the server | No database and no accounts. An in-memory cache of Qloo responses (10 minutes) and in-memory rate-limit counters, per server instance. A failed search logs only the error's class name. |
API keys are read server-side only and are never sent to the browser.
/v2/tags, /search, /v2/insights@anthropic-ai/sdk (tool use; default model claude-sonnet-5-5)src/
app/ pages (/, /profile, /results, /results/print), API routes (/api/recommend, /api/health), OG image
_landing/ landing sections: hero preview, See it work, Qloo proof, rules (stored real data)
components/ form inputs, safety zones, place image / cuisine art, chef logo, icons
lib/
agent/ Claude planner, fallback planner, SearchSession, recommend(), notices, explanation facts
qloo/ Qloo client, search layer, cache, non-restaurant filter
constraints/ allergy / diet / texture / comfort evaluation, keyword lists
ranking/ scoring and feedback
domain/ profile schema, Qloo place → candidate mapping, boroughs, city/area + coverage, related cuisines
ui/ client helpers: streaming, storage, status display, rank movement
tests/ Vitest suites (unit, route, agent with fake Claude/Qloo)
fixtures/qloo/samples/ trimmed real Qloo responses used by tests
scripts/ live probes: Qloo spike, smoke test, profile matrix
docs/ design notes and live-run findings
Requires Node.js 20 or newer.
git clone https://github.com/kirahszn/tastora.git
cd tastora
npm install
cp .env.example .env.local # then fill in the values
npm run dev
Open http://localhost:3000. QLOO_API_KEY and QLOO_API_BASE_URL are needed for searches.
ANTHROPIC_API_KEY is optional: without it, the built-in planner is used. ANTHROPIC_MODEL
defaults to claude-sonnet-5-5. The app builds and the tests run with no keys at all.
| Script | What it does |
|---|---|
npm run dev | Start the development server |
npm run build / npm start | Production build / serve it |
npm test | Run the Vitest suite once (no network, no keys) |
npm run lint / npm run typecheck | ESLint / tsc --noEmit |
npm run smoke:recommend | Live end-to-end run for two profiles (uses API credit) |
npm run matrix | Live 10-profile matrix (uses API credit, budget-capped) |
npm run qloo:probe | Phase 1 Qloo API spike (uses API credit) |
npm run qloo:coverage | Phase 9c coverage check for six cities (uses API credit, 24-call cap) |
Tests: npm test runs 341 tests in 20 files. They cover the constraint engine, ranking, the
Qloo client and search layer (recorded responses), the agent (fake Claude and Qloo: limits,
invented IDs, fallback, data minimisation), the API route (validation, streaming, rate limit), UI
helpers (banned words, storage, status display), locations beyond NYC (profile migration, the
same-city rule on live samples, coverage note, phone format), far-away venue exclusion, and the
landing page's stored data (each figure checked against the doc it quotes). One test reads a
gitignored raw Qloo response and is skipped in a fresh clone (340 pass, 1 skipped).
filter.location.query favours the area
but can return places elsewhere. Tastora excludes venues far from the area asked for (see
Where it works). Within range, nothing more is filtered: in New York the chosen
borough ranks higher (+6) and places outside NYC are labelled; elsewhere, places whose Qloo city
or metro is the chosen city rank higher (+6) and are labelled "In London" / "In the Sydney area",
and others "Nearby: ". The far-away check needs Qloo to echo a locality and give venue
coordinates; without them, venues are kept.MIT.
next/font.