AiActivity commited on
Commit
487a35d
Β·
verified Β·
1 Parent(s): 29911c6

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +405 -0
  2. requirements.txt +13 -0
app.py ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import torch
4
+ import requests
5
+ import re
6
+ import time
7
+ from transformers import AutoModelForCausalLM, AutoTokenizer
8
+ from bs4 import BeautifulSoup
9
+ import urllib.parse
10
+ from markdown import markdown
11
+
12
+ # Set environment variables for better performance
13
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
14
+
15
+ # Initialize the model and tokenizer
16
+ print("Loading model... Please wait...")
17
+
18
+ # Model selection - Microsoft Phi-2 (optimized for free tier)
19
+ MODEL_ID = "microsoft/phi-2"
20
+
21
+ # Load tokenizer
22
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
23
+
24
+ # Load model with optimizations for free tier
25
+ model = AutoModelForCausalLM.from_pretrained(
26
+ MODEL_ID,
27
+ torch_dtype=torch.float16, # Use float16 for efficiency
28
+ device_map="auto", # Auto-decide CPU/GPU usage
29
+ load_in_4bit=True, # 4-bit quantization for extreme memory efficiency
30
+ trust_remote_code=True
31
+ )
32
+
33
+ def search_web(query, max_results=3):
34
+ """Search the web using direct API access - optimized for reliability"""
35
+ results = []
36
+ try:
37
+ # Try Wikipedia API first (most reliable on free tier)
38
+ wiki_url = f"https://en.wikipedia.org/w/api.php?action=opensearch&search={urllib.parse.quote(query)}&limit={max_results}&namespace=0&format=json"
39
+ response = requests.get(wiki_url)
40
+
41
+ if response.status_code == 200:
42
+ data = response.json()
43
+ titles = data[1]
44
+ urls = data[3]
45
+
46
+ for i in range(min(len(titles), len(urls))):
47
+ # Get summary for each page
48
+ page_url = f"https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro&explaintext&titles={urllib.parse.quote(titles[i])}&format=json"
49
+ page_response = requests.get(page_url)
50
+
51
+ if page_response.status_code == 200:
52
+ page_data = page_response.json()
53
+ # Extract page ID
54
+ try:
55
+ page_id = next(iter(page_data['query']['pages'].keys()))
56
+ if page_id != "-1": # Valid page
57
+ extract = page_data['query']['pages'][page_id].get('extract', '')
58
+ # Truncate to a reasonable snippet length
59
+ snippet = extract[:200] + "..." if len(extract) > 200 else extract
60
+
61
+ results.append({
62
+ 'title': f"Wikipedia - {titles[i]}",
63
+ 'url': urls[i],
64
+ 'snippet': snippet
65
+ })
66
+ except Exception as e:
67
+ print(f"Error extracting Wikipedia data: {e}")
68
+ except Exception as e:
69
+ print(f"Wikipedia API error: {e}")
70
+
71
+ # If we don't have enough results from Wikipedia, try SerpAPI demo
72
+ if len(results) < max_results:
73
+ try:
74
+ serpapi_url = f"https://serpapi.com/search.json?engine=google&q={urllib.parse.quote(query)}&api_key=demo"
75
+ response = requests.get(serpapi_url)
76
+
77
+ if response.status_code == 200:
78
+ data = response.json()
79
+ if "organic_results" in data:
80
+ for result in data["organic_results"]:
81
+ if len(results) >= max_results:
82
+ break
83
+ results.append({
84
+ 'title': result.get('title', ''),
85
+ 'url': result.get('link', ''),
86
+ 'snippet': result.get('snippet', '')
87
+ })
88
+ except Exception as e:
89
+ print(f"SerpAPI error: {e}")
90
+
91
+ # If we still don't have enough results, use fallback
92
+ if len(results) < max_results:
93
+ fallback_results = [
94
+ {
95
+ 'title': f"Search results for {query}",
96
+ 'url': f"https://www.google.com/search?q={urllib.parse.quote(query)}",
97
+ 'snippet': f"Find information about {query} from various sources."
98
+ },
99
+ {
100
+ 'title': f"{query} - Overview",
101
+ 'url': f"https://en.wikipedia.org/wiki/Special:Search?search={urllib.parse.quote(query)}",
102
+ 'snippet': f"Learn about {query} on Wikipedia, the free encyclopedia."
103
+ },
104
+ {
105
+ 'title': f"Latest on {query}",
106
+ 'url': f"https://news.google.com/search?q={urllib.parse.quote(query)}",
107
+ 'snippet': f"Recent news and updates about {query}."
108
+ }
109
+ ]
110
+
111
+ # Add fallback results until we have enough
112
+ for result in fallback_results:
113
+ if len(results) >= max_results:
114
+ break
115
+ results.append(result)
116
+
117
+ return results[:max_results]
118
+
119
+ # Cache for frequently searched queries
120
+ answer_cache = {}
121
+
122
+ def generate_response(prompt, max_new_tokens=512):
123
+ """Generate response using Phi-2 - optimized for speed"""
124
+ try:
125
+ # Format for Phi-2
126
+ phi_prompt = f"Instruct: {prompt}\nOutput:"
127
+
128
+ # Tokenize input
129
+ inputs = tokenizer(phi_prompt, return_tensors="pt").to(model.device)
130
+
131
+ # Generate with efficient settings
132
+ with torch.no_grad():
133
+ outputs = model.generate(
134
+ inputs.input_ids,
135
+ max_new_tokens=max_new_tokens,
136
+ temperature=0.7,
137
+ top_p=0.9,
138
+ num_beams=1, # Greedy decoding for speed
139
+ do_sample=True,
140
+ pad_token_id=tokenizer.eos_token_id
141
+ )
142
+
143
+ # Decode response
144
+ response = tokenizer.decode(outputs[0][inputs.input_ids.size(1):], skip_special_tokens=True).strip()
145
+ return response
146
+
147
+ except Exception as e:
148
+ print(f"Error generating response: {e}")
149
+ return "I couldn't generate a response. Please try again with a different query."
150
+
151
+ def extract_citations(text, search_results):
152
+ """Ensure citations are properly added to the text"""
153
+ if not re.search(r'\[\d+\]', text):
154
+ # Add citations if not present
155
+ for i, result in enumerate(search_results, 1):
156
+ # Try to find snippet content in the answer
157
+ key_phrases = result['snippet'].split('.')
158
+ for phrase in key_phrases:
159
+ if len(phrase) > 20 and phrase.strip() in text:
160
+ text = text.replace(phrase, f"{phrase} [{i}]", 1)
161
+
162
+ return text
163
+
164
+ def generate_related_topics(query, answer):
165
+ """Generate related topics - optimized for speed"""
166
+ # Pre-defined topics for common queries
167
+ query_lower = query.lower()
168
+
169
+ if "quantum" in query_lower and "comput" in query_lower:
170
+ return [
171
+ "How does quantum entanglement work?",
172
+ "What are qubits?",
173
+ "Real-world applications of quantum computing"
174
+ ]
175
+ elif "artificial intelligence" in query_lower or "ai" == query_lower or "machine learning" in query_lower:
176
+ return [
177
+ "Differences between AI and machine learning",
178
+ "How does deep learning work?",
179
+ "Ethical concerns in artificial intelligence"
180
+ ]
181
+ elif "climate" in query_lower or "global warming" in query_lower:
182
+ return [
183
+ "How does carbon capture work?",
184
+ "Impact of climate change on ecosystems",
185
+ "Renewable energy technologies"
186
+ ]
187
+ else:
188
+ # Generate simple variations for any query
189
+ return [
190
+ f"History of {query}",
191
+ f"Latest developments in {query}",
192
+ f"How does {query} work?"
193
+ ]
194
+
195
+ def search_and_answer(query):
196
+ """Main function to search and generate answer - optimized for free tier"""
197
+ try:
198
+ # Check cache first
199
+ cache_key = query.lower().strip()
200
+ if cache_key in answer_cache:
201
+ return answer_cache[cache_key]
202
+
203
+ # Step 1: Search the web
204
+ search_results = search_web(query, max_results=3)
205
+
206
+ if not search_results:
207
+ return {
208
+ "answer": "I couldn't find relevant information for this query. Please try a different search term.",
209
+ "sources": [],
210
+ "related_topics": []
211
+ }
212
+
213
+ # Step 2: Create context for the model - keep it concise for smaller model
214
+ context = f"Query: {query}\n\nSearch Results:\n\n"
215
+
216
+ for i, result in enumerate(search_results, 1):
217
+ context += f"Source {i}:\n"
218
+ context += f"Title: {result['title']}\n"
219
+ context += f"Content: {result['snippet']}\n\n"
220
+
221
+ # Step 3: Create prompt for the model - optimized for Phi-2
222
+ prompt = f"""You are a helpful AI assistant that provides accurate and comprehensive answers based on search results.
223
+
224
+ {context}
225
+
226
+ Based on these search results, please provide a concise answer to the query: "{query}"
227
+ Include citations like [1], [2], etc. to reference the sources.
228
+ Be factual and accurate. If the search results don't contain enough information, acknowledge this limitation.
229
+ Format your answer in clear paragraphs with bullet points where appropriate."""
230
+
231
+ # Step 4: Generate answer with optimized settings
232
+ answer = generate_response(prompt, max_new_tokens=384) # Shorter for efficiency
233
+
234
+ # Step 5: Ensure citations
235
+ answer = extract_citations(answer, search_results)
236
+
237
+ # Step 6: Generate related topics efficiently
238
+ related_topics = generate_related_topics(query, answer)
239
+
240
+ # Store in cache for future use
241
+ result = {
242
+ "answer": answer,
243
+ "sources": search_results,
244
+ "related_topics": related_topics
245
+ }
246
+ answer_cache[cache_key] = result
247
+
248
+ return result
249
+
250
+ except Exception as e:
251
+ print(f"Error in search_and_answer: {e}")
252
+ return {
253
+ "answer": f"An error occurred while processing your query. Please try again.",
254
+ "sources": [],
255
+ "related_topics": []
256
+ }
257
+
258
+ def format_sources(sources):
259
+ """Format sources for display"""
260
+ if not sources:
261
+ return ""
262
+
263
+ html = ""
264
+ for i, source in enumerate(sources, 1):
265
+ html += f"""
266
+ <div style="margin-bottom: 15px; padding: 15px; background-color: #f8f9fa;
267
+ border-radius: 8px; border-left: 4px solid #1976d2;">
268
+ <a href="{source['url']}" target="_blank" style="font-weight: bold;
269
+ color: #1976d2; text-decoration: none;">
270
+ {source['title']}
271
+ </a>
272
+ <div style="color: #5f6368; font-size: 14px; margin-top: 5px;">{source['url']}</div>
273
+ <div style="margin-top: 10px;">{source['snippet']}</div>
274
+ </div>
275
+ """
276
+ return html
277
+
278
+ def format_related(topics):
279
+ """Format related topics for display"""
280
+ if not topics:
281
+ return ""
282
+
283
+ html = "<div style='display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;'>"
284
+ for topic in topics:
285
+ html += f"""
286
+ <div style="background-color: #e3f2fd; padding: 8px 16px; border-radius: 20px;
287
+ color: #1976d2; font-size: 14px; cursor: pointer; display: inline-block;"
288
+ onclick="document.getElementById('query-input').value = '{topic}'; search();">
289
+ {topic}
290
+ </div>
291
+ """
292
+ html += "</div>"
293
+ return html
294
+
295
+ def search_interface(query):
296
+ """Main function for the Gradio interface"""
297
+ if not query.strip():
298
+ return (
299
+ "Please enter a search query.",
300
+ "",
301
+ ""
302
+ )
303
+
304
+ start_time = time.time()
305
+
306
+ # Perform search and answer generation
307
+ result = search_and_answer(query)
308
+
309
+ # Format answer with markdown
310
+ answer_html = markdown(result["answer"])
311
+
312
+ # Format sources
313
+ sources_html = format_sources(result["sources"])
314
+
315
+ # Format related topics
316
+ related_html = format_related(result["related_topics"])
317
+
318
+ # Calculate processing time
319
+ processing_time = time.time() - start_time
320
+ print(f"Query processed in {processing_time:.2f} seconds")
321
+
322
+ return (
323
+ answer_html,
324
+ sources_html,
325
+ related_html
326
+ )
327
+
328
+ # Create the Gradio interface - optimized for loading speed
329
+ css = """
330
+ body {
331
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
332
+ max-width: 1200px;
333
+ margin: 0 auto;
334
+ }
335
+ .container {
336
+ margin-top: 20px;
337
+ }
338
+ .answer {
339
+ border-radius: 8px;
340
+ background-color: white;
341
+ padding: 20px;
342
+ box-shadow: 0 1px 3px rgba(0,0,0,0.12);
343
+ margin-bottom: 20px;
344
+ }
345
+ h1 {
346
+ color: #1976d2;
347
+ font-size: 2.2rem;
348
+ font-weight: 600;
349
+ margin-bottom: 10px;
350
+ }
351
+ h3 {
352
+ color: #1976d2;
353
+ font-weight: 500;
354
+ margin-top: 25px;
355
+ margin-bottom: 15px;
356
+ }
357
+ """
358
+
359
+ with gr.Blocks(css=css) as demo:
360
+ gr.HTML("""
361
+ <h1>πŸ” AI Search System</h1>
362
+ <p style="margin-bottom: 20px;">Powered by Microsoft Phi-2 - Ask any question and get accurate answers with sources</p>
363
+ """)
364
+
365
+ with gr.Row():
366
+ query_input = gr.Textbox(
367
+ label="Search Query",
368
+ placeholder="Enter your search query here...",
369
+ elem_id="query-input"
370
+ )
371
+ search_button = gr.Button("Search πŸ”", variant="primary")
372
+
373
+ with gr.Row():
374
+ with gr.Column(scale=2):
375
+ gr.HTML("<h3>πŸ“ Answer</h3>")
376
+ answer_output = gr.HTML(elem_classes=["answer"])
377
+
378
+ gr.HTML("<h3>πŸ”— Related Topics</h3>")
379
+ related_output = gr.HTML()
380
+
381
+ with gr.Column(scale=1):
382
+ gr.HTML("<h3>πŸ“š Sources</h3>")
383
+ sources_output = gr.HTML()
384
+
385
+ search_button.click(
386
+ fn=search_interface,
387
+ inputs=[query_input],
388
+ outputs=[answer_output, sources_output, related_output]
389
+ )
390
+
391
+ query_input.submit(
392
+ fn=search_interface,
393
+ inputs=[query_input],
394
+ outputs=[answer_output, sources_output, related_output]
395
+ )
396
+
397
+ gr.HTML("""
398
+ <div style="margin-top: 20px; text-align: center; color: #666;">
399
+ <p>Built with Phi-2, Gradio, and Hugging Face Spaces</p>
400
+ </div>
401
+ """)
402
+
403
+ # Ensure quicker startup
404
+ demo.queue(max_size=10)
405
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio==3.50.2
2
+ torch==2.0.1
3
+ transformers==4.35.2
4
+ accelerate==0.25.0
5
+ bitsandbytes==0.40.2
6
+ markdown==3.5.1
7
+
8
+ # Web search dependencies (minimized)
9
+ requests==2.31.0
10
+ beautifulsoup4==4.12.2
11
+
12
+ # Utilities (minimized)
13
+ numpy==1.24.3