Spaces:
Sleeping
Sleeping
| 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) | |