00Boobs00 commited on
Commit
b120e64
·
verified ·
1 Parent(s): de85e7d

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. app.py +353 -0
  2. requirements.txt +3 -0
app.py ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import textwrap
3
+ import google.generativeai as genai
4
+ from flask import Flask, request, jsonify, render_template_string
5
+
6
+ # ==========================================
7
+ # CONFIGURATION & SETUP
8
+ # ==========================================
9
+
10
+ app = Flask(__name__)
11
+
12
+ # Configure the Google Gemini API
13
+ # It looks for the GEMINI_API_KEY environment variable
14
+ API_KEY = os.getenv("GEMINI_API_KEY")
15
+
16
+ if not API_KEY:
17
+ print("Warning: GEMINI_API_KEY environment variable not set.")
18
+ print("Please set it in your Hugging Face Space secrets or Docker environment.")
19
+
20
+ genai.configure(api_key=API_KEY)
21
+
22
+ # ==========================================
23
+ # FRONTEND TEMPLATE (HTML/CSS/JS)
24
+ # ==========================================
25
+
26
+ HTML_TEMPLATE = """
27
+ <!DOCTYPE html>
28
+ <html lang="en">
29
+ <head>
30
+ <meta charset="UTF-8">
31
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
32
+ <title>Deep Research with Google Gemini</title>
33
+ <style>
34
+ :root {
35
+ --primary: #2563eb;
36
+ --primary-hover: #1d4ed8;
37
+ --bg: #f8fafc;
38
+ --card-bg: #ffffff;
39
+ --text: #1e293b;
40
+ --text-light: #64748b;
41
+ --border: #e2e8f0;
42
+ }
43
+ body {
44
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
45
+ background-color: var(--bg);
46
+ color: var(--text);
47
+ margin: 0;
48
+ padding: 0;
49
+ line-height: 1.6;
50
+ }
51
+ .container {
52
+ max-width: 800px;
53
+ margin: 0 auto;
54
+ padding: 2rem 1rem;
55
+ }
56
+ /* Header & Branding */
57
+ header {
58
+ text-align: center;
59
+ margin-bottom: 3rem;
60
+ }
61
+ h1 {
62
+ font-size: 2.5rem;
63
+ font-weight: 800;
64
+ margin-bottom: 0.5rem;
65
+ background: linear-gradient(135deg, #2563eb, #10b981);
66
+ -webkit-background-clip: text;
67
+ -webkit-text-fill-color: transparent;
68
+ }
69
+ .subtitle {
70
+ color: var(--text-light);
71
+ font-size: 1.1rem;
72
+ }
73
+ .anycoder-link {
74
+ display: inline-block;
75
+ margin-top: 1rem;
76
+ font-size: 0.9rem;
77
+ color: var(--text-light);
78
+ text-decoration: none;
79
+ border: 1px solid var(--border);
80
+ padding: 0.25rem 0.75rem;
81
+ border-radius: 999px;
82
+ transition: all 0.2s;
83
+ }
84
+ .anycoder-link:hover {
85
+ background-color: var(--card-bg);
86
+ color: var(--text);
87
+ border-color: var(--text);
88
+ }
89
+
90
+ /* Main Interface */
91
+ .search-box {
92
+ background: var(--card-bg);
93
+ padding: 2rem;
94
+ border-radius: 1rem;
95
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
96
+ border: 1px solid var(--border);
97
+ margin-bottom: 2rem;
98
+ }
99
+ textarea {
100
+ width: 100%;
101
+ min-height: 120px;
102
+ padding: 1rem;
103
+ border: 1px solid var(--border);
104
+ border-radius: 0.5rem;
105
+ font-size: 1rem;
106
+ font-family: inherit;
107
+ resize: vertical;
108
+ box-sizing: border-box;
109
+ margin-bottom: 1rem;
110
+ transition: border-color 0.2s;
111
+ }
112
+ textarea:focus {
113
+ outline: none;
114
+ border-color: var(--primary);
115
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
116
+ }
117
+ .btn {
118
+ background-color: var(--primary);
119
+ color: white;
120
+ border: none;
121
+ padding: 0.75rem 1.5rem;
122
+ font-size: 1rem;
123
+ font-weight: 600;
124
+ border-radius: 0.5rem;
125
+ cursor: pointer;
126
+ transition: background-color 0.2s;
127
+ display: flex;
128
+ align-items: center;
129
+ gap: 0.5rem;
130
+ }
131
+ .btn:hover {
132
+ background-color: var(--primary-hover);
133
+ }
134
+ .btn:disabled {
135
+ background-color: var(--text-light);
136
+ cursor: not-allowed;
137
+ }
138
+
139
+ /* Results Area */
140
+ .results {
141
+ background: var(--card-bg);
142
+ border-radius: 1rem;
143
+ padding: 2rem;
144
+ border: 1px solid var(--border);
145
+ min-height: 200px;
146
+ white-space: pre-wrap;
147
+ display: none; /* Hidden by default */
148
+ }
149
+ .results.active {
150
+ display: block;
151
+ animation: fadeIn 0.5s ease-out;
152
+ }
153
+ .results h2 {
154
+ margin-top: 0;
155
+ font-size: 1.5rem;
156
+ }
157
+ .error-msg {
158
+ color: #ef4444;
159
+ background-color: #fef2f2;
160
+ padding: 1rem;
161
+ border-radius: 0.5rem;
162
+ margin-top: 1rem;
163
+ border: 1px solid #fecaca;
164
+ display: none;
165
+ }
166
+
167
+ /* Loading Spinner */
168
+ .spinner {
169
+ width: 20px;
170
+ height: 20px;
171
+ border: 3px solid rgba(255,255,255,0.3);
172
+ border-radius: 50%;
173
+ border-top-color: white;
174
+ animation: spin 1s ease-in-out infinite;
175
+ display: none;
176
+ }
177
+ .btn.loading .spinner { display: block; }
178
+ .btn.loading span { display: none; }
179
+
180
+ @keyframes spin { to { transform: rotate(360deg); } }
181
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
182
+
183
+ /* Markdown-like styling for output */
184
+ .markdown-content h1, .markdown-content h2, .markdown-content h3 { margin-top: 1.5em; margin-bottom: 0.5em; color: #111; }
185
+ .markdown-content p { margin-bottom: 1em; }
186
+ .markdown-content ul, .markdown-content ol { margin-bottom: 1em; padding-left: 2em; }
187
+ .markdown-content li { margin-bottom: 0.25em; }
188
+ .markdown-content code { background: #f1f5f9; padding: 0.2em 0.4em; border-radius: 3px; font-family: monospace; font-size: 0.9em; }
189
+ .markdown-content blockquote { border-left: 4px solid var(--border); padding-left: 1em; color: var(--text-light); margin: 1em 0; }
190
+ </style>
191
+ </head>
192
+ <body>
193
+
194
+ <div class="container">
195
+ <header>
196
+ <h1>🧠 Deep Research</h1>
197
+ <div class="subtitle">Powered by Google Gemini</div>
198
+ <!-- CRITICAL: Built with anycoder link -->
199
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
200
+ Built with anycoder
201
+ </a>
202
+ </header>
203
+
204
+ <main>
205
+ <div class="search-box">
206
+ <textarea id="topic" placeholder="Enter a research topic (e.g., 'The impact of AI on renewable energy efficiency')..."></textarea>
207
+ <div style="display: flex; justify-content: flex-end;">
208
+ <button id="researchBtn" class="btn" onclick="startResearch()">
209
+ <span>Generate Report</span>
210
+ <div class="spinner"></div>
211
+ </button>
212
+ </div>
213
+ <div id="errorMsg" class="error-msg"></div>
214
+ </div>
215
+
216
+ <div id="resultsArea" class="results">
217
+ <div id="outputContent" class="markdown-content"></div>
218
+ </div>
219
+ </main>
220
+ </div>
221
+
222
+ <script>
223
+ async function startResearch() {
224
+ const topic = document.getElementById('topic').value.trim();
225
+ const btn = document.getElementById('researchBtn');
226
+ const resultsArea = document.getElementById('resultsArea');
227
+ const outputContent = document.getElementById('outputContent');
228
+ const errorMsg = document.getElementById('errorMsg');
229
+
230
+ if (!topic) {
231
+ showError("Please enter a topic first.");
232
+ return;
233
+ }
234
+
235
+ // Reset UI
236
+ errorMsg.style.display = 'none';
237
+ resultsArea.classList.remove('active');
238
+ btn.classList.add('loading');
239
+ btn.disabled = true;
240
+ outputContent.innerHTML = '<p>Analyzing topic and gathering data...</p>';
241
+ resultsArea.classList.add('active');
242
+
243
+ try {
244
+ const response = await fetch('/api/research', {
245
+ method: 'POST',
246
+ headers: { 'Content-Type': 'application/json' },
247
+ body: JSON.stringify({ topic: topic })
248
+ });
249
+
250
+ const data = await response.json();
251
+
252
+ if (!response.ok) {
253
+ throw new Error(data.error || 'Failed to generate research');
254
+ }
255
+
256
+ // Simple formatting for the output (converting newlines to breaks)
257
+ // In a production app, use a library like marked.js
258
+ const formattedText = data.report
259
+ .replace(/^### (.*$)/gim, '<h3>$1</h3>')
260
+ .replace(/^## (.*$)/gim, '<h2>$1</h2>')
261
+ .replace(/^# (.*$)/gim, '<h1>$1</h1>')
262
+ .replace(/\*\*(.*)\*\*/gim, '<b>$1</b>')
263
+ .replace(/\n/gim, '<br>');
264
+
265
+ outputContent.innerHTML = formattedText;
266
+
267
+ } catch (err) {
268
+ showError(err.message);
269
+ resultsArea.classList.remove('active');
270
+ } finally {
271
+ btn.classList.remove('loading');
272
+ btn.disabled = false;
273
+ }
274
+ }
275
+
276
+ function showError(msg) {
277
+ const el = document.getElementById('errorMsg');
278
+ el.textContent = msg;
279
+ el.style.display = 'block';
280
+ }
281
+ </script>
282
+ </body>
283
+ </html>
284
+ """
285
+
286
+ # ==========================================
287
+ # BACKEND LOGIC
288
+ # ==========================================
289
+
290
+ def get_gemini_response(topic):
291
+ """
292
+ Interacts with Google Gemini API to generate a deep research report.
293
+ """
294
+ try:
295
+ model = genai.GenerativeModel('gemini-pro')
296
+
297
+ prompt = textwrap.dedent(f"""
298
+ Act as an expert researcher. Conduct a deep dive into the following topic: "{topic}".
299
+
300
+ Your report should follow this structure:
301
+ 1. **Executive Summary**: A brief overview of the topic.
302
+ 2. **Key Concepts**: Define the most important terms and ideas.
303
+ 3. **Historical Context**: How did we get here? (if applicable).
304
+ 4. **Current State of Affairs**: What is happening right now?
305
+ 5. **Challenges & Controversies**: What are the debated points?
306
+ 6. **Future Outlook**: Predictions and trends.
307
+
308
+ Use Markdown formatting (headers, bolding, lists) to make the report readable.
309
+ Be thorough, objective, and detailed.
310
+ """)
311
+
312
+ response = model.generate_content(prompt)
313
+ return response.text
314
+ except Exception as e:
315
+ print(f"Error calling Gemini API: {e}")
316
+ raise Exception(f"AI Error: {str(e)}")
317
+
318
+ # ==========================================
319
+ # FLASK ROUTES
320
+ # ==========================================
321
+
322
+ @app.route('/')
323
+ def home():
324
+ """Serves the frontend HTML."""
325
+ return render_template_string(HTML_TEMPLATE)
326
+
327
+ @app.route('/api/research', methods=['POST'])
328
+ def research():
329
+ """API endpoint to handle research requests."""
330
+ data = request.get_json()
331
+
332
+ if not data or 'topic' not in data:
333
+ return jsonify({"error": "No topic provided"}), 400
334
+
335
+ if not API_KEY:
336
+ return jsonify({"error": "Server misconfiguration: GEMINI_API_KEY is missing."}), 500
337
+
338
+ topic = data['topic']
339
+
340
+ try:
341
+ report = get_gemini_response(topic)
342
+ return jsonify({"report": report})
343
+ except Exception as e:
344
+ return jsonify({"error": str(e)}), 500
345
+
346
+ # ==========================================
347
+ # MAIN ENTRY POINT
348
+ # ==========================================
349
+
350
+ if __name__ == '__main__':
351
+ # Run the app on 0.0.0.0 so it is accessible externally in Docker
352
+ port = int(os.environ.get('PORT', 7860))
353
+ app.run(host='0.0.0.0', port=port)
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ flask
2
+ google
3
+ textwrap