Spaces:
Sleeping
Sleeping
pleabargain
commited on
Update app.py
Browse files
app.py
CHANGED
@@ -1,11 +1,17 @@
|
|
1 |
import gradio as gr
|
2 |
import datetime
|
3 |
-
from typing import Dict, List
|
4 |
import random
|
5 |
from huggingface_hub import InferenceClient
|
|
|
|
|
6 |
|
7 |
-
#
|
8 |
-
|
|
|
|
|
|
|
|
|
9 |
|
10 |
class TravelPlanner:
|
11 |
def __init__(self):
|
@@ -31,6 +37,28 @@ class TravelPlanner:
|
|
31 |
'street_food': (5, 10)
|
32 |
}
|
33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
def generate_itinerary(self,
|
35 |
destination: str,
|
36 |
num_days: int,
|
@@ -38,30 +66,35 @@ class TravelPlanner:
|
|
38 |
"""
|
39 |
Generate a daily itinerary based on destination and budget level
|
40 |
"""
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
]
|
53 |
-
|
54 |
-
for day in range(1, num_days + 1):
|
55 |
-
daily_schedule = {
|
56 |
-
'day': day,
|
57 |
-
'morning': random.choice(activities),
|
58 |
-
'afternoon': random.choice(activities),
|
59 |
-
'evening': random.choice(activities),
|
60 |
-
'accommodation': f"{budget_level} accommodation"
|
61 |
-
}
|
62 |
-
itinerary.append(daily_schedule)
|
63 |
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
def calculate_budget(self,
|
67 |
num_days: int,
|
@@ -70,35 +103,39 @@ class TravelPlanner:
|
|
70 |
"""
|
71 |
Calculate estimated budget based on duration and comfort level
|
72 |
"""
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
|
|
102 |
|
103 |
def format_output(self,
|
104 |
destination: str,
|
@@ -107,29 +144,67 @@ class TravelPlanner:
|
|
107 |
"""
|
108 |
Format the itinerary and budget into a readable string
|
109 |
"""
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
|
128 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
|
130 |
def respond(
|
131 |
message: str,
|
132 |
-
history: List[Dict[str, str]],
|
133 |
system_message: str,
|
134 |
max_tokens: int,
|
135 |
temperature: float,
|
@@ -138,64 +213,113 @@ def respond(
|
|
138 |
"""
|
139 |
Process chat message and generate travel plan if requested
|
140 |
"""
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
destination_idx = parts.index("to") + 1
|
147 |
-
days_idx = parts.index("days") - 1
|
148 |
-
budget_idx = parts.index("budget") - 1
|
149 |
-
people_idx = parts.index("people") - 1
|
150 |
-
|
151 |
-
destination = parts[destination_idx].capitalize()
|
152 |
-
num_days = int(parts[days_idx])
|
153 |
-
budget_level = parts[budget_idx]
|
154 |
-
num_people = int(parts[people_idx])
|
155 |
-
|
156 |
-
# Generate travel plan
|
157 |
planner = TravelPlanner()
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
184 |
|
185 |
-
# Create the Gradio interface
|
186 |
demo = gr.ChatInterface(
|
187 |
respond,
|
188 |
additional_inputs=[
|
189 |
-
gr.Textbox(
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
gr.Slider(
|
194 |
-
|
195 |
-
|
196 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
)
|
198 |
|
199 |
# Launch the application
|
200 |
if __name__ == "__main__":
|
201 |
-
|
|
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
import datetime
|
3 |
+
from typing import Dict, List, Union, Optional
|
4 |
import random
|
5 |
from huggingface_hub import InferenceClient
|
6 |
+
import logging
|
7 |
+
import json
|
8 |
|
9 |
+
# Set up logging
|
10 |
+
logging.basicConfig(
|
11 |
+
level=logging.INFO,
|
12 |
+
format='%(asctime)s - %(levelname)s - %(message)s'
|
13 |
+
)
|
14 |
+
logger = logging.getLogger(__name__)
|
15 |
|
16 |
class TravelPlanner:
|
17 |
def __init__(self):
|
|
|
37 |
'street_food': (5, 10)
|
38 |
}
|
39 |
|
40 |
+
def validate_inputs(self,
|
41 |
+
destination: str,
|
42 |
+
num_days: int,
|
43 |
+
budget_level: str,
|
44 |
+
num_people: int) -> tuple[bool, str]:
|
45 |
+
"""
|
46 |
+
Validate input parameters
|
47 |
+
"""
|
48 |
+
if not destination or len(destination.strip()) == 0:
|
49 |
+
return False, "Destination cannot be empty"
|
50 |
+
|
51 |
+
if num_days < 1 or num_days > 30:
|
52 |
+
return False, "Number of days must be between 1 and 30"
|
53 |
+
|
54 |
+
if budget_level not in self.accommodation_types:
|
55 |
+
return False, f"Budget level must be one of: {', '.join(self.accommodation_types.keys())}"
|
56 |
+
|
57 |
+
if num_people < 1 or num_people > 10:
|
58 |
+
return False, "Number of people must be between 1 and 10"
|
59 |
+
|
60 |
+
return True, ""
|
61 |
+
|
62 |
def generate_itinerary(self,
|
63 |
destination: str,
|
64 |
num_days: int,
|
|
|
66 |
"""
|
67 |
Generate a daily itinerary based on destination and budget level
|
68 |
"""
|
69 |
+
try:
|
70 |
+
activities = [
|
71 |
+
'Morning sightseeing',
|
72 |
+
'Museum visit',
|
73 |
+
'Local market tour',
|
74 |
+
'Cultural workshop',
|
75 |
+
'Historical site visit',
|
76 |
+
'Nature walk',
|
77 |
+
'Local neighborhood exploration',
|
78 |
+
'Evening entertainment'
|
79 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
|
81 |
+
itinerary = []
|
82 |
+
for day in range(1, num_days + 1):
|
83 |
+
# Ensure no duplicate activities in same day
|
84 |
+
day_activities = random.sample(activities, 3)
|
85 |
+
daily_schedule = {
|
86 |
+
'day': day,
|
87 |
+
'morning': day_activities[0],
|
88 |
+
'afternoon': day_activities[1],
|
89 |
+
'evening': day_activities[2],
|
90 |
+
'accommodation': f"{budget_level} accommodation"
|
91 |
+
}
|
92 |
+
itinerary.append(daily_schedule)
|
93 |
+
|
94 |
+
return itinerary
|
95 |
+
except Exception as e:
|
96 |
+
logger.error(f"Error generating itinerary: {str(e)}")
|
97 |
+
raise
|
98 |
|
99 |
def calculate_budget(self,
|
100 |
num_days: int,
|
|
|
103 |
"""
|
104 |
Calculate estimated budget based on duration and comfort level
|
105 |
"""
|
106 |
+
try:
|
107 |
+
accommodation_range = self.accommodation_types[budget_level]
|
108 |
+
meal_range = self.meal_costs[budget_level]
|
109 |
+
|
110 |
+
# Use median values for more stable estimates
|
111 |
+
daily_accommodation = sum(accommodation_range) / 2
|
112 |
+
daily_meals = (sum(meal_range) / 2) * 3 # 3 meals per day
|
113 |
+
daily_activities = 65 # Median activity cost
|
114 |
+
daily_transport = 30 # Median transport cost
|
115 |
+
|
116 |
+
total_accommodation = daily_accommodation * num_days
|
117 |
+
total_meals = daily_meals * num_days * num_people
|
118 |
+
total_activities = daily_activities * num_days * num_people
|
119 |
+
total_transport = daily_transport * num_days * num_people
|
120 |
+
|
121 |
+
# Add 15% buffer for unexpected expenses
|
122 |
+
subtotal = total_accommodation + total_meals + total_activities + total_transport
|
123 |
+
buffer = subtotal * 0.15
|
124 |
+
|
125 |
+
total_cost = subtotal + buffer
|
126 |
+
|
127 |
+
return {
|
128 |
+
'accommodation': round(total_accommodation, 2),
|
129 |
+
'meals': round(total_meals, 2),
|
130 |
+
'activities': round(total_activities, 2),
|
131 |
+
'transport': round(total_transport, 2),
|
132 |
+
'buffer': round(buffer, 2),
|
133 |
+
'total': round(total_cost, 2),
|
134 |
+
'per_person': round(total_cost / num_people, 2)
|
135 |
+
}
|
136 |
+
except Exception as e:
|
137 |
+
logger.error(f"Error calculating budget: {str(e)}")
|
138 |
+
raise
|
139 |
|
140 |
def format_output(self,
|
141 |
destination: str,
|
|
|
144 |
"""
|
145 |
Format the itinerary and budget into a readable string
|
146 |
"""
|
147 |
+
try:
|
148 |
+
output = [f"π Travel Plan for {destination}", ""]
|
149 |
+
output.append("ποΈ ITINERARY")
|
150 |
+
output.append("=" * 20)
|
151 |
+
|
152 |
+
for day in itinerary:
|
153 |
+
output.extend([
|
154 |
+
f"Day {day['day']}:",
|
155 |
+
f"π
Morning: {day['morning']}",
|
156 |
+
f"βοΈ Afternoon: {day['afternoon']}",
|
157 |
+
f"π Evening: {day['evening']}",
|
158 |
+
f"π Accommodation: {day['accommodation']}",
|
159 |
+
""
|
160 |
+
])
|
161 |
+
|
162 |
+
output.extend([
|
163 |
+
"π° BUDGET BREAKDOWN",
|
164 |
+
"=" * 20,
|
165 |
+
f"π¨ Accommodation: ${budget['accommodation']:,.2f}",
|
166 |
+
f"π½οΈ Meals: ${budget['meals']:,.2f}",
|
167 |
+
f"π« Activities: ${budget['activities']:,.2f}",
|
168 |
+
f"π Local Transport: ${budget['transport']:,.2f}",
|
169 |
+
f"β οΈ Buffer (15%): ${budget['buffer']:,.2f}",
|
170 |
+
"=" * 20,
|
171 |
+
f"π΅ Total Cost: ${budget['total']:,.2f}",
|
172 |
+
f"π₯ Cost per person: ${budget['per_person']:,.2f}"
|
173 |
+
])
|
174 |
+
|
175 |
+
return "\n".join(output)
|
176 |
+
except Exception as e:
|
177 |
+
logger.error(f"Error formatting output: {str(e)}")
|
178 |
+
return "Error formatting travel plan. Please try again."
|
179 |
+
|
180 |
+
def parse_travel_request(message: str) -> Optional[Dict]:
|
181 |
+
"""
|
182 |
+
Parse travel request message and extract parameters
|
183 |
+
"""
|
184 |
+
try:
|
185 |
+
message = message.lower()
|
186 |
+
if "plan a trip" not in message:
|
187 |
+
return None
|
188 |
+
|
189 |
+
parts = message.split()
|
190 |
+
destination_idx = parts.index("to") + 1
|
191 |
+
days_idx = parts.index("days") - 1
|
192 |
+
budget_idx = parts.index("budget") - 1
|
193 |
+
people_idx = parts.index("people") - 1
|
194 |
|
195 |
+
return {
|
196 |
+
"destination": parts[destination_idx].capitalize(),
|
197 |
+
"num_days": int(parts[days_idx]),
|
198 |
+
"budget_level": parts[budget_idx],
|
199 |
+
"num_people": int(parts[people_idx])
|
200 |
+
}
|
201 |
+
except (ValueError, IndexError) as e:
|
202 |
+
logger.warning(f"Error parsing travel request: {str(e)}")
|
203 |
+
return None
|
204 |
|
205 |
def respond(
|
206 |
message: str,
|
207 |
+
history: List[Dict[str, str]],
|
208 |
system_message: str,
|
209 |
max_tokens: int,
|
210 |
temperature: float,
|
|
|
213 |
"""
|
214 |
Process chat message and generate travel plan if requested
|
215 |
"""
|
216 |
+
try:
|
217 |
+
# Check if this is a travel planning request
|
218 |
+
travel_params = parse_travel_request(message)
|
219 |
+
|
220 |
+
if travel_params:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
planner = TravelPlanner()
|
222 |
+
|
223 |
+
# Validate inputs
|
224 |
+
is_valid, error_msg = planner.validate_inputs(
|
225 |
+
travel_params["destination"],
|
226 |
+
travel_params["num_days"],
|
227 |
+
travel_params["budget_level"],
|
228 |
+
travel_params["num_people"]
|
229 |
+
)
|
230 |
+
|
231 |
+
if not is_valid:
|
232 |
+
return f"Error: {error_msg}\n\nPlease use the format: 'Plan a trip to [destination] for [X] days, [budget_level] budget, [X] people'"
|
233 |
+
|
234 |
+
try:
|
235 |
+
# Generate travel plan
|
236 |
+
itinerary = planner.generate_itinerary(
|
237 |
+
travel_params["destination"],
|
238 |
+
travel_params["num_days"],
|
239 |
+
travel_params["budget_level"]
|
240 |
+
)
|
241 |
+
|
242 |
+
budget = planner.calculate_budget(
|
243 |
+
travel_params["num_days"],
|
244 |
+
travel_params["budget_level"],
|
245 |
+
travel_params["num_people"]
|
246 |
+
)
|
247 |
+
|
248 |
+
return planner.format_output(
|
249 |
+
travel_params["destination"],
|
250 |
+
itinerary,
|
251 |
+
budget
|
252 |
+
)
|
253 |
+
except Exception as e:
|
254 |
+
logger.error(f"Error generating travel plan: {str(e)}")
|
255 |
+
return "Sorry, there was an error generating your travel plan. Please try again."
|
256 |
+
|
257 |
+
# If not a travel request, use the Hugging Face model
|
258 |
+
try:
|
259 |
+
client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
|
260 |
+
|
261 |
+
messages = [{"role": "system", "content": system_message}]
|
262 |
+
for msg in history:
|
263 |
+
if isinstance(msg, dict) and "role" in msg and "content" in msg:
|
264 |
+
messages.append(msg)
|
265 |
+
|
266 |
+
messages.append({"role": "user", "content": message})
|
267 |
+
|
268 |
+
response = ""
|
269 |
+
for token in client.chat_completion(
|
270 |
+
messages,
|
271 |
+
max_tokens=max_tokens,
|
272 |
+
stream=True,
|
273 |
+
temperature=temperature,
|
274 |
+
top_p=top_p,
|
275 |
+
):
|
276 |
+
if hasattr(token.choices[0].delta, 'content'):
|
277 |
+
response += token.choices[0].delta.content or ""
|
278 |
+
return response
|
279 |
+
|
280 |
+
except Exception as e:
|
281 |
+
logger.error(f"Error with chat model: {str(e)}")
|
282 |
+
return "I apologize, but I'm having trouble connecting to the chat service. You can still use me for travel planning by saying 'Plan a trip to [destination]...'"
|
283 |
+
|
284 |
+
except Exception as e:
|
285 |
+
logger.error(f"Unexpected error in respond function: {str(e)}")
|
286 |
+
return "An unexpected error occurred. Please try again."
|
287 |
|
288 |
+
# Create the Gradio interface
|
289 |
demo = gr.ChatInterface(
|
290 |
respond,
|
291 |
additional_inputs=[
|
292 |
+
gr.Textbox(
|
293 |
+
value="You are a travel planning assistant who can also chat about other topics.",
|
294 |
+
label="System message"
|
295 |
+
),
|
296 |
+
gr.Slider(
|
297 |
+
minimum=1,
|
298 |
+
maximum=2048,
|
299 |
+
value=512,
|
300 |
+
step=1,
|
301 |
+
label="Max new tokens"
|
302 |
+
),
|
303 |
+
gr.Slider(
|
304 |
+
minimum=0.1,
|
305 |
+
maximum=4.0,
|
306 |
+
value=0.7,
|
307 |
+
step=0.1,
|
308 |
+
label="Temperature"
|
309 |
+
),
|
310 |
+
gr.Slider(
|
311 |
+
minimum=0.1,
|
312 |
+
maximum=1.0,
|
313 |
+
value=0.95,
|
314 |
+
step=0.05,
|
315 |
+
label="Top-p (nucleus sampling)"
|
316 |
+
),
|
317 |
+
]
|
318 |
)
|
319 |
|
320 |
# Launch the application
|
321 |
if __name__ == "__main__":
|
322 |
+
try:
|
323 |
+
demo.launch()
|
324 |
+
except Exception as e:
|
325 |
+
logger.error(f"Error launching Gradio interface: {str(e)}")
|