AiActivity commited on
Commit
49b4035
·
verified ·
1 Parent(s): baab88c
Files changed (1) hide show
  1. app.py +13 -405
app.py CHANGED
@@ -1,405 +1,13 @@
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()
 
1
+ gradio==3.50.2
2
+ transformers==4.36.2 # Version that definitely supports Phi models
3
+ accelerate==0.25.0
4
+ torch==2.0.1
5
+ bitsandbytes==0.41.1
6
+
7
+ # Web search dependencies
8
+ requests==2.31.0
9
+ beautifulsoup4==4.12.2
10
+
11
+ # Utilities
12
+ markdown==3.5.1
13
+ numpy==1.24.3