00Boobs00's picture
Upload folder using huggingface_hub
b120e64 verified
import os
import textwrap
import google.generativeai as genai
from flask import Flask, request, jsonify, render_template_string
# ==========================================
# CONFIGURATION & SETUP
# ==========================================
app = Flask(__name__)
# Configure the Google Gemini API
# It looks for the GEMINI_API_KEY environment variable
API_KEY = os.getenv("GEMINI_API_KEY")
if not API_KEY:
print("Warning: GEMINI_API_KEY environment variable not set.")
print("Please set it in your Hugging Face Space secrets or Docker environment.")
genai.configure(api_key=API_KEY)
# ==========================================
# FRONTEND TEMPLATE (HTML/CSS/JS)
# ==========================================
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deep Research with Google Gemini</title>
<style>
:root {
--primary: #2563eb;
--primary-hover: #1d4ed8;
--bg: #f8fafc;
--card-bg: #ffffff;
--text: #1e293b;
--text-light: #64748b;
--border: #e2e8f0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg);
color: var(--text);
margin: 0;
padding: 0;
line-height: 1.6;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem 1rem;
}
/* Header & Branding */
header {
text-align: center;
margin-bottom: 3rem;
}
h1 {
font-size: 2.5rem;
font-weight: 800;
margin-bottom: 0.5rem;
background: linear-gradient(135deg, #2563eb, #10b981);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle {
color: var(--text-light);
font-size: 1.1rem;
}
.anycoder-link {
display: inline-block;
margin-top: 1rem;
font-size: 0.9rem;
color: var(--text-light);
text-decoration: none;
border: 1px solid var(--border);
padding: 0.25rem 0.75rem;
border-radius: 999px;
transition: all 0.2s;
}
.anycoder-link:hover {
background-color: var(--card-bg);
color: var(--text);
border-color: var(--text);
}
/* Main Interface */
.search-box {
background: var(--card-bg);
padding: 2rem;
border-radius: 1rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
border: 1px solid var(--border);
margin-bottom: 2rem;
}
textarea {
width: 100%;
min-height: 120px;
padding: 1rem;
border: 1px solid var(--border);
border-radius: 0.5rem;
font-size: 1rem;
font-family: inherit;
resize: vertical;
box-sizing: border-box;
margin-bottom: 1rem;
transition: border-color 0.2s;
}
textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.btn {
background-color: var(--primary);
color: white;
border: none;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 600;
border-radius: 0.5rem;
cursor: pointer;
transition: background-color 0.2s;
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn:hover {
background-color: var(--primary-hover);
}
.btn:disabled {
background-color: var(--text-light);
cursor: not-allowed;
}
/* Results Area */
.results {
background: var(--card-bg);
border-radius: 1rem;
padding: 2rem;
border: 1px solid var(--border);
min-height: 200px;
white-space: pre-wrap;
display: none; /* Hidden by default */
}
.results.active {
display: block;
animation: fadeIn 0.5s ease-out;
}
.results h2 {
margin-top: 0;
font-size: 1.5rem;
}
.error-msg {
color: #ef4444;
background-color: #fef2f2;
padding: 1rem;
border-radius: 0.5rem;
margin-top: 1rem;
border: 1px solid #fecaca;
display: none;
}
/* Loading Spinner */
.spinner {
width: 20px;
height: 20px;
border: 3px solid rgba(255,255,255,0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s ease-in-out infinite;
display: none;
}
.btn.loading .spinner { display: block; }
.btn.loading span { display: none; }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
/* Markdown-like styling for output */
.markdown-content h1, .markdown-content h2, .markdown-content h3 { margin-top: 1.5em; margin-bottom: 0.5em; color: #111; }
.markdown-content p { margin-bottom: 1em; }
.markdown-content ul, .markdown-content ol { margin-bottom: 1em; padding-left: 2em; }
.markdown-content li { margin-bottom: 0.25em; }
.markdown-content code { background: #f1f5f9; padding: 0.2em 0.4em; border-radius: 3px; font-family: monospace; font-size: 0.9em; }
.markdown-content blockquote { border-left: 4px solid var(--border); padding-left: 1em; color: var(--text-light); margin: 1em 0; }
</style>
</head>
<body>
<div class="container">
<header>
<h1>🧠 Deep Research</h1>
<div class="subtitle">Powered by Google Gemini</div>
<!-- CRITICAL: Built with anycoder link -->
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
Built with anycoder
</a>
</header>
<main>
<div class="search-box">
<textarea id="topic" placeholder="Enter a research topic (e.g., 'The impact of AI on renewable energy efficiency')..."></textarea>
<div style="display: flex; justify-content: flex-end;">
<button id="researchBtn" class="btn" onclick="startResearch()">
<span>Generate Report</span>
<div class="spinner"></div>
</button>
</div>
<div id="errorMsg" class="error-msg"></div>
</div>
<div id="resultsArea" class="results">
<div id="outputContent" class="markdown-content"></div>
</div>
</main>
</div>
<script>
async function startResearch() {
const topic = document.getElementById('topic').value.trim();
const btn = document.getElementById('researchBtn');
const resultsArea = document.getElementById('resultsArea');
const outputContent = document.getElementById('outputContent');
const errorMsg = document.getElementById('errorMsg');
if (!topic) {
showError("Please enter a topic first.");
return;
}
// Reset UI
errorMsg.style.display = 'none';
resultsArea.classList.remove('active');
btn.classList.add('loading');
btn.disabled = true;
outputContent.innerHTML = '<p>Analyzing topic and gathering data...</p>';
resultsArea.classList.add('active');
try {
const response = await fetch('/api/research', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ topic: topic })
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Failed to generate research');
}
// Simple formatting for the output (converting newlines to breaks)
// In a production app, use a library like marked.js
const formattedText = data.report
.replace(/^### (.*$)/gim, '<h3>$1</h3>')
.replace(/^## (.*$)/gim, '<h2>$1</h2>')
.replace(/^# (.*$)/gim, '<h1>$1</h1>')
.replace(/\*\*(.*)\*\*/gim, '<b>$1</b>')
.replace(/\n/gim, '<br>');
outputContent.innerHTML = formattedText;
} catch (err) {
showError(err.message);
resultsArea.classList.remove('active');
} finally {
btn.classList.remove('loading');
btn.disabled = false;
}
}
function showError(msg) {
const el = document.getElementById('errorMsg');
el.textContent = msg;
el.style.display = 'block';
}
</script>
</body>
</html>
"""
# ==========================================
# BACKEND LOGIC
# ==========================================
def get_gemini_response(topic):
"""
Interacts with Google Gemini API to generate a deep research report.
"""
try:
model = genai.GenerativeModel('gemini-pro')
prompt = textwrap.dedent(f"""
Act as an expert researcher. Conduct a deep dive into the following topic: "{topic}".
Your report should follow this structure:
1. **Executive Summary**: A brief overview of the topic.
2. **Key Concepts**: Define the most important terms and ideas.
3. **Historical Context**: How did we get here? (if applicable).
4. **Current State of Affairs**: What is happening right now?
5. **Challenges & Controversies**: What are the debated points?
6. **Future Outlook**: Predictions and trends.
Use Markdown formatting (headers, bolding, lists) to make the report readable.
Be thorough, objective, and detailed.
""")
response = model.generate_content(prompt)
return response.text
except Exception as e:
print(f"Error calling Gemini API: {e}")
raise Exception(f"AI Error: {str(e)}")
# ==========================================
# FLASK ROUTES
# ==========================================
@app.route('/')
def home():
"""Serves the frontend HTML."""
return render_template_string(HTML_TEMPLATE)
@app.route('/api/research', methods=['POST'])
def research():
"""API endpoint to handle research requests."""
data = request.get_json()
if not data or 'topic' not in data:
return jsonify({"error": "No topic provided"}), 400
if not API_KEY:
return jsonify({"error": "Server misconfiguration: GEMINI_API_KEY is missing."}), 500
topic = data['topic']
try:
report = get_gemini_response(topic)
return jsonify({"report": report})
except Exception as e:
return jsonify({"error": str(e)}), 500
# ==========================================
# MAIN ENTRY POINT
# ==========================================
if __name__ == '__main__':
# Run the app on 0.0.0.0 so it is accessible externally in Docker
port = int(os.environ.get('PORT', 7860))
app.run(host='0.0.0.0', port=port)