MusaR commited on
Commit
c3300de
·
verified ·
1 Parent(s): ac6532a

Update research_agent/agent.py

Browse files
Files changed (1) hide show
  1. research_agent/agent.py +162 -637
research_agent/agent.py CHANGED
@@ -1,676 +1,201 @@
1
  import os
2
- import gradio as gr
3
- import google.generativeai as genai
4
- from tavily import TavilyClient
5
- from sentence_transformers import SentenceTransformer, CrossEncoder
6
- from datetime import datetime
7
  import json
8
  import time
 
 
 
9
 
10
 
11
- google_key = os.getenv("GOOGLE_API_KEY")
12
- tavily_key = os.getenv("TAVILY_API_KEY")
13
-
14
- if not google_key or not tavily_key:
15
- raise ValueError("API keys not found.")
16
-
17
- # Professional CSS with dark theme
18
- CSS = """
19
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono&display=swap');
20
- * {
21
- transition: all 0.3s ease;
22
- }
23
- body, .gradio-container {
24
- font-family: 'Inter', -apple-system, system-ui, sans-serif !important;
25
- background: #0a0a0a !important;
26
- color: #e4e4e7 !important;
27
- }
28
- .gradio-container {
29
- max-width: 1200px !important;
30
- margin: 0 auto !important;
31
- }
32
- /* Header */
33
- .header {
34
- background: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%);
35
- padding: 3rem 2rem;
36
- border-radius: 24px;
37
- margin-bottom: 2rem;
38
- text-align: center;
39
- box-shadow: 0 20px 40px rgba(0,0,0,0.5);
40
- }
41
- .header h1 {
42
- font-size: 3.5rem;
43
- font-weight: 800;
44
- background: linear-gradient(135deg, #818cf8 0%, #c084fc 50%, #f472b6 100%);
45
- -webkit-background-clip: text;
46
- -webkit-text-fill-color: transparent;
47
- margin: 0;
48
- letter-spacing: -1px;
49
- }
50
- .header p {
51
- color: #a5b4fc;
52
- font-size: 1.25rem;
53
- margin-top: 0.5rem;
54
- font-weight: 300;
55
- }
56
- /* Status Bar */
57
- .status-bar {
58
- background: #18181b;
59
- border: 1px solid #27272a;
60
- border-radius: 16px;
61
- padding: 1rem 1.5rem;
62
- margin-bottom: 2rem;
63
- display: flex;
64
- justify-content: space-between;
65
- align-items: center;
66
- }
67
- .status-indicator {
68
- display: flex;
69
- align-items: center;
70
- gap: 0.75rem;
71
- }
72
- .pulse-dot {
73
- width: 10px;
74
- height: 10px;
75
- background: #22c55e;
76
- border-radius: 50%;
77
- box-shadow: 0 0 0 0 rgba(34, 197, 94, 1);
78
- animation: pulse-animation 2s infinite;
79
- }
80
- @keyframes pulse-animation {
81
- 0% {
82
- box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7);
83
- }
84
- 70% {
85
- box-shadow: 0 0 0 10px rgba(34, 197, 94, 0);
86
- }
87
- 100% {
88
- box-shadow: 0 0 0 0 rgba(34, 197, 94, 0);
89
- }
90
- }
91
- /* Chat Interface */
92
- #chatbot {
93
- background: #18181b !important;
94
- border: 1px solid #27272a !important;
95
- border-radius: 20px !important;
96
- box-shadow: 0 10px 30px rgba(0,0,0,0.5) !important;
97
- }
98
- #chatbot .message-wrap {
99
- padding: 0 !important;
100
- }
101
- #chatbot .message {
102
- padding: 1.5rem !important;
103
- margin: 0.5rem !important;
104
- border-radius: 16px !important;
105
- font-size: 1.05rem !important;
106
- line-height: 1.7 !important;
107
- }
108
- #chatbot .user {
109
- background: linear-gradient(135deg, #4c1d95 0%, #5b21b6 100%) !important;
110
- color: #f3f4f6 !important;
111
- margin-left: 20% !important;
112
- box-shadow: 0 4px 12px rgba(139, 92, 246, 0.3) !important;
113
- }
114
- #chatbot .bot {
115
- background: #27272a !important;
116
- color: #e4e4e7 !important;
117
- margin-right: 20% !important;
118
- border: 1px solid #3f3f46 !important;
119
- }
120
- #chatbot .bot h1, #chatbot .bot h2, #chatbot .bot h3 {
121
- color: #a78bfa !important;
122
- margin-top: 1.5rem !important;
123
- margin-bottom: 1rem !important;
124
- }
125
- #chatbot .bot h1 { font-size: 2rem !important; }
126
- #chatbot .bot h2 { font-size: 1.5rem !important; }
127
- #chatbot .bot h3 { font-size: 1.25rem !important; }
128
- #chatbot .bot strong {
129
- color: #c4b5fd !important;
130
- }
131
- #chatbot .bot code {
132
- background: #374151 !important;
133
- color: #fbbf24 !important;
134
- padding: 2px 6px !important;
135
- border-radius: 4px !important;
136
- font-family: 'JetBrains Mono', monospace !important;
137
- }
138
- #chatbot .bot a {
139
- color: #60a5fa !important;
140
- text-decoration: none !important;
141
- border-bottom: 1px solid transparent !important;
142
- transition: border-color 0.2s !important;
143
- }
144
- #chatbot .bot a:hover {
145
- border-bottom-color: #60a5fa !important;
146
- }
147
- /* Progress Messages */
148
- .progress-message {
149
- background: #1e1b4b;
150
- border-left: 4px solid #6366f1;
151
- padding: 1rem 1.5rem;
152
- margin: 1rem 0;
153
- border-radius: 0 8px 8px 0;
154
- }
155
- /* Input Area */
156
- .input-group {
157
- background: #18181b;
158
- border: 1px solid #27272a;
159
- border-radius: 16px;
160
- padding: 1.5rem;
161
- margin-top: 2rem;
162
- }
163
- #user-input textarea {
164
- background: #27272a !important;
165
- border: 2px solid #3f3f46 !important;
166
- color: #f3f4f6 !important;
167
- border-radius: 12px !important;
168
- padding: 1rem !important;
169
- font-size: 1.05rem !important;
170
- min-height: 80px !important;
171
- }
172
- #user-input textarea:focus {
173
- border-color: #6366f1 !important;
174
- outline: none !important;
175
- box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1) !important;
176
- }
177
- /* Buttons */
178
- .submit-btn {
179
- background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%) !important;
180
- color: white !important;
181
- font-weight: 600 !important;
182
- font-size: 1.1rem !important;
183
- padding: 0.875rem 2rem !important;
184
- border: none !important;
185
- border-radius: 12px !important;
186
- cursor: pointer !important;
187
- box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4) !important;
188
- }
189
- .submit-btn:hover {
190
- transform: translateY(-2px) !important;
191
- box-shadow: 0 6px 20px rgba(99, 102, 241, 0.5) !important;
192
- }
193
- .submit-btn:active {
194
- transform: translateY(0) !important;
195
- }
196
- /* Examples */
197
- .examples-container {
198
- background: #18181b;
199
- border: 1px solid #27272a;
200
- border-radius: 16px;
201
- padding: 1.5rem;
202
- margin-top: 2rem;
203
- }
204
- .examples-container h3 {
205
- color: #a78bfa;
206
- margin-bottom: 1rem;
207
- font-size: 1.25rem;
208
- font-weight: 600;
209
- }
210
- .examples-grid {
211
- display: grid;
212
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
213
- gap: 1rem;
214
- }
215
- .example-card {
216
- background: #27272a;
217
- border: 1px solid #3f3f46;
218
- padding: 1rem;
219
- border-radius: 12px;
220
- cursor: pointer;
221
- transition: all 0.2s;
222
- }
223
- .example-card:hover {
224
- background: #374151;
225
- border-color: #6366f1;
226
- transform: translateY(-2px);
227
- box-shadow: 0 4px 12px rgba(0,0,0,0.5);
228
- }
229
- /* Loading Animation */
230
- .loading-wave {
231
- display: inline-flex;
232
- gap: 4px;
233
- }
234
- .loading-wave span {
235
- width: 4px;
236
- height: 20px;
237
- background: #6366f1;
238
- border-radius: 2px;
239
- animation: wave 1.2s linear infinite;
240
- }
241
- .loading-wave span:nth-child(2) { animation-delay: -1.1s; }
242
- .loading-wave span:nth-child(3) { animation-delay: -1s; }
243
- .loading-wave span:nth-child(4) { animation-delay: -0.9s; }
244
- .loading-wave span:nth-child(5) { animation-delay: -0.8s; }
245
- @keyframes wave {
246
- 0%, 40%, 100% { transform: scaleY(0.4); }
247
- 20% { transform: scaleY(1); }
248
- }
249
- /* Report Sections */
250
- .report-section {
251
- background: #1e1b4b;
252
- border: 1px solid #312e81;
253
- border-radius: 12px;
254
- padding: 1.5rem;
255
- margin: 1rem 0;
256
- }
257
- .source-badge {
258
- display: inline-block;
259
- background: #374151;
260
- color: #60a5fa;
261
- padding: 4px 12px;
262
- border-radius: 16px;
263
- font-size: 0.875rem;
264
- margin: 0.25rem;
265
- }
266
- /* Export Section */
267
- .export-section {
268
- background: #18181b;
269
- border: 1px solid #27272a;
270
- border-radius: 16px;
271
- padding: 2rem;
272
- margin-top: 2rem;
273
- text-align: center;
274
- }
275
- .export-buttons {
276
- display: flex;
277
- gap: 1rem;
278
- justify-content: center;
279
- margin-top: 1.5rem;
280
- }
281
- .export-btn {
282
- background: #27272a !important;
283
- border: 1px solid #3f3f46 !important;
284
- color: #e4e4e7 !important;
285
- padding: 0.75rem 1.5rem !important;
286
- border-radius: 10px !important;
287
- font-weight: 500 !important;
288
- cursor: pointer !important;
289
- display: flex !important;
290
- align-items: center !important;
291
- gap: 0.5rem !important;
292
- }
293
- .export-btn:hover {
294
- background: #374151 !important;
295
- border-color: #6366f1 !important;
296
- }
297
- /* Responsive */
298
- @media (max-width: 768px) {
299
- .header h1 { font-size: 2.5rem; }
300
- #chatbot .user { margin-left: 10% !important; }
301
- #chatbot .bot { margin-right: 10% !important; }
302
- .examples-grid { grid-template-columns: 1fr; }
303
- }
304
- """
305
 
 
 
 
 
 
 
