R-Kentaren commited on
Commit
13597bf
Β·
verified Β·
1 Parent(s): f7b1360

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +175 -91
app.py CHANGED
@@ -13,6 +13,8 @@ from transformers import AutoTokenizer
13
  from bs4 import BeautifulSoup
14
  import requests
15
  from urllib.parse import quote_plus
 
 
16
  from config import MODELS
17
 
18
  # Global event to signal cancellation from the UI thread to the generation thread
@@ -23,93 +25,189 @@ access_token = os.environ.get('HF_TOKEN', '')
23
  # Global cache for pipelines to avoid re-loading.
24
  PIPELINES = {}
25
 
26
- # Base64 encoded simple avatar images (1x1 pixel transparent PNG)
27
- # These are minimal placeholders - you can replace with actual base64 images
28
- USER_AVATAR = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
29
- BOT_AVATAR = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
- def google_search(query, max_results=6, max_chars=50):
32
- """
33
- Perform Google search without API (scraping).
34
- Safe search is turned off.
35
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  try:
37
- # Prepare search URL with safe search off
38
- search_url = f"https://www.google.com/search?q={quote_plus(query)}&safe=off&num={max_results}"
39
-
40
  headers = {
41
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
42
  }
 
43
 
44
  response = requests.get(search_url, headers=headers, timeout=10)
45
  response.raise_for_status()
46
 
47
  soup = BeautifulSoup(response.text, 'html.parser')
48
-
49
- # Find search result containers
50
  results = []
51
- search_results = soup.find_all('div', class_='g')
52
 
53
- for i, result in enumerate(search_results[:max_results]):
 
 
 
54
  try:
55
- # Get title
56
- title_elem = result.find('h3')
57
  title = title_elem.text if title_elem else "No Title"
58
 
59
- # Get snippet/description
60
- snippet_elem = result.find('div', class_='VwiC3b')
61
- if not snippet_elem:
62
- snippet_elem = result.find('div', class_='IsZvec')
63
  snippet = snippet_elem.text if snippet_elem else ""
64
 
65
- # Get link
66
- link_elem = result.find('a')
67
- link = link_elem.get('href') if link_elem else ""
68
- if link and link.startswith('/url?q='):
69
- link = link.split('/url?q=')[1].split('&')[0]
70
-
71
- # Truncate snippet
72
  if len(snippet) > max_chars:
73
  snippet = snippet[:max_chars] + "..."
74
 
75
- results.append({
76
- 'title': title,
77
- 'snippet': snippet,
78
- 'link': link
79
- })
80
- except Exception as e:
81
  continue
82
 
83
- # Format results
84
- formatted_results = []
85
- for i, r in enumerate(results):
86
- formatted_results.append(f"{i+1}. {r['title']} - {r['snippet']}")
87
-
88
- return formatted_results
89
-
90
- except Exception as e:
91
- print(f"Google search error: {e}")
92
  return []
93
 
94
  def retrieve_context(query, max_results=6, max_chars=50):
95
  """
96
- Retrieve search snippets from Google (scraping, no API).
97
- Safe search is off.
98
  Returns a list of result strings.
99
  """
100
- try:
101
- results = google_search(query, max_results, max_chars)
102
- if results:
103
- return results
104
- else:
105
- # Fallback to DDG if Google fails
106
- from ddgs import DDGS
107
- with DDGS() as ddgs:
108
- return [f"{i+1}. {r.get('title','No Title')} - {r.get('body','')[:max_chars]}"
109
- for i, r in enumerate(islice(ddgs.text(query, region="wt-wt", safesearch="off", timelimit="y"), max_results))]
110
- except Exception as e:
111
- print(f"Search error: {e}")
112
- return []
 
 
 
 
 
 
 
113
 
114
  def load_pipeline(model_name):
115
  """
