Spaces:
Running
Running
Update app.py
Browse files
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 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
-
def
|
| 32 |
-
"""
|
| 33 |
-
|
| 34 |
-
|
| 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/
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
| 54 |
try:
|
| 55 |
-
|
| 56 |
-
title_elem = result.find('h3')
|
| 57 |
title = title_elem.text if title_elem else "No Title"
|
| 58 |
|
| 59 |
-
|
| 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 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
})
|
| 80 |
-
except Exception as e:
|
| 81 |
continue
|
| 82 |
|
| 83 |
-
|
| 84 |
-
|
| 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
|
| 97 |
-
Safe search is off.
|
| 98 |
Returns a list of result strings.
|
| 99 |
"""
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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"β
|
| 220 |
f"- {r}" for r in search_results
|
| 221 |
)
|
| 222 |
else:
|
| 223 |
-
debug = "β No
|
| 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
|
| 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 (
|
| 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
|
| 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
|
| 427 |
value=False,
|
| 428 |
-
info="
|
| 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 |
-
β οΈ **
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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",
|
| 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"
|
| 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
|
| 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 {}
|