306
 
307
- writer_model, planner_model, embedding_model, reranker, tavily_client = None, None, None, None, None
308
- IS_PROCESSING = False
309
 
310
- def initialize_models():
311
- """Initializes all the models and clients using keys from environment variables."""
312
- global writer_model, planner_model, embedding_model, reranker, tavily_client, IS_PROCESSING
313
  try:
314
- genai.configure(api_key=google_key)
315
- tavily_client = TavilyClient(api_key=tavily_key)
316
- writer_model = genai.GenerativeModel(config.WRITER_MODEL)
317
- planner_model = genai.GenerativeModel(config.WRITER_MODEL)
318
- embedding_model = SentenceTransformer('all-MiniLM-L6-v2', device='cpu')
319
- reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2', device='cpu')
320
  except Exception as e:
321
- print(f"FATAL: Failed to initialize models. Error: {str(e)}")
322
- raise gr.Error(f"Failed to initialize models. Please check the logs. Error: {str(e)}")
323
- IS_PROCESSING = False
324
- print("Models and clients initialized successfully.")
325
-
326
- # Initialize models on startup
327
- initialize_models()
328
-
329
- # Store the last generated report for export
330
- LAST_REPORT = {"content": "", "timestamp": "", "topic": ""}
331
-
332
- def format_timestamp():
333
- """Get formatted timestamp"""
334
- return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
335
 
