Inspiration
Every group trip has the same moment: five people in a group chat, five different budgets, five different ideas of a "good day," and someone eventually just picks for everyone. Most travel apps optimize for where — a single user searching for a single destination. They don't handle the much harder, much more common case: a group of people with conflicting constraints who all have to end up on the same itinerary.
We also noticed the flip side of the same problem for solo travelers: apps ask you what you want once, then forget it. We wanted something that actually learns how you travel — pace, budget sensitivity, crowd tolerance — and lets that shape every recommendation afterward, with a visible reason attached.
What it does
Sage Adventurer is two things wired into one app:
- A personalization engine. Every interaction (a save, a dismiss, a search) is logged as a signal that mutates a structured travel profile — not a black box, a typed record with a dimension score and a provenance tag for every number in it. Recommendations change visibly as the profile changes, and every recommendation states why, citing only real signals. Nothing is invented.
- A group trip planner ("MyTrip"). Each traveler fills out a short private survey — arrival/departure windows, budget band and flexibility, pace, dietary and accessibility needs, must-dos and can't-dos. The backend merges everyone's private constraints into one shared itinerary: hard limits are never silently broken, no traveler's private input leaks to anyone else, and when no perfect plan exists the app explains the tradeoff instead of pretending one doesn't exist.
On top of the deterministic core, Claude reads the merged trip + preferences and writes a short natural-language read — group vibe, neighborhood recommendation, packing tips, things to watch out for. It's a narrative layer, not a source of truth: if there's no API key or the call fails, the app still works, it just loses the prose.
We also pull real signal where we can: Open-Meteo for weather, Stay22 for accommodation search, and AeroXplorer for BTS-sampled historical airfare estimates (clearly labeled as historical, not live pricing).
How we built it
- Frontend: Next.js (App Router) + React + TypeScript in strict mode,
Tailwind CSS, Leaflet/OpenStreetMap for maps, Supabase for auth (Google OAuth
- email/password) and Postgres.
- Backend: FastAPI, split into modular routers (
trips,group_trips,members,preferences,plans,accommodations,weather,auth), talking to Supabase and to Open-Meteo, Stay22, AeroXplorer, and the Anthropic API. - The rule that shaped everything: the LLM never does math and never ranks. Scoring, profile updates, budget arithmetic, and group consensus are pure deterministic functions. The LLM only converts computed results into prose or extracts structure from messy free text — so the app is explainable and reproducible, and a flaky API call can never break a core screen.
- Every external dependency (AI, weather, maps, Supabase, accommodation search) was built behind an interface with a fallback, so a missing API key degrades a feature instead of crashing the page.
Challenges we ran into
- Merging conflicting constraints without lying about it. It's easy to build a planner that always produces a plan; it's much harder to build one that says "there's no way to fit everyone's hard budget limit and the museum tickets — here's the tradeoff" instead of quietly dropping someone's constraint.
- Auth that doesn't fake success. Our Google sign-in used to fall back
silently to a demo user when OAuth wasn't configured, which made broken
config indistinguishable from working config. We changed it to fail loudly
with a visible error and kept an explicit
admin/admin123path as the intentional demo fallback — a small decision that saved a lot of confused debugging. - Merging parallel branches under time pressure. With trip creation, preferences, Stay22, and AI insights all being built concurrently, integrating without regressing the "it still works with zero API keys" guarantee took real care — particularly getting every service down to one FastAPI port and one shared session model.
- Keeping the LLM honest. It's tempting to let a model fill gaps with plausible-sounding travel advice. We kept hard constraints, budgets, and personalization scores as deterministic code so the AI is never the source of truth for anything a judge — or a traveler — could catch being wrong.
Accomplishments that we're proud of
- A group planner that treats "explain the tradeoff" as a first-class outcome, not an edge case.
- A personalization loop where every recommendation can point to the exact signal that produced it.
- Every external integration (Claude, Stay22, AeroXplorer, Open-Meteo, Google OAuth) degrades gracefully instead of breaking the app — we can demo with any subset of API keys present.
- Real test coverage on both sides: Vitest on the frontend, pytest on the backend, covering auth, validation, and provider failure paths.
What we learned
- Deterministic-core-plus-narrative-LLM is a genuinely good pattern for travel planning: it keeps the app trustworthy while still getting the benefit of natural language for the parts that are actually about language.
- Designing the fallback path for a service before wiring in the real one makes integration under time pressure far less risky.
- Writing down engineering decisions (and the alternatives we rejected) as we went made merging four people's work together dramatically faster than relying on tribal knowledge.
What's next for Sage Adventurer
- Photo/screenshot and Instagram-link ingestion for the "Research Inbox," so messy trip research (a saved reel, a screenshot of a restaurant) gets extracted into structured itinerary candidates automatically.
- Real booking handoff for accommodations beyond the current Stay22 link-out.
- Live fare pricing to sit alongside AeroXplorer's historical estimates.
- Expanding the personalization engine's signal set with post-trip feedback (place ratings, "would go again") to close the loop.
Built With
- aeroxplorer
- anthropic
- claude
- fastapi
- google-oauth
- leaflet.js
- nextjs
- open-meteo
- openstreetmap
- postgresql
- pytest
- python
- react
- stay22
- supabase
- tailwindcss
- typescript
- vitest

Log in or sign up for Devpost to join the conversation.