Skip to content

sailingsam/Proactive-Ride-Food-Assistant

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Proactive Ride + Food Assistant

A system that acts first — watches time, patterns, and live signals, and surfaces a one-tap suggestion when a ride or food order is likely. Built for the "Proactive Assistant" assignment.

What's in the box

  • Backend — NestJS + TypeScript + Prisma + SQLite, with six internal modules: connection, adapters, memory, trigger, suggestions, debug.
  • Real adapters — Uber (GraphQL) and Zomato (REST), both using session cookies captured via Playwright.
  • Mock adapters — Ola, Rapido, Swiggy (and Uber/Zomato fallbacks), so the full multi-platform architecture is demonstrable with or without real logins.
  • Cookie vault — AES-256-GCM encrypted cookie storage with an auto-generated per-machine key.
  • Playwright login flow — headful browser launches, user signs in normally, cookies are captured and encrypted.
  • Frontend — React + Vite + TypeScript + Tailwind; polls the backend every 3 s.
  • Seed data — 3 months of realistic fake history for a single demo user (so the UI has something to show before any real connection).
  • Debug panel — time-travel + live-signal injection so flows can be reproduced without waiting for real-world events.

Screenshots

Suggestion card — urgent ride with traffic delay applied Urgent ride suggestion

Learned patterns from history Learned patterns

Connection status + debug panel (time-travel and live signals) Connections and debug

Edit flow — tweak any field before confirming Edit modal

Quick start (local)

Prerequisites: Node.js 20+, npm.

# 1. Install
cd backend   && npm install
npx playwright install chromium
cd ../frontend && npm install

# 2. Initialise DB and seed
cd ../backend
npx prisma migrate dev --name init
npx ts-node src/seed/seed.ts      # 3 months of fake history

# 3. Start backend + frontend (two terminals)
npx nest start                    # backend on :4000
# in another terminal:
cd frontend && npm run dev        # UI on :5173

Open http://localhost:5173. The Vite dev server proxies /api/* to the backend on :4000.

Architecture

┌─ Frontend (React, polls /api) ───────────────────────────┐
│  ConnectAccount • SuggestionCard • EditModal             │
│  PatternsView • DebugPanel • RecentActivity              │
└───────────────────────────────┬──────────────────────────┘
                                │
┌─ Backend (NestJS) ─────────────▼──────────────────────────┐
│                                                           │
│  ConnectionModule                                         │
│   ├─ ConnectionManagerService (Playwright headful)        │
│   └─ CookieVaultService (AES-256-GCM)                     │
│                                                           │
│  AdaptersModule                                           │
│   ├─ UberAdapter  (REAL, GraphQL)                         │
│   ├─ ZomatoAdapter (REAL, REST)                           │
│   ├─ Mock{Uber,Ola,Rapido,Swiggy,Zomato}Adapter           │
│   ├─ CircuitBreakerService (3 fails → 5-min skip)         │
│   └─ AdapterRegistry (real if healthy, else mock)         │
│                                                           │
│  MemoryModule (Ingestion → PatternLearner → Feedback)     │
│   └─ exponential decay (30-day half-life)                 │
│                                                           │
│  TriggerModule (pure TriggerEngine + @Cron every 30 s)    │
│                                                           │
│  SuggestionsModule (Builder + StateMachine + REST)        │
│                                                           │
│  DebugModule (time-travel, signal injection, Uber-down)   │
│                                                           │
│  Prisma → SQLite (data/app.db)                            │
└───────────────────────────────────────────────────────────┘

The four pillars (assignment requirements)

1. Trigger logicTriggerEngineService.shouldTrigger() is a pure function (no DB/HTTP), fully unit-tested. It applies, in order:

  • Day-of-week + time-window match
  • Confidence threshold (default 0.25)
  • Dismissal penalty: confidence × 0.8^(dismissals in last 7 days)
  • Already-acted suppression (user placed a ride/order in the last 2 h → no trigger)
  • Cooldown (default 30 min per pattern)
  • Daily limit per type (default 4)
  • Urgency elevation when traffic ≥10 min over baseline or delivery delay ≥10 min

2. Memory — two-tier storage so a better algorithm doesn't lose data:

  • RawEvent (append-only, source of truth)
  • Pattern (derived, recomputed from raw events)
  • Feedback (confirm/dismiss/edit log, fed back into future triggers)

Pattern learner uses exponential recency decay with a 30-day half-life: weight = exp(-ln(2) · daysAgo / 30). Confidence for a (dayOfWeek, time-bucket, target) group is the ratio of its weight sum to the total weight of events in that same time bucket.

3. Real-world data integration — every adapter implements DataSource. Adapters are wrapped in CircuitBreakerService (3 failures → 5-min skip). AdapterRegistry routes every call to the real adapter if its healthCheck() passes (connection active with cookies), otherwise it transparently falls back to the mock. SuggestionBuilderService adds a second tier of fallback: live → LiveDataCache → graceful empty. Stale data surfaces with a visible "Live data unavailable" badge in the UI. Schema changes are caught by SchemaChangedError from the adapter — it never returns broken data upstream.

4. UI — every suggestion card shows a "Why:" line (time + frequency + signal reason). Confirm / Edit / Dismiss are all one-tap. StateMachineService enforces valid transitions (shown → confirmed|dismissed|expired) and every action is logged to Feedback for the learning loop. Failure states (stale data, expired cookies) are always visible to the user.

Real integration flow

  1. User clicks Connect next to Uber or Zomato in the UI.
  2. ConnectionManagerService launches a visible Chromium window (Playwright, headless: false).
  3. User signs into the platform normally (email + OTP — nothing new to learn).
  4. A background watcher polls the page URL; when it matches the post-login signature and the required cookies are present, the cookies are encrypted with AES-256-GCM using the vault key and stored in PlatformConnection.
  5. The browser closes automatically. The UI shows the platform as connected.
  6. Clicking Fetch history ingests real history through the real adapter.
  7. Every live call checks connection health; if the server ever returns 401/403, the connection is marked expired and the UI prompts the user to reconnect.