336
- def save_report_to_file(report_content, topic):
337
- """Save report as markdown file"""
338
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
339
- filename = f"research_report_{topic.replace(' ', '_')[:30]}_{timestamp}.md"
340
-
341
- # Add metadata to the report
342
- full_content = f"""---
343
- title: {topic}
344
- generated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
345
- generator: DeepSearch Research Agent
346
- ---
347
 
348
- {report_content}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  """
350
 
351
- with open(filename, 'w', encoding='utf-8') as f:
352
- f.write(full_content)
353
-
354
- return filename
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
 
356
- def create_loading_message(stage):
357
- """Create animated loading message"""
358
- return f"""
359
- <div style="display: flex; align-items: center; gap: 12px; color: #a78bfa;">
360
- <div class="loading-wave">
361
- <span></span><span></span><span></span><span></span><span></span>
362
- </div>
363
- <span style="font-weight: 500;">{stage}</span>
364
- </div>
365
- """
366
 
367
- def format_progress_update(message):
368
- """Format progress messages with icons"""
369
- icons = {
370
- "Step": "🎯",
371
- "Searching": "🔍",
372
- "Found": "✅",
373
- "Processing": "⚡",
374
- "Writing": "✍️",
375
- "Synthesizing": "🧪",
376
- "Fact-checking": "🔎",
377
- "Indexing": "📚",
378
- "Expanding": "🔄"
379
- }
380
 
