aqibali06 commited on
Commit
155d07f
Β·
verified Β·
1 Parent(s): 1dce01b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +572 -0
app.py ADDED
@@ -0,0 +1,572 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import time
4
+ import logging
5
+ from typing import List, Dict, Tuple, Optional
6
+ import warnings
7
+ warnings.filterwarnings("ignore")
8
+
9
+ # Core libraries
10
+ import numpy as np
11
+ import pandas as pd
12
+ from sentence_transformers import SentenceTransformer
13
+ import faiss
14
+ import pickle
15
+ import json
16
+
17
+ # Translation and TTS
18
+ from googletrans import Translator
19
+ import gtts
20
+ import io
21
+ import tempfile
22
+
23
+ # Web scraping and API calls
24
+ import requests
25
+ from bs4 import BeautifulSoup
26
+ import wikipedia
27
+ import urllib.parse
28
+
29
+ # Speech recognition
30
+ import speech_recognition as sr
31
+ from pydub import AudioSegment
32
+
33
+ # LLM API clients
34
+ import groq
35
+ from google.generativeai import GenerativeModel
36
+ import google.generativeai as genai
37
+
38
+ # Setup logging
39
+ logging.basicConfig(level=logging.INFO)
40
+ logger = logging.getLogger(__name__)
41
+
42
+ class TouristGuideBot:
43
+ def __init__(self):
44
+ """Initialize the Tourist Guide Bot with all necessary components."""
45
+
46
+ # Initialize components
47
+ self.translator = Translator()
48
+ self.embedding_model = None
49
+ self.faiss_index = None
50
+ self.knowledge_base = []
51
+ self.groq_client = None
52
+ self.gemini_model = None
53
+
54
+ # Supported languages
55
+ self.languages = {
56
+ "English": "en",
57
+ "Urdu": "ur",
58
+ "Arabic": "ar",
59
+ "French": "fr",
60
+ "Spanish": "es",
61
+ "German": "de",
62
+ "Italian": "it",
63
+ "Chinese": "zh",
64
+ "Japanese": "ja",
65
+ "Hindi": "hi"
66
+ }
67
+
68
+ # Initialize models and data
69
+ self.setup_models()
70
+ self.load_or_create_knowledge_base()
71
+
72
+ def setup_models(self):
73
+ """Setup embedding model and LLM APIs."""
74
+ try:
75
+ # Load multilingual sentence transformer
76
+ self.embedding_model = SentenceTransformer('sentence-transformers/distiluse-base-multilingual-cased-v2')
77
+ logger.info("βœ… Embedding model loaded successfully")
78
+
79
+ # Setup API clients (add your API keys)
80
+ groq_api_key = os.getenv('GROQ_API_KEY')
81
+ gemini_api_key = os.getenv('GEMINI_API_KEY')
82
+
83
+ if groq_api_key:
84
+ self.groq_client = groq.Groq(api_key=groq_api_key)
85
+ logger.info("βœ… Groq API client initialized")
86
+
87
+ if gemini_api_key:
88
+ genai.configure(api_key=gemini_api_key)
89
+ self.gemini_model = GenerativeModel('gemini-pro')
90
+ logger.info("βœ… Gemini API client initialized")
91
+
92
+ except Exception as e:
93
+ logger.error(f"❌ Error setting up models: {e}")
94
+
95
+ def load_or_create_knowledge_base(self):
96
+ """Load existing knowledge base or create a new one."""
97
+ kb_path = "travel_knowledge_base.pkl"
98
+ index_path = "faiss_index.idx"
99
+
100
+ try:
101
+ # Try to load existing knowledge base
102
+ if os.path.exists(kb_path) and os.path.exists(index_path):
103
+ with open(kb_path, 'rb') as f:
104
+ self.knowledge_base = pickle.load(f)
105
+ self.faiss_index = faiss.read_index(index_path)
106
+ logger.info(f"βœ… Loaded existing knowledge base with {len(self.knowledge_base)} chunks")
107
+ else:
108
+ # Create new knowledge base
109
+ self.create_knowledge_base()
110
+
111
+ except Exception as e:
112
+ logger.error(f"❌ Error loading knowledge base: {e}")
113
+ self.create_knowledge_base()
114
+
115
+ def create_knowledge_base(self):
116
+ """Create knowledge base from sample travel data."""
117
+ logger.info("πŸ”„ Creating new knowledge base...")
118
+
119
+ # Sample travel knowledge (in practice, load from files/Wikipedia)
120
+ sample_data = [
121
+ {
122
+ "content": "Paris is the capital city of France, known for its iconic Eiffel Tower, Louvre Museum, and romantic atmosphere. Best visited in spring or fall.",
123
+ "location": "Paris, France",
124
+ "category": "destination"
125
+ },
126
+ {
127
+ "content": "Dubai offers luxury shopping, ultramodern architecture, and desert safaris. The Burj Khalifa is the world's tallest building.",
128
+ "location": "Dubai, UAE",
129
+ "category": "destination"
130
+ },
131
+ {
132
+ "content": "Tokyo combines traditional Japanese culture with cutting-edge technology. Visit temples, enjoy sushi, and experience the bustling city life.",
133
+ "location": "Tokyo, Japan",
134
+ "category": "destination"
135
+ },
136
+ {
137
+ "content": "New York City offers Broadway shows, world-class museums, Central Park, and diverse neighborhoods like Times Square and Brooklyn.",
138
+ "location": "New York, USA",
139
+ "category": "destination"
140
+ },
141
+ {
142
+ "content": "Always carry a universal adapter, pack light, research local customs, and keep copies of important documents when traveling internationally.",
143
+ "location": "General",
144
+ "category": "travel_tips"
145
+ },
146
+ {
147
+ "content": "Book flights 2-3 months in advance for best prices. Use flight comparison websites and be flexible with dates.",
148
+ "location": "General",
149
+ "category": "travel_tips"
150
+ },
151
+ {
152
+ "content": "Istanbul bridges Europe and Asia, featuring the Blue Mosque, Hagia Sophia, and Grand Bazaar. Turkish cuisine is exceptional.",
153
+ "location": "Istanbul, Turkey",
154
+ "category": "destination"
155
+ },
156
+ {
157
+ "content": "Rome offers ancient history with the Colosseum, Vatican City, Trevi Fountain, and delicious Italian food.",
158
+ "location": "Rome, Italy",
159
+ "category": "destination"
160
+ }
161
+ ]
162
+
163
+ # Create embeddings for all content
164
+ texts = [item["content"] for item in sample_data]
165
+ embeddings = self.embedding_model.encode(texts)
166
+
167
+ # Create FAISS index
168
+ dimension = embeddings.shape[1]
169
+ self.faiss_index = faiss.IndexFlatL2(dimension)
170
+ self.faiss_index.add(embeddings.astype('float32'))
171
+
172
+ # Store knowledge base
173
+ self.knowledge_base = sample_data
174
+
175
+ # Save to disk
176
+ with open("travel_knowledge_base.pkl", 'wb') as f:
177
+ pickle.dump(self.knowledge_base, f)
178
+ faiss.write_index(self.faiss_index, "faiss_index.idx")
179
+
180
+ logger.info(f"βœ… Created knowledge base with {len(self.knowledge_base)} chunks")
181
+
182
+ def translate_text(self, text: str, target_lang: str = "en", source_lang: str = "auto") -> str:
183
+ """Translate text using Google Translate."""
184
+ try:
185
+ if source_lang == target_lang or target_lang == "en" and source_lang == "auto":
186
+ # Check if text is already in English
187
+ detected = self.translator.detect(text)
188
+ if detected.lang == "en":
189
+ return text
190
+
191
+ result = self.translator.translate(text, dest=target_lang, src=source_lang)
192
+ return result.text
193
+ except Exception as e:
194
+ logger.error(f"Translation error: {e}")
195
+ return text
196
+
197
+ def search_wikipedia(self, query: str, max_results: int = 3) -> List[str]:
198
+ """Search Wikipedia for relevant travel information."""
199
+ try:
200
+ # Search Wikipedia
201
+ search_results = wikipedia.search(query, results=max_results)
202
+ summaries = []
203
+
204
+ for title in search_results[:max_results]:
205
+ try:
206
+ summary = wikipedia.summary(title, sentences=3)
207
+ summaries.append(f"Wikipedia - {title}: {summary}")
208
+ except wikipedia.exceptions.DisambiguationError as e:
209
+ # Try the first option
210
+ try:
211
+ summary = wikipedia.summary(e.options[0], sentences=3)
212
+ summaries.append(f"Wikipedia - {e.options[0]}: {summary}")
213
+ except:
214
+ continue
215
+ except:
216
+ continue
217
+
218
+ return summaries
219
+
220
+ except Exception as e:
221
+ logger.error(f"Wikipedia search error: {e}")
222
+ return []
223
+
224
+ def search_duckduckgo(self, query: str, max_results: int = 3) -> List[str]:
225
+ """Search DuckDuckGo for travel information."""
226
+ try:
227
+ # DuckDuckGo instant answer API
228
+ url = f"https://api.duckduckgo.com/"
229
+ params = {
230
+ 'q': query + " travel guide",
231
+ 'format': 'json',
232
+ 'no_html': '1',
233
+ 'skip_disambig': '1'
234
+ }
235
+
236
+ response = requests.get(url, params=params, timeout=10)
237
+ data = response.json()
238
+
239
+ results = []
240
+
241
+ # Extract abstract
242
+ if data.get('Abstract'):
243
+ results.append(f"DuckDuckGo: {data['Abstract']}")
244
+
245
+ # Extract related topics
246
+ for topic in data.get('RelatedTopics', [])[:2]:
247
+ if isinstance(topic, dict) and topic.get('Text'):
248
+ results.append(f"DuckDuckGo: {topic['Text']}")
249
+
250
+ return results
251
+
252
+ except Exception as e:
253
+ logger.error(f"DuckDuckGo search error: {e}")
254
+ return []
255
+
256
+ def retrieve_similar_chunks(self, query: str, k: int = 3) -> List[Dict]:
257
+ """Retrieve similar chunks from knowledge base using FAISS."""
258
+ try:
259
+ # Create embedding for query
260
+ query_embedding = self.embedding_model.encode([query])
261
+
262
+ # Search in FAISS index
263
+ distances, indices = self.faiss_index.search(query_embedding.astype('float32'), k)
264
+
265
+ # Get relevant chunks
266
+ similar_chunks = []
267
+ for i, idx in enumerate(indices[0]):
268
+ if idx < len(self.knowledge_base):
269
+ chunk = self.knowledge_base[idx].copy()
270
+ chunk['similarity_score'] = float(distances[0][i])
271
+ similar_chunks.append(chunk)
272
+
273
+ return similar_chunks
274
+
275
+ except Exception as e:
276
+ logger.error(f"Retrieval error: {e}")
277
+ return []
278
+
279
+ def query_llm(self, prompt: str) -> str:
280
+ """Query LLM using available APIs."""
281
+ try:
282
+ # Try Groq first
283
+ if self.groq_client:
284
+ try:
285
+ completion = self.groq_client.chat.completions.create(
286
+ messages=[
287
+ {
288
+ "role": "system",
289
+ "content": "You are a helpful and knowledgeable tourist guide assistant. Provide accurate, helpful, and engaging travel advice."
290
+ },
291
+ {
292
+ "role": "user",
293
+ "content": prompt
294
+ }
295
+ ],
296
+ model="llama3-8b-8192",
297
+ temperature=0.7,
298
+ max_tokens=1024
299
+ )
300
+ return completion.choices[0].message.content
301
+ except Exception as e:
302
+ logger.error(f"Groq API error: {e}")
303
+
304
+ # Try Gemini as fallback
305
+ if self.gemini_model:
306
+ try:
307
+ response = self.gemini_model.generate_content(prompt)
308
+ return response.text
309
+ except Exception as e:
310
+ logger.error(f"Gemini API error: {e}")
311
+
312
+ # Fallback response if no API available
313
+ return "I'm sorry, but I'm currently unable to access the AI models. Please check your API keys and try again."
314
+
315
+ except Exception as e:
316
+ logger.error(f"LLM query error: {e}")
317
+ return "An error occurred while processing your request. Please try again."
318
+
319
+ def process_audio_input(self, audio_file) -> str:
320
+ """Convert speech to text."""
321
+ if audio_file is None:
322
+ return ""
323
+
324
+ try:
325
+ # Initialize recognizer
326
+ r = sr.Recognizer()
327
+
328
+ # Load audio file
329
+ with sr.AudioFile(audio_file) as source:
330
+ audio = r.record(source)
331
+
332
+ # Convert speech to text
333
+ text = r.recognize_google(audio)
334
+ return text
335
+
336
+ except Exception as e:
337
+ logger.error(f"Speech recognition error: {e}")
338
+ return "Could not understand audio. Please try again."
339
+
340
+ def generate_speech(self, text: str, lang_code: str = "en") -> str:
341
+ """Generate speech from text using gTTS."""
342
+ try:
343
+ tts = gtts.gTTS(text=text, lang=lang_code, slow=False)
344
+
345
+ # Create temporary file
346
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.mp3') as tmp_file:
347
+ tts.save(tmp_file.name)
348
+ return tmp_file.name
349
+
350
+ except Exception as e:
351
+ logger.error(f"TTS error: {e}")
352
+ return None
353
+
354
+ def answer_question(self, question: str, language: str, enable_tts: bool = True) -> Tuple[str, Optional[str]]:
355
+ """Main function to answer user questions."""
356
+ try:
357
+ lang_code = self.languages.get(language, "en")
358
+
359
+ # Step 1: Translate question to English if needed
360
+ if lang_code != "en":
361
+ english_question = self.translate_text(question, target_lang="en")
362
+ else:
363
+ english_question = question
364
+
365
+ # Step 2: Retrieve similar chunks from knowledge base
366
+ similar_chunks = self.retrieve_similar_chunks(english_question, k=3)
367
+
368
+ # Step 3: Get live web data
369
+ wikipedia_results = self.search_wikipedia(english_question)
370
+ duckduckgo_results = self.search_duckduckgo(english_question)
371
+
372
+ # Step 4: Build context from all sources
373
+ context_parts = []
374
+
375
+ # Add knowledge base chunks
376
+ if similar_chunks:
377
+ context_parts.append("From Knowledge Base:")
378
+ for chunk in similar_chunks:
379
+ context_parts.append(f"- {chunk['content']}")
380
+
381
+ # Add Wikipedia results
382
+ if wikipedia_results:
383
+ context_parts.append("\nFrom Wikipedia:")
384
+ for result in wikipedia_results:
385
+ context_parts.append(f"- {result}")
386
+
387
+ # Add DuckDuckGo results
388
+ if duckduckgo_results:
389
+ context_parts.append("\nFrom DuckDuckGo:")
390
+ for result in duckduckgo_results:
391
+ context_parts.append(f"- {result}")
392
+
393
+ context = "\n".join(context_parts)
394
+
395
+ # Step 5: Create prompt for LLM
396
+ prompt = f"""Based on the following context, please answer the user's travel question comprehensively and helpfully.
397
+
398
+ Context:
399
+ {context}
400
+
401
+ User Question: {english_question}
402
+
403
+ Please provide a detailed, helpful answer that combines information from the context. Focus on practical travel advice, recommendations, and useful tips. Keep the response informative but conversational."""
404
+
405
+ # Step 6: Query LLM
406
+ english_answer = self.query_llm(prompt)
407
+
408
+ # Step 7: Translate answer back to user's language
409
+ if lang_code != "en":
410
+ final_answer = self.translate_text(english_answer, target_lang=lang_code)
411
+ else:
412
+ final_answer = english_answer
413
+
414
+ # Step 8: Generate speech if enabled
415
+ audio_file = None
416
+ if enable_tts and final_answer:
417
+ audio_file = self.generate_speech(final_answer, lang_code)
418
+
419
+ return final_answer, audio_file
420
+
421
+ except Exception as e:
422
+ logger.error(f"Answer processing error: {e}")
423
+ error_msg = "Sorry, I encountered an error while processing your question. Please try again."
424
+ if lang_code != "en":
425
+ error_msg = self.translate_text(error_msg, target_lang=lang_code)
426
+ return error_msg, None
427
+
428
+ # Initialize bot
429
+ bot = TouristGuideBot()
430
+
431
+ def process_text_input(question, language, enable_tts):
432
+ """Process text input from user."""
433
+ if not question.strip():
434
+ return "Please enter a question.", None
435
+
436
+ answer, audio = bot.answer_question(question, language, enable_tts)
437
+ return answer, audio
438
+
439
+ def process_audio_input(audio, language, enable_tts):
440
+ """Process audio input from user."""
441
+ if audio is None:
442
+ return "Please record an audio message.", None
443
+
444
+ # Convert speech to text
445
+ question = bot.process_audio_input(audio)
446
+ if not question or question.startswith("Could not understand"):
447
+ return question, None
448
+
449
+ # Process the transcribed question
450
+ answer, audio_response = bot.answer_question(question, language, enable_tts)
451
+ return f"Your question: {question}\n\nAnswer: {answer}", audio_response
452
+
453
+ # Create Gradio interface
454
+ def create_interface():
455
+ """Create the Gradio interface."""
456
+
457
+ with gr.Blocks(title="🌍 Multilingual Tourist Guide Bot", theme=gr.themes.Soft()) as demo:
458
+
459
+ gr.Markdown("""
460
+ # 🌍 Multilingual Tourist Guide Bot
461
+
462
+ Your AI-powered travel companion that speaks your language! Get personalized travel advice, destination information,
463
+ and travel tips using RAG technology and live web data.
464
+
465
+ **Features:**
466
+ - πŸ—£οΈ Voice input and output
467
+ - 🌐 Support for 10+ languages
468
+ - πŸ“š Knowledge base + live web search
469
+ - 🎯 Personalized travel recommendations
470
+ """)
471
+
472
+ with gr.Row():
473
+ with gr.Column(scale=1):
474
+ language_dropdown = gr.Dropdown(
475
+ choices=list(bot.languages.keys()),
476
+ value="English",
477
+ label="🌐 Select Language",
478
+ info="Choose your preferred language"
479
+ )
480
+
481
+ enable_tts_checkbox = gr.Checkbox(
482
+ value=True,
483
+ label="πŸ”Š Enable Text-to-Speech",
484
+ info="Generate audio response"
485
+ )
486
+
487
+ with gr.Column(scale=2):
488
+ gr.Markdown("### Ask your travel question in text or voice:")
489
+
490
+ with gr.Tabs():
491
+ with gr.TabItem("πŸ’¬ Text Input"):
492
+ with gr.Row():
493
+ with gr.Column():
494
+ text_input = gr.Textbox(
495
+ label="Your Travel Question",
496
+ placeholder="Ask me anything about travel, destinations, tips, etc.",
497
+ lines=3
498
+ )
499
+ text_submit_btn = gr.Button("πŸš€ Get Answer", variant="primary")
500
+
501
+ with gr.Column():
502
+ text_output = gr.Textbox(
503
+ label="Answer",
504
+ lines=8,
505
+ show_copy_button=True
506
+ )
507
+ text_audio_output = gr.Audio(label="πŸ”Š Audio Response")
508
+
509
+ with gr.TabItem("🎀 Voice Input"):
510
+ with gr.Row():
511
+ with gr.Column():
512
+ audio_input = gr.Audio(
513
+ sources=["microphone"],
514
+ type="filepath",
515
+ label="🎀 Record Your Question"
516
+ )
517
+ audio_submit_btn = gr.Button("πŸš€ Process Audio", variant="primary")
518
+
519
+ with gr.Column():
520
+ audio_text_output = gr.Textbox(
521
+ label="Answer",
522
+ lines=8,
523
+ show_copy_button=True
524
+ )
525
+ audio_audio_output = gr.Audio(label="πŸ”Š Audio Response")
526
+
527
+ # Example questions
528
+ gr.Markdown("""
529
+ ### πŸ’‘ Example Questions:
530
+ - "What are the best places to visit in Paris?"
531
+ - "Give me travel tips for first-time international travelers"
532
+ - "What's the best time to visit Japan?"
533
+ - "How can I save money while traveling in Europe?"
534
+ - "Tell me about local customs in Dubai"
535
+ """)
536
+
537
+ # Event handlers
538
+ text_submit_btn.click(
539
+ fn=process_text_input,
540
+ inputs=[text_input, language_dropdown, enable_tts_checkbox],
541
+ outputs=[text_output, text_audio_output]
542
+ )
543
+
544
+ audio_submit_btn.click(
545
+ fn=process_audio_input,
546
+ inputs=[audio_input, language_dropdown, enable_tts_checkbox],
547
+ outputs=[audio_text_output, audio_audio_output]
548
+ )
549
+
550
+ # Auto-submit on Enter for text input
551
+ text_input.submit(
552
+ fn=process_text_input,
553
+ inputs=[text_input, language_dropdown, enable_tts_checkbox],
554
+ outputs=[text_output, text_audio_output]
555
+ )
556
+
557
+ return demo
558
+
559
+ # Launch the app
560
+ if __name__ == "__main__":
561
+
562
+
563
+ print("πŸš€ Starting Tourist Guide Bot...")
564
+ print("πŸ“ Note: Add your API keys as environment variables:")
565
+ print(" - GROQ_API_KEY for Groq API")
566
+ print(" - GEMINI_API_KEY for Google Gemini API")
567
+
568
+ demo = create_interface()
569
+ demo.launch(
570
+ share=True,
571
+ show_error=True
572
+ )