mingbaer's picture
Update app.py
515dd18 verified
import gradio as gr
import json
import os
import requests
import sys
from gradio.themes.utils import colors
# === Environment Setup ===
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
if not GROQ_API_KEY:
print("⚠️ GROQ_API_KEY not set. Set it in Hugging Face Secrets.")
GROQ_API_KEY = "gsk-fake-for-testing" # Fallback for local testing
# === File Definitions for Persistence ===
HISTORY_FILE_TEMPLATE = "chat_history_{}.json"
USER_PROFILE_FILE = "user_profiles.json"
# --- Personalized History Management ---
def get_history_filename(user_id):
return HISTORY_FILE_TEMPLATE.format(user_id)
def save_history(history, user_id):
filename = get_history_filename(user_id)
with open(filename, 'w', encoding='utf-8') as f:
json.dump(history, f, ensure_ascii=False, indent=2)
def load_history(history, user_id):
filename = get_history_filename(user_id)
if os.path.exists(filename):
with open(filename, 'r', encoding='utf-8') as f:
return json.load(f)
return []
def clear_history_file(user_id):
if not isinstance(user_id, str): # Ensure user_id is a valid string
print(f"❌ Invalid user_id format: {user_id}")
user_id = "anonymous" # Fallback to prevent file errors
filename = get_history_filename(user_id) # Generates correct file name
print(f"πŸ—‘ Clearing chat history for {user_id}, file: {filename}")
try:
with open(filename, 'w', encoding='utf-8') as f:
json.dump([], f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"❌ Failed to clear chat history: {e}")
# === GROQ API Config ===
GROQ_API_URL = "https://api.groq.com/openai/v1/chat/completions"
MODEL_NAME = "llama3-8b-8192"
def query_groq(message, history):
if not GROQ_API_KEY or not GROQ_API_KEY.startswith("gsk_"):
print("🚫 GROQ_API_KEY is missing or invalid.", file=sys.stderr)
return "GROQ_API_KEY is missing or invalid. Please check Hugging Face Secrets."
headers = {
"Authorization": f"Bearer {GROQ_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": MODEL_NAME,
"messages": history,
"temperature": 0.7
}
try:
response = requests.post(GROQ_API_URL, headers=headers, json=payload)
if response.status_code == 200:
return response.json()["choices"][0]["message"]["content"].strip()
else:
return f"❌ GROQ API Error {response.status_code}: {response.text}"
except Exception as e:
return f"⚠️ Failed to call GROQ API: {str(e)}"
# === Chatbot Response Function ===
def respond_chat(message, history, user_id, profile):
if history is None or not history:
history = load_history(user_id, profile) # Load user-specific past history
user_profile = update_user_profile(user_id, message)
system_message = {
"role": "system",
"content": f"You are a friendly travel assistant named Sky. User profile: {user_profile}"
}
# Include conversation history in API request
messages_for_api = history + [system_message, {"role": "user", "content": message}]
response_text = query_groq(message, messages_for_api)
history.append({"role": "user", "content": message})
history.append({"role": "assistant", "content": response_text})
save_history(history, user_id) # Save chat history after each interaction
return history, history # Persist chat history in Gradio UI
# === User Profile Management ===
def save_user_profile(user_id, profile_data):
profiles = load_user_profiles()
profiles[user_id] = profile_data
with open(USER_PROFILE_FILE, 'w', encoding='utf-8') as f:
json.dump(profiles, f, ensure_ascii=False, indent=2)
def load_user_profiles():
if os.path.exists(USER_PROFILE_FILE):
with open(USER_PROFILE_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
return {}
def update_user_profile(user_id, user_msg):
profiles = load_user_profiles()
profile = profiles.get(user_id, {
"name": user_id,
"preferences": {},
"past_trips": [],
"travel_style": "Unknown"
})
keywords = ["beach", "mountain", "budget", "luxury", "adventure", "relaxation"]
for word in keywords:
if word in user_msg.lower():
profile["preferences"][word] = True
save_user_profile(user_id, profile)
return profile
# === Function to Update Preferences from Sidebar ===
def update_preferences(budget, language, destination, current_location, days, traveling_alone, diet):
user_id = "anonymous"
profiles = load_user_profiles()
profile = profiles.get(user_id, {
"name": user_id,
"preferences": {},
"past_trips": [],
"travel_style": "Unknown"
})
profile["budget"] = budget
profile["language"] = language
profile["destination"] = destination
profile["travel_days"] = days
profile["traveling_alone"] = traveling_alone
profile["current_location"] = current_location
profile["dietary_restrictions"] = diet
save_user_profile(user_id, profile)
return f"Travel preferences updated for user {user_id}."
# === Function to Clear Chat History ===
def clear_chat_history(user_id):
print(f"πŸ—‘ Clearing chat history for user: {user_id}") # Debugging
clear_history_file(user_id) # Delete chat history file
return [], [] # Reset displayed chatbot conversation
# Function to generate a receipt for budget tab
def generate_receipt(expenses):
categories = {
"Food": 0,
"Transportation": 0,
"Accommodation": 0,
"Other": 0
}
total_cost = 0
for item in expenses:
category = item.get("category", "Other")
amount = item.get("amount", 0)
categories[category] += amount
total_cost += amount
receipt = "**Receipt:**\n\n"
for category, amount in categories.items():
receipt += f"{category}: ${amount}\n"
receipt += f"\n**Total Cost:** ${total_cost}\n"
return receipt
# Function to generate an itinerary based on number of days and solo travel
def create_itinerary(days, traveling_alone):
itinerary = "**Your Trip Itinerary:**\n\n"
for day in range(1, days + 1):
if traveling_alone == "Yes":
itinerary += f"Day {day}: Explore local attractions, enjoy a quiet meal, and relax.\n"
else:
itinerary += f"Day {day}: Group tour, shared meals, and adventure activities.\n"
return itinerary
#Theme stuff
travelwise_purple = colors.Color(
name="travelwise_purple",
c50="#f4f0f8",
c100="#e3d9ee",
c200="#c7b3dc",
c300="#ab8dca",
c400="#8f67b8",
c500="#947EB0", # main color
c600="#7e5f97",
c700="#5e476f",
c800="#3f2f47",
c900="#1f1820",
c950="#0f0c10"
)
light_neutral = colors.Color(
name="light_neutral",
c50="#ffffff",
c100="#f9f9f9",
c200="#f3f3f3",
c300="#eaeaea",
c400="#d9d9d9",
c500="#c0c0c0",
c600="#a7a7a7",
c700="#8e8e8e",
c800="#757575",
c900="#5c5c5c",
c950="#3c3c3c"
)
my_theme = gr.themes.Soft(
primary_hue= travelwise_purple,
neutral_hue= light_neutral, # optional
).set(
body_background_fill="#f1eeccff",
button_secondary_background_fill="#ABA8A6",
button_primary_text_color="#004643"
)
def generate_receipt(expenses):
categories = {
"Food": 0,
"Transportation": 0,
"Accommodation": 0,
"Other": 0
}
total_cost = 0
for item in expenses:
category = item.get("category", "Other")
amount = item.get("amount", 0)
# If a category is not predefined, accumulate it under 'Other'
if category not in categories:
categories["Other"] += amount
else:
categories[category] += amount
total_cost += amount
receipt = "**Receipt:**\n\n"
for cat, amt in categories.items():
receipt += f"{cat}: ${amt}\n"
receipt += f"\n**Total Cost:** ${total_cost}\n"
return receipt
def parse_expenses(user_input):
"""
Parses a string of expenses in the format:
"Food - 20, Transportation - 15, Accommodation - 100"
into a list of dictionaries required by generate_receipt.
"""
expenses = []
if not user_input.strip():
return "⚠️ Please enter some expenses."
entries = user_input.split(",")
for entry in entries:
try:
category, amount = entry.strip().split("-")
expenses.append({
"category": category.strip().title(),
"amount": float(amount.strip())
})
except ValueError:
return ("⚠️ Please ensure each expense is entered in the format: "
"Category - Amount (e.g., Food - 20, Transportation - 15)")
return generate_receipt(expenses)
# Gradio UI
# === Gradio UI Setup ===
# === Gradio UI Setup ===
with gr.Blocks(theme=my_theme) as demo:
chat_history = gr.State([]) # Maintain chat history across interactions
# πŸ”Ή Add `user_id_input` to store user identity across sessions πŸ”Ή
user_id_input = gr.Textbox(value="anonymous", interactive=False, visible=False)
with gr.Tabs():
with gr.TabItem("Chatbot"):
with gr.Row():
with gr.Column(scale=2):
gr.Image(
value="logo.png",
width=400,
show_label=False,
show_download_button=False,
container=False,
show_share_button=False,
interactive=False
)
gr.Markdown("Plan your perfect trip with ease! Travelwise helps you customize itineraries, explore budgeting options, and get personalized tipsβ€”all based on your travel style and preferences. Whether you're going solo or in a group, relaxing or adventuring, the bot adapts to your needs in real time.")
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("## Chatbot")
chatbot = gr.Chatbot(type="messages") # Display chat conversation
chat_input = gr.Textbox(label="Your Message") # User input field
chat_button = gr.Button("Send Message") # Send message button
clear_history_btn = gr.Button("Clear Chat History") # Reset conversation button
with gr.Column(scale=1):
gr.Markdown("### ✈️ Travel Preferences")
budget = gr.Textbox(label="πŸ’° Budget")
language = gr.Dropdown(
label="🌐 Language",
choices=["English", "Korean", "Japanese", "French", "Spanish", "Chinese", "Italian"]
)
destination = gr.Textbox(label="πŸ—ΊοΈ Travel Location")
current_location = gr.Textbox(label="πŸ“ Current Location")
days = gr.Slider(minimum=1, maximum=200, step=1, label="πŸ“† Days Traveling")
traveling_alone = gr.Dropdown(label="Traveling Alone?", choices=["Yes", "No"], value="No")
diet = gr.Textbox(label="πŸ₯— Dietary Restrictions")
pref_button = gr.Button("Update Preferences")
pref_confirmation = gr.Textbox(label="Preference Update Confirmation")
with gr.TabItem("Budget"):
gr.Markdown("### Enter your expenses in the format: `Category - Amount`, separated by commas.")
expense_input = gr.Textbox(
placeholder="Food - 20, Transportation - 15, Accommodation - 100",
label="Expenses"
)
generate_button = gr.Button("Generate Receipt")
receipt_output = gr.Markdown()
generate_button.click(
fn=parse_expenses,
inputs=[expense_input],
outputs=[receipt_output]
)
with gr.TabItem("Itinerary"):
days_input = gr.Slider(1, 14, step=1, label="Number of Days")
solo_input = gr.Dropdown(["Yes", "No"], label="Traveling Alone?")
generate_itinerary_button = gr.Button("Generate Itinerary")
itinerary_output = gr.Markdown()
generate_itinerary_button.click(
fn=create_itinerary,
inputs=[days_input, solo_input],
outputs=[itinerary_output]
)
# πŸ”Ή Fix: Ensure user ID is passed correctly πŸ”Ή
chat_button.click(
fn=respond_chat,
inputs=[chat_input, chat_history, user_id_input], # βœ… Ensure correct user ID is used
outputs=[chatbot, chat_history]
)
# πŸ”Ή Fix: Correctly clear history by passing `user_id_input` πŸ”Ή
clear_history_btn.click(
fn=clear_chat_history,
inputs=[user_id_input], # βœ… Pass stored user ID correctly
outputs=[chatbot, chat_history]
)
# testing with token
# Bind the sidebar button for updating travel preferences.
pref_button.click(
fn=update_preferences,
inputs=[budget, language, destination, current_location, days, traveling_alone, diet],
outputs=pref_confirmation
)
demo.launch(debug=True)