381
- for key, icon in icons.items():
382
- if key in message:
383
- return f'<div class="progress-message">{icon} {message}</div>'
384
 
385
- return f'<div class="progress-message">▶ {message}</div>'
386
-
387
- def chat_step_wrapper(user_input, history, current_agent_state, topic_state):
388
- """Enhanced wrapper with better error handling"""
389
- global IS_PROCESSING, LAST_REPORT
390
 
391
- if IS_PROCESSING:
392
- print("Ignoring duplicate request while processing.")
393
- if False: yield
394
- return
395
-
396
- IS_PROCESSING = True
397
- start_time = time.time()
398
 
 
 
 
 
399
  try:
400
- for update in chat_step(user_input, history, current_agent_state, topic_state):
401
- yield update
 
 
 
 
402
  except Exception as e:
403
- error_msg = f"""
404
- <div style="background: #7f1d1d; border: 1px solid #dc2626; padding: 1rem; border-radius: 8px; margin: 1rem 0;">
405
- <strong>❌ Error:</strong> {str(e)}
406
- <br><br>
407
- <small>Please check your API keys and try again.</small>
408
- </div>
409
- """
410
- history.append((None, error_msg))
411
- yield history, "INITIAL", "", gr.update(interactive=True, placeholder="What would you like to research?"), None, gr.update(visible=False)
412
- finally:
413
- IS_PROCESSING = False
414
- elapsed = time.time() - start_time
415
- print(f"Processing completed in {elapsed:.2f} seconds")
416
-
417
- def chat_step(user_input, history, current_agent_state, topic_state):
418
- """Enhanced chat step with rich visual feedback"""
419
- global LAST_REPORT
420
 
421
- history = history or []
422
- history.append((user_input, None))
423
 
424
- if current_agent_state == "INITIAL":
425
- # Initial topic analysis
426
- yield history, "CLARIFYING", user_input, gr.update(interactive=False, placeholder="Analyzing topic..."), None, gr.update(visible=False)
427
-
428
- history[-1] = (user_input, create_loading_message("Analyzing your research topic"))
429
- yield history, "CLARIFYING", user_input, gr.update(interactive=False), None, gr.update(visible=False)
430
 
431
- time.sleep(0.5) # Brief pause for effect
432
 
433
- questions = get_clarifying_questions(planner_model, user_input)
434
-
435
- formatted_response = f"""
436
- ## 🎯 Research Topic: {user_input}
437
 
438
- I'll help you create a comprehensive research report on this topic. To ensure I cover exactly what you're looking for, please answer these clarifying questions:
439
 
440
- {questions}
 
 
 
 
 
441
 
442
- 💡 **Tip**: The more detailed your answers, the more tailored and comprehensive your report will be!
443
  """
444
 
445
- history[-1] = (user_input, formatted_response)
446
- yield history, "CLARIFYING", user_input, gr.update(interactive=True, placeholder="Type your answers here..."), None, gr.update(visible=False)
447
-
448
- elif current_agent_state == "CLARIFYING":
449
- # Start research process
450
- history[-1] = (user_input, "## 🚀 Starting Deep Research Process\n\nYour answers have been recorded. Initiating comprehensive research...")
451
- yield history, "GENERATING", topic_state, gr.update(interactive=False, placeholder="Generating report..."), None, gr.update(visible=False)
452
-
453
  try:
