import gradio as gr from transformers import pipeline from duckduckgo_search import DDGS from datetime import datetime import asyncio # Initialize a lightweight text generation model (distilgpt2 for speed) generator = pipeline("text-generation", model="distilgpt2", device=0 if gr.cuda.is_available() else -1) # Web search function using DuckDuckGo async def get_web_results(query: str, max_results: int = 5) -> list: """Fetch web results asynchronously for deep research.""" try: with DDGS() as ddgs: results = await asyncio.to_thread(lambda: list(ddgs.text(query, max_results=max_results))) return [ {"title": r.get("title", "No Title"), "snippet": r["body"], "url": r["href"]} for r in results ] except Exception as e: return [{"title": "Error", "snippet": f"Failed to fetch results: {str(e)}", "url": "#"}] # Format prompt for the AI model def format_prompt(query: str, web_results: list) -> str: """Create a concise prompt with web context.""" current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") context = "\n".join([f"- {r['title']}: {r['snippet']}" for r in web_results]) return f"""Time: {current_time} Query: {query} Web Context: {context} Provide a detailed, well-structured answer in markdown format with citations [1], [2], etc.""" # Generate answer using the AI model def generate_answer(prompt: str) -> str: """Generate a detailed research answer.""" response = generator(prompt, max_length=300, num_return_sequences=1, truncation=True)[0]["generated_text"] # Extract the answer after the prompt answer_start = response.find("Provide a detailed") + len("Provide a detailed, well-structured answer in markdown format with citations [1], [2], etc.") return response[answer_start:].strip() # Format sources for display def format_sources(web_results: list) -> str: """Create an HTML list of sources.""" if not web_results: return "
No sources available
" sources_html = "
" for i, res in enumerate(web_results, 1): sources_html += f"""
[{i}] {res['title']}: {res['snippet'][:150]}...
""" sources_html += "
" return sources_html # Main processing function async def process_deep_research(query: str, history: list): """Handle the deep research process with progressive updates.""" if not history: history = [] # Step 1: Initial loading state yield { "answer": "*Searching the web...*", "sources": "
Fetching sources...
", "history": history + [[query, "*Searching...*"]] } # Step 2: Fetch web results web_results = await get_web_results(query) sources_html = format_sources(web_results) # Step 3: Update with web search completed yield { "answer": "*Analyzing results...*", "sources": sources_html, "history": history + [[query, "*Analyzing...*"]] } # Step 4: Generate detailed answer prompt = format_prompt(query, web_results) answer = generate_answer(prompt) final_history = history + [[query, answer]] # Step 5: Final result yield { "answer": answer, "sources": sources_html, "history": final_history } # Custom CSS for a cool, modern UI css = """ body { font-family: 'Arial', sans-serif; background: #1a1a1a; color: #ffffff; } .gradio-container { max-width: 1000px; margin: 0 auto; padding: 20px; } .header { text-align: center; padding: 20px; background: linear-gradient(135deg, #2c3e50, #3498db); border-radius: 10px; margin-bottom: 20px; } .header h1 { font-size: 2.5em; margin: 0; color: #ffffff; } .header p { color: #bdc3c7; font-size: 1.1em; } .search-box { background: #2c2c2c; padding: 15px; border-radius: 10px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); } .search-box input { background: #3a3a3a !important; color: #ffffff !important; border: none !important; border-radius: 5px !important; } .search-box button { background: #3498db !important; border: none !important; border-radius: 5px !important; 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: 10px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); } .answer-box .markdown { color: #ecf0f1; line-height: 1.6; } .sources-list { flex: 1; background: #2c2c2c; padding: 15px; border-radius: 10px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); } .source-item { margin-bottom: 10px; } .source-number { color: #3498db; font-weight: bold; margin-right: 5px; } .source-item a { color: #3498db; text-decoration: none; } .source-item a:hover { text-decoration: underline; } .history-box { margin-top: 20px; background: #2c2c2c; padding: 15px; border-radius: 10px; max-height: 300px; overflow-y: auto; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); } """ # Gradio app setup with Blocks for better control with gr.Blocks(title="Deep Research Engine", 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 answers with real-time web insights.") # Search input and button with gr.Row(elem_classes="search-box"): search_input = gr.Textbox(label="", placeholder="Ask anything...", 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 with gr.Row(): history_display = gr.Chatbot(label="History", elem_classes="history-box") # Event handling async def handle_search(query, history): async for step in process_deep_research(query, history): yield step["answer"], step["sources"], step["history"] search_btn.click( fn=handle_search, inputs=[search_input, history_state], outputs=[answer_output, sources_output, history_display], _js="() => [document.querySelector('.search-box input').value, null]" # Ensure history is managed ).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()