Umar - Bootcamp
This commit is contained in:
33
week8/community_contributions/w8d5/agents/agent.py
Normal file
33
week8/community_contributions/w8d5/agents/agent.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import logging
|
||||
|
||||
class Agent:
|
||||
"""
|
||||
An abstract superclass for Agents
|
||||
Used to log messages in a way that can identify each Agent
|
||||
"""
|
||||
|
||||
# Foreground colors
|
||||
RED = '\033[31m'
|
||||
GREEN = '\033[32m'
|
||||
YELLOW = '\033[33m'
|
||||
BLUE = '\033[34m'
|
||||
MAGENTA = '\033[35m'
|
||||
CYAN = '\033[36m'
|
||||
WHITE = '\033[37m'
|
||||
|
||||
# Background color
|
||||
BG_BLACK = '\033[40m'
|
||||
|
||||
# Reset code to return to default color
|
||||
RESET = '\033[0m'
|
||||
|
||||
name: str = ""
|
||||
color: str = '\033[37m'
|
||||
|
||||
def log(self, message):
|
||||
"""
|
||||
Log this as an info message, identifying the agent
|
||||
"""
|
||||
color_code = self.BG_BLACK + self.color
|
||||
message = f"[{self.name}] {message}"
|
||||
logging.info(color_code + message + self.RESET)
|
||||
@@ -0,0 +1,75 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import List, Dict
|
||||
from openai import OpenAI
|
||||
from sentence_transformers import SentenceTransformer
|
||||
|
||||
w8d5_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
if w8d5_path not in sys.path:
|
||||
sys.path.insert(0, w8d5_path)
|
||||
|
||||
from agents.agent import Agent
|
||||
|
||||
|
||||
class TravelEstimatorAgent(Agent):
|
||||
|
||||
name = "Travel Estimator"
|
||||
color = Agent.BLUE
|
||||
|
||||
MODEL = "gpt-4o-mini"
|
||||
|
||||
def __init__(self, collection):
|
||||
self.log("Travel Estimator initializing")
|
||||
self.client = OpenAI()
|
||||
self.MODEL = "gpt-4o-mini"
|
||||
self.log("Travel Estimator using OpenAI")
|
||||
self.collection = collection
|
||||
self.model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
|
||||
self.log("Travel Estimator ready")
|
||||
|
||||
def make_context(self, similars: List[str], prices: List[float]) -> str:
|
||||
message = "Here are similar travel deals for context:\n\n"
|
||||
for similar, price in zip(similars, prices):
|
||||
message += f"Similar deal:\n{similar}\nPrice: ${price:.2f}\n\n"
|
||||
return message
|
||||
|
||||
def messages_for(self, description: str, similars: List[str], prices: List[float]) -> List[Dict[str, str]]:
|
||||
system_message = "You estimate fair market prices for travel deals. Reply only with the price estimate, no explanation"
|
||||
user_prompt = self.make_context(similars, prices)
|
||||
user_prompt += "Now estimate the fair market price for:\n\n"
|
||||
user_prompt += description
|
||||
return [
|
||||
{"role": "system", "content": system_message},
|
||||
{"role": "user", "content": user_prompt},
|
||||
{"role": "assistant", "content": "Fair price estimate: $"}
|
||||
]
|
||||
|
||||
def find_similars(self, description: str):
|
||||
self.log("Travel Estimator searching for similar deals")
|
||||
vector = self.model.encode([description])
|
||||
results = self.collection.query(query_embeddings=vector.astype(float).tolist(), n_results=5)
|
||||
documents = results['documents'][0][:]
|
||||
prices = [m['price'] for m in results['metadatas'][0][:]]
|
||||
self.log("Travel Estimator found similar deals")
|
||||
return documents, prices
|
||||
|
||||
def get_price(self, s) -> float:
|
||||
s = s.replace('$','').replace(',','')
|
||||
match = re.search(r"[-+]?\d*\.\d+|\d+", s)
|
||||
return float(match.group()) if match else 0.0
|
||||
|
||||
def estimate(self, description: str) -> float:
|
||||
documents, prices = self.find_similars(description)
|
||||
self.log(f"Travel Estimator calling {self.MODEL}")
|
||||
response = self.client.chat.completions.create(
|
||||
model=self.MODEL,
|
||||
messages=self.messages_for(description, documents, prices),
|
||||
seed=42,
|
||||
max_tokens=10
|
||||
)
|
||||
reply = response.choices[0].message.content
|
||||
result = self.get_price(reply)
|
||||
self.log(f"Travel Estimator complete - ${result:.2f}")
|
||||
return result
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import os
|
||||
import sys
|
||||
import http.client
|
||||
import urllib
|
||||
|
||||
w8d5_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
if w8d5_path not in sys.path:
|
||||
sys.path.insert(0, w8d5_path)
|
||||
|
||||
from agents.agent import Agent
|
||||
from helpers.travel_deals import TravelOpportunity
|
||||
|
||||
DO_PUSH = True
|
||||
|
||||
class TravelMessagingAgent(Agent):
|
||||
|
||||
name = "Travel Messenger"
|
||||
color = Agent.WHITE
|
||||
|
||||
def __init__(self):
|
||||
self.log("Travel Messenger initializing")
|
||||
if DO_PUSH:
|
||||
self.pushover_user = os.getenv('PUSHOVER_USER', 'your-pushover-user-if-not-using-env')
|
||||
self.pushover_token = os.getenv('PUSHOVER_TOKEN', 'your-pushover-token-if-not-using-env')
|
||||
self.log("Travel Messenger has initialized Pushover")
|
||||
|
||||
def push(self, text):
|
||||
self.log("Travel Messenger sending push notification")
|
||||
conn = http.client.HTTPSConnection("api.pushover.net:443")
|
||||
conn.request("POST", "/1/messages.json",
|
||||
urllib.parse.urlencode({
|
||||
"token": self.pushover_token,
|
||||
"user": self.pushover_user,
|
||||
"message": text,
|
||||
"sound": "cashregister"
|
||||
}), { "Content-type": "application/x-www-form-urlencoded" })
|
||||
conn.getresponse()
|
||||
|
||||
def alert(self, opportunity: TravelOpportunity):
|
||||
text = f"Travel Deal! {opportunity.deal.destination} - "
|
||||
text += f"Price=${opportunity.deal.price:.2f}, "
|
||||
text += f"Est=${opportunity.estimate:.2f}, "
|
||||
text += f"Save ${opportunity.discount:.2f}! "
|
||||
text += opportunity.deal.url
|
||||
if DO_PUSH:
|
||||
self.push(text)
|
||||
self.log("Travel Messenger completed")
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional, List
|
||||
|
||||
w8d5_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
if w8d5_path not in sys.path:
|
||||
sys.path.insert(0, w8d5_path)
|
||||
|
||||
from agents.agent import Agent
|
||||
from helpers.travel_deals import TravelDeal, TravelOpportunity
|
||||
from agents.travel_scanner_agent import TravelScannerAgent
|
||||
from agents.travel_estimator_agent import TravelEstimatorAgent
|
||||
from agents.travel_messaging_agent import TravelMessagingAgent
|
||||
|
||||
|
||||
class TravelPlanningAgent(Agent):
|
||||
|
||||
name = "Travel Planner"
|
||||
color = Agent.GREEN
|
||||
DEAL_THRESHOLD = 50
|
||||
|
||||
def __init__(self, collection):
|
||||
self.log("Travel Planner initializing")
|
||||
self.scanner = TravelScannerAgent()
|
||||
self.estimator = TravelEstimatorAgent(collection)
|
||||
self.messenger = TravelMessagingAgent()
|
||||
self.log("Travel Planner ready")
|
||||
|
||||
def evaluate(self, deal: TravelDeal) -> TravelOpportunity:
|
||||
self.log(f"Travel Planner evaluating {deal.destination}")
|
||||
estimate = self.estimator.estimate(deal.description)
|
||||
discount = estimate - deal.price
|
||||
self.log(f"Travel Planner found discount ${discount:.2f}")
|
||||
return TravelOpportunity(deal=deal, estimate=estimate, discount=discount)
|
||||
|
||||
def plan(self, memory: List[str] = []) -> Optional[List[TravelOpportunity]]:
|
||||
self.log("Travel Planner starting run")
|
||||
selection = self.scanner.scan(memory=memory)
|
||||
if selection and selection.deals:
|
||||
opportunities = [self.evaluate(deal) for deal in selection.deals[:5]]
|
||||
if not opportunities:
|
||||
self.log("Travel Planner found no valid opportunities")
|
||||
return None
|
||||
opportunities.sort(key=lambda opp: opp.discount, reverse=True)
|
||||
good_deals = [opp for opp in opportunities if opp.discount > self.DEAL_THRESHOLD]
|
||||
if good_deals:
|
||||
best = good_deals[0]
|
||||
self.log(f"Travel Planner found {len(good_deals)} deals above threshold, best: ${best.discount:.2f} off")
|
||||
self.messenger.alert(best)
|
||||
self.log("Travel Planner completed")
|
||||
return good_deals
|
||||
else:
|
||||
self.log(f"Travel Planner completed - no deals above ${self.DEAL_THRESHOLD} threshold")
|
||||
return None
|
||||
self.log("Travel Planner found no deals to evaluate")
|
||||
return None
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional, List
|
||||
from openai import OpenAI
|
||||
|
||||
w8d5_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
if w8d5_path not in sys.path:
|
||||
sys.path.insert(0, w8d5_path)
|
||||
|
||||
from agents.agent import Agent
|
||||
from helpers.travel_deals import ScrapedTravelDeal, TravelDealSelection
|
||||
|
||||
|
||||
class TravelScannerAgent(Agent):
|
||||
|
||||
MODEL = "gpt-4o-mini"
|
||||
|
||||
SYSTEM_PROMPT = """You identify and summarize the 5 most promising travel deals from a list.
|
||||
Focus on deals with destinations, deal types (flight/hotel/package), and detailed descriptions.
|
||||
If price is mentioned, extract it. If no specific price is given but there's a discount mentioned (e.g. "30% off"), estimate a reasonable price.
|
||||
If absolutely no pricing information exists, use a placeholder price of 500.
|
||||
Respond strictly in JSON with no explanation.
|
||||
|
||||
{"deals": [
|
||||
{
|
||||
"destination": "City or Country name",
|
||||
"deal_type": "Flight, Hotel, or Package",
|
||||
"description": "4-5 sentences describing the travel deal, dates, what's included, and key highlights",
|
||||
"price": 499.99,
|
||||
"url": "the url as provided"
|
||||
},
|
||||
...
|
||||
]}"""
|
||||
|
||||
USER_PROMPT_PREFIX = """Respond with the 5 most promising travel deals with destinations, types, and descriptions.
|
||||
Respond strictly in JSON. Provide detailed descriptions focusing on what travelers get.
|
||||
Extract the destination and deal type (Flight/Hotel/Package) from the title and description.
|
||||
For pricing: extract exact prices if available, estimate from percentage discounts, or use 500 as placeholder.
|
||||
|
||||
Travel Deals:
|
||||
|
||||
"""
|
||||
|
||||
USER_PROMPT_SUFFIX = "\n\nStrictly respond in JSON with exactly 5 deals."
|
||||
|
||||
name = "Travel Scanner"
|
||||
color = Agent.CYAN
|
||||
|
||||
def __init__(self):
|
||||
self.log("Travel Scanner is initializing")
|
||||
self.openai = OpenAI()
|
||||
self.log("Travel Scanner is ready")
|
||||
|
||||
def fetch_deals(self, memory) -> List[ScrapedTravelDeal]:
|
||||
self.log("Travel Scanner fetching deals from RSS feeds")
|
||||
urls = [opp.deal.url for opp in memory]
|
||||
scraped = ScrapedTravelDeal.fetch()
|
||||
result = [scrape for scrape in scraped if scrape.url not in urls]
|
||||
self.log(f"Travel Scanner found {len(result)} new deals")
|
||||
return result
|
||||
|
||||
def make_user_prompt(self, scraped) -> str:
|
||||
user_prompt = self.USER_PROMPT_PREFIX
|
||||
user_prompt += '\n\n'.join([scrape.describe() for scrape in scraped])
|
||||
user_prompt += self.USER_PROMPT_SUFFIX
|
||||
return user_prompt
|
||||
|
||||
def scan(self, memory: List[str]=[]) -> Optional[TravelDealSelection]:
|
||||
scraped = self.fetch_deals(memory)
|
||||
if scraped:
|
||||
user_prompt = self.make_user_prompt(scraped)
|
||||
self.log("Travel Scanner calling OpenAI")
|
||||
result = self.openai.beta.chat.completions.parse(
|
||||
model=self.MODEL,
|
||||
messages=[
|
||||
{"role": "system", "content": self.SYSTEM_PROMPT},
|
||||
{"role": "user", "content": user_prompt}
|
||||
],
|
||||
response_format=TravelDealSelection
|
||||
)
|
||||
result = result.choices[0].message.parsed
|
||||
valid_deals = [deal for deal in result.deals if deal.price > 0]
|
||||
result.deals = valid_deals
|
||||
self.log(f"Travel Scanner received {len(result.deals)} valid deals")
|
||||
return result if result.deals else None
|
||||
return None
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import os
|
||||
import sys
|
||||
import numpy as np
|
||||
import joblib
|
||||
from sentence_transformers import SentenceTransformer
|
||||
import xgboost as xgb
|
||||
|
||||
w8d5_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
if w8d5_path not in sys.path:
|
||||
sys.path.insert(0, w8d5_path)
|
||||
|
||||
from agents.agent import Agent
|
||||
|
||||
|
||||
class TravelXGBoostAgent(Agent):
|
||||
|
||||
name = "XGBoost Estimator"
|
||||
color = Agent.GREEN
|
||||
|
||||
def __init__(self, collection):
|
||||
self.log("XGBoost Estimator initializing")
|
||||
self.collection = collection
|
||||
self.model_path = os.path.join(w8d5_path, 'helpers', 'travel_xgboost_model.pkl')
|
||||
self.embedder = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
|
||||
|
||||
if os.path.exists(self.model_path):
|
||||
self.log("Loading existing XGBoost model")
|
||||
self.model = joblib.load(self.model_path)
|
||||
else:
|
||||
self.log("Training new XGBoost model")
|
||||
self.model = self._train_model()
|
||||
joblib.dump(self.model, self.model_path)
|
||||
self.log(f"XGBoost model saved to {self.model_path}")
|
||||
|
||||
self.log("XGBoost Estimator ready")
|
||||
|
||||
def _train_model(self):
|
||||
self.log("Fetching training data from ChromaDB")
|
||||
result = self.collection.get(include=['embeddings', 'metadatas'])
|
||||
|
||||
X = np.array(result['embeddings'])
|
||||
y = np.array([m['price'] for m in result['metadatas']])
|
||||
|
||||
self.log(f"Training on {len(X)} samples")
|
||||
|
||||
model = xgb.XGBRegressor(
|
||||
n_estimators=100,
|
||||
max_depth=6,
|
||||
learning_rate=0.1,
|
||||
subsample=0.8,
|
||||
colsample_bytree=0.8,
|
||||
random_state=42,
|
||||
n_jobs=-1
|
||||
)
|
||||
|
||||
model.fit(X, y)
|
||||
self.log("XGBoost training complete")
|
||||
|
||||
return model
|
||||
|
||||
def estimate(self, description: str) -> float:
|
||||
self.log(f"XGBoost estimating price for: {description[:50]}...")
|
||||
|
||||
embedding = self.embedder.encode([description])[0]
|
||||
embedding_2d = embedding.reshape(1, -1)
|
||||
|
||||
prediction = self.model.predict(embedding_2d)[0]
|
||||
|
||||
prediction = max(0, prediction)
|
||||
|
||||
self.log(f"XGBoost estimate: ${prediction:.2f}")
|
||||
return float(prediction)
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
import os
|
||||
import random
|
||||
from dotenv import load_dotenv
|
||||
from huggingface_hub import login
|
||||
from sentence_transformers import SentenceTransformer
|
||||
import chromadb
|
||||
from tqdm import tqdm
|
||||
|
||||
load_dotenv(override=True)
|
||||
os.environ['HF_TOKEN'] = os.getenv('HF_TOKEN', 'your-key-if-not-using-env')
|
||||
|
||||
hf_token = os.environ['HF_TOKEN']
|
||||
login(hf_token, add_to_git_credential=True)
|
||||
|
||||
DB = "travel_vectorstore"
|
||||
CATEGORIES = ['Flights', 'Hotels', 'Car_Rentals', 'Vacation_Packages', 'Cruises', 'Activities']
|
||||
|
||||
AIRLINES = ['American Airlines', 'Delta', 'United', 'Southwest', 'JetBlue', 'Spirit', 'Frontier', 'Alaska Airlines', 'Emirates', 'British Airways', 'Air France', 'Lufthansa', 'Qatar Airways']
|
||||
CITIES = ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Miami', 'San Francisco', 'Boston', 'Seattle', 'Denver', 'Atlanta', 'Las Vegas', 'Orlando', 'Phoenix', 'London', 'Paris', 'Tokyo', 'Dubai', 'Singapore', 'Sydney', 'Rome']
|
||||
HOTELS = ['Hilton', 'Marriott', 'Hyatt', 'Holiday Inn', 'Best Western', 'Sheraton', 'Ritz-Carlton', 'Four Seasons', 'Westin', 'Radisson']
|
||||
CLASSES = ['Economy', 'Premium Economy', 'Business', 'First Class']
|
||||
CAR_COMPANIES = ['Hertz', 'Enterprise', 'Avis', 'Budget', 'National', 'Alamo']
|
||||
CAR_TYPES = ['Compact', 'Sedan', 'SUV', 'Luxury', 'Van']
|
||||
|
||||
def generate_flight_description():
|
||||
airline = random.choice(AIRLINES)
|
||||
source = random.choice(CITIES)
|
||||
dest = random.choice([c for c in CITIES if c != source])
|
||||
flight_class = random.choice(CLASSES)
|
||||
stops = random.choice(['non-stop', 'one-stop', 'two-stops'])
|
||||
duration = f"{random.randint(1, 15)} hours {random.randint(0, 59)} minutes"
|
||||
|
||||
description = f"{airline} {flight_class} {stops} flight from {source} to {dest}. "
|
||||
description += f"Flight duration approximately {duration}. "
|
||||
|
||||
if random.random() > 0.5:
|
||||
description += f"Includes {random.randint(1, 2)} checked bag"
|
||||
if random.random() > 0.5:
|
||||
description += "s"
|
||||
description += ". "
|
||||
|
||||
if flight_class in ['Business', 'First Class']:
|
||||
description += random.choice(['Priority boarding included. ', 'Lounge access available. ', 'Lie-flat seats. '])
|
||||
|
||||
price = random.randint(150, 2500) if flight_class == 'Economy' else random.randint(800, 8000)
|
||||
return description, price
|
||||
|
||||
def generate_hotel_description():
|
||||
hotel = random.choice(HOTELS)
|
||||
city = random.choice(CITIES)
|
||||
stars = random.randint(2, 5)
|
||||
room_type = random.choice(['Standard Room', 'Deluxe Room', 'Suite', 'Executive Suite'])
|
||||
nights = random.randint(1, 7)
|
||||
|
||||
description = f"{hotel} {stars}-star hotel in {city}. {room_type} for {nights} night"
|
||||
if nights > 1:
|
||||
description += "s"
|
||||
description += ". "
|
||||
|
||||
amenities = []
|
||||
if random.random() > 0.3:
|
||||
amenities.append('Free WiFi')
|
||||
if random.random() > 0.5:
|
||||
amenities.append('Breakfast included')
|
||||
if random.random() > 0.6:
|
||||
amenities.append('Pool access')
|
||||
if random.random() > 0.7:
|
||||
amenities.append('Gym')
|
||||
if random.random() > 0.8:
|
||||
amenities.append('Spa services')
|
||||
|
||||
if amenities:
|
||||
description += f"Amenities: {', '.join(amenities)}. "
|
||||
|
||||
price_per_night = random.randint(80, 500) if stars <= 3 else random.randint(200, 1200)
|
||||
total_price = price_per_night * nights
|
||||
|
||||
return description, total_price
|
||||
|
||||
def generate_car_rental_description():
|
||||
company = random.choice(CAR_COMPANIES)
|
||||
car_type = random.choice(CAR_TYPES)
|
||||
city = random.choice(CITIES)
|
||||
days = random.randint(1, 14)
|
||||
|
||||
description = f"{company} car rental in {city}. {car_type} class vehicle for {days} day"
|
||||
if days > 1:
|
||||
description += "s"
|
||||
description += ". "
|
||||
|
||||
if random.random() > 0.6:
|
||||
description += "Unlimited mileage included. "
|
||||
if random.random() > 0.5:
|
||||
description += "Airport pickup available. "
|
||||
if random.random() > 0.7:
|
||||
description += "GPS navigation included. "
|
||||
|
||||
daily_rate = {'Compact': random.randint(25, 45), 'Sedan': random.randint(35, 65), 'SUV': random.randint(50, 90), 'Luxury': random.randint(80, 200), 'Van': random.randint(60, 100)}
|
||||
total_price = daily_rate[car_type] * days
|
||||
|
||||
return description, total_price
|
||||
|
||||
def generate_vacation_package_description():
|
||||
city = random.choice(CITIES)
|
||||
nights = random.randint(3, 10)
|
||||
|
||||
description = f"All-inclusive vacation package to {city} for {nights} nights. "
|
||||
description += f"Includes round-trip {random.choice(CLASSES)} flights, {random.choice(HOTELS)} hotel accommodation, "
|
||||
|
||||
extras = []
|
||||
if random.random() > 0.3:
|
||||
extras.append('daily breakfast')
|
||||
if random.random() > 0.5:
|
||||
extras.append('airport transfers')
|
||||
if random.random() > 0.6:
|
||||
extras.append('city tour')
|
||||
if random.random() > 0.7:
|
||||
extras.append('travel insurance')
|
||||
|
||||
if extras:
|
||||
description += f"and {', '.join(extras)}. "
|
||||
|
||||
base_price = random.randint(800, 4000)
|
||||
return description, base_price
|
||||
|
||||
def generate_cruise_description():
|
||||
destinations = [', '.join(random.sample(['Caribbean', 'Mediterranean', 'Alaska', 'Hawaii', 'Baltic Sea', 'South Pacific'], k=random.randint(2, 4)))]
|
||||
nights = random.choice([3, 5, 7, 10, 14])
|
||||
|
||||
description = f"{nights}-night cruise visiting {destinations[0]}. "
|
||||
description += f"All meals and entertainment included. "
|
||||
|
||||
cabin_type = random.choice(['Interior cabin', 'Ocean view cabin', 'Balcony cabin', 'Suite'])
|
||||
description += f"{cabin_type}. "
|
||||
|
||||
if random.random() > 0.5:
|
||||
description += "Unlimited beverage package available. "
|
||||
if random.random() > 0.6:
|
||||
description += "Shore excursions at each port. "
|
||||
|
||||
base_price = random.randint(500, 5000)
|
||||
return description, base_price
|
||||
|
||||
def generate_activity_description():
|
||||
city = random.choice(CITIES)
|
||||
activities = ['City sightseeing tour', 'Museum pass', 'Adventure sports package', 'Wine tasting tour', 'Cooking class', 'Hot air balloon ride', 'Snorkeling excursion', 'Helicopter tour', 'Spa day package', 'Theme park tickets']
|
||||
activity = random.choice(activities)
|
||||
|
||||
description = f"{activity} in {city}. "
|
||||
|
||||
if 'tour' in activity.lower():
|
||||
description += f"Duration: {random.randint(2, 8)} hours. "
|
||||
if random.random() > 0.5:
|
||||
description += "Hotel pickup included. "
|
||||
if random.random() > 0.6:
|
||||
description += "Small group experience. "
|
||||
|
||||
price = random.randint(30, 500)
|
||||
return description, price
|
||||
|
||||
GENERATORS = {
|
||||
'Flights': generate_flight_description,
|
||||
'Hotels': generate_hotel_description,
|
||||
'Car_Rentals': generate_car_rental_description,
|
||||
'Vacation_Packages': generate_vacation_package_description,
|
||||
'Cruises': generate_cruise_description,
|
||||
'Activities': generate_activity_description
|
||||
}
|
||||
|
||||
print("Generating synthetic travel dataset...")
|
||||
travel_data = []
|
||||
|
||||
items_per_category = 3334
|
||||
for category in CATEGORIES:
|
||||
print(f"Generating {category}...")
|
||||
generator = GENERATORS[category]
|
||||
for _ in range(items_per_category):
|
||||
description, price = generator()
|
||||
travel_data.append((description, float(price), category))
|
||||
|
||||
random.shuffle(travel_data)
|
||||
print(f"Generated {len(travel_data)} travel deals")
|
||||
|
||||
print("\nInitializing SentenceTransformer model...")
|
||||
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
|
||||
|
||||
print(f"Connecting to ChromaDB at {DB}...")
|
||||
client = chromadb.PersistentClient(path=DB)
|
||||
|
||||
collection_name = "travel_deals"
|
||||
existing_collections = [col.name for col in client.list_collections()]
|
||||
if collection_name in existing_collections:
|
||||
client.delete_collection(collection_name)
|
||||
print(f"Deleted existing collection: {collection_name}")
|
||||
|
||||
collection = client.create_collection(collection_name)
|
||||
print(f"Created new collection: {collection_name}")
|
||||
|
||||
print("\nCreating embeddings and adding to ChromaDB...")
|
||||
for i in tqdm(range(0, len(travel_data), 1000)):
|
||||
batch = travel_data[i:i+1000]
|
||||
documents = [desc for desc, _, _ in batch]
|
||||
vectors = model.encode(documents).astype(float).tolist()
|
||||
metadatas = [{"category": cat, "price": price} for _, price, cat in batch]
|
||||
ids = [f"travel_{j}" for j in range(i, i+len(batch))]
|
||||
|
||||
collection.add(
|
||||
ids=ids,
|
||||
documents=documents,
|
||||
embeddings=vectors,
|
||||
metadatas=metadatas
|
||||
)
|
||||
|
||||
total_items = collection.count()
|
||||
print(f"\nVectorstore created successfully with {total_items} travel deals")
|
||||
|
||||
result = collection.get(include=['metadatas'], limit=total_items)
|
||||
categories = [m['category'] for m in result['metadatas']]
|
||||
prices = [m['price'] for m in result['metadatas']]
|
||||
category_counts = {}
|
||||
for cat in categories:
|
||||
category_counts[cat] = category_counts.get(cat, 0) + 1
|
||||
|
||||
print("\nCategory distribution:")
|
||||
for category, count in sorted(category_counts.items()):
|
||||
print(f" {category}: {count}")
|
||||
|
||||
avg_price = sum(prices) / len(prices) if prices else 0
|
||||
print(f"\nAverage price: ${avg_price:.2f}")
|
||||
print(f"Price range: ${min(prices):.2f} - ${max(prices):.2f}")
|
||||
@@ -0,0 +1,99 @@
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import json
|
||||
from typing import List, Optional
|
||||
from dotenv import load_dotenv
|
||||
import chromadb
|
||||
import numpy as np
|
||||
from sklearn.manifold import TSNE
|
||||
|
||||
w8d5_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
if w8d5_path not in sys.path:
|
||||
sys.path.insert(0, w8d5_path)
|
||||
|
||||
from agents.travel_planning_agent import TravelPlanningAgent
|
||||
from helpers.travel_deals import TravelOpportunity
|
||||
|
||||
BG_BLUE = '\033[44m'
|
||||
WHITE = '\033[37m'
|
||||
RESET = '\033[0m'
|
||||
|
||||
CATEGORIES = ['Flights', 'Hotels', 'Car_Rentals', 'Vacation_Packages', 'Cruises', 'Activities']
|
||||
COLORS = ['red', 'blue', 'green', 'orange', 'purple', 'cyan']
|
||||
|
||||
def init_logging():
|
||||
root = logging.getLogger()
|
||||
root.setLevel(logging.INFO)
|
||||
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter(
|
||||
"[%(asctime)s] [Travel Agents] [%(levelname)s] %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S %z",
|
||||
)
|
||||
handler.setFormatter(formatter)
|
||||
root.addHandler(handler)
|
||||
|
||||
class TravelDealFramework:
|
||||
|
||||
DB = "travel_vectorstore"
|
||||
MEMORY_FILENAME = "travel_memory.json"
|
||||
|
||||
def __init__(self):
|
||||
init_logging()
|
||||
load_dotenv()
|
||||
client = chromadb.PersistentClient(path=self.DB)
|
||||
self.memory = self.read_memory()
|
||||
self.collection = client.get_or_create_collection('travel_deals')
|
||||
self.planner = None
|
||||
|
||||
def init_agents_as_needed(self):
|
||||
if not self.planner:
|
||||
self.log("Initializing Travel Agent Framework")
|
||||
self.planner = TravelPlanningAgent(self.collection)
|
||||
self.log("Travel Agent Framework ready")
|
||||
|
||||
def read_memory(self) -> List[TravelOpportunity]:
|
||||
if os.path.exists(self.MEMORY_FILENAME):
|
||||
with open(self.MEMORY_FILENAME, "r") as file:
|
||||
data = json.load(file)
|
||||
opportunities = [TravelOpportunity(**item) for item in data]
|
||||
return opportunities
|
||||
return []
|
||||
|
||||
def write_memory(self) -> None:
|
||||
data = [opportunity.dict() for opportunity in self.memory]
|
||||
with open(self.MEMORY_FILENAME, "w") as file:
|
||||
json.dump(data, file, indent=2)
|
||||
|
||||
def log(self, message: str):
|
||||
text = BG_BLUE + WHITE + "[Travel Framework] " + message + RESET
|
||||
logging.info(text)
|
||||
|
||||
def run(self) -> List[TravelOpportunity]:
|
||||
self.init_agents_as_needed()
|
||||
logging.info("Starting Travel Planning Agent")
|
||||
results = self.planner.plan(memory=self.memory)
|
||||
logging.info(f"Travel Planning Agent completed with {len(results) if results else 0} results")
|
||||
if results:
|
||||
self.memory.extend(results)
|
||||
self.write_memory()
|
||||
return self.memory
|
||||
|
||||
@classmethod
|
||||
def get_plot_data(cls, max_datapoints=10000):
|
||||
client = chromadb.PersistentClient(path=cls.DB)
|
||||
collection = client.get_or_create_collection('travel_deals')
|
||||
result = collection.get(include=['embeddings', 'documents', 'metadatas'], limit=max_datapoints)
|
||||
vectors = np.array(result['embeddings'])
|
||||
documents = result['documents']
|
||||
categories = [metadata['category'] for metadata in result['metadatas']]
|
||||
colors = [COLORS[CATEGORIES.index(c)] for c in categories]
|
||||
tsne = TSNE(n_components=3, random_state=42, n_jobs=-1)
|
||||
reduced_vectors = tsne.fit_transform(vectors)
|
||||
return documents, reduced_vectors, colors
|
||||
|
||||
if __name__=="__main__":
|
||||
TravelDealFramework().run()
|
||||
|
||||
67
week8/community_contributions/w8d5/helpers/travel_deals.py
Normal file
67
week8/community_contributions/w8d5/helpers/travel_deals.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict, Self
|
||||
from bs4 import BeautifulSoup
|
||||
import re
|
||||
import feedparser
|
||||
from tqdm import tqdm
|
||||
import requests
|
||||
import time
|
||||
|
||||
feeds = [
|
||||
"https://thepointsguy.com/feed/",
|
||||
]
|
||||
|
||||
def extract(html_snippet: str) -> str:
|
||||
soup = BeautifulSoup(html_snippet, 'html.parser')
|
||||
text = soup.get_text(strip=True)
|
||||
text = re.sub('<[^<]+?>', '', text)
|
||||
return text.replace('\n', ' ').strip()
|
||||
|
||||
class ScrapedTravelDeal:
|
||||
title: str
|
||||
summary: str
|
||||
url: str
|
||||
details: str
|
||||
|
||||
def __init__(self, entry: Dict[str, str]):
|
||||
self.title = entry.get('title', '')
|
||||
summary_text = entry.get('summary', entry.get('description', ''))
|
||||
self.summary = extract(summary_text)
|
||||
self.url = entry.get('link', '')
|
||||
self.details = self.summary
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.title}>"
|
||||
|
||||
def describe(self):
|
||||
return f"Title: {self.title}\nDetails: {self.details.strip()}\nURL: {self.url}"
|
||||
|
||||
@classmethod
|
||||
def fetch(cls, show_progress: bool = False) -> List[Self]:
|
||||
deals = []
|
||||
feed_iter = tqdm(feeds) if show_progress else feeds
|
||||
for feed_url in feed_iter:
|
||||
try:
|
||||
feed = feedparser.parse(feed_url)
|
||||
for entry in feed.entries[:10]:
|
||||
deals.append(cls(entry))
|
||||
time.sleep(0.3)
|
||||
except Exception as e:
|
||||
print(f"Error fetching {feed_url}: {e}")
|
||||
return deals
|
||||
|
||||
class TravelDeal(BaseModel):
|
||||
destination: str
|
||||
deal_type: str
|
||||
description: str
|
||||
price: float
|
||||
url: str
|
||||
|
||||
class TravelDealSelection(BaseModel):
|
||||
deals: List[TravelDeal]
|
||||
|
||||
class TravelOpportunity(BaseModel):
|
||||
deal: TravelDeal
|
||||
estimate: float
|
||||
discount: float
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import json
|
||||
from typing import List, Tuple
|
||||
from dotenv import load_dotenv
|
||||
import chromadb
|
||||
import numpy as np
|
||||
from sklearn.manifold import TSNE
|
||||
|
||||
w8d5_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
if w8d5_path not in sys.path:
|
||||
sys.path.insert(0, w8d5_path)
|
||||
|
||||
from agents.travel_scanner_agent import TravelScannerAgent
|
||||
from agents.travel_estimator_agent import TravelEstimatorAgent
|
||||
from agents.travel_xgboost_agent import TravelXGBoostAgent
|
||||
from agents.travel_messaging_agent import TravelMessagingAgent
|
||||
from helpers.travel_deals import TravelOpportunity, TravelDeal
|
||||
|
||||
BG_BLUE = '\033[44m'
|
||||
WHITE = '\033[37m'
|
||||
RESET = '\033[0m'
|
||||
|
||||
CATEGORIES = ['Flights', 'Hotels', 'Car_Rentals', 'Vacation_Packages', 'Cruises', 'Activities']
|
||||
COLORS = ['red', 'blue', 'green', 'orange', 'purple', 'cyan']
|
||||
|
||||
def init_logging():
|
||||
root = logging.getLogger()
|
||||
root.setLevel(logging.INFO)
|
||||
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter(
|
||||
"[%(asctime)s] [Travel Agents] [%(levelname)s] %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S %z",
|
||||
)
|
||||
handler.setFormatter(formatter)
|
||||
root.addHandler(handler)
|
||||
|
||||
|
||||
class TravelDualFramework:
|
||||
|
||||
DB = "travel_vectorstore"
|
||||
LLM_MEMORY_FILE = "travel_memory_llm.json"
|
||||
XGB_MEMORY_FILE = "travel_memory_xgb.json"
|
||||
DEAL_THRESHOLD = 200.0
|
||||
|
||||
def __init__(self):
|
||||
init_logging()
|
||||
load_dotenv()
|
||||
client = chromadb.PersistentClient(path=self.DB)
|
||||
self.collection = client.get_or_create_collection('travel_deals')
|
||||
|
||||
self.llm_memory = self.read_memory(self.LLM_MEMORY_FILE)
|
||||
self.xgb_memory = self.read_memory(self.XGB_MEMORY_FILE)
|
||||
|
||||
self.scanner = None
|
||||
self.llm_estimator = None
|
||||
self.xgb_estimator = None
|
||||
self.messenger = None
|
||||
|
||||
def init_agents_as_needed(self):
|
||||
if not self.scanner:
|
||||
self.log("Initializing Travel Dual Estimation Framework")
|
||||
self.scanner = TravelScannerAgent()
|
||||
self.llm_estimator = TravelEstimatorAgent(self.collection)
|
||||
self.xgb_estimator = TravelXGBoostAgent(self.collection)
|
||||
self.messenger = TravelMessagingAgent()
|
||||
self.log("Travel Dual Framework ready")
|
||||
|
||||
def read_memory(self, filename: str) -> List[TravelOpportunity]:
|
||||
if os.path.exists(filename):
|
||||
with open(filename, "r") as file:
|
||||
data = json.load(file)
|
||||
opportunities = [TravelOpportunity(**item) for item in data]
|
||||
return opportunities
|
||||
return []
|
||||
|
||||
def write_memory(self, opportunities: List[TravelOpportunity], filename: str) -> None:
|
||||
data = [opportunity.dict() for opportunity in opportunities]
|
||||
with open(filename, "w") as file:
|
||||
json.dump(data, file, indent=2)
|
||||
|
||||
def log(self, message: str):
|
||||
text = BG_BLUE + WHITE + "[Dual Framework] " + message + RESET
|
||||
logging.info(text)
|
||||
|
||||
def run(self) -> Tuple[List[TravelOpportunity], List[TravelOpportunity]]:
|
||||
self.init_agents_as_needed()
|
||||
|
||||
self.log("Starting dual estimation scan")
|
||||
deal_selection = self.scanner.scan()
|
||||
|
||||
if not deal_selection or not deal_selection.deals:
|
||||
self.log("No deals found")
|
||||
return self.llm_memory, self.xgb_memory
|
||||
|
||||
deals = deal_selection.deals
|
||||
self.log(f"Processing {len(deals)} deals with both estimators")
|
||||
|
||||
llm_opportunities = []
|
||||
xgb_opportunities = []
|
||||
|
||||
for deal in deals:
|
||||
llm_estimate = self.llm_estimator.estimate(deal.description)
|
||||
llm_discount = llm_estimate - deal.price
|
||||
|
||||
if llm_discount >= self.DEAL_THRESHOLD:
|
||||
llm_opp = TravelOpportunity(
|
||||
deal=deal,
|
||||
estimate=llm_estimate,
|
||||
discount=llm_discount
|
||||
)
|
||||
llm_opportunities.append(llm_opp)
|
||||
self.log(f"LLM found opportunity: {deal.destination} - ${llm_discount:.0f} savings")
|
||||
self.messenger.alert(llm_opp)
|
||||
|
||||
xgb_estimate = self.xgb_estimator.estimate(deal.description)
|
||||
xgb_discount = xgb_estimate - deal.price
|
||||
|
||||
if xgb_discount >= self.DEAL_THRESHOLD:
|
||||
xgb_opp = TravelOpportunity(
|
||||
deal=deal,
|
||||
estimate=xgb_estimate,
|
||||
discount=xgb_discount
|
||||
)
|
||||
xgb_opportunities.append(xgb_opp)
|
||||
self.log(f"XGBoost found opportunity: {deal.destination} - ${xgb_discount:.0f} savings")
|
||||
self.messenger.alert(xgb_opp)
|
||||
|
||||
if llm_opportunities:
|
||||
self.llm_memory.extend(llm_opportunities)
|
||||
self.write_memory(self.llm_memory, self.LLM_MEMORY_FILE)
|
||||
|
||||
if xgb_opportunities:
|
||||
self.xgb_memory.extend(xgb_opportunities)
|
||||
self.write_memory(self.xgb_memory, self.XGB_MEMORY_FILE)
|
||||
|
||||
self.log(f"Scan complete: {len(llm_opportunities)} LLM, {len(xgb_opportunities)} XGBoost opportunities")
|
||||
|
||||
return self.llm_memory, self.xgb_memory
|
||||
|
||||
@classmethod
|
||||
def get_plot_data(cls, max_datapoints=10000):
|
||||
client = chromadb.PersistentClient(path=cls.DB)
|
||||
collection = client.get_or_create_collection('travel_deals')
|
||||
result = collection.get(include=['embeddings', 'documents', 'metadatas'], limit=max_datapoints)
|
||||
vectors = np.array(result['embeddings'])
|
||||
documents = result['documents']
|
||||
categories = [metadata['category'] for metadata in result['metadatas']]
|
||||
colors = [COLORS[CATEGORIES.index(c)] for c in categories]
|
||||
tsne = TSNE(n_components=3, random_state=42, n_jobs=-1)
|
||||
reduced_vectors = tsne.fit_transform(vectors)
|
||||
return documents, reduced_vectors, colors, categories
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
framework = TravelDualFramework()
|
||||
framework.run()
|
||||
|
||||
66
week8/community_contributions/w8d5/tests/test_components.py
Normal file
66
week8/community_contributions/w8d5/tests/test_components.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import os
|
||||
import sys
|
||||
from dotenv import load_dotenv
|
||||
|
||||
project_root = os.path.join(os.path.dirname(__file__), '..')
|
||||
sys.path.insert(0, project_root)
|
||||
sys.path.insert(0, os.path.join(project_root, '..', '..'))
|
||||
|
||||
from helpers.travel_deals import ScrapedTravelDeal
|
||||
from agents.travel_scanner_agent import TravelScannerAgent
|
||||
from agents.travel_estimator_agent import TravelEstimatorAgent
|
||||
|
||||
load_dotenv()
|
||||
|
||||
print("\nTesting Travel Deal Hunter Components\n")
|
||||
|
||||
print("1. RSS Feed Scraping")
|
||||
deals = ScrapedTravelDeal.fetch(show_progress=False)
|
||||
print(f"Fetched {len(deals)} deals from RSS feeds")
|
||||
if deals:
|
||||
print(f"Sample: {deals[0].title[:60]}...")
|
||||
|
||||
|
||||
print("\n2. OpenAI Connection")
|
||||
if os.getenv("OPENAI_API_KEY"):
|
||||
print("OPENAI_API_KEY found")
|
||||
else:
|
||||
print("OPENAI_API_KEY not found - set in .env file")
|
||||
|
||||
print("\n3. Scanner Agent")
|
||||
scanner = TravelScannerAgent()
|
||||
print("Scanner agent initialized")
|
||||
|
||||
print("\n4. Deal Scanning")
|
||||
try:
|
||||
selection = scanner.scan(memory=[])
|
||||
if selection and selection.deals:
|
||||
print(f"Scanner found {len(selection.deals)} processed deals")
|
||||
print(f"Sample: {selection.deals[0].destination} - ${selection.deals[0].price}")
|
||||
else:
|
||||
print("No deals returned")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
print("\n5. ChromaDB Access")
|
||||
import chromadb
|
||||
try:
|
||||
db_path = "travel_vectorstore"
|
||||
client = chromadb.PersistentClient(path=db_path)
|
||||
collection = client.get_or_create_collection('travel_deals')
|
||||
count = collection.count()
|
||||
print(f"ChromaDB connected - {count} travel items in collection")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
print("\n6. Estimator Check using travel vectorstore")
|
||||
try:
|
||||
estimator = TravelEstimatorAgent(collection)
|
||||
sample = "Non-stop economy flight from New York to London, duration 7 hours"
|
||||
estimate = estimator.estimate(sample)
|
||||
print(f"Estimate: ${estimate:.2f}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
print("\nComponent tests complete")
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import os
|
||||
import sys
|
||||
from dotenv import load_dotenv
|
||||
|
||||
project_root = os.path.join(os.path.dirname(__file__), '..')
|
||||
sys.path.insert(0, project_root)
|
||||
sys.path.insert(0, os.path.join(project_root, '..', '..'))
|
||||
|
||||
from agents.travel_estimator_agent import TravelEstimatorAgent
|
||||
from agents.travel_xgboost_agent import TravelXGBoostAgent
|
||||
import chromadb
|
||||
|
||||
load_dotenv()
|
||||
|
||||
print("\nTesting Dual Estimation (LLM vs XGBoost)\n")
|
||||
|
||||
client = chromadb.PersistentClient(path='travel_vectorstore')
|
||||
collection = client.get_collection('travel_deals')
|
||||
|
||||
print("Initializing agents...")
|
||||
llm_agent = TravelEstimatorAgent(collection)
|
||||
xgb_agent = TravelXGBoostAgent(collection)
|
||||
|
||||
test_cases = [
|
||||
"Round trip flight from New York to London, Economy class, non-stop",
|
||||
"5-star Marriott hotel in Paris, 3 nights, Suite with breakfast included",
|
||||
"7-night Caribbean cruise, Balcony cabin, all meals included",
|
||||
"Hertz SUV rental in Los Angeles for 5 days with unlimited mileage",
|
||||
"All-inclusive vacation package to Dubai for 7 nights with Business class flights"
|
||||
]
|
||||
|
||||
print("\n" + "="*80)
|
||||
print(f"{'Travel Deal Description':<60} {'LLM Est.':<12} {'XGB Est.':<12}")
|
||||
print("="*80)
|
||||
|
||||
for desc in test_cases:
|
||||
llm_est = llm_agent.estimate(desc)
|
||||
xgb_est = xgb_agent.estimate(desc)
|
||||
|
||||
short_desc = desc[:57] + "..." if len(desc) > 60 else desc
|
||||
print(f"{short_desc:<60} ${llm_est:>9.2f} ${xgb_est:>9.2f}")
|
||||
|
||||
print("="*80)
|
||||
print("\nDual estimation test complete!")
|
||||
print("\nKey Observations:")
|
||||
print("- LLM: Uses semantic understanding + RAG context")
|
||||
print("- XGBoost: Uses pattern recognition from embeddings")
|
||||
print("- Both trained on same 20K travel deals dataset")
|
||||
|
||||
38
week8/community_contributions/w8d5/tests/test_pipeline.py
Normal file
38
week8/community_contributions/w8d5/tests/test_pipeline.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import os
|
||||
import sys
|
||||
from dotenv import load_dotenv
|
||||
|
||||
project_root = os.path.join(os.path.dirname(__file__), '..')
|
||||
sys.path.insert(0, project_root)
|
||||
sys.path.insert(0, os.path.join(project_root, '..', '..'))
|
||||
|
||||
from helpers.travel_deal_framework import TravelDealFramework
|
||||
|
||||
load_dotenv()
|
||||
|
||||
print("\nTesting Full Travel Deal Pipeline\n")
|
||||
|
||||
print("Initializing framework...")
|
||||
framework = TravelDealFramework()
|
||||
framework.init_agents_as_needed()
|
||||
|
||||
print("\nRunning one iteration...")
|
||||
try:
|
||||
result = framework.run()
|
||||
print(f"\nPipeline completed")
|
||||
print(f"Memory now has {len(result)} opportunities")
|
||||
if result:
|
||||
latest = result[-1]
|
||||
print(f"\nLatest opportunity:")
|
||||
print(f" Destination: {latest.deal.destination}")
|
||||
print(f" Type: {latest.deal.deal_type}")
|
||||
print(f" Price: ${latest.deal.price:.2f}")
|
||||
print(f" Estimate: ${latest.estimate:.2f}")
|
||||
print(f" Discount: ${latest.discount:.2f}")
|
||||
except Exception as e:
|
||||
print(f"\nError during pipeline: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print("\n")
|
||||
|
||||
57
week8/community_contributions/w8d5/travel_memory.json
Normal file
57
week8/community_contributions/w8d5/travel_memory.json
Normal file
@@ -0,0 +1,57 @@
|
||||
[
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Southwest Airlines",
|
||||
"deal_type": "Credit Card Offer",
|
||||
"description": "Earn 85,000 bonus points with the current welcome bonus on any of the personal Southwest Airlines credit cards. This deal allows travelers to maximize their rewards for future flights and services. Perfect for frequent travelers looking to enhance their earning potential.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/credit-cards/southwest-cards-current-offers/"
|
||||
},
|
||||
"estimate": 800.0,
|
||||
"discount": 300.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Riyadh, Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is launching non-stop flights from Atlanta to Riyadh in October. This move makes Delta the only U.S. airline with direct access to Saudi Arabia, offering travelers an excellent opportunity to explore this culturally rich and historically significant region.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 1200.0,
|
||||
"discount": 700.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Discover the breathtaking beauty of Alaska through seasonal cruises that offer unique experiences month by month. Travelers can enjoy a range of activities from discounted pricing to wildlife sightings, depending on the timing of their trip. This deal provides valuable insights to help guests decide the best time to embark on their Alaskan adventure. Ideal for nature lovers and adventure seekers!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 1200.0,
|
||||
"discount": 700.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "Book a flight to Paris this December for as low as 7,100 credit card points plus just $5.60. This deal offers fantastic award availability from select U.S. cities, making it an excellent opportunity for travelers looking to explore the City of Lights. Don't miss out on this limited-time offer to experience iconic attractions such as the Eiffel Tower and Louvre Museum.",
|
||||
"price": 7.6,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 1200.0,
|
||||
"discount": 1192.4
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Various Destinations with American Airlines",
|
||||
"deal_type": "Flight",
|
||||
"description": "American Airlines is enhancing its service by adding four new routes, including a resumption of a previously suspended transatlantic route. This expansion aims to provide travelers with more options and flexibility in their travel plans, catering especially to those looking for international travel. Book a flight on one of these new routes to explore exciting new destinations.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/airline/american-adds-new-routes-delta-turf-war/"
|
||||
},
|
||||
"estimate": 1200.0,
|
||||
"discount": 700.0
|
||||
}
|
||||
]
|
||||
244
week8/community_contributions/w8d5/travel_memory_llm.json
Normal file
244
week8/community_contributions/w8d5/travel_memory_llm.json
Normal file
@@ -0,0 +1,244 @@
|
||||
[
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is launching new nonstop flights from Atlanta to Riyadh, Saudi Arabia, starting next October. This makes Delta the only U.S. airline to offer this route, providing unique travel opportunities for those looking to explore the Middle East. Experience rich culture and history in the Kingdom of Saudi Arabia with convenient flight options.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 1200.0,
|
||||
"discount": 700.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "Travelers have a fantastic opportunity to fly to Paris for just 7,100 credit card points plus a minimal fee of $5.60 this December. This deal features excellent award availability from select U.S. cities, making it an exceptional choice for those looking to explore the City of Light. It's a great time to enjoy Parisian culture, cuisine, and landmarks while redeeming your points efficiently. Don't miss out on this limited-time offer to experience Europe's romantic capital without breaking the bank.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 7100.0,
|
||||
"discount": 6600.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Alaska is a breathtaking cruise destination with a limited season that offers various advantages depending on the month. This detailed guide helps travelers choose the best time to cruise, whether it's for discounted pricing or to witness abundant wildlife. Each month brings unique characteristics, ensuring every traveler can find the perfect time for their voyage. Embark on an unforgettable journey through stunning landscapes and vibrant marine life in Alaska.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 1200.0,
|
||||
"discount": 700.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is set to become the only U.S. airline providing nonstop flights to Riyadh, Saudi Arabia, from Atlanta starting next October. This new route opens exciting travel opportunities for those interested in exploring the rich culture and history of Saudi Arabia's capital. Ideal for both business and leisure travelers, this service enhances connectivity to an emerging destination with much to offer. Book your ticket to be among the first to experience this new travel path.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 1200.0,
|
||||
"discount": 700.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Starting next October, Delta Air Lines will introduce nonstop flights from Atlanta to Riyadh, marking itself as the only U.S. airline offering this route. This is a significant opportunity for travelers interested in exploring the Kingdom with access to its rich culture and history. Plan your travels to take advantage of this new connection to Saudi Arabia.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 900.0,
|
||||
"discount": 400.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is set to introduce nonstop flights from Atlanta to Riyadh, Saudi Arabia, next October, making it the only U.S. airline offering this route. This exciting development opens up new travel opportunities for American travelers to explore the Kingdom's rich history and culture. Be among the first to book these flights and experience a new adventure!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 900.0,
|
||||
"discount": 400.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Alaska is known for its breathtaking views and wildlife, and the best time to cruise there can vary. This guide helps travelers understand the advantages of cruising in Alaska based on the month they choose to embark. Travelers can benefit from discounted pricing during specific seasons, ensuring a unique and memorable experience out at sea. Enjoy the natural beauty and adventures that Alaska has to offer!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 1500.0,
|
||||
"discount": 1000.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "This incredible deal allows you to fly to Paris for just 7,100 credit card points plus a mere $5.60. You can book this award flight departing from select U.S. cities this December, making it an affordable dream destination during the holiday season. Enjoy the beauty of Paris and explore its iconic landmarks without breaking the bank. Don't miss this opportunity to visit the City of Lights on a budget!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 2500.0,
|
||||
"discount": 2000.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Alaska offers a unique cruising experience with a limited season where travelers can see wildlife and stunning landscapes. This guide helps you choose the best time to cruise Alaska, discussing each month\u2019s pros and cons, including potential discounts and wildlife viewing opportunities. Planning your cruise around peak seasons can enhance your experience and save you money on your adventure.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 1500.0,
|
||||
"discount": 1000.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is launching a new route from Atlanta to Riyadh in October, making it the only U.S. airline to offer direct flights to Saudi Arabia. This is a great opportunity for travelers interested in experiencing Middle Eastern culture and hospitality. Don\u2019t miss your chance to be among the first to explore this exciting new destination from the U.S.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 6200.0,
|
||||
"discount": 5700.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Multiple locations in Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Discover the stunning beauty of Alaska on a cruise, best enjoyed during its limited season. Each month offers unique advantages, ranging from discounted pricing to the chance of seeing abundant wildlife like whales and bears. This guide helps travelers decide the optimal time to embark on their Alaskan adventure, ensuring they experience all the natural wonders this amazing destination has to offer.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 4333.0,
|
||||
"discount": 3833.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines will soon be the only U.S. airline offering nonstop flights to Saudi Arabia, specifically connecting Atlanta to Riyadh starting next October. This unique route presents a valuable travel opportunity for those looking to explore the rich culture and history of Saudi Arabia. Be among the first to experience this new connection and discover the fantastic landscapes and heritage of the Kingdom.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 3600.0,
|
||||
"discount": 3100.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Alaska is known as a premier cruise destination with a limited season, each month providing unique advantages such as discounted pricing or exceptional wildlife viewing. This guide helps travelers determine the best time to experience the breathtaking landscapes and diverse wildlife of Alaska on a cruise. Whether looking for adventure or relaxation, this guide highlights the optimal months for your trip.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 2500.0,
|
||||
"discount": 2000.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Starting in October, Delta Air Lines will be launching a new direct flight route from Atlanta to Riyadh, making it the only U.S. airline serving this destination. This offer provides an excellent opportunity for travelers interested in exploring the rich culture and history of Saudi Arabia. Book your flights in advance to secure your spot on this new route, which promises a convenient travel option to a growing destination.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 3500.0,
|
||||
"discount": 3000.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Hyatt Vacation Club properties",
|
||||
"deal_type": "Hotel",
|
||||
"description": "Earn 3,000 Hyatt points per night this winter when you stay at Hyatt Vacation Club properties, allowing you to enjoy luxurious accommodations and various amenities. This is a great way to enhance your travel experience while gaining significant rewards for future stays. The offer is available exclusively this winter, making it a perfect opportunity to plan a getaway.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 800.0,
|
||||
"discount": 300.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "This deal allows travelers to fly to Paris for only 7,100 credit card points plus a minimal fee of $5.60. Award availability is particularly favorable from select U.S. cities, making this an incredible opportunity for those looking to explore the City of Light this December. With just a small cash outlay, travelers can experience all that Paris has to offer, from its iconic landmarks to its renowned cuisine.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 2500.0,
|
||||
"discount": 2000.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is set to launch a new route connecting Atlanta to Riyadh, Saudi Arabia, starting next October. This will make Delta the only U.S. airline offering nonstop service to this growing destination. This route presents a unique opportunity for travelers looking to explore Saudi Arabia's rich culture and history, with convenient travel options from one of the largest U.S. cities.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 6800.0,
|
||||
"discount": 6300.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Alaska's cruise season has specific months with unique advantages, including discounted pricing and abundant wildlife viewings. This guide helps travelers decide the best time for their Alaskan cruise adventure, whether they\u2019re looking for the best deals or the most scenic wildlife experiences. Ideal for nature lovers and adventure seekers alike, this destination promises thrilling experiences throughout the season.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 3500.0,
|
||||
"discount": 3000.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Various Hyatt Vacation Club properties",
|
||||
"deal_type": "Package",
|
||||
"description": "Hyatt Vacation Club properties are offering an enticing promotion this winter where travelers can earn 3,000 points per night. This promotion is perfect for those looking to experience the luxury and comfort of Hyatt properties while accumulating points for future stays. Ideal for families or couples, it combines relaxation with the potential for rewards during the winter season.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 1200.0,
|
||||
"discount": 700.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is launching a new nonstop route from Atlanta to Riyadh starting next October, making it the only US airline to connect directly to Saudi Arabia. This new service opens up opportunities for business and leisure travel to a historically rich destination. Travelers can expect a blend of modernity and tradition in Riyadh's stunning architecture and culture.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 3500.0,
|
||||
"discount": 3000.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Experience the breathtaking beauty of Alaska with its limited cruise season. Each month offers unique perks, from discounted pricing to abundant wildlife sightings. This guide will help you determine the best timing for your Alaskan adventure, ensuring an unforgettable trip filled with natural wonders and exploration of majestic landscapes.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 2000.0,
|
||||
"discount": 1500.0
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Multiple Hyatt locations",
|
||||
"deal_type": "Hotel",
|
||||
"description": "This winter, earn 3,000 Hyatt points per night at Hyatt Vacation Club properties. Enjoy spacious accommodations and upscale amenities across beautiful locations, ideal for family getaways or longer stays. Don't miss this chance to maximize your rewards while relaxing in comfort at a Hyatt destination of your choice.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 1500.0,
|
||||
"discount": 1000.0
|
||||
}
|
||||
]
|
||||
497
week8/community_contributions/w8d5/travel_memory_xgb.json
Normal file
497
week8/community_contributions/w8d5/travel_memory_xgb.json
Normal file
@@ -0,0 +1,497 @@
|
||||
[
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "This deal allows travelers to fly to Paris for just 7,100 credit card points and a minimal fee of $5.60. Award availability is great, particularly this December, making it an ideal time to explore the City of Light. Enjoy iconic attractions, delightful cuisine, and the beautiful winter ambiance of Paris without breaking the bank.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 2466.2734375,
|
||||
"discount": 1966.2734375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Learn about the optimal times to cruise in Alaska with considerations for wildlife, discounted pricing, and unique experiences available throughout the different months. This destination offers breathtaking scenery and adventure opportunities that are perfect for nature lovers and cruisers alike. Make informed decisions on when to book your unforgettable Alaskan adventure.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 2976.80078125,
|
||||
"discount": 2476.80078125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is launching new nonstop flights from Atlanta to Riyadh, Saudi Arabia, starting next October. This makes Delta the only U.S. airline to offer this route, providing unique travel opportunities for those looking to explore the Middle East. Experience rich culture and history in the Kingdom of Saudi Arabia with convenient flight options.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 2248.259521484375,
|
||||
"discount": 1748.259521484375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Hyatt Vacation Club",
|
||||
"deal_type": "Hotel",
|
||||
"description": "Earn 3,000 Hyatt points per night this winter when staying at Hyatt Vacation Club properties. This offer provides guests with rewards for their stays, adding significant value as those points can be redeemed for future accommodations and experiences. Travelers can enjoy high-quality accommodations in beautiful settings while accumulating points for future trips.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 2887.52685546875,
|
||||
"discount": 2387.52685546875
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Various IHG properties",
|
||||
"deal_type": "Hotel",
|
||||
"description": "IHG One Rewards members can save up to 30% on stays at participating properties during this flash sale. Membership is free, allowing travelers to take advantage of significant savings on hotel bookings around the world. It's a perfect chance to enjoy luxurious stays while maximizing your budget during travel.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/ihg-flash-sale/"
|
||||
},
|
||||
"estimate": 2464.5458984375,
|
||||
"discount": 1964.5458984375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "Travelers have a fantastic opportunity to fly to Paris for just 7,100 credit card points plus a minimal fee of $5.60 this December. This deal features excellent award availability from select U.S. cities, making it an exceptional choice for those looking to explore the City of Light. It's a great time to enjoy Parisian culture, cuisine, and landmarks while redeeming your points efficiently. Don't miss out on this limited-time offer to experience Europe's romantic capital without breaking the bank.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 2827.6220703125,
|
||||
"discount": 2327.6220703125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Alaska is a breathtaking cruise destination with a limited season that offers various advantages depending on the month. This detailed guide helps travelers choose the best time to cruise, whether it's for discounted pricing or to witness abundant wildlife. Each month brings unique characteristics, ensuring every traveler can find the perfect time for their voyage. Embark on an unforgettable journey through stunning landscapes and vibrant marine life in Alaska.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 3351.845458984375,
|
||||
"discount": 2851.845458984375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is set to become the only U.S. airline providing nonstop flights to Riyadh, Saudi Arabia, from Atlanta starting next October. This new route opens exciting travel opportunities for those interested in exploring the rich culture and history of Saudi Arabia's capital. Ideal for both business and leisure travelers, this service enhances connectivity to an emerging destination with much to offer. Book your ticket to be among the first to experience this new travel path.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 3028.00341796875,
|
||||
"discount": 2528.00341796875
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "IHG Hotels",
|
||||
"deal_type": "Hotel",
|
||||
"description": "IHG is currently running a flash sale that allows members to save up to 30% on stays at participating properties. This deal is perfect for IHG One Rewards members, and signing up is free, making the offer accessible to a broader audience. With a variety of locations and accommodations, guests can enjoy significant discounts, enhancing their travel experience without a hefty price tag. Take advantage of this limited-time offer to enjoy luxury stays at a fraction of the cost!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/ihg-flash-sale/"
|
||||
},
|
||||
"estimate": 2123.265380859375,
|
||||
"discount": 1623.265380859375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Hyatt Vacation Club",
|
||||
"deal_type": "Hotel",
|
||||
"description": "This winter, guests can earn 3,000 Hyatt points per night at Hyatt Vacation Club properties, providing a great way to collect rewards while enjoying a winter getaway. Travelers can expect relaxing accommodations in scenic locations with premium amenities. This offer is designed to enhance the vacation experience, ensuring points earners can redeem for future travels. It's a perfect opportunity to enjoy luxury stays while also boosting your reward balance for even more adventures ahead.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 2416.3115234375,
|
||||
"discount": 1916.3115234375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "Book a flight to Paris this December for as low as 7,100 credit card points plus only $5.60. This fantastic deal comes with great award availability departing from select U.S. cities, making it an excellent opportunity for travelers looking to visit the City of Light. Don't miss out on experiencing Paris, with its iconic attractions like the Eiffel Tower and Louvre Museum, at such a minimal cost.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 2097.250732421875,
|
||||
"discount": 1597.250732421875
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Discover the best time to cruise Alaska with our detailed guide that highlights the limited cruise season from May to September. Each month offers unique advantages such as discounted pricing and opportunities to see incredible wildlife. Whether you prefer the bustling summer or the quieter late season, learn when to book your Alaskan adventure for the best experience.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 3514.16943359375,
|
||||
"discount": 3014.16943359375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Starting next October, Delta Air Lines will introduce nonstop flights from Atlanta to Riyadh, marking itself as the only U.S. airline offering this route. This is a significant opportunity for travelers interested in exploring the Kingdom with access to its rich culture and history. Plan your travels to take advantage of this new connection to Saudi Arabia.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 3667.870361328125,
|
||||
"discount": 3167.870361328125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Various Locations (IHG Hotels)",
|
||||
"deal_type": "Hotel",
|
||||
"description": "IHG One Rewards members can enjoy a limited-time flash sale offering up to 30% off on stays at participating IHG properties. Membership is free, which provides additional travel perks. This offer is perfect for hotel stays across various destinations, making it an ideal opportunity for budget-conscious travelers.",
|
||||
"price": 350.0,
|
||||
"url": "https://thepointsguy.com/deals/ihg-flash-sale/"
|
||||
},
|
||||
"estimate": 2205.177978515625,
|
||||
"discount": 1855.177978515625
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Hyatt Vacation Club",
|
||||
"deal_type": "Hotel",
|
||||
"description": "Earn 3,000 Hyatt points per night when you stay at Hyatt Vacation Club properties this winter. It's a great way to enhance your stay at beautiful locations while racking up points that can be redeemed for future travel. This offer is perfect for winter getaway seekers looking to enjoy a relaxing vacation while earning rewards.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 3293.184814453125,
|
||||
"discount": 2793.184814453125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Package",
|
||||
"description": "Travelers can fly to Paris for an incredible deal of only 7,100 credit card points plus $5.60. The offer is valid from select U.S. cities this December, making it a perfect opportunity for holiday travel or a romantic getaway. Don\u2019t miss the chance to explore the City of Light while saving on airfare!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 1937.4169921875,
|
||||
"discount": 1437.4169921875
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is set to introduce nonstop flights from Atlanta to Riyadh, Saudi Arabia, next October, making it the only U.S. airline offering this route. This exciting development opens up new travel opportunities for American travelers to explore the Kingdom's rich history and culture. Be among the first to book these flights and experience a new adventure!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 2180.265625,
|
||||
"discount": 1680.265625
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Various Hyatt Vacation Club properties",
|
||||
"deal_type": "Hotel",
|
||||
"description": "This winter, enjoy a stay at Hyatt Vacation Club properties and earn 3,000 points per night. This promotion encourages travelers to indulge in luxury at various locations, with every night spent contributing to your loyalty rewards. Experience comfort, amenities, and the revenue of points to redeem for future stays!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 2734.311767578125,
|
||||
"discount": 2234.311767578125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Various IHG properties",
|
||||
"deal_type": "Hotel",
|
||||
"description": "IHG One Rewards members can take advantage of a flash sale providing up to 30% off at participating properties. This limited-time offer allows members to book accommodations across various locations at significant discounts, perfect for weekend getaways or extended vacations. Sign up for free to access this amazing deal!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/ihg-flash-sale/"
|
||||
},
|
||||
"estimate": 2387.095703125,
|
||||
"discount": 1887.095703125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Alaska is known for its breathtaking views and wildlife, and the best time to cruise there can vary. This guide helps travelers understand the advantages of cruising in Alaska based on the month they choose to embark. Travelers can benefit from discounted pricing during specific seasons, ensuring a unique and memorable experience out at sea. Enjoy the natural beauty and adventures that Alaska has to offer!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 3685.193115234375,
|
||||
"discount": 3185.193115234375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "This incredible deal allows you to fly to Paris for just 7,100 credit card points plus a mere $5.60. You can book this award flight departing from select U.S. cities this December, making it an affordable dream destination during the holiday season. Enjoy the beauty of Paris and explore its iconic landmarks without breaking the bank. Don't miss this opportunity to visit the City of Lights on a budget!",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 2293.15283203125,
|
||||
"discount": 1793.15283203125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Alaska offers a unique cruising experience with a limited season where travelers can see wildlife and stunning landscapes. This guide helps you choose the best time to cruise Alaska, discussing each month\u2019s pros and cons, including potential discounts and wildlife viewing opportunities. Planning your cruise around peak seasons can enhance your experience and save you money on your adventure.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 2563.173583984375,
|
||||
"discount": 2063.173583984375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is launching a new route from Atlanta to Riyadh in October, making it the only U.S. airline to offer direct flights to Saudi Arabia. This is a great opportunity for travelers interested in experiencing Middle Eastern culture and hospitality. Don\u2019t miss your chance to be among the first to explore this exciting new destination from the U.S.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 1687.6510009765625,
|
||||
"discount": 1187.6510009765625
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Hyatt Vacation Club",
|
||||
"deal_type": "Hotel",
|
||||
"description": "Earn 3,000 Hyatt points per night this winter when staying at Hyatt Vacation Club properties. This limited-time offer allows travelers to enjoy a luxurious and comfortable stay while racking up points for future trips. Ideal for points-savvy travelers looking to maximize their rewards during their winter getaway.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 2384.860595703125,
|
||||
"discount": 1884.860595703125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Various IHG properties",
|
||||
"deal_type": "Hotel",
|
||||
"description": "IHG One Rewards members can save up to 30% on stays at participating properties through a flash sale. This deal is perfect for those planning a trip and willing to stay at IHG hotels. Become a member for free and take advantage of steep discounts on your accommodations, making your travels more affordable without compromising on comfort. Act fast, as this sale is limited.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/ihg-flash-sale/"
|
||||
},
|
||||
"estimate": 2723.661376953125,
|
||||
"discount": 2223.661376953125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "Take advantage of this incredible deal to fly to Paris for only 7,100 credit card points plus a nominal fee of $5.60. With fantastic award availability from select U.S. cities this December, it's an opportunity for travelers to experience the City of Lights at a fraction of the usual point cost. This limited-time offer encourages travelers to book quickly to secure their tickets for a winter getaway in one of the world\u2019s most romantic destinations.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 1811.4459228515625,
|
||||
"discount": 1311.4459228515625
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Multiple locations in Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Discover the stunning beauty of Alaska on a cruise, best enjoyed during its limited season. Each month offers unique advantages, ranging from discounted pricing to the chance of seeing abundant wildlife like whales and bears. This guide helps travelers decide the optimal time to embark on their Alaskan adventure, ensuring they experience all the natural wonders this amazing destination has to offer.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 3470.741455078125,
|
||||
"discount": 2970.741455078125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines will soon be the only U.S. airline offering nonstop flights to Saudi Arabia, specifically connecting Atlanta to Riyadh starting next October. This unique route presents a valuable travel opportunity for those looking to explore the rich culture and history of Saudi Arabia. Be among the first to experience this new connection and discover the fantastic landscapes and heritage of the Kingdom.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 3656.968505859375,
|
||||
"discount": 3156.968505859375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Multiple IHG Hotels",
|
||||
"deal_type": "Hotel",
|
||||
"description": "IHG One Rewards members can take advantage of a flash sale that promises savings of up to 30% on stays at participating properties. This offer is a great way for travelers to enjoy top-notch accommodations while enjoying significant savings. Joining the IHG One Rewards program is free, allowing even more travelers to access this lucrative deal.",
|
||||
"price": 350.0,
|
||||
"url": "https://thepointsguy.com/deals/ihg-flash-sale/"
|
||||
},
|
||||
"estimate": 2630.43603515625,
|
||||
"discount": 2280.43603515625
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Hyatt Vacation Club properties",
|
||||
"deal_type": "Hotel",
|
||||
"description": "Earn 3,000 Hyatt points per night this winter at Hyatt Vacation Club locations. This deal invites travelers to enjoy luxurious accommodations while earning valuable points that can be redeemed for future stays and perks. This offer is perfect for those looking to enjoy a winter getaway and build their loyalty points at the same time\u2014making it a win-win for leisure seekers.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 2742.44580078125,
|
||||
"discount": 2242.44580078125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "Book a flight to Paris for only 7,100 credit card points plus $5.60 this December. This is a fantastic opportunity to travel to one of the most romantic cities in the world at an unbelievably low cost. The deal includes round-trip flights from select U.S. cities with great award availability. Don't miss out on this limited-time offer to experience Parisian culture, cuisine, and landmarks.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 2132.895263671875,
|
||||
"discount": 1632.895263671875
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Alaska is known as a premier cruise destination with a limited season, each month providing unique advantages such as discounted pricing or exceptional wildlife viewing. This guide helps travelers determine the best time to experience the breathtaking landscapes and diverse wildlife of Alaska on a cruise. Whether looking for adventure or relaxation, this guide highlights the optimal months for your trip.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 2560.084716796875,
|
||||
"discount": 2060.084716796875
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Starting in October, Delta Air Lines will be launching a new direct flight route from Atlanta to Riyadh, making it the only U.S. airline serving this destination. This offer provides an excellent opportunity for travelers interested in exploring the rich culture and history of Saudi Arabia. Book your flights in advance to secure your spot on this new route, which promises a convenient travel option to a growing destination.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 1428.189697265625,
|
||||
"discount": 928.189697265625
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Various IHG properties",
|
||||
"deal_type": "Hotel",
|
||||
"description": "IHG One Rewards members can take advantage of a flash sale offering up to 30% off at participating hotels. This limited-time offer allows you to enjoy significant savings on memorable stays at IHG properties around the world. Sign up for free to become an IHG One Rewards member and access this exclusive deal and more. Stay at top hotels while maximizing your rewards.",
|
||||
"price": 350.0,
|
||||
"url": "https://thepointsguy.com/deals/ihg-flash-sale/"
|
||||
},
|
||||
"estimate": 2310.8056640625,
|
||||
"discount": 1960.8056640625
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Hyatt Vacation Club properties",
|
||||
"deal_type": "Hotel",
|
||||
"description": "Earn 3,000 Hyatt points per night this winter when you stay at Hyatt Vacation Club properties, allowing you to enjoy luxurious accommodations and various amenities. This is a great way to enhance your travel experience while gaining significant rewards for future stays. The offer is available exclusively this winter, making it a perfect opportunity to plan a getaway.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 2529.80712890625,
|
||||
"discount": 2029.80712890625
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "This deal allows travelers to fly to Paris for only 7,100 credit card points plus a minimal fee of $5.60. Award availability is particularly favorable from select U.S. cities, making this an incredible opportunity for those looking to explore the City of Light this December. With just a small cash outlay, travelers can experience all that Paris has to offer, from its iconic landmarks to its renowned cuisine.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 2171.856201171875,
|
||||
"discount": 1671.856201171875
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is set to launch a new route connecting Atlanta to Riyadh, Saudi Arabia, starting next October. This will make Delta the only U.S. airline offering nonstop service to this growing destination. This route presents a unique opportunity for travelers looking to explore Saudi Arabia's rich culture and history, with convenient travel options from one of the largest U.S. cities.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 2526.151123046875,
|
||||
"discount": 2026.151123046875
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Various IHG properties",
|
||||
"deal_type": "Hotel",
|
||||
"description": "IHG One Rewards members can take advantage of a flash sale offering up to 30% off on stays at participating properties. With various locations available, this deal allows for substantial savings on accommodations, ideal for travelers looking to book winter getaways or holiday trips. Signing up for the rewards program is free, making this an accessible option for all.",
|
||||
"price": 350.0,
|
||||
"url": "https://thepointsguy.com/deals/ihg-flash-sale/"
|
||||
},
|
||||
"estimate": 2769.24365234375,
|
||||
"discount": 2419.24365234375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Alaska's cruise season has specific months with unique advantages, including discounted pricing and abundant wildlife viewings. This guide helps travelers decide the best time for their Alaskan cruise adventure, whether they\u2019re looking for the best deals or the most scenic wildlife experiences. Ideal for nature lovers and adventure seekers alike, this destination promises thrilling experiences throughout the season.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 2547.8408203125,
|
||||
"discount": 2047.8408203125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Various Hyatt Vacation Club properties",
|
||||
"deal_type": "Package",
|
||||
"description": "Hyatt Vacation Club properties are offering an enticing promotion this winter where travelers can earn 3,000 points per night. This promotion is perfect for those looking to experience the luxury and comfort of Hyatt properties while accumulating points for future stays. Ideal for families or couples, it combines relaxation with the potential for rewards during the winter season.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 2638.575439453125,
|
||||
"discount": 2138.575439453125
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Paris",
|
||||
"deal_type": "Flight",
|
||||
"description": "Fly to Paris for only 7,100 credit card points plus a minimal fee of $5.60. The deal offers terrific award availability for trips from select U.S. cities in December, making it an ideal time to explore the City of Light. Enjoy an unforgettable holiday season with landmarks, culture, and cuisine awaiting you in Paris.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/deal-of-the-day/"
|
||||
},
|
||||
"estimate": 2073.568603515625,
|
||||
"discount": 1573.568603515625
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Saudi Arabia",
|
||||
"deal_type": "Flight",
|
||||
"description": "Delta Air Lines is launching a new nonstop route from Atlanta to Riyadh starting next October, making it the only US airline to connect directly to Saudi Arabia. This new service opens up opportunities for business and leisure travel to a historically rich destination. Travelers can expect a blend of modernity and tradition in Riyadh's stunning architecture and culture.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/delta-air-lines-saudi-arabia-riyadh-nonstop-flights/"
|
||||
},
|
||||
"estimate": 3828.263427734375,
|
||||
"discount": 3328.263427734375
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Alaska",
|
||||
"deal_type": "Cruise",
|
||||
"description": "Experience the breathtaking beauty of Alaska with its limited cruise season. Each month offers unique perks, from discounted pricing to abundant wildlife sightings. This guide will help you determine the best timing for your Alaskan adventure, ensuring an unforgettable trip filled with natural wonders and exploration of majestic landscapes.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/cruise/best-time-to-cruise-alaska/"
|
||||
},
|
||||
"estimate": 2574.424560546875,
|
||||
"discount": 2074.424560546875
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Multiple Hyatt locations",
|
||||
"deal_type": "Hotel",
|
||||
"description": "This winter, earn 3,000 Hyatt points per night at Hyatt Vacation Club properties. Enjoy spacious accommodations and upscale amenities across beautiful locations, ideal for family getaways or longer stays. Don't miss this chance to maximize your rewards while relaxing in comfort at a Hyatt destination of your choice.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/news/hyatt-vacation-club-bonus-points-offer/"
|
||||
},
|
||||
"estimate": 2946.070556640625,
|
||||
"discount": 2446.070556640625
|
||||
},
|
||||
{
|
||||
"deal": {
|
||||
"destination": "Multiple IHG locations",
|
||||
"deal_type": "Hotel",
|
||||
"description": "IHG is offering a flash sale for its One Rewards members with discounts of up to 30% on stays at participating properties. This is a perfect opportunity for frequent travelers to save substantially while enjoying the hotel brand's high standards. Membership is free, making it a great time to sign up and take advantage of the offer.",
|
||||
"price": 500.0,
|
||||
"url": "https://thepointsguy.com/deals/ihg-flash-sale/"
|
||||
},
|
||||
"estimate": 1302.68408203125,
|
||||
"discount": 802.68408203125
|
||||
}
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
306
week8/community_contributions/w8d5/w8d5_dual.py
Normal file
306
week8/community_contributions/w8d5/w8d5_dual.py
Normal file
@@ -0,0 +1,306 @@
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import queue
|
||||
import threading
|
||||
import time
|
||||
import gradio as gr
|
||||
import plotly.graph_objects as go
|
||||
|
||||
w8d5_path = os.path.abspath(os.path.dirname(__file__))
|
||||
week8_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
if w8d5_path not in sys.path:
|
||||
sys.path.insert(0, w8d5_path)
|
||||
if week8_path not in sys.path:
|
||||
sys.path.insert(0, week8_path)
|
||||
|
||||
from log_utils import reformat
|
||||
from helpers.travel_dual_framework import TravelDualFramework
|
||||
from helpers.travel_deals import TravelOpportunity, TravelDeal
|
||||
|
||||
|
||||
class QueueHandler(logging.Handler):
|
||||
def __init__(self, log_queue):
|
||||
super().__init__()
|
||||
self.log_queue = log_queue
|
||||
|
||||
def emit(self, record):
|
||||
self.log_queue.put(self.format(record))
|
||||
|
||||
|
||||
log_queue = queue.Queue()
|
||||
queue_handler = QueueHandler(log_queue)
|
||||
queue_handler.setFormatter(
|
||||
logging.Formatter(
|
||||
"[%(asctime)s] [%(levelname)s] %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
)
|
||||
logging.getLogger().addHandler(queue_handler)
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
agent_framework = TravelDualFramework()
|
||||
agent_framework.init_agents_as_needed()
|
||||
|
||||
CHECK_INTERVAL = 300
|
||||
|
||||
|
||||
def run_agent_framework():
|
||||
while True:
|
||||
try:
|
||||
agent_framework.run()
|
||||
except Exception as e:
|
||||
logging.error(f"Error in agent framework: {e}")
|
||||
time.sleep(CHECK_INTERVAL)
|
||||
|
||||
|
||||
framework_thread = threading.Thread(target=run_agent_framework, daemon=True)
|
||||
framework_thread.start()
|
||||
|
||||
|
||||
def get_llm_table(llm_opps):
|
||||
return [[
|
||||
opp.deal.destination,
|
||||
opp.deal.deal_type,
|
||||
f"${opp.deal.price:.2f}",
|
||||
f"${opp.estimate:.2f}",
|
||||
f"${opp.discount:.2f}",
|
||||
opp.deal.url[:50] + "..." if len(opp.deal.url) > 50 else opp.deal.url
|
||||
] for opp in llm_opps]
|
||||
|
||||
|
||||
def get_xgb_table(xgb_opps):
|
||||
return [[
|
||||
opp.deal.destination,
|
||||
opp.deal.deal_type,
|
||||
f"${opp.deal.price:.2f}",
|
||||
f"${opp.estimate:.2f}",
|
||||
f"${opp.discount:.2f}",
|
||||
opp.deal.url[:50] + "..." if len(opp.deal.url) > 50 else opp.deal.url
|
||||
] for opp in xgb_opps]
|
||||
|
||||
|
||||
log_data = []
|
||||
|
||||
def update_ui():
|
||||
global log_data
|
||||
llm_data = get_llm_table(agent_framework.llm_memory)
|
||||
xgb_data = get_xgb_table(agent_framework.xgb_memory)
|
||||
|
||||
while not log_queue.empty():
|
||||
try:
|
||||
message = log_queue.get_nowait()
|
||||
log_data.append(reformat(message))
|
||||
except:
|
||||
break
|
||||
|
||||
logs_html = '<div style="height: 500px; overflow-y: auto; border: 1px solid #ccc; background-color: #1a1a1a; padding: 10px; font-family: monospace; font-size: 12px; color: #fff;">'
|
||||
logs_html += '<br>'.join(log_data[-50:])
|
||||
logs_html += '</div>'
|
||||
|
||||
llm_count = len(agent_framework.llm_memory)
|
||||
xgb_count = len(agent_framework.xgb_memory)
|
||||
|
||||
stats = f"LLM Opportunities: {llm_count} | XGBoost Opportunities: {xgb_count}"
|
||||
|
||||
return llm_data, xgb_data, logs_html, stats
|
||||
|
||||
|
||||
def create_3d_plot():
|
||||
try:
|
||||
documents, vectors, colors, categories = TravelDualFramework.get_plot_data(max_datapoints=5000)
|
||||
|
||||
if len(vectors) == 0:
|
||||
fig = go.Figure()
|
||||
fig.add_annotation(
|
||||
text="No data available yet. Vectorstore will load after initialization.",
|
||||
xref="paper", yref="paper",
|
||||
x=0.5, y=0.5, showarrow=False,
|
||||
font=dict(size=16)
|
||||
)
|
||||
return fig
|
||||
|
||||
fig = go.Figure()
|
||||
|
||||
unique_categories = list(set(categories))
|
||||
category_colors = {cat: colors[categories.index(cat)] for cat in unique_categories}
|
||||
|
||||
for category in unique_categories:
|
||||
mask = [cat == category for cat in categories]
|
||||
cat_vectors = vectors[mask]
|
||||
|
||||
fig.add_trace(go.Scatter3d(
|
||||
x=cat_vectors[:, 0],
|
||||
y=cat_vectors[:, 1],
|
||||
z=cat_vectors[:, 2],
|
||||
mode='markers',
|
||||
marker=dict(
|
||||
size=3,
|
||||
color=category_colors[category],
|
||||
opacity=0.6
|
||||
),
|
||||
name=category.replace('_', ' '),
|
||||
hovertemplate='<b>%{text}</b><extra></extra>',
|
||||
text=[category] * len(cat_vectors)
|
||||
))
|
||||
|
||||
fig.update_layout(
|
||||
title={
|
||||
'text': f'3D Travel Vectorstore Visualization ({len(vectors):,} deals)',
|
||||
'x': 0.5,
|
||||
'xanchor': 'center'
|
||||
},
|
||||
scene=dict(
|
||||
xaxis_title='Dimension 1',
|
||||
yaxis_title='Dimension 2',
|
||||
zaxis_title='Dimension 3',
|
||||
camera=dict(
|
||||
eye=dict(x=1.5, y=1.5, z=1.5)
|
||||
)
|
||||
),
|
||||
width=1200,
|
||||
height=600,
|
||||
margin=dict(r=0, b=0, l=0, t=40),
|
||||
showlegend=True,
|
||||
legend=dict(
|
||||
yanchor="top",
|
||||
y=0.99,
|
||||
xanchor="left",
|
||||
x=0.01
|
||||
)
|
||||
)
|
||||
|
||||
return fig
|
||||
except Exception as e:
|
||||
logging.error(f"Error creating 3D plot: {e}")
|
||||
fig = go.Figure()
|
||||
fig.add_annotation(
|
||||
text=f"Error loading plot: {str(e)}",
|
||||
xref="paper", yref="paper",
|
||||
x=0.5, y=0.5, showarrow=False,
|
||||
font=dict(size=14, color="red")
|
||||
)
|
||||
return fig
|
||||
|
||||
|
||||
with gr.Blocks(title="Travel Deal Hunter - Dual Estimation", fill_width=True, theme=gr.themes.Soft()) as ui:
|
||||
|
||||
gr.Markdown(
|
||||
"""
|
||||
<div style="text-align: center;">
|
||||
<h1 style="margin-bottom: 10px;">Travel Deal Hunter - Dual Estimation System</h1>
|
||||
<p style="color: #666; font-size: 16px;">
|
||||
Comparing LLM-based Semantic Estimation vs XGBoost Machine Learning
|
||||
</p>
|
||||
<p style="color: #999; font-size: 14px; margin-top: 10px;">
|
||||
System scans RSS feeds every 5 minutes. Use the button below to trigger a manual scan.
|
||||
</p>
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column(scale=3):
|
||||
stats_display = gr.Textbox(
|
||||
label="",
|
||||
value="LLM Opportunities: 0 | XGBoost Opportunities: 0",
|
||||
interactive=False,
|
||||
show_label=False,
|
||||
container=False
|
||||
)
|
||||
with gr.Column(scale=1):
|
||||
scan_button = gr.Button("Scan Now", variant="primary")
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column(scale=1):
|
||||
gr.Markdown("### LLM Estimates")
|
||||
llm_dataframe = gr.Dataframe(
|
||||
headers=["Destination", "Type", "Price", "LLM Est.", "Savings", "URL"],
|
||||
datatype=["str", "str", "str", "str", "str", "str"],
|
||||
wrap=True,
|
||||
column_widths=[2, 1, 1, 1, 1, 2],
|
||||
row_count=5,
|
||||
col_count=6,
|
||||
interactive=False
|
||||
)
|
||||
|
||||
with gr.Column(scale=1):
|
||||
gr.Markdown("### XGBoost Estimates")
|
||||
xgb_dataframe = gr.Dataframe(
|
||||
headers=["Destination", "Type", "Price", "XGB Est.", "Savings", "URL"],
|
||||
datatype=["str", "str", "str", "str", "str", "str"],
|
||||
wrap=True,
|
||||
column_widths=[2, 1, 1, 1, 1, 2],
|
||||
row_count=5,
|
||||
col_count=6,
|
||||
interactive=False
|
||||
)
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column(scale=2):
|
||||
plot_output = gr.Plot(label="3D Travel Vectorstore Visualization")
|
||||
|
||||
with gr.Column(scale=1):
|
||||
gr.Markdown("### Agent Activity Logs")
|
||||
log_output = gr.HTML(
|
||||
value='<div style="height: 500px; overflow-y: auto; border: 1px solid #ccc; background-color: #1a1a1a; padding: 10px; font-family: monospace; font-size: 12px; color: #fff;"></div>'
|
||||
)
|
||||
|
||||
ui.load(
|
||||
fn=lambda: (
|
||||
get_llm_table(agent_framework.llm_memory),
|
||||
get_xgb_table(agent_framework.xgb_memory),
|
||||
"",
|
||||
f"LLM Opportunities: {len(agent_framework.llm_memory)} | XGBoost Opportunities: {len(agent_framework.xgb_memory)}",
|
||||
create_3d_plot()
|
||||
),
|
||||
outputs=[llm_dataframe, xgb_dataframe, log_output, stats_display, plot_output]
|
||||
)
|
||||
|
||||
# Manual scan button
|
||||
def manual_scan():
|
||||
try:
|
||||
agent_framework.run()
|
||||
return update_ui()
|
||||
except Exception as e:
|
||||
logging.error(f"Manual scan error: {e}")
|
||||
return update_ui()
|
||||
|
||||
scan_button.click(
|
||||
fn=manual_scan,
|
||||
outputs=[llm_dataframe, xgb_dataframe, log_output, stats_display]
|
||||
)
|
||||
|
||||
# Click handlers for notifications
|
||||
def llm_click_handler(selected_index: gr.SelectData):
|
||||
try:
|
||||
row = selected_index.index[0]
|
||||
if row < len(agent_framework.llm_memory):
|
||||
opportunity = agent_framework.llm_memory[row]
|
||||
agent_framework.messenger.alert(opportunity)
|
||||
logging.info(f"Manual alert sent for LLM opportunity: {opportunity.deal.destination}")
|
||||
except Exception as e:
|
||||
logging.error(f"Error sending LLM notification: {e}")
|
||||
|
||||
def xgb_click_handler(selected_index: gr.SelectData):
|
||||
try:
|
||||
row = selected_index.index[0]
|
||||
if row < len(agent_framework.xgb_memory):
|
||||
opportunity = agent_framework.xgb_memory[row]
|
||||
agent_framework.messenger.alert(opportunity)
|
||||
logging.info(f"Manual alert sent for XGBoost opportunity: {opportunity.deal.destination}")
|
||||
except Exception as e:
|
||||
logging.error(f"Error sending XGBoost notification: {e}")
|
||||
|
||||
llm_dataframe.select(fn=llm_click_handler)
|
||||
xgb_dataframe.select(fn=xgb_click_handler)
|
||||
|
||||
gr.Timer(5).tick(
|
||||
fn=update_ui,
|
||||
outputs=[llm_dataframe, xgb_dataframe, log_output, stats_display]
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ui.launch(inbrowser=True, share=False)
|
||||
|
||||
Reference in New Issue
Block a user