454
- # Phase 1: Planning
455
- history[-1] = (user_input, history[-1][1] + "\n\n" + format_progress_update("Step 1: Creating research plan and outline"))
456
- yield history, "GENERATING", topic_state, gr.update(interactive=False), None, gr.update(visible=False)
457
-
458
- plan = research_and_plan(config, planner_model, tavily_client, topic_state, user_input)
459
-
460
- # Show plan
461
- sections_list = "\n".join([f" {i+1}. **{s.get('title', f'Section {i+1}')}**" for i, s in enumerate(plan['sections'])])
462
- plan_display = f"""
463
- ## 🚀 Starting Deep Research Process
464
-
465
- ### 📋 Research Plan Created
466
-
467
- **Research Focus**: {plan['detailed_topic']}
468
-
469
- **Report Structure**:
470
- {sections_list}
471
-
472
- **Total Sections**: {len(plan['sections'])}
473
- """
474
- history[-1] = (user_input, plan_display)
475
- yield history, "GENERATING", topic_state, gr.update(interactive=False), None, gr.update(visible=False)
476
-
477
- # Phase 2: Research and Writing
478
- report_generator = write_report_stream(config, writer_model, tavily_client, embedding_model, reranker, plan)
479
-
480
- full_report = ""
481
- progress_display = plan_display
482
-
483
- for update in report_generator:
484
- if update.startswith("#"):
485
- # This is the actual report content
486
- full_report = update
487
- LAST_REPORT = {
488
- "content": full_report,
489
- "timestamp": format_timestamp(),
490
- "topic": plan['detailed_topic']
491
- }
492
- else:
493
- # This is a progress update
494
- progress_display = plan_display + "\n\n---\n\n### 📊 Current Progress\n\n" + format_progress_update(update)
495
-
496
- # Show preview of report if we have content
497
- if full_report:
498
- preview = full_report[:500] + "..." if len(full_report) > 500 else full_report
499
- progress_display += f"\n\n---\n\n### 📄 Report Preview\n\n{preview}"
500
-
501
- history[-1] = (user_input, progress_display)
502
- yield history, "GENERATING", topic_state, gr.update(interactive=False), None, gr.update(visible=False)
503
-
504
- # Final report display
505
- completion_msg = f"""
506
- ## ✅ Research Complete!
507
-
508
- **Topic**: {plan['detailed_topic']}
509
- **Generated**: {format_timestamp()}
510
- **Sections**: {len(plan['sections'])}
511
-
512
- ---
513
-
514
- {full_report}
515
-
516
- ---
517
-
518
- ### 💾 Export Options
519
-
520
- Your report has been saved and is ready for download. Use the export buttons below to:
521
- - 📄 Download as Markdown file
522
- - 📋 Copy to clipboard
523
- - 🔄 Start a new research topic
524
- """
525
-
526
- history[-1] = (user_input, completion_msg)
527
-
528
- # Save report to file
529
- report_file = save_report_to_file(full_report, plan['detailed_topic'])
530
-
531
- yield history, "INITIAL", "", gr.update(interactive=True, placeholder="What would you like to research next?"), report_file, gr.update(visible=True)
532
-
533
  except Exception as e:
534
- error_display = f"""
535
- <div style="background: #7f1d1d; border: 1px solid #dc2626; padding: 1.5rem; border-radius: 12px;">
536
- <h3 style="color: #fca5a5; margin-top: 0;">❌ Research Error</h3>
537
- <p style="color: #fecaca;">{str(e)}</p>
538
- <hr style="border-color: #dc2626; margin: 1rem 0;">
539
- <p style="color: #fca5a5; font-size: 0.9rem;">Please try again with a different topic or check your API configuration.</p>
540
- </div>
541
- """
542
- history.append((None, error_display))
543
- yield history, "INITIAL", "", gr.update(interactive=True, placeholder="Let's try again. What would you like to research?"), None, gr.update(visible=False)
544
-
545
- # Build the interface
546
- with gr.Blocks(css=CSS, theme=gr.themes.Base()) as app:
547
- # Header
548
- gr.HTML("""
549
- <div class="header">
550
- <h1>DeepSearch Research Agent</h1>
551
- <p>AI-Powered Comprehensive Research & Analysis</p>
552
- </div>
553
- """)
554
-
555
- # Status Bar
556
- gr.HTML("""
557
- <div class="status-bar">
558
- <div class="status-indicator">
559
- <div class="pulse-dot"></div>
560
- <span style="font-weight: 500;">System Active</span>
561
- </div>
562
- <div style="display: flex; gap: 2rem; align-items: center;">
563
- <span style="color: #71717a;">Powered by Gemini Flash</span>
564
- <span style="color: #71717a;">Enhanced with Tavily Search</span>
565
- </div>
566
- </div>
567
- """)
568
-
569
- # State management
570
- agent_state = gr.State("INITIAL")
571
- initial_topic_state = gr.State("")
572
 