@@ -201,7 +299,7 @@ def chat_response(user_msg, chat_history, system_prompt,
201
  debug = ''
202
  search_results = []
203
  if enable_search:
204
- debug = 'πŸ” Google search started (safe search: OFF)...'
205
  thread_search = threading.Thread(
206
  target=lambda: search_results.extend(
207
  retrieve_context(user_msg, int(max_results), int(max_chars))
@@ -216,11 +314,11 @@ def chat_response(user_msg, chat_history, system_prompt,
216
  if enable_search:
217
  thread_search.join(timeout=float(search_timeout))
218
  if search_results:
219
- debug = f"βœ… Google search completed - Found {len(search_results)} results\n\n" + "\n".join(
220
  f"- {r}" for r in search_results
221
  )
222
  else:
223
- debug = "❌ No web search results found."
224
 
225
  try:
226
  cur_date = datetime.now().strftime('%Y-%m-%d')
@@ -229,7 +327,7 @@ def chat_response(user_msg, chat_history, system_prompt,
229
  if search_results:
230
  enriched = system_prompt.strip() + f"""
231
  # SEARCH CONTEXT (TRUSTED SOURCES ONLY)
232
- Below are Google search results. Treat them as the ONLY source of truth for answering.
233
  {search_results}
234
 
235
  RULES (VERY IMPORTANT):
@@ -367,7 +465,7 @@ def update_duration_estimate(model_name, enable_search, max_results, max_chars,
367
  model_size = get_model_size(model_name)
368
  return (f"⏱️ **Estimated GPU Time: {duration:.1f} seconds**\n\n"
369
  f"πŸ“Š **Model Size:** {model_size:.1f}B parameters\n"
370
- f"πŸ” **Web Search:** {'Enabled (Google, SafeSearch: OFF)' if enable_search else 'Disabled'}")
371
  except Exception as e:
372
  return f"⚠️ Error calculating estimate: {e}"
373
 
@@ -388,26 +486,11 @@ with gr.Blocks(
388
  .chatbot { border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }
389
  button.primary { font-weight: 600; }
390
  .gradio-accordion { margin-bottom: 12px; }
391
- /* Custom avatar styling */
392
- .message-wrap { align-items: flex-start !important; }
393
- .avatar-image {
394
- border-radius: 50% !important;
395
- border: 2px solid #667eea !important;
396
- box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
397
- }
398
- .bot-avatar {
399
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
400
- padding: 2px !important;
401
- }
402
- .user-avatar {
403
- background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%) !important;
404
- padding: 2px !important;
405
- }
406
  """
407
  ) as demo:
408
  # Header
409
  gr.Markdown("""
410
- # 🧠 LLM Inference with Google Search
411
  """)
412
 
413
  with gr.Row():
@@ -423,9 +506,9 @@ with gr.Blocks(
423
  info="Select the language model to use"
424
  )
425
  search_chk = gr.Checkbox(
426
- label="πŸ” Enable Web Search (Google, SafeSearch: OFF)",
427
  value=False,
428
- info="Augment responses with real-time web data from Google (no API required)"
429
  )
430
  sys_prompt = gr.Textbox(label="πŸ“ System Prompt", lines=3, value=update_default_prompt(False), placeholder="Define the assistant's behavior and personality...")
431
 
@@ -482,7 +565,11 @@ with gr.Blocks(
482
  info="Maximum time to wait for search results"
483
  )
484
  gr.Markdown("""
485
- ⚠️ **Note:** Google search uses web scraping (no API required).
 
 
 
 
486
  SafeSearch is **OFF** for comprehensive results.
487
  """)
488
 
@@ -498,14 +585,12 @@ with gr.Blocks(
498
  label="πŸ’¬ Conversation",
499
  show_copy_button=True,
500
  avatar_images=(
501
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Crect width='40' height='40' rx='20' fill='%23f093fb'/%3E%3Ctext x='20' y='28' text-anchor='middle' font-size='20' fill='white' font-family='Arial'%3EπŸ‘€%3C/text%3E%3C/svg%3E", # User avatar
502
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Crect width='40' height='40' rx='20' fill='%23667eea'/%3E%3Ctext x='20' y='28' text-anchor='middle' font-size='20' fill='white' font-family='Arial'%3EπŸ€–%3C/text%3E%3C/svg%3E" # Bot avatar
503
  ),
504
  bubble_full_width=False,
505
  render_markdown=True,
506
- sanitize_html=False,
507
- elem_id="chatbot",
508
- elem_classes="chatbot"
509
  )
510
 
511
  # Input Area
@@ -544,7 +629,7 @@ with gr.Blocks(
544
  ---
545
  πŸ’‘ **Tips:**
546
  - Use **Advanced Parameters** to fine-tune creativity and response length
547
- - Enable **Web Search** for real-time, up-to-date information from Google
548
  - SafeSearch is **OFF** for comprehensive results
549
  - Try different **models** for various tasks (reasoning, coding, general chat)
550
  - Click the **Copy** button on responses to save them to your clipboard
@@ -560,7 +645,6 @@ with gr.Blocks(
560
  def submit_and_manage_ui(user_msg, chat_history, *args):
561
  """
562
  Orchestrator function that manages UI state and calls the backend chat function.
563
- It uses a try...finally block to ensure the UI is always reset.
564
  """
565
  if not user_msg.strip():
566
  yield {}
 
13
  from bs4 import BeautifulSoup
14
  import requests
15
  from urllib.parse import quote_plus
16
+ import json
17
+ import urllib.parse
18
  from config import MODELS
19
 
20
  # Global event to signal cancellation from the UI thread to the generation thread
 
25
  # Global cache for pipelines to avoid re-loading.
26
  PIPELINES = {}
27
 
28
+ def google_search_web(query, max_results=6, max_chars=50):
29
+ """Search using Google web scraping with multiple approaches"""
30
+
31
+ # Try multiple User-Agents
32
+ user_agents = [
33
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
34
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
35
+ 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
36
+ ]
37
+
38
+ for user_agent in user_agents:
39
+ try:
40
+ # Try different search URLs
41
+ search_urls = [
42
+ f"https://www.google.com/search?q={quote_plus(query)}&safe=off&num={max_results}",
43
+ f"https://www.google.com/search?q={quote_plus(query)}&safe=off&num={max_results}&hl=en",
44
+ f"https://www.google.com/webhp?safe=off&q={quote_plus(query)}&num={max_results}"
45
+ ]
46
+
47
+ for search_url in search_urls:
48
+ try:
49
+ headers = {
50
+ 'User-Agent': user_agent,
51
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
52
+ 'Accept-Language': 'en-US,en;q=0.5',
53
+ 'Accept-Encoding': 'gzip, deflate',
54
+ 'Connection': 'keep-alive',
55
+ 'Upgrade-Insecure-Requests': '1',
56
+ 'Cache-Control': 'max-age=0'
57
+ }
58
+
59
+ response = requests.get(search_url, headers=headers, timeout=15, verify=True)
60
+ response.raise_for_status()
61
+
62
+ soup = BeautifulSoup(response.text, 'html.parser')
63
+
64
+ # Find search result containers
65
+ results = []
66
+
67
+ # Try different selectors
68
+ selectors = [
69
+ ('div', 'g'),
70
+ ('div', 'tF2Cxc'),
71
+ ('div', 'MjjYud'),
72
+ ('div', 'yuRUbf')
73
+ ]
74
+
75
+ search_results = []
76
+ for tag, class_name in selectors:
77
+ search_results = soup.find_all(tag, class_=class_name)
78
+ if search_results:
79
+ break
80
+
81
+ if not search_results:
82
+ # Try alternative parsing
83
+ search_results = soup.find_all('div', class_=re.compile(r'^(g|tF2Cxc|MjjYud|yuRUbf)'))
84
+
85
+ for result in search_results[:max_results]:
86
+ try:
87
+ # Get title
88
+ title_elem = result.find('h3')
89
+ if not title_elem:
90
+ title_elem = result.find('h2')
91
+ title = title_elem.text if title_elem else "No Title"
92
+
93
+ # Get snippet
94
+ snippet_elem = result.find('div', class_='VwiC3b')
95
+ if not snippet_elem:
96
+ snippet_elem = result.find('div', class_='IsZvec')
97
+ if not snippet_elem:
98
+ snippet_elem = result.find('div', class_='lEBKkf')
99
+ snippet = snippet_elem.text if snippet_elem else ""
100
+
101
+ # Get link
102
+ link_elem = result.find('a')
103
+ link = link_elem.get('href') if link_elem else ""
104
+ if link and link.startswith('/url?q='):
105
+ link = urllib.parse.unquote(link.split('/url?q=')[1].split('&')[0])
106
+
107
+ if link and not link.startswith('http'):
108
+ continue
109
+
110
+ # Clean up snippet
111
+ snippet = ' '.join(snippet.split())
112
+ if len(snippet) > max_chars:
113
+ snippet = snippet[:max_chars] + "..."
114
+
115
+ if title and snippet:
116
+ results.append(f"{len(results)+1}. {title} - {snippet}")
117
+
118
+ except Exception:
119
+ continue
120
+
121
+ if results:
122
+ return results
123
+
124
+ except Exception:
125
+ continue
126
+
127
+ except Exception:
128
+ continue
129
+
130
+ return []
131
 
132
+ def duckduckgo_search(query, max_results=6, max_chars=50):
133
+ """Fallback to DuckDuckGo search"""
134
+ try:
135
+ from ddgs import DDGS
136
+ with DDGS() as ddgs:
137
+ results = []
138
+ for r in islice(ddgs.text(query, region="wt-wt", safesearch="off", timelimit="y"), max_results):
139
+ title = r.get('title', 'No Title')
140
+ body = r.get('body', '')
141
+ if len(body) > max_chars:
142
+ body = body[:max_chars] + "..."
143
+ results.append(f"{len(results)+1}. {title} - {body}")
144
+ return results
145
+ except Exception:
146
+ return []
147
+
148
+ def bing_search(query, max_results=6, max_chars=50):
149
+ """Fallback to Bing search"""
150
  try:
 
 
 
151
  headers = {
152
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
153
  }
154
+ search_url = f"https://www.bing.com/search?q={quote_plus(query)}&safeSearch=off&count={max_results}"
155
 
156
  response = requests.get(search_url, headers=headers, timeout=10)
157
  response.raise_for_status()
158
 
159
  soup = BeautifulSoup(response.text, 'html.parser')
 
 
160
  results = []
 
161
 
162
+ # Find search results
163
+ search_results = soup.find_all('li', class_='b_algo')
164
+
165
+ for result in search_results[:max_results]:
166
  try:
167
+ title_elem = result.find('h2')
 
168
  title = title_elem.text if title_elem else "No Title"
169
 
170
+ snippet_elem = result.find('p')
 
 
 
171
  snippet = snippet_elem.text if snippet_elem else ""
172
 
 
 
 
 
 
 
 
173
  if len(snippet) > max_chars:
174
  snippet = snippet[:max_chars] + "..."
175
 
176
+ if title and snippet:
177
+ results.append(f"{len(results)+1}. {title} - {snippet}")
178
+
179
+ except Exception:
 
 
180
  continue
181
 
182
+ return results
183
+ except Exception:
 
 
 
 
 
 
 
184
  return []
185
 
186
  def retrieve_context(query, max_results=6, max_chars=50):
187
  """
188
+ Retrieve search snippets from multiple search engines.
 
189
  Returns a list of result strings.
190
  """
191
+ # Try Google first
192
+ results = google_search_web(query, max_results, max_chars)
193
+ if results:
194
+ print(f"βœ… Google search successful: {len(results)} results")
195
+ return results
196
+
197
+ # Try DuckDuckGo
198
+ results = duckduckgo_search(query, max_results, max_chars)
199
+ if results:
200
+ print(f"βœ… DuckDuckGo search successful: {len(results)} results")
201
+ return results
202
+
203
+ # Try Bing
204
+ results = bing_search(query, max_results, max_chars)
205
+ if results:
206
+ print(f"βœ… Bing search successful: {len(results)} results")
207
+ return results
208
+
209
+ print("❌ All search engines failed")
210
+ return []
211
 
212
  def load_pipeline(model_name):
213
  """
 
299
  debug = ''
300
  search_results = []
301
  if enable_search:
302
+ debug = 'πŸ” Searching (Google β†’ DuckDuckGo β†’ Bing)...'
303
  thread_search = threading.Thread(
304
  target=lambda: search_results.extend(
305
  retrieve_context(user_msg, int(max_results), int(max_chars))
 
314
  if enable_search:
315
  thread_search.join(timeout=float(search_timeout))
316
  if search_results:
317
+ debug = f"βœ… Search completed - Found {len(search_results)} results\n\n" + "\n".join(
318
  f"- {r}" for r in search_results
319
  )
320
  else:
321
+ debug = "❌ No search results found. Check internet connection or try again."
322
 
323
  try:
324
  cur_date = datetime.now().strftime('%Y-%m-%d')
 
327
  if search_results:
328
  enriched = system_prompt.strip() + f"""
329
  # SEARCH CONTEXT (TRUSTED SOURCES ONLY)
330
+ Below are search results. Treat them as the ONLY source of truth for answering.
331
  {search_results}
332
 
333
  RULES (VERY IMPORTANT):
 
465
  model_size = get_model_size(model_name)
466
  return (f"⏱️ **Estimated GPU Time: {duration:.1f} seconds**\n\n"
467
  f"πŸ“Š **Model Size:** {model_size:.1f}B parameters\n"
468
+ f"πŸ” **Web Search:** {'Enabled (Multi-Engine)' if enable_search else 'Disabled'}")
469
  except Exception as e:
470
  return f"⚠️ Error calculating estimate: {e}"
471
 
 
486
  .chatbot { border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }
487
  button.primary { font-weight: 600; }
488
  .gradio-accordion { margin-bottom: 12px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  """
490
  ) as demo:
491
  # Header
492
  gr.Markdown("""
493
+ # 🧠 LLM Inference with Multi-Engine Search
494
  """)
495
 
496
  with gr.Row():
 
506
  info="Select the language model to use"
507
  )
508
  search_chk = gr.Checkbox(
509
+ label="πŸ” Enable Web Search",
510
  value=False,
511
+ info="Search across Google, DuckDuckGo, and Bing (no API required)"
512
  )
513
  sys_prompt = gr.Textbox(label="πŸ“ System Prompt", lines=3, value=update_default_prompt(False), placeholder="Define the assistant's behavior and personality...")
514
 
 
565
  info="Maximum time to wait for search results"
566
  )
567
  gr.Markdown("""
568
+ ⚠️ **Search Engines:**
569
+ - Google (primary)
570
+ - DuckDuckGo (fallback)
571
+ - Bing (fallback)
572
+
573
  SafeSearch is **OFF** for comprehensive results.
574
  """)
575
 
 
585
  label="πŸ’¬ Conversation",
586
  show_copy_button=True,
587
  avatar_images=(
588
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Crect width='40' height='40' rx='20' fill='%23f093fb'/%3E%3Ctext x='20' y='28' text-anchor='middle' font-size='20' fill='white' font-family='Arial'%3EπŸ‘€%3C/text%3E%3C/svg%3E",
589
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Crect width='40' height='40' rx='20' fill='%23667eea'/%3E%3Ctext x='20' y='28' text-anchor='middle' font-size='20' fill='white' font-family='Arial'%3EπŸ€–%3C/text%3E%3C/svg%3E"
590
  ),
591
  bubble_full_width=False,
592
  render_markdown=True,
593
+ sanitize_html=False
 
 
594
  )
595
 
596
  # Input Area
 
629
  ---
630
  πŸ’‘ **Tips:**
631
  - Use **Advanced Parameters** to fine-tune creativity and response length
632
+ - Enable **Web Search** for real-time information (uses multiple search engines)
633
  - SafeSearch is **OFF** for comprehensive results
634
  - Try different **models** for various tasks (reasoning, coding, general chat)
635
  - Click the **Copy** button on responses to save them to your clipboard
 
645
  def submit_and_manage_ui(user_msg, chat_history, *args):
646
  """
647
  Orchestrator function that manages UI state and calls the backend chat function.
 
648
  """
649
  if not user_msg.strip():
650
  yield {}