Skip to content

Trip Planner MiniApp - Complete Documentation

Version: 2.0.0 Last Updated: December 3, 2025 Status: Production Ready ✅


Table of Contents

  1. Overview
  2. Architecture
  3. Features
  4. Use Cases
  5. API Reference
  6. Data Schema
  7. Routing Logic
  8. Test Coverage
  9. Future Enhancements
  10. Known Limitations

Overview

The Trip Planner MiniApp is an intelligent travel planning assistant that stores rich venue data and enables conversational queries during travel. It integrates seamlessly with the Archety orchestrator, supporting both 1:1 and group chat contexts.

Key Capabilities

  • Venue Management: Add, track, and rate venues with rich metadata
  • Natural Language Queries: "What's good for dinner in Jing'an?"
  • Visit Tracking: Mark venues as visited with optional ratings and notes
  • Smart Filtering: Filter by category, district, time of day, or visit status
  • Hallucination Prevention: Only returns user-provided information, never invents data

Architecture

Component Diagram

┌─────────────────────────────────────────────────────────────┐
│                     TwoStageHandler                          │
│                    (two_stage_handler.py)                    │
└─────────────────────┬────────────────────────────────────────┘
                      │ _route_to_miniapp_if_applicable()
┌─────────────────────────────────────────────────────────────┐
│                    MiniApp Routing Layer                     │
│  ┌─────────────┐  ┌─────────────────┐  ┌─────────────────┐  │
│  │  Detector   │  │ SessionManager  │  │    Registry     │  │
│  │ (triggers)  │  │ (persistence)   │  │  (handlers)     │  │
│  └─────────────┘  └─────────────────┘  └─────────────────┘  │
└─────────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│               TripPlannerHandler                             │
│              (trip_planner.py - 784 lines)                   │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                    Commands                          │    │
│  │  • set_destination  • add_venue  • mark_visited     │    │
│  │  • query_venues     • filter_by_district            │    │
│  │  • get_must_try     • show_unvisited  • list_all    │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                    Data Models                       │    │
│  │  • Venue (dataclass with 15+ fields)                │    │
│  │  • TripState (destination, dates, venues, districts)│    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

Key Files

File Purpose
app/miniapps/handlers/trip_planner.py Main handler (784 lines)
app/miniapps/routing/session_manager.py Session persistence
app/miniapps/routing/registry.py Handler registration
app/miniapps/routing/base_handler.py Base classes & context
app/orchestrator/miniapp_detector.py Trigger detection
app/orchestrator/two_stage_handler.py Main routing (lines 1530-1750)

Features

1. Venue Management

Adding Venues:

User: "add Yaya's Pasta Bar - trendy Italian spot in Jing'an"
Sage: "Added 🍴 Yaya's Pasta Bar in Jing'an"

Bulk Import:

User: "Jing'an District:
Yaya's Pasta Bar - 329 Tongren Road - trendy Italian
Bastard - No. 102, Lane 30 - neon-lit fusion"

Sage: "Added 2 venues to your Shanghai trip!
Jing'an: 2"

2. Smart Queries

Query Type Example Result
Time-based "what's good for dinner?" Restaurants + dinner venues
Category "any good bars?" Bar-category venues
District "in Jing'an" Venues in that district
Unvisited "what haven't I done?" Planned venues only
Must-try "what should I order at Yaya's?" Saved must-try items

3. Visit Tracking

User: "visited Yaya's, loved it, 5 stars"
Sage: "Marked Yaya's Pasta Bar as visited! ⭐⭐⭐⭐⭐
'loved it'

3/10 spots complete."

4. Escape Patterns (Group Chat Support)

When an active trip planning session exists, users can still get normal Sage responses:

User: "hey sage, how are you?"  → Routes to GPT-5, not trip planner
User: "what's the weather?"     → Routes to GPT-5
User: "@sage what time is it?"  → Routes to GPT-5

Use Cases

Primary Use Case: Pre-Trip Research

  1. User discovers trip is being planned: "trip to Shanghai"
  2. User pastes venue research from social media/blogs
  3. Handler extracts venues with districts, categories, must-try items
  4. User queries: "dinner spots in Xuhui?"
  5. Handler returns filtered, organized results

Secondary Use Case: On-Trip Tracking

  1. User at venue: "visited Yaya's, amazing food, 5 stars"
  2. Handler updates status, records rating
  3. User later: "what haven't I done in Jing'an?"
  4. Handler shows remaining planned venues

Group Chat Use Case

  1. Group planning trip together
  2. Multiple users add venues
  3. Query: "what do we still need to visit?"
  4. Non-trip messages ("hey how's everyone?") bypass handler

API Reference

MiniAppContext (Input)

@dataclass
class MiniAppContext:
    message: str              # User's message text
    sender: str               # User phone number
    chat_guid: str            # Chat identifier
    timestamp: datetime       # Message timestamp
    is_group: bool            # Group vs 1:1 chat
    user_id: str              # User identifier
    session_id: str           # Active session ID
    session_state: str        # Current state (initial, active, etc.)
    session_data: Dict        # Persisted trip data
    assistant_id: str         # Persona (sage/echo)
    participants: List[str]   # Group participants (optional)