573
- # Chat interface
574
- chatbot = gr.Chatbot(
575
- elem_id="chatbot",
576
- bubble_full_width=False,
577
- height=650,
578
- visible=True,
579
- value=[(None, """
580
- ## 👋 Welcome to DeepSearch!
581
-
582
- I'm your AI research assistant, capable of creating comprehensive, well-researched reports on any topic.
583
-
584
- ### 🎯 How It Works:
585
- 1. **Choose a Topic** - Tell me what you want to research
586
- 2. **Clarify Details** - I'll ask a few questions to understand your needs
587
- 3. **Deep Research** - I'll search, analyze, and synthesize information
588
- 4. **Get Your Report** - Receive a detailed, sourced report ready for download
589
-
590
- ### 💡 Example Topics:
591
- - Impact of AI on healthcare
592
- - Climate change mitigation strategies
593
- - History of quantum computing
594
- - Economic effects of remote work
595
- - Future of renewable energy
596
-
597
- **Ready to start? Enter your research topic below!**
598
- """)],
599
- avatar_images=(None, "🔬")
600
- )
601
 
602
- # Input section
603
- with gr.Group(elem_classes="input-group"):
604
- with gr.Row():
605
- chat_input = gr.Textbox(
606
- placeholder="What would you like to research today?",
607
- interactive=True,
608
- show_label=False,
609
- scale=8,
610
- elem_id="user-input"
611
- )
612
- submit_button = gr.Button(
613
- "🚀 Start Research",
614
- scale=2,
615
- elem_classes="submit-btn"
616
- )
617
 
618
- # Examples section
619
- with gr.Group(elem_classes="examples-container"):
620
- gr.HTML("<h3>💡 Popular Research Topics</h3>")
621
- gr.Examples(
622
- examples=[
623
- "Impact of artificial intelligence on job markets",
624
- "Sustainable urban development strategies",
625
- "The psychology of social media addiction",
626
- "Blockchain technology in supply chain management",
627
- "Future of space exploration and colonization",
628
- "Gene editing and CRISPR technology implications"
629
- ],
630
- inputs=chat_input,
631
- label=""
632
- )
633
 
634
- # Export section (hidden initially)
635
- with gr.Group(visible=False, elem_classes="export-section") as export_group:
636
- gr.HTML("""
637
- <h3 style="color: #a78bfa; margin-bottom: 0.5rem;">📥 Download Your Report</h3>
638
- <p style="color: #71717a;">Your research report has been generated and saved.</p>
639
- """)
640
- report_file = gr.File(
641
- label="Download Report",
642
- visible=True,
643
- file_types=[".md"],
644
- elem_classes="export-btn"
645
- )
646
 
647
- # Event handlers
648
- submit_event = submit_button.click(
649
- fn=chat_step_wrapper,
650
- inputs=[chat_input, chatbot, agent_state, initial_topic_state],
651
- outputs=[chatbot, agent_state, initial_topic_state, chat_input, report_file, export_group],
652
- ).then(
653
- fn=lambda: "",
654
- outputs=[chat_input],
655
- queue=False
656
- )
657
 
658
- chat_input.submit(
659
- fn=chat_step_wrapper,
660
- inputs=[chat_input, chatbot, agent_state, initial_topic_state],
661
- outputs=[chatbot, agent_state, initial_topic_state, chat_input, report_file, export_group],
662
- ).then(
663
- fn=lambda: "",
664
- outputs=[chat_input],
665
- queue=False
666
- )
667
-
668
- # Launch
669
- if __name__ == "__main__":
670
- app.queue()
671
- app.launch(
672
- debug=True,
673
- share=False,
674
- server_name="0.0.0.0",
675
- server_port=7860
676
- )
 
