rdune71 commited on
Commit
6bd8bb5
·
1 Parent(s): 0ceffbf
Files changed (3) hide show
  1. app.py +97 -88
  2. modules/analyzer.py +28 -2
  3. modules/visualizer.py +44 -105
app.py CHANGED
@@ -15,6 +15,7 @@ from modules.retriever import Retriever
15
  from modules.analyzer import Analyzer
16
  from modules.citation import CitationManager
17
  from modules.formatter import OutputFormatter
 
18
 
19
  # Configure logging
20
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -617,6 +618,42 @@ html, body {
617
  color: #7e7e7e;
618
  }
619
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
620
  /* Spinner Animation */
621
  @keyframes spin {
622
  0% { transform: rotate(0deg); }
@@ -626,110 +663,82 @@ html, body {
626
  /* Responsive Design */
627
  @media (max-width: 768px) {
628
  .title {
629
- font-size: 2rem;
630
  }
631
 
632
  .subtitle {
633
- font-size: 1.1rem;
634
  }
635
 
636
  .card {
637
- padding: 20px 15px;
638
  }
639
 
640
  .btn-primary {
641
- padding: 12px 20px;
642
- font-size: 16px;
643
  }
644
 
645
  .streaming-content {
646
- padding: 15px;
647
- font-size: 15px;
 
 
 
 
 
 
 
 
 
 
 
 
648
  }
649
  }
650
- """
651
 
652
- def research_assistant(query, file, use_ddg, progress=gr.Progress()):
653
- """Main entry point for the research assistant with streaming"""
654
- logging.info(f"Research assistant called with query: {query}")
655
- for step in orchestrator.run(query, file, use_ddg, progress):
656
- yield step
657
-
658
- # Create Gradio interface
659
- with gr.Blocks(
660
- css=custom_css,
661
- title="Research Assistant",
662
- head="""
663
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
664
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
665
- <script>hljs.highlightAll();</script>
666
- """
667
- ) as demo:
668
- gr.Markdown(f"""
669
- <div class="header">
670
- <h1 class="title">🧠 AI Research Assistant</h1>
671
- <p class="subtitle">Your intelligent research companion</p>
672
- <div class="version-info">🧬 Version: {VERSION['version']}</div>
673
- </div>
674
- """)
675
 
676
- with gr.Row():
677
- with gr.Column(scale=1):
678
- with gr.Group(elem_classes=["card", "input-section"]):
679
- gr.Markdown("### 🔍 Research Input")
680
- query_input = gr.Textbox(
681
- label="Research Query",
682
- placeholder="Enter your research question...",
683
- lines=3
684
- )
685
- file_upload = gr.File(
686
- label="📄 Upload Files (PDF, Images)",
687
- file_types=[".pdf", ".png", ".jpg", ".jpeg"]
688
- )
689
- ddg_checkbox = gr.Checkbox(
690
- label="Use DuckDuckGo Search (Alternative)",
691
- value=False
692
- )
693
- submit_btn = gr.Button("Research", elem_classes=["btn-primary"])
694
-
695
- with gr.Column(scale=2):
696
- with gr.Group(elem_classes=["card", "output-section"]):
697
- gr.Markdown("### 📊 Analysis Results")
698
-
699
- # Progress bar
700
- progress_container = gr.HTML("""
701
- <div class="progress-container">
702
- <div class="progress-bar" id="progress-bar"></div>
703
- </div>
704
- """)
705
-
706
- output = gr.Markdown(elem_classes=["streaming-content"], visible=True)
707
- status_indicator = gr.HTML("""
708
- <div class="status-indicator">
709
- <div class="spinner"></div>
710
- <span class="progress-text">Ready for your research query</span>
711
- </div>
712
- """)
713
 
714
- submit_btn.click(
715
- fn=research_assistant,
716
- inputs=[query_input, file_upload, ddg_checkbox],
717
- outputs=output
718
- )
 
 
 
 
 
 
 
 
 
 
 
719
 
720
- query_input.submit(
721
- fn=research_assistant,
722
- inputs=[query_input, file_upload, ddg_checkbox],
723
- outputs=output
724
- )
725
 
726
- gr.Markdown("""
727
- <div class="footer">
728
- <p>Built with ❤️ for researchers worldwide</p>
729
- <p><span class="highlight">Features:</span> File upload • Web search • Real-time analysis</p>
730
- <p><strong>Note:</strong> The AI model server may take up to 4-5 minutes to start initially.</p>
731
- </div>
732
- """)
733
-
734
- if __name__ == "__main__":
735
- demo.launch(share=True)
 
 
15
  from modules.analyzer import Analyzer
16
  from modules.citation import CitationManager
17
  from modules.formatter import OutputFormatter
18
+ from modules.visualizer import generate_chart, generate_line_chart, generate_pie_chart
19
 
20
  # Configure logging
21
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
618
  color: #7e7e7e;
619
  }
620
 
621
+ /* Accordion styling for collapsible sections */
622
+ .accordion {
623
+ margin-bottom: 10px;
624
+ border: 1px solid #333333;
625
+ border-radius: 8px;
626
+ overflow: hidden;
627
+ }
628
+
629
+ .accordion-header {
630
+ background: #262626;
631
+ padding: 15px;
632
+ cursor: pointer;
633
+ font-weight: 600;
634
+ color: #00ffff;
635
+ display: flex;
636
+ justify-content: space-between;
637
+ align-items: center;
638
+ }
639
+
640
+ .accordion-header:hover {
641
+ background: #2d2d2d;
642
+ }
643
+
644
+ .accordion-content {
645
+ padding: 15px;
646
+ background: #1f1f1f;
647
+ border-top: 1px solid #333333;
648
+ }
649
+
650
+ .accordion-content h3 {
651
+ color: #00cccc !important;
652
+ margin-top: 0;
653
+ border-bottom: 1px solid #333333;
654
+ padding-bottom: 10px;
655
+ }
656
+
657
  /* Spinner Animation */
658
  @keyframes spin {
659
  0% { transform: rotate(0deg); }
 
663
  /* Responsive Design */
664
  @media (max-width: 768px) {
665
  .title {
666
+ font-size: 1.8rem;
667
  }
668
 
669
  .subtitle {
670
+ font-size: 1rem;
671
  }
672
 
673
  .card {
674
+ padding: 15px;
675
  }
676
 
677
  .btn-primary {
678
+ font-size: 15px;
679
+ padding: 12px;
680
  }
681
 
682
  .streaming-content {
683
+ font-size: 14px;
684
+ padding: 10px;
685
+ }
686
+
687
+ .gr-textbox input, .gr-textbox textarea {
688
+ font-size: 14px;
689
+ }
690
+
691
+ .accordion {
692
+ font-size: 14px;
693
+ }
694
+
695
+ .container {
696
+ padding: 10px;
697
  }
698
  }
 
699
 
700
+ /* Mobile optimization */
701
+ @media (max-width: 480px) {
702
+ .header {
703
+ padding: 15px 10px;
704
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
705
 
706
+ .title {
707
+ font-size: 1.5rem;
708
+ }
709
+
710
+ .card {
711
+ padding: 12px;
712
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
713
 
714
+ .btn-primary {
715
+ padding: 10px;
716
+ font-size: 14px;
717
+ }
718
+ }
719
+ """
720
+
721
+ def parse_analysis_sections(analysis_text):
722
+ """Parse analysis text into sections"""
723
+ sections = {
724
+ "Overview": "",
725
+ "Key Findings": "",
726
+ "Perspectives": "",
727
+ "Implications": "",
728
+ "Controversies": ""
729
+ }
730
 
731
+ current_section = None
732
+ lines = analysis_text.split('\n')
 
 
 
733
 
734
+ for line in lines:
735
+ if line.startswith("# Overview"):
736
+ current_section = "Overview"
737
+ continue
738
+ elif line.startswith("# Key Findings"):
739
+ current_section = "Key Findings"
740
+ continue
741
+ elif line.startswith("# Perspectives"):
742
+ current_section = "Perspectives"
743
+ continue
744
+ elif line.startswith
modules/analyzer.py CHANGED
@@ -104,7 +104,20 @@ Please provide:
104
  4. Potential implications or future directions
105
  5. Any controversies or conflicting viewpoints
106
 
107
- Structure your response clearly with these sections. If there is insufficient information, state that clearly."""
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
  try:
110
  # First check if server is ready with extended timeout and user feedback
@@ -175,7 +188,20 @@ Please provide:
175
  4. Potential implications or future directions
176
  5. Any controversies or conflicting viewpoints
177
 
178
- Structure your response clearly with these sections. If there is insufficient information, state that clearly."""
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
  try:
181
  # First check if server is ready with extended timeout
 
104
  4. Potential implications or future directions
105
  5. Any controversies or conflicting viewpoints
106
 
107
+ Structure your response clearly with these sections. If there is insufficient information, state that clearly.
108
+
109
+ Format:
110
+ - Use the exact headings below
111
+ - Use markdown for formatting
112
+ - Wrap code in triple backticks
113
+ - Separate each section clearly
114
+
115
+ Sections:
116
+ # Overview
117
+ # Key Findings
118
+ # Perspectives
119
+ # Implications
120
+ # Controversies"""
121
 
122
  try:
123
  # First check if server is ready with extended timeout and user feedback
 
188
  4. Potential implications or future directions
189
  5. Any controversies or conflicting viewpoints
190
 
191
+ Structure your response clearly with these sections. If there is insufficient information, state that clearly.
192
+
193
+ Format:
194
+ - Use the exact headings below
195
+ - Use markdown for formatting
196
+ - Wrap code in triple backticks
197
+ - Separate each section clearly
198
+
199
+ Sections:
200
+ # Overview
201
+ # Key Findings
202
+ # Perspectives
203
+ # Implications
204
+ # Controversies"""
205
 
206
  try:
207
  # First check if server is ready with extended timeout
modules/visualizer.py CHANGED
@@ -1,111 +1,50 @@
1
  # modules/visualizer.py
2
- import pandas as pd
 
3
  import matplotlib.pyplot as plt
4
- from datetime import datetime
5
- import os
6
- import logging
7
 
8
- def load_status_log(log_file="server_status_log.csv"):
9
- """Load server status log from CSV file"""
10
- if not os.path.exists(log_file):
11
- raise FileNotFoundError(f"Log file not found: {log_file}")
 
 
 
 
12
 
13
- try:
14
- df = pd.read_csv(log_file, parse_dates=["timestamp"])
15
- return df
16
- except Exception as e:
17
- logging.error(f"Error loading log file: {e}")
18
- raise
19
-
20
- def plot_uptime_trend(df, output_file="server_uptime_trend.png"):
21
- """Plot server uptime trend over time"""
22
- try:
23
- # Sort by timestamp
24
- df = df.sort_values("timestamp")
25
-
26
- # Convert status to boolean
27
- df["status_bool"] = df["status"].map({"UP": 1, "DOWN": 0})
28
-
29
- plt.figure(figsize=(12, 4))
30
- plt.plot(df["timestamp"], df["status_bool"], drawstyle='steps-post',
31
- marker='o', linestyle='-', markersize=3, color='#1f77b4')
32
-
33
- plt.title("Server Uptime Trend", fontsize=14, pad=20)
34
- plt.xlabel("Time", fontsize=12)
35
- plt.ylabel("Status", fontsize=12)
36
- plt.yticks([0, 1], ["DOWN", "UP"])
37
- plt.grid(True, linestyle='--', alpha=0.5)
38
- plt.tight_layout()
39
- plt.savefig(output_file)
40
- plt.close()
41
-
42
- print(f"📈 Chart saved to: {output_file}")
43
- logging.info(f"Uptime trend chart saved to: {output_file}")
44
- return output_file
45
- except Exception as e:
46
- logging.error(f"Error plotting uptime trend: {e}")
47
- raise
48
 
49
- def plot_uptime_summary(df, output_file="server_uptime_summary.png"):
50
- """Plot server uptime summary as pie chart"""
51
- try:
52
- # Calculate uptime percentage
53
- total = len(df)
54
- up = df[df["status"] == "UP"].shape[0]
55
- down = df[df["status"] == "DOWN"].shape[0]
56
-
57
- if total == 0:
58
- raise ValueError("No data in log file")
59
-
60
- uptime_pct = (up / total) * 100
61
- downtime_pct = (down / total) * 100
62
-
63
- labels = [f'UP ({uptime_pct:.1f}%)', f'DOWN ({downtime_pct:.1f}%)']
64
- sizes = [up, down]
65
- colors = ['#28a745', '#dc3545']
66
-
67
- plt.figure(figsize=(6, 6))
68
- wedges, texts, autotexts = plt.pie(sizes, labels=labels, autopct='%1.1f%%',
69
- startangle=140, colors=colors)
70
-
71
- # Make percentage text white and bold
72
- for autotext in autotexts:
73
- autotext.set_color('white')
74
- autotext.set_fontweight('bold')
75
-
76
- plt.title("Server Uptime Summary", fontsize=14, pad=20)
77
- plt.axis('equal')
78
- plt.tight_layout()
79
- plt.savefig(output_file)
80
- plt.close()
81
-
82
- print(f"📊 Summary chart saved to: {output_file}")
83
- logging.info(f"Uptime summary chart saved to: {output_file}")
84
- return output_file
85
- except Exception as e:
86
- logging.error(f"Error plotting uptime summary: {e}")
87
- raise
88
 
89
- def get_uptime_stats(df):
90
- """Get uptime statistics"""
91
- try:
92
- total = len(df)
93
- up = df[df["status"] == "UP"].shape[0]
94
- down = df[df["status"] == "DOWN"].shape[0]
95
-
96
- if total == 0:
97
- return {"total_checks": 0, "uptime_pct": 0, "downtime_pct": 0}
98
-
99
- uptime_pct = (up / total) * 100
100
- downtime_pct = (down / total) * 100
101
-
102
- return {
103
- "total_checks": total,
104
- "up_checks": up,
105
- "down_checks": down,
106
- "uptime_pct": round(uptime_pct, 2),
107
- "downtime_pct": round(downtime_pct, 2)
108
- }
109
- except Exception as e:
110
- logging.error(f"Error calculating uptime stats: {e}")
111
- raise
 
1
  # modules/visualizer.py
2
+ import base64
3
+ from io import BytesIO
4
  import matplotlib.pyplot as plt
5
+ import pandas as pd
6
+ import numpy as np
 
7
 
8
+ def generate_chart(data, title="Chart", xlabel="X", ylabel="Y"):
9
+ """Generate a chart and return as base64 encoded string"""
10
+ plt.figure(figsize=(8, 4))
11
+ plt.bar(data.keys(), data.values())
12
+ plt.title(title)
13
+ plt.xlabel(xlabel)
14
+ plt.ylabel(ylabel)
15
+ plt.tight_layout()
16
 
17
+ buf = BytesIO()
18
+ plt.savefig(buf, format='png')
19
+ plt.close()
20
+ data_uri = base64.b64encode(buf.getvalue()).decode('utf-8')
21
+ return f"![{title}](data:image/png;base64,{data_uri})"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ def generate_line_chart(data, title="Line Chart", xlabel="X", ylabel="Y"):
24
+ """Generate a line chart and return as base64 encoded string"""
25
+ plt.figure(figsize=(8, 4))
26
+ plt.plot(list(data.keys()), list(data.values()), marker='o')
27
+ plt.title(title)
28
+ plt.xlabel(xlabel)
29
+ plt.ylabel(ylabel)
30
+ plt.grid(True)
31
+ plt.tight_layout()
32
+
33
+ buf = BytesIO()
34
+ plt.savefig(buf, format='png')
35
+ plt.close()
36
+ data_uri = base64.b64encode(buf.getvalue()).decode('utf-8')
37
+ return f"![{title}](data:image/png;base64,{data_uri})"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ def generate_pie_chart(data, title="Pie Chart"):
40
+ """Generate a pie chart and return as base64 encoded string"""
41
+ plt.figure(figsize=(6, 6))
42
+ plt.pie(data.values(), labels=data.keys(), autopct='%1.1f%%')
43
+ plt.title(title)
44
+ plt.tight_layout()
45
+
46
+ buf = BytesIO()
47
+ plt.savefig(buf, format='png')
48
+ plt.close()
49
+ data_uri = base64.b64encode(buf.getvalue()).decode('utf-8')
50
+ return f"![{title}](data:image/png;base64,{data_uri})"