If cookies are missing or expired, AdapterRegistry.get() silently returns the mock adapter instead — nothing breaks, suggestions still surface, and the "Connected" indicator in the UI reflects the true state.

What is real vs mocked

Component State
Connection + cookie capture (Playwright) Real
Cookie encryption (AES-256-GCM) Real
Uber adapter (GraphQL) Real — hits riders.uber.com/graphql with captured cookies
Zomato adapter (REST) Real — hits zomato.com/webroutes/user/orders with captured cookies
Ola / Rapido / Swiggy adapters Mock (Ola/Rapido live APIs are optional in the assignment; Swiggy is included as a mock alongside the real Zomato)
Pattern learning, trigger engine, memory Real logic
Circuit breaker, state machine, feedback loop Real
Live ETA/price/surge Both adapters return a real healthCheck() gate, but the Estimate body itself is best-effort (Uber/Zomato's public estimate endpoints are ephemeral) — see "Honest caveats" below

Honest caveats

  • The Uber GraphQL Activities query uses a reasonable shape based on community-documented reverse-engineering (see sources). If Uber changes the schema, SchemaChangedError fires and the mock transparently takes over.
  • The Zomato order-history endpoint parser walks several possible nestings (sections.SECTION_USER_ORDER_HISTORY, entities.ORDER, page_data.sections...) because the exact shape changes across builds. Again, schema issues fall through to the mock.
  • Live fares and ETAs are best-effort values — real-time pricing APIs are behind additional flows that differ between rider/driver/guest and aren't needed to demonstrate the assignment's trigger logic. The architecture isolates this in one method per adapter, so a production replacement plugs in without touching anything else.

Key assumptions

  • Single user — hardcoded demo-user everywhere. Multi-user auth is not implemented (the PlatformConnection table is keyed on userId already, so the slot is there).
  • Time zone — everything runs in local machine time. Time-travel uses ISO strings.
  • Scheduler cadence@Cron fires every 30 s (demo-friendly). Flip to EVERY_5_MINUTES for production.
  • Booking is not executed — "Confirmed" is a visual state, not a real API call (per assignment).
  • Vault keydata/vault.key is auto-generated on first run, stored with 0600 permissions, and gitignored.

Useful endpoints

Endpoint What it does
GET /api/connections All platforms + connection status
POST /api/connections/:p/start Launch Playwright login for a platform
POST /api/connections/:p/disconnect Forget cookies for a platform
POST /api/memory/ingest Fetch history via the current adapter (real or mock fallback)
GET /api/memory/patterns Learned patterns (for transparency)
POST /api/memory/recompute Re-derive patterns from raw events
GET /api/suggestions/active Currently shown suggestions
POST /api/suggestions/:id/confirm One-tap confirm
POST /api/suggestions/:id/edit-confirm Edit + confirm in one call
POST /api/suggestions/:id/dismiss Dismiss (penalty applies)
POST /api/debug/time { iso } — set virtual time (null resets)
POST /api/debug/signals { rideEtaDeltaMin, deliveryDelayMin }
POST /api/debug/uber-down { down: true/false } — simulate adapter failure
POST /api/debug/tick Run one trigger evaluation immediately
POST /api/debug/reset-suggestions Clear suggestions + feedback

Testing

cd backend
npx jest   # 7 trigger engine tests, all passing

Tests cover: time match, miss, already-acted, cooldown, urgency boost, dismissal math, confidence threshold.

Project layout

pokus/
├── ass.txt                 # original assignment
├── README.md
├── backend/
│   ├── prisma/schema.prisma
│   └── src/
│       ├── connection/     # Playwright headful + AES-256-GCM vault
│       ├── adapters/
│       │   ├── uber/       # Real GraphQL adapter
│       │   ├── zomato/     # Real REST adapter
│       │   ├── mock/       # Mock for every platform
│       │   ├── base/       # DataSource + CircuitBreaker
│       │   └── adapter-registry.service.ts
│       ├── memory/         # Ingestion, PatternLearner (decay), Feedback
│       ├── trigger/        # Pure TriggerEngine + @Cron scheduler
│       ├── suggestions/    # Builder, StateMachine, REST controllers
│       ├── debug/          # Time-travel + signal injection for demo
│       └── seed/seed.ts    # 3-month fake history
├── frontend/src/
│   ├── components/
│   │   ├── ConnectAccount.tsx       # Real connection UI
│   │   ├── SuggestionCard.tsx
│   │   ├── EditModal.tsx
│   │   ├── PatternsView.tsx
│   │   ├── DebugPanel.tsx
│   │   └── RecentActivity.tsx
│   ├── hooks/              # useSuggestions, useDebugState
│   ├── api/client.ts       # Typed REST client
│   └── App.tsx
└── data/
    ├── app.db              # SQLite (generated)
    └── vault.key           # AES key (generated, gitignored)

Things deliberately not built

  • Multi-user auth — single hardcoded user (demo-user) throughout.
  • Live deployment — localhost only. Deployment was marked optional in the assignment.
  • WebSocket push — frontend polls every 3 s. Equivalent UX for a demo, simpler wire.
  • Production-grade Uber/Zomato price APIs — the live Estimate body is best-effort; the health gate and architecture are correct.

Sources for the reverse-engineering work

About

An AI-powered assistant that proactively monitors ride fares and food delivery prices, automatically finding the best deals, tracking price drops, and helping users save money without constantly checking multiple apps.

Resources

Stars

Watchers

Forks

Contributors

Languages