1
  import os
 
 
 
 
 
2
  import json
3
  import time
4
+ from datetime import datetime
5
+ from typing import List, Dict, Any, Generator
6
+ import google.generativeai as genai
7
 
8
 
9
+ def get_clarifying_questions(model, topic: str) -> str:
10
+ """Generate clarifying questions for a research topic"""
11
+ prompt = f"""
12
+ You are a research assistant. Given the topic "{topic}", generate 3-5 clarifying questions that would help create a more focused and comprehensive research report.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ The questions should help understand:
15
+ - Specific aspects the user is most interested in
16
+ - The intended audience or use case
17
+ - The scope and depth required
18
+ - Any particular angle or perspective needed
19
+ - Timeline or geographical focus if relevant
20
 
21
+ Format your response as a numbered list of questions.
22
+ Be concise but thorough.
23
 
24
+ Topic: {topic}
25
+ """
26
+
27
  try:
28
+ response = model.generate_content(prompt)
29
+ return response.text
 
 
 
 
30
  except Exception as e:
31
+ return f"""
32
+ 1. What specific aspects of {topic} would you like me to focus on?
33
+ 2. Who is the intended audience for this research report?
34
+ 3. Are you looking for recent developments, historical context, or both?
35
+ 4. What level of detail would be most helpful for your needs?
36
+ 5. Are there any particular perspectives or viewpoints you'd like me to include?
37
+
38
+ Please provide your answers to help me create the most relevant research report for you.
39
+ """
 
 
 
 
 
40
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
+ def research_and_plan(config, model, tavily_client, topic: str, clarifications: str) -> Dict[str, Any]:
43
+ """Create a research plan based on topic and clarifications"""
44
+
45
+ prompt = f"""
46
+ You are a research planner. Create a comprehensive research plan for the topic: "{topic}"
47
+
48
+ User clarifications: {clarifications}
49
+
50
+ Please provide your response in the following JSON format:
51
+ {{
52
+ "detailed_topic": "A refined, specific topic statement based on the clarifications",
53
+ "sections": [
54
+ {{"title": "Introduction", "description": "Overview and context setting"}},
55
+ {{"title": "Background", "description": "Historical context and foundational information"}},
56
+ {{"title": "Current State", "description": "Present situation and recent developments"}},
57
+ {{"title": "Key Findings", "description": "Main research findings and data"}},
58
+ {{"title": "Analysis", "description": "Critical analysis and insights"}},
59
+ {{"title": "Future Implications", "description": "Trends and future outlook"}},
60
+ {{"title": "Conclusion", "description": "Summary and key takeaways"}}
61
+ ],
62
+ "research_questions": [
63
+ "What is the current state of {topic}?",
64
+ "What are the key challenges and opportunities?",
65
+ "What are the future implications and trends?"
66
+ ]
67
+ }}
68
+
69
+ Make sure the JSON is valid and properly formatted.
70
  """
71
 
72
+ try:
73
+ response = model.generate_content(prompt)
74
+ response_text = response.text.strip()
75
+
76
+ # Try to extract JSON from the response
77
+ json_start = response_text.find('{')
78
+ json_end = response_text.rfind('}') + 1
79
+
80
+ if json_start != -1 and json_end != -1:
81
+ json_text = response_text[json_start:json_end]
82
+ plan_data = json.loads(json_text)
83
+ else:
84
+ raise ValueError("No valid JSON found in response")
85
+
86
+ return plan_data
87
+
88
+ except Exception as e:
89
+ print(f"Error in research_and_plan: {str(e)}")
90
+ # Fallback plan
91
+ return {
92
+ "detailed_topic": f"Comprehensive Analysis of {topic}",
93
+ "sections": [
94
+ {"title": "Introduction", "description": "Overview and context"},
95
+ {"title": "Background", "description": "Historical context and current state"},
96
+ {"title": "Key Findings", "description": "Main research findings and data"},
97
+ {"title": "Current Trends", "description": "Recent developments and patterns"},
98
+ {"title": "Analysis", "description": "Critical analysis and insights"},
99
+ {"title": "Future Outlook", "description": "Predictions and implications"},
100
+ {"title": "Conclusion", "description": "Summary and recommendations"}
101
+ ],
102
+ "research_questions": [
103
+ f"What is the current state of {topic}?",
104
+ f"What are the key challenges in {topic}?",
105
+ f"What are the future implications of {topic}?"
106
+ ]
107
+ }
108
 
 
 
 
 
 
 
 
 
 
 
