AlexanderStaniel commited on
Commit
34e9bb0
·
verified ·
1 Parent(s): 758a6dd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +412 -0
app.py CHANGED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ✨🚀 The ULTIMATE Step-by-Step V1.0 Build Guide for Your AI Travel Bot (Granular for Beginners)
2
+
3
+ Welcome, future coder! 🎉 This guide contains every single step and command you need—no external references required. Follow along on Windows 11, and you'll have a working AI Travel Bot by the end.
4
+
5
+ 📋 Prerequisites (Install Everything First)
6
+
7
+ Before we begin, install all tools below. Each is required for the project.
8
+
9
+ Git
10
+
11
+ Open your browser to https://git-scm.com/downloads
12
+
13
+ Click Download for Windows.
14
+
15
+ Run the downloaded installer (Git-*.exe).
16
+
17
+ In each dialog: click Next, accept defaults, then Finish.
18
+
19
+ Verify by opening Command Prompt and running:
20
+
21
+
22
+
23
+ git --version
24
+ ```
25
+ You should see git version 2.x.x.
26
+
27
+ Python 3.10+
28
+
29
+ Go to https://python.org/downloads
30
+
31
+ Click Download Python 3.10.x.
32
+
33
+ Run the installer.
34
+
35
+ Important: Check Add Python to PATH.
36
+
37
+ Click Install Now.
38
+
39
+ Verify:
40
+
41
+
42
+
43
+ python --version
44
+ ```
45
+ You should see Python 3.10.x.
46
+
47
+ Node.js (LTS)
48
+
49
+ Visit https://nodejs.org/en/download/
50
+
51
+ Download Windows Installer (.msi) for LTS.
52
+
53
+ Run the installer: Next → Next → Install → Finish.
54
+
55
+ Verify:
56
+
57
+
58
+
59
+ node --version
60
+ npm --version
61
+ ```
62
+ You should see versions (e.g., v18.x.x, 8.x.x).
63
+
64
+ Hugging Face Account
65
+
66
+ Open https://huggingface.co/ and click Sign up.
67
+
68
+ Register with your email or GitHub.
69
+
70
+ Confirm your email and log in.
71
+
72
+ 1️⃣ Create Your Online Home: Hugging Face Space
73
+
74
+ In your browser, go to https://huggingface.co/spaces.
75
+
76
+ Click Create new Space (blue button top right).
77
+
78
+ Fill in:
79
+
80
+ Space name: WanderlustAI-v1
81
+
82
+ SDK: select Gradio
83
+
84
+ Visibility: choose Public for now
85
+
86
+ Click Create Space.
87
+
88
+ Wait until you see the file explorer panel on the left. That’s your project directory.
89
+
90
+ 2️⃣ Set Up Your Project Files (The Skeleton)
91
+
92
+ In the Hugging Face Space file explorer, create these files one at a time:
93
+
94
+ app.py
95
+
96
+ Click Add file → Create file.
97
+
98
+ Enter app.py → click Create.
99
+
100
+ README.md
101
+
102
+ Add file → README.md → Create.
103
+
104
+ .env.example
105
+
106
+ Add file → .env.example → Create.
107
+
108
+ requirements.txt
109
+
110
+ Add file → requirements.txt → Create.
111
+
112
+ packages.txt
113
+
114
+ Add file → packages.txt → Create.
115
+
116
+ Confirm all five files appear in the list.
117
+
118
+ 3️⃣ Document the Essentials (Fill In Templates)
119
+
120
+ 3.1 README.md
121
+
122
+ Click README.md → pencil icon to edit.
123
+
124
+ Paste:
125
+
126
+ # AI Travel Bot
127
+ This project builds an AI-powered travel assistant using ChatGPT and web scraping.
128
+
129
+ ## How to Use
130
+ 1. Set up secrets in `.env`.
131
+ 2. Run `python app.py` locally or deploy on Hugging Face.
132
+
133
+ Click Commit changes.
134
+
135
+ 3.2 .env.example
136
+
137
+ Open .env.example → edit.
138
+
139
+ Paste:
140
+
141
+ OPENAI_API_KEY=YOUR_OPENAI_KEY_HERE
142
+ GOOGLE_API_KEY=YOUR_GOOGLE_KEY_HERE
143
+
144
+ Commit changes.
145
+
146
+ 4️⃣ List Your Tools (Dependencies)
147
+
148
+ 4.1 requirements.txt
149
+
150
+ Open requirements.txt → edit.
151
+
152
+ Paste exactly:
153
+
154
+ gradio
155
+ langchain
156
+ langchain-openai
157
+ langchain-google-genai
158
+ langchain-community
159
+ playwright
160
+ scrapy
161
+ beautifulsoup4
162
+ lxml
163
+ requests
164
+ python-dotenv
165
+ cachetools
166
+ tenacity
167
+
168
+ Commit changes.
169
+
170
+ 4.2 packages.txt
171
+
172
+ Open packages.txt → edit.
173
+
174
+ Paste:
175
+
176
+ chromium-driver
177
+
178
+ Commit changes.
179
+
180
+ 5️⃣ Secure Your Secrets (Add Real Keys)
181
+
182
+ In the Space UI, click Settings (top menu).
183
+
184
+ Scroll to Repository secrets → click New secret.
185
+
186
+ Add:
187
+
188
+ Name: OPENAI_API_KEY
189
+
190
+ Value: paste your actual key
191
+
192
+ Click Add secret.
193
+
194
+ Repeat for:
195
+
196
+ GOOGLE_API_KEY
197
+
198
+ 🔒 Secrets are hidden; your code will read them via os.environ.
199
+
200
+ 6️⃣ Build the Brain: Full app.py Code (One Paste)
201
+
202
+ Open app.py → click pencil.
203
+
204
+ Delete any existing text.
205
+
206
+ Copy & paste the ENTIRE code below in one go:
207
+
208
+ # 1. IMPORTS & SETUP
209
+ import asyncio
210
+ import datetime
211
+ import logging
212
+ import os
213
+ import gradio as gr
214
+ from cachetools import TTLCache
215
+ from langchain_core.output_parsers import JsonOutputParser
216
+ from langchain_core.prompts import ChatPromptTemplate
217
+ from langchain_core.pydantic_v1 import BaseModel, Field
218
+ from langchain_openai import ChatOpenAI
219
+
220
+ # Configure logging
221
+ logging.basicConfig(
222
+ level=logging.INFO,
223
+ format='%(asctime)s - %(levelname)s - %(message)s'
224
+ )
225
+
226
+ # Simple in-memory cache
227
+ ttl_cache = TTLCache(maxsize=100, ttl=3600)
228
+
229
+ # 2. DATA MODEL
230
+ class TravelRequest(BaseModel):
231
+ origin_city: str = Field(description="Starting city or airport")
232
+ destination_city: str = Field(description="Destination city or airport")
233
+ start_date: str = Field(description="Trip start date (YYYY-MM-DD)")
234
+ end_date: str = Field(description="Trip end date (YYYY-MM-DD)")
235
+ budget: int = Field(description="Total budget in USD")
236
+ interests: list[str] = Field(description="List of interests like 'hiking', 'museums'")
237
+
238
+ # 3. PARSE USER INPUT
239
+ async def _extract_user_request(question: str) -> dict:
240
+ llm = ChatOpenAI(model="gpt-4o", temperature=0)
241
+ parser = JsonOutputParser(pydantic_object=TravelRequest)
242
+ prompt = ChatPromptTemplate.from_messages([
243
+ ("system", "You are a travel assistant. Extract details into JSON."),
244
+ ("human", "{format_instructions}\n{request}")
245
+ ]).partial(format_instructions=parser.get_format_instructions())
246
+ date_str = datetime.date.today().isoformat()
247
+ logging.info(f"Extracting: {question}")
248
+ return await (prompt | llm | parser).ainvoke({
249
+ "request": question,
250
+ "current_date": date_str
251
+ })
252
+
253
+ # 4. SEARCH HELPERS
254
+ async def _cached_search(func, details):
255
+ key = (func.__name__, str(details))
256
+ if key in ttl_cache:
257
+ logging.info(f"Cache hit for {func.__name__}")
258
+ return ttl_cache[key]
259
+ logging.info(f"Cache miss for {func.__name__}")
260
+ res = await func(details)
261
+ ttl_cache[key] = res
262
+ return res
263
+
264
+ async def _search_skyscanner(details: dict) -> list:
265
+ try:
266
+ logging.info(f"Skyscanner: {details['destination_city']}")
267
+ await asyncio.sleep(1)
268
+ return [{
269
+ "source": "Skyscanner",
270
+ "type": "flight",
271
+ "details": f"Round-trip to {details['destination_city']}",
272
+ "price": 800,
273
+ "link": "https://skyscanner.com"
274
+ }]
275
+ except Exception as e:
276
+ logging.error(e)
277
+ return []
278
+
279
+ async def _search_expedia(details: dict) -> list:
280
+ try:
281
+ logging.info(f"Expedia: {details['destination_city']}")
282
+ await asyncio.sleep(1)
283
+ return [{
284
+ "source": "Expedia",
285
+ "type": "hotel",
286
+ "details": f"Hotel in {details['destination_city']}",
287
+ "price": 1200,
288
+ "link": "https://expedia.com"
289
+ }]
290
+ except Exception as e:
291
+ logging.error(e)
292
+ return []
293
+
294
+ async def _search_getyourguide(details: dict) -> list:
295
+ try:
296
+ logging.info(f"GetYourGuide: {details['interests']}")
297
+ await asyncio.sleep(1)
298
+ return [{
299
+ "source": "GetYourGuide",
300
+ "type": "activity",
301
+ "details": f"Tour: {details['interests']}",
302
+ "price": 150,
303
+ "link": "https://getyourguide.com"
304
+ }]
305
+ except Exception as e:
306
+ logging.error(e)
307
+ return []
308
+
309
+ # 5. GATHER ALL RESULTS
310
+ async def _gather_travel_data(req: dict) -> list:
311
+ tasks = [
312
+ _cached_search(_search_skyscanner, req),
313
+ _cached_search(_search_expedia, req),
314
+ _cached_search(_search_getyourguide, req)
315
+ ]
316
+ results = await asyncio.gather(*tasks, return_exceptions=True)
317
+ combined = []
318
+ for r in results:
319
+ if isinstance(r, Exception):
320
+ logging.error(r)
321
+ else:
322
+ combined.extend(r)
323
+ return combined
324
+
325
+ # 6. FORMAT OUTPUT
326
+ def _format_response(req: dict, data: list) -> str:
327
+ dest = req['destination_city']
328
+ text = f"## Travel Plan for {dest}\n"
329
+ flights = [i for i in data if i['type']=='flight']
330
+ hotels = [i for i in data if i['type']=='hotel']
331
+ acts = [i for i in data if i['type']=='activity']
332
+ if flights:
333
+ text += "\n### Flights:\n"
334
+ for f in flights:
335
+ text += f"- {f['details']} (${f['price']}) [Book]({f['link']})\n"
336
+ if hotels:
337
+ text += "\n### Hotels:\n"
338
+ for h in hotels:
339
+ text += f"- {h['details']} (${h['price']}) [Book]({h['link']})\n"
340
+ if acts:
341
+ text += "\n### Activities:\n"
342
+ for a in acts:
343
+ text += f"- {a['details']} (${a['price']}) [Book]({a['link']})\n"
344
+ return text
345
+
346
+ # 7. MAIN ORCHESTRATOR & UI
347
+ async def ask_bot(question: str) -> str:
348
+ if not question:
349
+ return "Tell me your trip!"
350
+ try:
351
+ req = await _extract_user_request(question)
352
+ data = await _gather_travel_data(req)
353
+ return _format_response(req, data)
354
+ except Exception as e:
355
+ logging.error(e)
356
+ return "Error. Try again."
357
+
358
+ iface = gr.Interface(
359
+ fn=ask_bot,
360
+ inputs=gr.Textbox(lines=4, label="Your dream trip?"),
361
+ outputs=gr.Markdown(),
362
+ title="AI Travel Bot"
363
+ )
364
+
365
+ if __name__ == "__main__":
366
+ iface.launch(server_name="0.0.0.0", server_port=7860)
367
+
368
+ Click Commit changes to save and trigger build.
369
+
370
+ 7️⃣ Local Smoke Test (Catch Errors Early)
371
+
372
+ On Windows Command Prompt:
373
+
374
+ git clone https://huggingface.co/spaces/YOUR_USERNAME/WanderlustAI-v1
375
+ cd WanderlustAI-v1
376
+ copy .env.example .env
377
+ # Open .env in Notepad and paste real keys
378
+ pip install -r requirements.txt
379
+ python app.py
380
+
381
+ A browser window opens with your bot.
382
+
383
+ Type e.g. "Cheap flight from NYC to Paris" → Submit.
384
+
385
+ Check console: you should see INFO logs like "Extracting:" and "Skyscanner:".
386
+
387
+ If it runs without errors, move to deployment.
388
+
389
+ 8️⃣ Deploy to Hugging Face
390
+
391
+ git add .
392
+ git commit -m "V1 ready"
393
+ git push origin main
394
+
395
+ In your Space on the web, click App tab.
396
+
397
+ Wait ~60 seconds for build.
398
+
399
+ Test live UI again.
400
+
401
+ 🎯 Roadmap to V2.0
402
+
403
+ Split code into modules (extract.py, search.py, format.py).
404
+
405
+ Write tests with pytest & vcrpy.
406
+
407
+ Add retry/backoff examples using tenacity.
408
+
409
+ Automate CI/CD with GitHub Actions.
410
+
411
+ Congratulations! You've got every step—install, code, test, deploy—all in one place. Now build your AI Travel Bot! 🌍✨
412
+