MiniAppResponse (Output)

@dataclass
class MiniAppResponse:
    message: str = ""                    # Response text
    bubbles: List[str] = []              # Multiple messages
    state_data_updates: Dict = None      # Data to persist
    new_state: str = None                # State transition
    end_session: bool = False            # Close session?
    end_reason: str = None               # Why ended
    ui_update: bool = False              # Refresh UI?

Handler Commands

Command Pattern Method Description
trip to {destination} _set_destination() Start trip planning
add {venue} _add_single_venue() Add one venue
Multi-line text _extract_and_add_venues() Bulk import
visited {venue} _mark_visited() Mark as visited
what's good for {time}? _query_venues() Time-based query
in {district} _filter_by_district() District filter
what should I order at {venue}? _get_must_try() Get recommendations
what haven't I visited? _show_unvisited() Show planned venues
list _list_all() Show all venues
help _show_help() Show help

Data Schema

Venue Dataclass

@dataclass
class Venue:
    id: str                    # Unique ID (v_xxxxxxxx)
    name: str                  # Venue name
    district: str = ""         # Jing'an, Xuhui, etc.
    address: str = ""          # Street address
    category: str = "restaurant"  # restaurant, bar, cafe, shop, landmark, hotel, activity
    vibe: List[str] = []       # trendy, cozy, neon, etc.
    must_try: List[str] = []   # Specific dishes/items
    best_for: List[str] = []   # morning, dinner, photos, etc.
    source_note: str = ""      # "From XHS", etc.
    status: str = "planned"    # planned, visited, skipped
    visited_at: str = None     # ISO timestamp
    user_rating: int = None    # 1-5 stars
    user_notes: str = ""       # User comments
    added_at: str = ""         # ISO timestamp

TripState Dataclass

@dataclass
class TripState:
    destination: str = ""              # "Shanghai"
    dates: Dict[str, str] = {}         # {start: "2024-01-01", end: "2024-01-07"}
    venues: List[Venue] = []           # All saved venues
    districts: List[str] = []          # Auto-populated from venues

Session Persistence

Sessions are stored in PostgreSQL via MiniAppSession model:

class MiniAppSession(Base):
    id: UUID                    # Primary key
    chat_guid: str              # Chat identifier
    user_id: UUID               # User foreign key
    mini_app_id: str            # "trip_planner"
    assistant_id: str           # "sage"
    state: str                  # "initial", "active"
    state_data: JSON            # TripState as dict
    started_at: datetime
    last_activity: datetime
    ended_at: datetime          # NULL if active
    end_reason: str             # "completed", "timeout", etc.
    message_count: int
    turn_count: int

Routing Logic

Message Flow

┌──────────────────────────────────────────────────────────────┐
│                    Incoming Message                           │
└──────────────────────────────┬───────────────────────────────┘
                    ┌──────────────────┐
                    │ Escape Pattern?  │ "hey sage", "@sage", "weather"
                    └────────┬─────────┘
            ┌────────────────┴────────────────┐
            │ Yes                             │ No
            ▼                                 ▼
    ┌───────────────┐              ┌──────────────────┐
    │ Route to GPT-5│              │ Check Trigger    │
    └───────────────┘              └────────┬─────────┘
                        ┌───────────────────┴───────────────────┐
                        │ Triggered?                            │ No
                        │ (keywords: trip, venue, planning)     │
                        ▼                                       ▼
                ┌───────────────┐                    ┌──────────────────┐
                │ Create/Update │                    │ Active Session?  │
                │ Session       │                    └────────┬─────────┘
                └───────┬───────┘                             │
                        │                         ┌───────────┴───────────┐
                        │                         │ Yes                   │ No
                        │                         ▼                       ▼
                        │              ┌─────────────────┐       ┌───────────────┐
                        │              │ Message Relevant│       │ Route to GPT-5│
                        │              │ to Active App?  │       └───────────────┘
                        │              └────────┬────────┘
                        │                       │
                        │           ┌───────────┴───────────┐
                        │           │ Yes                   │ No
                        │           ▼                       ▼
                        │   ┌───────────────┐       ┌───────────────┐
                        │   │ Route to      │       │ Route to GPT-5│
                        │   │ Handler       │       └───────────────┘
                        │   └───────┬───────┘
                        │           │
                        └───────────┼───────────────────────────────┐
                                    ▼                               │
                          ┌─────────────────┐                       │
                          │ Handler.handle()│                       │
                          └────────┬────────┘                       │
                                   ▼                                │
                          ┌─────────────────┐                       │
                          │ Update Session  │                       │
                          │ Send Response   │                       │
                          └─────────────────┘                       │

Relevance Check Keywords

For trip_planner: - Actions: add, remove, delete, visited, skip, list, show - Queries: what's good, what should i, where, dinner, lunch, breakfast - Categories: coffee, drinks, bar, restaurant, cafe, food, eat - Trip-related: venue, place, spot, district, neighborhood, area - Rating: rating, stars, rate, loved, hated, amazing


