Spaces:
Running
on
Zero
Running
on
Zero
File size: 10,550 Bytes
cf40b67 ed0c3c5 12a0d68 cf40b67 835fc41 379919c ed0c3c5 12a0d68 b17a402 ed0c3c5 12a0d68 cf40b67 ed0c3c5 12a0d68 cf40b67 60c475d cf40b67 12a0d68 60c475d 12a0d68 60c475d 12a0d68 835fc41 12a0d68 60c475d 12a0d68 cf40b67 12a0d68 60c475d 12a0d68 60c475d 12a0d68 60c475d 12a0d68 cf40b67 12a0d68 60c475d cf40b67 60c475d 12a0d68 cf40b67 60c475d ed0c3c5 12a0d68 60c475d 379919c ed0c3c5 60c475d ed0c3c5 60c475d 379919c 6c1f2d1 379919c 60c475d 12a0d68 cf40b67 60c475d cf40b67 12a0d68 60c475d 12a0d68 cf40b67 60c475d cf40b67 12a0d68 60c475d 12a0d68 cf40b67 12a0d68 cf40b67 60c475d 12a0d68 60c475d 12a0d68 60c475d 12a0d68 cf40b67 60c475d cf40b67 12a0d68 cf40b67 12a0d68 cf40b67 12a0d68 60c475d 12a0d68 cf40b67 60c475d 12a0d68 cf40b67 12a0d68 60c475d 12a0d68 ed0c3c5 12a0d68 ed0c3c5 12a0d68 ed0c3c5 12a0d68 60c475d 12a0d68 60c475d 12a0d68 cf40b67 12a0d68 835fc41 cf40b67 ed0c3c5 60c475d 12a0d68 60c475d 12a0d68 60c475d 379919c 60c475d 379919c 60c475d ed0c3c5 cf40b67 60c475d 379919c 60c475d cf40b67 60c475d cf40b67 60c475d cf40b67 60c475d cf40b67 60c475d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
import gradio as gr
import spaces # Required for ZeroGPU
from transformers import pipeline, AutoTokenizer
from duckduckgo_search import DDGS
from datetime import datetime
# Initialize a lightweight text generation model on CPU
generator = pipeline("text-generation", model="distilgpt2", device=-1) # -1 ensures CPU by default
tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased") # For better token handling
# Web search function (CPU-based)
def get_web_results(query: str, max_results: int = 5) -> list:
"""Fetch web results synchronously for Zero GPU compatibility, focusing on high-quality sources."""
try:
with DDGS() as ddgs:
results = list(ddgs.text(query, max_results=max_results))
# Filter for high-quality, relevant results (e.g., educational, authoritative sites)
filtered_results = [
{"title": r.get("title", "No Title"), "snippet": r["body"], "url": r["href"]}
for r in results
if any(domain in r["href"] for domain in ["geeksforgeeks.org", "realpython.com", "coursera.org", "udemy.com", "stackexchange.com"])
or "edu" in r["href"]
]
return filtered_results if filtered_results else results # Fall back to all results if no high-quality ones found
except Exception as e:
return [{"title": "Error", "snippet": f"Failed to fetch results: {str(e)}", "url": "#"}]
# Format prompt for the AI model to generate high-quality, structured answers
def format_prompt(query: str, web_results: list) -> str:
"""Create a detailed prompt with web context to guide the model toward high-quality answers."""
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S IST")
context = "\n".join([f"- {r['title']}: {r['snippet']}" for r in web_results])
return f"""Current Time: {current_time}
Query: {query}
Web Context (High-Quality Sources):
{context}
Provide a detailed, step-by-step answer in markdown format with clear headings (e.g., #, ##), bullet points, and citations [1], [2], etc. Ensure the answer is structured, relevant, and visually appealing, addressing the user's intent comprehensively. If the query is informational (e.g., 'what,' 'how,' 'why'), offer in-depth insights, examples, and practical advice. If no high-quality answer is possible, state, 'I couldn’t find sufficient high-quality information to provide a detailed answer, but here’s what I found:' followed by a summary of web results."""
# GPU-decorated answer generation for high-quality output
@spaces.GPU(duration=180) # Increased duration for more detailed generation
def generate_answer(prompt: str) -> str:
"""Generate a detailed, high-quality research answer using GPU."""
# Tokenize and truncate prompt to fit within limits
tokenized_prompt = tokenizer(prompt, truncation=True, max_length=200, return_tensors="pt")
input_ids = tokenized_prompt["input_ids"]
# Generate response with more tokens and better sampling for quality
response = generator(
prompt,
max_new_tokens=400, # Increased for more detailed output
num_return_sequences=1,
truncation=True,
do_sample=True,
temperature=0.7, # Controlled randomness for coherent, detailed output
top_p=0.9, # Focus on top probabilities for quality
top_k=50 # Limit to top 50 tokens for better coherence
)[0]["generated_text"]
answer_start = response.find("Provide a detailed") + len("Provide a detailed, step-by-step answer in markdown format with clear headings (e.g., #, ##), bullet points, and citations [1], [2], etc. Ensure the answer is structured, relevant, and visually appealing, addressing the user's intent comprehensively. If the query is informational (e.g., 'what,' 'how,' 'why'), offer in-depth insights, examples, and practical advice. If no high-quality answer is possible, state, 'I couldn’t find sufficient high-quality information to provide a detailed answer, but here’s what I found:' followed by a summary of web results.")
return response[answer_start:].strip() if answer_start > -1 else "I couldn’t find sufficient high-quality information to provide a detailed answer, but here’s what I found:\n\n" + "\n".join([f"- {r['title']}: {r['snippet']}" for r in get_web_results(query, max_results=3)])
# Format sources for display with enhanced styling
def format_sources(web_results: list) -> str:
"""Create a styled HTML list of sources with better visuals for high-quality presentation."""
if not web_results:
return "<div class='no-sources'>No sources available</div>"
sources_html = "<div class='sources-list'>"
for i, res in enumerate(web_results, 1):
sources_html += f"""
<div class='source-item'>
<span class='source-number'>[{i}]</span>
<a href='{res['url']}' target='_blank' class='source-link'>{res['title']}</a>
<p class='source-snippet'>{res['snippet'][:150]}...</p>
</div>
"""
sources_html += "</div>"
return sources_html
# Main processing function
def process_deep_research(query: str, history: list):
"""Handle the deep research process for any query with high-quality output."""
if not history:
history = []
# Fetch web results (CPU)
web_results = get_web_results(query)
sources_html = format_sources(web_results)
# Generate answer (GPU via @spaces.GPU)
prompt = format_prompt(query, web_results)
answer = generate_answer(prompt)
# Convert history to messages format (role/content)
new_history = history + [{"role": "user", "content": query}, {"role": "assistant", "content": answer}]
return answer, sources_html, new_history
# Enhanced CSS for a polished, visually appealing UI
css = """
body {
font-family: 'Arial', sans-serif;
background: #1a1a1a;
color: #ffffff;
}
.gradio-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
padding: 20px;
background: linear-gradient(135deg, #2c3e50, #3498db);
border-radius: 12px;
margin-bottom: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.header h1 { font-size: 2.5em; margin: 0; color: #ffffff; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); }
.header p { color: #bdc3c7; font-size: 1.1em; }
.search-box {
background: #2c2c2c;
padding: 15px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
margin-bottom: 20px;
}
.search-box input {
background: #3a3a3e !important;
color: #ffffff !important;
border: none !important;
border-radius: 8px !important;
padding: 10px;
font-size: 1em;
}
.search-box button {
background: #3498db !important;
border: none !important;
border-radius: 8px !important;
padding: 10px 20px;
font-size: 1em;
transition: background 0.3s;
}
.search-box button:hover { background: #2980b9 !important; }
.results-container {
margin-top: 20px;
display: flex;
gap: 20px;
}
.answer-box {
flex: 2;
background: #2c2c2c;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
overflow-y: auto;
max-height: 600px;
}
.answer-box .markdown {
color: #ecf0f1;
line-height: 1.6;
}
.answer-box .markdown h1 {
color: #ffffff;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.answer-box .markdown h2 {
color: #a8b5c3;
margin-top: 20px;
}
.answer-box .markdown ul {
list-style-type: none;
padding-left: 20px;
}
.answer-box .markdown ul li::before {
content: "•";
color: #3498db;
display: inline-block;
width: 1em;
margin-left: -1em;
}
.answer-box .markdown a { color: #60a5fa; text-decoration: underline; }
.sources-list {
flex: 1;
background: #2c2c2c;
padding: 15px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
max-height: 600px;
overflow-y: auto;
}
.no-sources { color: #a8a9ab; font-style: italic; }
.source-item {
margin-bottom: 15px;
padding: 10px;
background: #3a3a3e;
border-radius: 8px;
transition: background 0.2s;
}
.source-item:hover { background: #4a4b4e; }
.source-number { color: #3498db; font-weight: bold; margin-right: 10px; }
.source-link { color: #60a5fa; font-weight: 500; display: block; margin-bottom: 5px; }
.source-snippet { color: #e5e7eb; font-size: 0.9em; line-height: 1.4; }
.history-box {
margin-top: 20px;
background: #2c2c2c;
padding: 15px;
border-radius: 12px;
max-height: 300px;
overflow-y: auto;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
"""
# Gradio app setup with Blocks
with gr.Blocks(title="Deep Research Engine - ZeroGPU", css=css) as demo:
history_state = gr.State([])
# Header
with gr.Column(elem_classes="header"):
gr.Markdown("# Deep Research Engine")
gr.Markdown("Your gateway to in-depth, high-quality research for any query with real-time web insights.")
# Search input and button
with gr.Row(elem_classes="search-box"):
search_input = gr.Textbox(label="", placeholder="Ask anything (e.g., 'What are the latest AI trends in 2025?')", lines=2)
search_btn = gr.Button("Research", variant="primary")
# Results layout
with gr.Row(elem_classes="results-container"):
with gr.Column():
answer_output = gr.Markdown(label="Research Findings", elem_classes="answer-box")
with gr.Column():
sources_output = gr.HTML(label="Sources", elem_classes="sources-list")
# Chat history (using messages format)
with gr.Row():
history_display = gr.Chatbot(label="History", elem_classes="history-box", type="messages")
# Event handling
def handle_search(query, history):
answer, sources, new_history = process_deep_research(query, history)
return answer, sources, new_history
search_btn.click(
fn=handle_search,
inputs=[search_input, history_state],
outputs=[answer_output, sources_output, history_display]
).then(
fn=lambda x: x,
inputs=[history_display],
outputs=[history_state]
)
search_input.submit(
fn=handle_search,
inputs=[search_input, history_state],
outputs=[answer_output, sources_output, history_display]
).then(
fn=lambda x: x,
inputs=[history_display],
outputs=[history_state]
)
# Launch the app
if __name__ == "__main__":
demo.launch() |