mingbaer commited on
Commit
de7999b
ยท
verified ยท
1 Parent(s): fa6f309

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +379 -0
app.py ADDED
@@ -0,0 +1,379 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ import os
4
+ import requests
5
+ import sys
6
+ from gradio.themes.utils import colors
7
+
8
+ # === Environment Setup ===
9
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
10
+ if not GROQ_API_KEY:
11
+ print("โš ๏ธ GROQ_API_KEY not set. Set it in Hugging Face Secrets.")
12
+ GROQ_API_KEY = "gsk-fake-for-testing" # Fallback for local testing
13
+
14
+ # === File Definitions for Persistence ===
15
+ HISTORY_FILE_TEMPLATE = "chat_history_{}.json"
16
+ USER_PROFILE_FILE = "user_profiles.json"
17
+
18
+ # --- Personalized History Management ---
19
+ def get_history_filename(user_id):
20
+ return HISTORY_FILE_TEMPLATE.format(user_id)
21
+
22
+ def save_history(history, user_id):
23
+ filename = get_history_filename(user_id)
24
+ with open(filename, 'w', encoding='utf-8') as f:
25
+ json.dump(history, f, ensure_ascii=False, indent=2)
26
+
27
+ def load_history(user_id):
28
+ filename = get_history_filename(user_id)
29
+ if os.path.exists(filename):
30
+ with open(filename, 'r', encoding='utf-8') as f:
31
+ return json.load(f)
32
+ return []
33
+
34
+ def clear_history_file(user_id):
35
+ if not isinstance(user_id, str): # Ensure user_id is a valid string
36
+ print(f"โŒ Invalid user_id format: {user_id}")
37
+ user_id = "anonymous" # Fallback to prevent file errors
38
+
39
+ filename = get_history_filename(user_id) # Generates correct file name
40
+ print(f"๐Ÿ—‘ Clearing chat history for {user_id}, file: {filename}")
41
+
42
+ try:
43
+ with open(filename, 'w', encoding='utf-8') as f:
44
+ json.dump([], f, ensure_ascii=False, indent=2)
45
+ except Exception as e:
46
+ print(f"โŒ Failed to clear chat history: {e}")
47
+
48
+ # === GROQ API Config ===
49
+ GROQ_API_URL = "https://api.groq.com/openai/v1/chat/completions"
50
+ MODEL_NAME = "llama3-8b-8192"
51
+
52
+ def query_groq(message, history):
53
+ if not GROQ_API_KEY or not GROQ_API_KEY.startswith("gsk_"):
54
+ print("๐Ÿšซ GROQ_API_KEY is missing or invalid.", file=sys.stderr)
55
+ return "GROQ_API_KEY is missing or invalid. Please check Hugging Face Secrets."
56
+
57
+ headers = {
58
+ "Authorization": f"Bearer {GROQ_API_KEY}",
59
+ "Content-Type": "application/json"
60
+ }
61
+ payload = {
62
+ "model": MODEL_NAME,
63
+ "messages": history,
64
+ "temperature": 0.7
65
+ }
66
+ try:
67
+ response = requests.post(GROQ_API_URL, headers=headers, json=payload)
68
+ if response.status_code == 200:
69
+ return response.json()["choices"][0]["message"]["content"].strip()
70
+ else:
71
+ return f"โŒ GROQ API Error {response.status_code}: {response.text}"
72
+ except Exception as e:
73
+ return f"โš ๏ธ Failed to call GROQ API: {str(e)}"
74
+
75
+ # === Chatbot Response Function ===
76
+ def respond_chat(message, history, user_id):
77
+ if history is None or not history:
78
+ history = load_history(user_id) # Load user-specific past history
79
+
80
+ user_profile = update_user_profile(user_id, message)
81
+
82
+ system_message = {
83
+ "role": "system",
84
+ "content": f"You are a friendly travel assistant named Sky. User profile: {user_profile}"
85
+ }
86
+
87
+ # Include conversation history in API request
88
+ messages_for_api = history + [system_message, {"role": "user", "content": message}]
89
+
90
+ response_text = query_groq(message, messages_for_api)
91
+
92
+ history.append({"role": "user", "content": message})
93
+ history.append({"role": "assistant", "content": response_text})
94
+
95
+ save_history(history, user_id) # Save chat history after each interaction
96
+
97
+ return history, history # Persist chat history in Gradio UI
98
+
99
+ # === User Profile Management ===
100
+ def save_user_profile(user_id, profile_data):
101
+ profiles = load_user_profiles()
102
+ profiles[user_id] = profile_data
103
+ with open(USER_PROFILE_FILE, 'w', encoding='utf-8') as f:
104
+ json.dump(profiles, f, ensure_ascii=False, indent=2)
105
+
106
+ def load_user_profiles():
107
+ if os.path.exists(USER_PROFILE_FILE):
108
+ with open(USER_PROFILE_FILE, 'r', encoding='utf-8') as f:
109
+ return json.load(f)
110
+ return {}
111
+
112
+ def update_user_profile(user_id, user_msg):
113
+ profiles = load_user_profiles()
114
+ profile = profiles.get(user_id, {
115
+ "name": user_id,
116
+ "preferences": {},
117
+ "past_trips": [],
118
+ "travel_style": "Unknown"
119
+ })
120
+
121
+ keywords = ["beach", "mountain", "budget", "luxury", "adventure", "relaxation"]
122
+ for word in keywords:
123
+ if word in user_msg.lower():
124
+ profile["preferences"][word] = True
125
+
126
+ save_user_profile(user_id, profile)
127
+ return profile
128
+
129
+ # === Function to Update Preferences from Sidebar ===
130
+ def update_preferences(budget, language, destination, current_location, days, traveling_alone, diet):
131
+ user_id = "anonymous"
132
+
133
+ profiles = load_user_profiles()
134
+ profile = profiles.get(user_id, {
135
+ "name": user_id,
136
+ "preferences": {},
137
+ "past_trips": [],
138
+ "travel_style": "Unknown"
139
+ })
140
+
141
+ profile["budget"] = budget
142
+ profile["language"] = language
143
+ profile["destination"] = destination
144
+ profile["travel_days"] = days
145
+ profile["traveling_alone"] = traveling_alone
146
+ profile["current_location"] = current_location
147
+ profile["dietary_restrictions"] = diet
148
+
149
+ save_user_profile(user_id, profile)
150
+
151
+ return f"Travel preferences updated for user {user_id}."
152
+
153
+
154
+ # === Function to Clear Chat History ===
155
+ def clear_chat_history(user_id):
156
+ print(f"๐Ÿ—‘ Clearing chat history for user: {user_id}") # Debugging
157
+ clear_history_file(user_id) # Delete chat history file
158
+ return [], [] # Reset displayed chatbot conversation
159
+
160
+
161
+
162
+
163
+
164
+
165
+ # Function to generate a receipt for budget tab
166
+ def generate_receipt(expenses):
167
+ categories = {
168
+ "Food": 0,
169
+ "Transportation": 0,
170
+ "Accommodation": 0,
171
+ "Other": 0
172
+ }
173
+ total_cost = 0
174
+
175
+ for item in expenses:
176
+ category = item.get("category", "Other")
177
+ amount = item.get("amount", 0)
178
+ categories[category] += amount
179
+ total_cost += amount
180
+
181
+ receipt = "**Receipt:**\n\n"
182
+ for category, amount in categories.items():
183
+ receipt += f"{category}: ${amount}\n"
184
+ receipt += f"\n**Total Cost:** ${total_cost}\n"
185
+
186
+ return receipt
187
+
188
+ # Function to generate an itinerary based on number of days and solo travel
189
+ def create_itinerary(days, traveling_alone):
190
+ itinerary = "**Your Trip Itinerary:**\n\n"
191
+ for day in range(1, days + 1):
192
+ if traveling_alone == "Yes":
193
+ itinerary += f"Day {day}: Explore local attractions, enjoy a quiet meal, and relax.\n"
194
+ else:
195
+ itinerary += f"Day {day}: Group tour, shared meals, and adventure activities.\n"
196
+
197
+ return itinerary
198
+
199
+
200
+ #Theme stuff
201
+ travelwise_purple = colors.Color(
202
+ name="travelwise_purple",
203
+ c50="#f4f0f8",
204
+ c100="#e3d9ee",
205
+ c200="#c7b3dc",
206
+ c300="#ab8dca",
207
+ c400="#8f67b8",
208
+ c500="#947EB0", # main color
209
+ c600="#7e5f97",
210
+ c700="#5e476f",
211
+ c800="#3f2f47",
212
+ c900="#1f1820",
213
+ c950="#0f0c10"
214
+ )
215
+
216
+ light_neutral = colors.Color(
217
+ name="light_neutral",
218
+ c50="#ffffff",
219
+ c100="#f9f9f9",
220
+ c200="#f3f3f3",
221
+ c300="#eaeaea",
222
+ c400="#d9d9d9",
223
+ c500="#c0c0c0",
224
+ c600="#a7a7a7",
225
+ c700="#8e8e8e",
226
+ c800="#757575",
227
+ c900="#5c5c5c",
228
+ c950="#3c3c3c"
229
+ )
230
+
231
+ my_theme = gr.themes.Soft(
232
+ primary_hue= travelwise_purple,
233
+ neutral_hue= light_neutral, # optional
234
+ ).set(
235
+ body_background_fill="#f1eeccff",
236
+ button_secondary_background_fill="#ABA8A6",
237
+ button_primary_text_color="#004643"
238
+ )
239
+
240
+
241
+ def generate_receipt(expenses):
242
+ categories = {
243
+ "Food": 0,
244
+ "Transportation": 0,
245
+ "Accommodation": 0,
246
+ "Other": 0
247
+ }
248
+ total_cost = 0
249
+ for item in expenses:
250
+ category = item.get("category", "Other")
251
+ amount = item.get("amount", 0)
252
+ # If a category is not predefined, accumulate it under 'Other'
253
+ if category not in categories:
254
+ categories["Other"] += amount
255
+ else:
256
+ categories[category] += amount
257
+ total_cost += amount
258
+ receipt = "**Receipt:**\n\n"
259
+ for cat, amt in categories.items():
260
+ receipt += f"{cat}: ${amt}\n"
261
+ receipt += f"\n**Total Cost:** ${total_cost}\n"
262
+ return receipt
263
+
264
+ def parse_expenses(user_input):
265
+ """
266
+ Parses a string of expenses in the format:
267
+ "Food - 20, Transportation - 15, Accommodation - 100"
268
+ into a list of dictionaries required by generate_receipt.
269
+ """
270
+ expenses = []
271
+ if not user_input.strip():
272
+ return "โš ๏ธ Please enter some expenses."
273
+ entries = user_input.split(",")
274
+ for entry in entries:
275
+ try:
276
+ category, amount = entry.strip().split("-")
277
+ expenses.append({
278
+ "category": category.strip().title(),
279
+ "amount": float(amount.strip())
280
+ })
281
+ except ValueError:
282
+ return ("โš ๏ธ Please ensure each expense is entered in the format: "
283
+ "Category - Amount (e.g., Food - 20, Transportation - 15)")
284
+ return generate_receipt(expenses)
285
+ # Gradio UI
286
+ # === Gradio UI Setup ===
287
+ # === Gradio UI Setup ===
288
+ with gr.Blocks(theme=my_theme) as demo:
289
+
290
+ chat_history = gr.State([]) # Maintain chat history across interactions
291
+
292
+ # ๐Ÿ”น Add `user_id_input` to store user identity across sessions ๐Ÿ”น
293
+ user_id_input = gr.Textbox(value="anonymous", interactive=False, visible=False)
294
+
295
+ with gr.Tabs():
296
+ with gr.TabItem("Chatbot"):
297
+ with gr.Row():
298
+ with gr.Column(scale=2):
299
+ gr.Image(
300
+ value="logo.png",
301
+ width=400,
302
+ show_label=False,
303
+ show_download_button=False,
304
+ container=False,
305
+ show_share_button=False,
306
+ interactive=False
307
+ )
308
+ 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.")
309
+ with gr.Row():
310
+ with gr.Column(scale=2):
311
+ gr.Markdown("## Chatbot")
312
+ chatbot = gr.Chatbot(type="messages") # Display chat conversation
313
+ chat_input = gr.Textbox(label="Your Message") # User input field
314
+ chat_button = gr.Button("Send Message") # Send message button
315
+ clear_history_btn = gr.Button("Clear Chat History") # Reset conversation button
316
+ with gr.Column(scale=1):
317
+ gr.Markdown("### โœˆ๏ธ Travel Preferences")
318
+ budget = gr.Textbox(label="๐Ÿ’ฐ Budget")
319
+ language = gr.Dropdown(
320
+ label="๐ŸŒ Language",
321
+ choices=["English", "Korean", "Japanese", "French", "Spanish", "Chinese", "Italian"]
322
+ )
323
+ destination = gr.Textbox(label="๐Ÿ—บ๏ธ Travel Location")
324
+ current_location = gr.Textbox(label="๐Ÿ“ Current Location")
325
+ days = gr.Slider(minimum=1, maximum=200, step=1, label="๐Ÿ“† Days Traveling")
326
+ traveling_alone = gr.Dropdown(label="Traveling Alone?", choices=["Yes", "No"], value="No")
327
+ diet = gr.Textbox(label="๐Ÿฅ— Dietary Restrictions")
328
+ pref_button = gr.Button("Update Preferences")
329
+ pref_confirmation = gr.Textbox(label="Preference Update Confirmation")
330
+
331
+ with gr.TabItem("Budget"):
332
+ gr.Markdown("### Enter your expenses in the format: `Category - Amount`, separated by commas.")
333
+ expense_input = gr.Textbox(
334
+ placeholder="Food - 20, Transportation - 15, Accommodation - 100",
335
+ label="Expenses"
336
+ )
337
+ generate_button = gr.Button("Generate Receipt")
338
+ receipt_output = gr.Markdown()
339
+
340
+ generate_button.click(
341
+ fn=parse_expenses,
342
+ inputs=[expense_input],
343
+ outputs=[receipt_output]
344
+ )
345
+
346
+ with gr.TabItem("Itinerary"):
347
+ days_input = gr.Slider(1, 14, label="Number of Days")
348
+ solo_input = gr.Dropdown(["Yes", "No"], label="Traveling Alone?")
349
+ generate_itinerary_button = gr.Button("Generate Itinerary")
350
+ itinerary_output = gr.Markdown()
351
+
352
+ generate_itinerary_button.click(
353
+ fn=create_itinerary,
354
+ inputs=[days_input, solo_input],
355
+ outputs=[itinerary_output]
356
+ )
357
+
358
+ # ๐Ÿ”น Fix: Ensure user ID is passed correctly ๐Ÿ”น
359
+ chat_button.click(
360
+ fn=respond_chat,
361
+ inputs=[chat_input, chat_history, user_id_input], # โœ… Ensure correct user ID is used
362
+ outputs=[chatbot, chat_history]
363
+ )
364
+
365
+ # ๐Ÿ”น Fix: Correctly clear history by passing `user_id_input` ๐Ÿ”น
366
+ clear_history_btn.click(
367
+ fn=clear_chat_history,
368
+ inputs=[user_id_input], # โœ… Pass stored user ID correctly
369
+ outputs=[chatbot, chat_history]
370
+ )
371
+
372
+ # Bind the sidebar button for updating travel preferences.
373
+ pref_button.click(
374
+ fn=update_preferences,
375
+ inputs=[budget, language, destination, current_location, days, traveling_alone, diet],
376
+ outputs=pref_confirmation
377
+ )
378
+
379
+ demo.launch(debug=True)