* introducing airflow to run pipeline * chore: updating dag with upload to registry * introducing complete provider (non refactored and noisy) * chore: removing old shit * generic pricing baselines * feature: super simple model registry (to be updated maybe third party OS software) * chore: refactoring the providers docker config and requirements * chore: refactored and broke down components (braking * exporting all * local pipeline excution working * fix: fixing import structures from nonrelativistic * chore: enables cross comm pickling with fully e2e pipeline compilation * docs: what the pipeline is like now * pipelines local running and pipeline high level definition * cleaning old pipeline and vectorization * leaked but fixing, not so important * test: started with pipeline step testing * chore: cleaning up provider of prices * test: extra tests wit hsemantic meaning checks * migrating pricers * feature: introducing pricing predictors (pricers) * chore: e2e is done with new pipeline * extra session feature extraction * feature: experiemntal sessin pricer and metrics(vibe) * chore: redefined and connected pricers (#29)
This is a Next.js project bootstrapped with create-next-app.
Phantom Air/Hotels
Design Discovery Documentation: https://github.com/velocitatem/PHANTOM/wiki/Design-Discovery
This webapp serves two modes
{HOTEL,AIRLINE}which are given by an env variable
The webapp should serve under the / route the landing page which for both platforms is very similar. We define a set of components like Hero, Card, Button, Link ... This we can then pass to specific components each mode might demand that makes it behave differently, hotel cards showing hotel rooms from database and airline cards showing flights from database and each fetching prices from the pricing provider with a different HTTP parameter.
- globally we define a middleware.ts which is our switcher for modes.
- /app will have (airline) and (hotel) children which each have a layout.tsx and page.tsx where /app also has a parent layout defining layout.tsx and globals.css for any shared styling to avoid repretition.
- /components/ is gonna have ui/ which defines things like Button, Card, DatePicker with generic definitions and any tracking or observation code. We then define feats/airline/ and feats/hotel/ as children of components with specific components like AirlineHero and HotelCard.
- in /styles/ we define airline.css and hotel.css to tailor accents and styling for each.
How to Run
# install deps
npm install
# set store mode (hotel or airline)
export STORE_MODE=hotel
# run dev server
npm run dev
Server runs on http://localhost:3000
Environment Variables
| Variable | Description | Default | Example |
|---|---|---|---|
HOSTNAME |
Server hostname | localhost |
localhost |
STORE_MODE |
Mode switch for platform | hotel |
hotel or airline |
NEXT_PUBLIC_API_BASE |
Public API base URL | http://localhost:3000 |
http://localhost:3000 |
NEXT_PUBLIC_APP_ENV |
Application environment | dev |
dev, prod |
NEXT_PUBLIC_HOVER_THRESHOLD |
Hover dwell threshold (ms) | 1200 |
1200 |
BACKEND_URL |
Backend service URL | http://localhost:5000 |
http://localhost:5000 |
Routes
Public Pages
/— Landing page (mode-aware root)/hotel— Hotel mode landing/hotel/products— Hotel catalog/airline— Airline mode landing/airline/products— Flight catalog/admin/experiments— Experiment management UI
API Routes
GET /api/session— Fetch or create session, sets httpOnly cookieGET /api/pricing?productId=X&sessionId=Y&experimentId=Z— Get product price from providerPOST /api/ingest— Ingest event to Kafka via backendGET /api/admin/experiments— List all experimentsPOST /api/admin/experiments/start— Start new experiment for sessionPOST /api/admin/experiments/stop— Stop experiment by ID
Event Catalog
All events are ingested via POST /api/ingest and follow the EventBase schema. Below are the 17 canonical events:
| Event Name | Category | Payload Example |
|---|---|---|
session_start |
Session | { sessionId, experimentId?, storeMode, ts, page, eventName, userAgent? } |
page_view |
Navigation | { sessionId, experimentId?, storeMode, ts, page: "/hotel", eventName: "page_view" } |
view_item_page |
Discovery | { sessionId, storeMode, ts, page: "/hotel/products", productId: "H001", eventName: "view_item_page" } |
learn_more_about_item |
Discovery | { sessionId, storeMode, ts, page, productId, eventName: "learn_more_about_item" } |
add_item_to_cart |
Cart | { sessionId, storeMode, ts, page, productId, eventName: "add_item_to_cart" } |
remove_item |
Cart | { sessionId, storeMode, ts, page, productId, eventName: "remove_item" } |
checkout_start |
Cart | { sessionId, storeMode, ts, page, eventName: "checkout_start" } |
purchase_complete |
Cart | { sessionId, storeMode, ts, page, eventName: "purchase_complete", metadata?: { total: 500 } } |
search |
Filter/Search | { sessionId, storeMode, ts, page, eventName: "search", metadata: { query: "paris" } } |
filter_for_date |
Filter/Search | { sessionId, storeMode, ts, page, eventName: "filter_for_date", metadata: { from: "2025-01-15", to: "2025-01-20" } } |
filter_for_amenities |
Filter/Search | { sessionId, storeMode, ts, page, eventName: "filter_for_amenities", metadata: { amenities: ["wifi", "pool"] } } |
filter_for_price |
Filter/Search | { sessionId, storeMode, ts, page, eventName: "filter_for_price", metadata: { min: 100, max: 500 } } |
sort_change |
Filter/Search | { sessionId, storeMode, ts, page, eventName: "sort_change", metadata: { sort: "price_asc" } } |
hover_over_title |
Dwell signal | { sessionId, storeMode, ts, page, productId?, eventName: "hover_over_title", metadata: { duration: 1500 } } |
hover_over_paragraph |
Dwell signal | { sessionId, storeMode, ts, page, productId?, eventName: "hover_over_paragraph", metadata: { duration: 2000 } } |
hover_over_link |
Dwell signal | { sessionId, storeMode, ts, page, productId?, eventName: "hover_over_link", metadata: { href: "/hotel/products" } } |
hover_over_button |
Dwell signal | { sessionId, storeMode, ts, page, productId?, eventName: "hover_over_button", metadata: { label: "Book Now" } } |
Architecture
Route Groups
(hotel)— Hotel mode pages(airline)— Airline mode pagesapi/*— API routes (session, pricing, ingest, admin)
Middleware Flow
- Request arrives at Next.js
- Session middleware checks for
phantom_session_idcookie - If missing,
/api/sessionmints new session + sets cookie - Store mode (
STORE_MODEenv) determines rendered page variant - Client-side components fetch pricing via
/api/pricing - User interactions emit events to
/api/ingest→ Kafka