109
 
110
+ def write_report_stream(config, model, tavily_client, embedding_model, reranker, plan: Dict[str, Any]) -> Generator[str, None, None]:
111
+ """Generate a research report section by section with progress updates"""
 
 
 
 
 
 
 
 
 
 
 
112
 
113
+ sections = plan.get('sections', [])
114
+ detailed_topic = plan.get('detailed_topic', 'Research Topic')
 
115
 
116
+ # Start with report header
117
+ report_content = f"# {detailed_topic}\n\n"
118
+ report_content += f"*Research Report Generated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n"
 
 
119
 
120
+ yield "Step 1: Initializing research framework"
121
+ time.sleep(0.5)
 
 
 
 
 
122
 
123
+ yield "Step 2: Conducting preliminary search"
124
+ time.sleep(0.3)
125
+
126
+ # Conduct initial broad search
127
  try:
128
+ search_results = tavily_client.search(
129
+ query=detailed_topic,
130
+ max_results=5,
131
+ search_depth="advanced"
132
+ )
133
+ yield f"Found {len(search_results.get('results', []))} initial sources"
134
  except Exception as e:
135
+ yield f"Search warning: {str(e)}"
136
+ search_results = {"results": []}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
+ time.sleep(0.3)
 
139
 
140
+ # Process each section
141
+ for i, section in enumerate(sections):
142
+ section_title = section.get('title', f'Section {i+1}')
143
+ section_desc = section.get('description', '')
 
 
144
 
145
+ yield f"Writing section {i+1}/{len(sections)}: {section_title}"
146
 
147
+ # Generate content for this section
148
+ section_prompt = f"""
149
+ Write a comprehensive section titled "{section_title}" for a research report on "{detailed_topic}".
 
150
 
151
+ Section focus: {section_desc}
152
 
153
+ Requirements:
154
+ - Write 3-5 well-structured paragraphs
155
+ - Include relevant facts, data, or examples
156
+ - Use professional academic writing style
157
+ - Ensure content is accurate and informative
158
+ - Connect this section logically to the overall topic
159
 
160
+ Write only the section content without the title (it will be added separately).
161
  """
162
 
 
 
 
 
 
 
 
 
163
  try:
164
+ section_response = model.generate_content(
165
+ section_prompt,
166
+ generation_config=genai.types.GenerationConfig(
167
+ temperature=0.4,
168
+ max_output_tokens=1000
169
+ )
170
+ )
171
+ section_content = section_response.text.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  except Exception as e:
173
+ section_content = f"This section would cover {section_desc.lower()}. Further research would be needed for detailed information."
174
+
175
+ # Add section to report
176
+ report_content += f"## {section_title}\n\n{section_content}\n\n"
177
+
178
+ # Yield current progress with updated report
179
+ yield report_content
180
+ time.sleep(0.4)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
+ # Add sources section
183
+ yield "Adding sources and references"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
+ sources_content = "## Sources and References\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
+ if search_results.get('results'):
188
+ for i, result in enumerate(search_results['results'][:5], 1):
189
+ title = result.get('title', 'Source')
190
+ url = result.get('url', '#')
191
+ sources_content += f"{i}. [{title}]({url})\n"
192
+ else:
193
+ sources_content += "*Note: This report was generated using AI knowledge. For academic use, please verify with current sources.*\n"
 
 
 
 
 
 
 
 
194
 
195
+ report_content += sources_content
 
 
 
 
 
 
 
 
 
 
 
196
 
197
+ yield "Research report completed successfully"
198
+ time.sleep(0.2)
 
 
 
 
 
 
 
 
199
 
200
+ # Final yield with complete report
201
+ yield report_content