Test Coverage

Test File: tests/test_trip_planner_enhanced.py

Test Classes:

Class Tests Purpose
TestVenueDataclass 3 Venue creation, serialization
TestTripState 2 TripState creation, round-trip
TestHandlerCommands 12 All command handlers
TestVenueExtraction 5 Natural language parsing
TestUIConfig 3 UI configuration generation
TestEdgeCases 4 Error handling
TestHallucinationPrevention 5 Anti-hallucination

Total: 34 tests

Hallucination Prevention Tests

class TestHallucinationPrevention:
    """
    Critical tests ensuring the handler NEVER invents:
    - Menu items or dish names
    - Addresses or locations
    - Ratings or reviews
    - Opening hours or prices
    """

    test_no_hallucinated_dishes_for_empty_must_try    # ✅
    test_no_hallucinated_venues_in_queries            # ✅
    test_only_returns_user_provided_must_try          # ✅
    test_nonexistent_venue_query_fails_gracefully     # ✅
    test_district_filter_no_hallucination             # ✅

Running Tests

# All trip planner tests
python -m pytest tests/test_trip_planner_enhanced.py -v

# Hallucination tests only
python -m pytest tests/test_trip_planner_enhanced.py::TestHallucinationPrevention -v

# With coverage
python -m pytest tests/test_trip_planner_enhanced.py --cov=app/miniapps/handlers/trip_planner

Completed Features (December 2025)

The following features have been fully implemented:

Real-Time Intelligence ✅

  • Google Places Integration: Venue validation, geocoding, place details
  • Perplexity Integration: AI-powered venue descriptions and recommendations
  • "Is it open?" queries: Real-time opening status via _check_venue_open()
  • Walking time calculations: Distance between venues via get_walking_time()

Collaborative Trip Planning ✅

  • Group Session Scoping: Multiple users can contribute to the same trip in group chats
  • User Attribution: Every venue tracks created_by_user_id and created_by_name
  • Maps Links: Auto-generated Google Maps URLs for each venue

Location Features ✅

  • Full Geocoding: Venues get coordinates and place_id via Google Places
  • District Extraction: Auto-populated from Places API
  • Enrichment Data: Opening hours, ratings, price level, phone, website

Future Enhancements

Medium Priority

  1. Photo Memory Integration
  2. Link photo memories to venues
  3. "Show me photos from Yaya's"
  4. Auto-tag venues from photo analysis

  5. Visual Map UI

  6. Interactive map with venue pins
  7. Cluster by district
  8. Walking route visualization

  9. Time-Based Itinerary

  10. "Plan my Tuesday"
  11. Auto-schedule based on best_for tags
  12. Opening hours-aware scheduling

  13. Export/Share

  14. Export as PDF/spreadsheet
  15. Share trip with non-users
  16. Import from Google Maps lists

Low Priority

  1. Venue Voting
  2. Multiple users vote on venues
  3. Ranked preference for dinner spots

  4. Reservation Integration

  5. OpenTable/Resy API integration
  6. "Book a table at Yaya's for Thursday"

Known Limitations

Current Limitations

  1. Location Context Not Available
  2. "What's near me?" requires client to send location
  3. iOS app change needed to pass user coordinates

  4. Limited Parsing Edge Cases

  5. Complex multi-venue formats may fail
  6. Unusual address formats not always recognized

  7. Proactive Suggestions Not Implemented

  8. "It's 7 PM, want dinner?" style triggers not built
  9. Requires background trigger infrastructure

Design Decisions

  1. Why No LLM for Responses?
  2. Handler uses rule-based responses for speed
  3. Prevents hallucination of venue details
  4. Consistent, predictable output

  5. Why Session-Based?

  6. Maintains context across messages
  7. Enables stateful operations (add → query → visit)
  8. Supports group coordination

  9. Why Escape Patterns?

  10. Group chats need normal Sage functionality
  11. Not all messages are trip-related
  12. Better user experience

Appendix: Code Quality Audit

Reviewed Files (November 30, 2025)

File Lines Bugs Found Notes
trip_planner.py 784 0 Well-structured, handles edge cases
session_manager.py 314 0 Proper cleanup, UUID handling
two_stage_handler.py (routing) ~220 0 Fixed UUID issue, proper fallback

Key Fixes Applied

  1. UUID Mismatch Bug (Fixed)
  2. SessionManager.create_session() expected UUID
  3. Was receiving phone-derived string
  4. Fix: Look up actual user UUID from database
  5. Fallback: UUID5 deterministic generation

  6. Authentication Issue (Fixed)

  7. Was using HMAC auth (X-Signature headers)
  8. Endpoint uses Bearer token auth
  9. Fix: Use Authorization: Bearer {secret}

  10. Group Chat Support (Implemented)

  11. Escape patterns bypass miniapp
  12. Relevance checking for active sessions
  13. Non-relevant messages route to GPT-5

Documentation generated by Claude Code | Last tested: All 34 tests passing