CosmickVisions commited on
Commit
7c5a246
·
verified ·
1 Parent(s): d35bbe4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1256 -114
app.py CHANGED
@@ -54,10 +54,60 @@ chart_data_store = {}
54
 
55
  # Custom CSS for Finance theme with modern UI enhancements
56
  custom_css = """
57
- @import url('shared-base.css');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  /* Finance-specific styles */
59
  .finance-card {
60
- background: var(--surface-2);
61
  border: 1px solid var(--border-color);
62
  border-radius: var(--radius-md);
63
  padding: var(--spacing-lg);
@@ -65,45 +115,603 @@ custom_css = """
65
  box-shadow: var(--shadow-md);
66
  transition: transform 0.3s, box-shadow 0.3s;
67
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  .finance-card:hover {
69
  transform: translateY(-5px);
70
  box-shadow: var(--shadow-lg);
 
71
  }
 
72
  .chart-container {
73
- background: var(--white);
74
  border-radius: var(--radius-md);
75
  padding: var(--spacing-md);
76
  margin: var(--spacing-md) 0;
77
  box-shadow: var(--shadow-sm);
78
  }
 
79
  .news-feed {
80
  max-height: 400px;
81
  overflow-y: auto;
82
  padding: var(--spacing-md);
83
  border: 1px solid var(--border-color);
84
  border-radius: var(--radius-md);
85
- background: var(--surface-1);
86
  }
 
87
  .news-item {
88
  margin-bottom: var(--spacing-md);
89
  padding-bottom: var(--spacing-md);
90
  border-bottom: 1px solid var(--border-color);
91
  }
 
92
  .news-title {
93
  font-size: 1.1rem;
94
  font-weight: 600;
95
- color: var(--text-color);
96
  margin-bottom: var(--spacing-sm);
97
  }
 
98
  .news-summary {
99
- color: var(--text-color-secondary);
100
  line-height: 1.6;
101
  }
 
102
  .sentiment-gauge {
103
  width: 100%;
104
  height: 200px;
105
  margin: var(--spacing-md) 0;
106
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  """
108
 
109
  # Function to process PDF files
@@ -453,6 +1061,9 @@ def generate_response(message, session_id, model_name, history, current_ticker=N
453
  max_tokens=1024
454
  )
455
  response = completion.choices[0].message.content
 
 
 
456
  history.append((message, response))
457
  return history
458
  except Exception as e:
@@ -698,6 +1309,7 @@ def analyze_ticker(ticker_input, period, use_brave_search=False):
698
  search_provider = "Brave Search" if (use_brave_search and BRAVE_API_KEY) else "Serper" if SERPER_API_KEY else "AI Knowledge Base"
699
  summary = f"""
700
  ### {ticker} Analysis (Using {search_provider})
 
701
  **Current Price:** ${stock_data['current_price']}
702
  **52-Week High:** ${stock_data['52wk_high']}
703
  **Market Cap:** ${stock_data['market_cap']:,}
@@ -706,6 +1318,7 @@ def analyze_ticker(ticker_input, period, use_brave_search=False):
706
  **Beta:** {stock_data['beta']}
707
  **Avg Volume:** {stock_data['average_volume']:,}
708
  {sentiment_summary}
 
709
  For in-depth analysis of this chart, ask the chatbot by typing "/chart" or "/analyze chart".
710
  For latest news, type "/news {ticker}".
711
  """
@@ -729,14 +1342,17 @@ def analyze_image(image_file):
729
  mode = image.mode
730
 
731
  analysis = f"""## Technical Document Analysis
 
732
  **Image Properties:**
733
  - Dimensions: {width}x{height} pixels
734
  - Format: {format}
735
  - Color Mode: {mode}
 
736
  **Technical Analysis:**
737
  1. Document Quality:
738
  - Resolution: {'High' if width > 2000 or height > 2000 else 'Medium' if width > 1000 or height > 1000 else 'Low'}
739
  - Color Depth: {mode}
 
740
  2. Recommendations:
741
  - For text extraction, consider using PDF format
742
  - For technical diagrams, ensure high resolution
@@ -748,132 +1364,658 @@ def analyze_image(image_file):
748
  except Exception as e:
749
  return f"Error analyzing image: {str(e)}\n\nPlease try using PDF format instead."
750
 
751
- # Update the Gradio interface
752
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
 
753
  current_session_id = gr.State(None)
754
  pdf_state = gr.State({"page_images": [], "total_pages": 0, "total_words": 0})
755
- current_ticker = gr.State(None)
 
 
 
 
756
 
 
757
  gr.HTML("""
758
  <div class="header">
759
- <div class="header-title">Fin-Vision AI</div>
760
- <div class="header-subtitle">Advanced Financial Analysis Assistant</div>
 
 
761
  </div>
762
  """)
763
 
764
- # Main container with all functionality in tabs
765
- with gr.Tabs() as main_tabs:
766
- # Chat Assistant Tab
767
- with gr.TabItem("💬 Chat Assistant", id=0):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
768
  with gr.Row():
769
- with gr.Column(scale=1):
770
- model_dropdown = gr.Dropdown(
771
- choices=["llama3-70b-8192", "llama3-8b-8192", "mixtral-8x7b-32768", "gemma-7b-it"],
772
- value="llama3-70b-8192",
773
- label="Model Selection"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
774
  )
775
- web_search_toggle = gr.Checkbox(
776
- label="Enable Financial Search",
777
- value=True,
778
- info="Toggle web search functionality"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
779
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
780
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
781
  chatbot = gr.Chatbot(
782
- height=600,
783
  show_copy_button=True,
784
- elem_classes="chat-container",
785
- container=True,
786
- type="messages"
 
787
  )
788
 
789
- with gr.Row():
790
- msg = gr.Textbox(
791
- show_label=False,
792
- placeholder="Ask about financial markets, analyze stocks, or discuss financial documents...",
793
- scale=8
794
- )
795
- send_btn = gr.Button("Send", scale=1)
796
- clear_btn = gr.Button("Clear Conversation")
797
-
798
- # Document Analysis Tab
799
- with gr.TabItem("📄 Document Analysis", id=1):
800
- with gr.Row():
801
- with gr.Column(scale=1):
802
- pdf_file = gr.File(
803
- label="Upload Financial Document",
804
- file_types=[".pdf"],
805
- type="binary"
806
- )
807
- upload_button = gr.Button("Process Document", variant="primary")
808
- pdf_status = gr.Markdown("Upload a document to begin analysis")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
809
 
810
- with gr.Column(scale=2):
811
- with gr.Tabs():
812
- with gr.TabItem("Document Viewer"):
813
- page_slider = gr.Slider(
814
- minimum=1,
815
- maximum=1,
816
- step=1,
817
- label="Page Navigation",
818
- value=1
819
- )
820
- pdf_image = gr.Image(label="Document Page", type="pil")
821
- stats_display = gr.Markdown(elem_classes="stats-box")
822
-
823
- # Financial Tools Tab
824
- with gr.TabItem("🔍 Financial Tools", id=2):
825
- with gr.Tabs() as financial_tabs:
826
- # Stock Analysis
827
- with gr.TabItem("Stock Analysis"):
828
- with gr.Row():
829
- with gr.Column():
830
- ticker_input = gr.Textbox(
831
- label="Ticker Symbol",
832
- placeholder="e.g., AAPL, MSFT, GOOGL"
833
- )
834
- period_dropdown = gr.Dropdown(
835
- choices=["1mo", "3mo", "6mo", "1y", "2y", "5y", "max"],
836
- value="1y",
837
- label="Time Period"
838
- )
839
- analyze_stock_btn = gr.Button("Analyze Stock")
840
- with gr.Row():
841
- stock_chart = gr.Plot(label="Stock Price Chart")
842
- stock_analysis = gr.Markdown()
843
-
844
- # Market News
845
- with gr.TabItem("Market News"):
846
- news_ticker = gr.Textbox(
847
- label="Company/Ticker",
848
- placeholder="Enter company name or ticker symbol"
849
- )
850
- news_btn = gr.Button("Fetch News")
851
- news_results = gr.Markdown()
852
-
853
- # Financial Report Analysis
854
- with gr.TabItem("Report Analysis"):
855
- with gr.Row():
856
- with gr.Column():
857
- report_image = gr.File(
858
- label="Upload Financial Chart/Image",
859
- file_types=["image"],
860
- type="filepath"
861
- )
862
- analyze_report_btn = gr.Button("Analyze Image")
863
- with gr.Column():
864
- report_preview = gr.Image(label="Preview", type="pil")
865
- report_analysis = gr.Markdown()
866
-
867
- # Event Handlers
868
- # [Add appropriate event handlers based on fin-vision functions]
869
-
870
- # Add footer with attribution
871
- gr.HTML("""
872
- <div style="text-align: center; margin-top: 20px; padding: 20px; color: #666; font-size: 0.9rem; border-top: 1px solid #eee;">
873
- Created by Calvin Allen-Crawford<br>
874
- <span style="font-style: italic; font-size: 0.8rem;">Founder of Cosmick Visions</span>
875
- </div>
876
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
877
 
878
  # Launch the app with modern UI
879
  if __name__ == "__main__":
 
54
 
55
  # Custom CSS for Finance theme with modern UI enhancements
56
  custom_css = """
57
+ :root {
58
+ --main-bg: #ffffff;
59
+ --main-text: #000000;
60
+ --accent-primary: #10b981; // Finance green
61
+ --accent-secondary: #2563eb; // Blue for references
62
+ --accent-tertiary: #fb923c; // Orange for alerts
63
+ --border-color: #e5e7eb;
64
+ --radius-md: 8px;
65
+ --spacing-sm: 8px;
66
+ --spacing-md: 16px;
67
+ --spacing-lg: 24px;
68
+ --shadow-sm: 0 1px 3px rgba(0,0,0,0.12);
69
+ --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1);
70
+ --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);
71
+ }
72
+
73
+ .dark-mode {
74
+ --main-bg: #121212;
75
+ --main-text: #f3f4f6;
76
+ --border-color: #374151;
77
+ }
78
+
79
+ /* Chat Interface Styling */
80
+ .chat-container {
81
+ background: var(--main-bg);
82
+ border-radius: var(--radius-md);
83
+ border: 1px solid var(--border-color);
84
+ padding: var(--spacing-md);
85
+ margin-bottom: var(--spacing-md);
86
+ min-height: 600px;
87
+ }
88
+
89
+ .message {
90
+ padding: 12px 16px;
91
+ border-radius: 8px;
92
+ margin: 8px 0;
93
+ max-width: 80%;
94
+ }
95
+
96
+ .user-message {
97
+ background: #f0f4ff;
98
+ margin-left: auto;
99
+ border: 1px solid #d6e0ff;
100
+ }
101
+
102
+ .bot-message {
103
+ background: #fff;
104
+ margin-right: auto;
105
+ border: 1px solid #e0e0e0;
106
+ }
107
+
108
  /* Finance-specific styles */
109
  .finance-card {
110
+ background: #f8fafc;
111
  border: 1px solid var(--border-color);
112
  border-radius: var(--radius-md);
113
  padding: var(--spacing-lg);
 
115
  box-shadow: var(--shadow-md);
116
  transition: transform 0.3s, box-shadow 0.3s;
117
  }
118
+
119
+ .header {
120
+ text-align: center;
121
+ padding: var(--spacing-lg) 0;
122
+ border-bottom: 1px solid var(--border-color);
123
+ margin-bottom: var(--spacing-lg);
124
+ }
125
+
126
+ .header-title {
127
+ font-size: 1.5rem;
128
+ font-weight: 600;
129
+ color: var(--main-text);
130
+ display: flex;
131
+ align-items: center;
132
+ justify-content: center;
133
+ gap: 8px;
134
+ }
135
+
136
+ .badge {
137
+ font-size: 0.7em;
138
+ padding: 2px 8px;
139
+ border-radius: 12px;
140
+ background: var(--accent-primary);
141
+ color: white;
142
+ }
143
+
144
  .finance-card:hover {
145
  transform: translateY(-5px);
146
  box-shadow: var(--shadow-lg);
147
+ border-color: var(--accent-primary);
148
  }
149
+
150
  .chart-container {
151
+ background: var(--main-bg);
152
  border-radius: var(--radius-md);
153
  padding: var(--spacing-md);
154
  margin: var(--spacing-md) 0;
155
  box-shadow: var(--shadow-sm);
156
  }
157
+
158
  .news-feed {
159
  max-height: 400px;
160
  overflow-y: auto;
161
  padding: var(--spacing-md);
162
  border: 1px solid var(--border-color);
163
  border-radius: var(--radius-md);
164
+ background: var(--main-bg);
165
  }
166
+
167
  .news-item {
168
  margin-bottom: var(--spacing-md);
169
  padding-bottom: var(--spacing-md);
170
  border-bottom: 1px solid var(--border-color);
171
  }
172
+
173
  .news-title {
174
  font-size: 1.1rem;
175
  font-weight: 600;
176
+ color: var(--main-text);
177
  margin-bottom: var(--spacing-sm);
178
  }
179
+
180
  .news-summary {
181
+ color: var(--main-text-secondary);
182
  line-height: 1.6;
183
  }
184
+
185
  .sentiment-gauge {
186
  width: 100%;
187
  height: 200px;
188
  margin: var(--spacing-md) 0;
189
  }
190
+
191
+ /* Modern Financial Dashboard Styles */
192
+ .dashboard-grid {
193
+ display: grid;
194
+ gap: 1.5rem;
195
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
196
+ }
197
+
198
+ .ticker-tape {
199
+ background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
200
+ color: white;
201
+ padding: 1rem;
202
+ border-radius: var(--radius-md);
203
+ overflow: hidden;
204
+ position: relative;
205
+ }
206
+
207
+ .stock-card {
208
+ background: white;
209
+ border-radius: var(--radius-md);
210
+ padding: 1.5rem;
211
+ transition: transform 0.3s ease;
212
+ cursor: pointer;
213
+ position: relative;
214
+ overflow: hidden;
215
+ }
216
+
217
+ .stock-card::before {
218
+ content: '';
219
+ position: absolute;
220
+ top: -50%;
221
+ left: -50%;
222
+ width: 200%;
223
+ height: 200%;
224
+ background: linear-gradient(45deg, transparent, rgba(255,255,255,0.1));
225
+ transform: rotate(45deg);
226
+ transition: all 0.5s ease;
227
+ }
228
+
229
+ .stock-card:hover {
230
+ transform: translateY(-5px);
231
+ box-shadow: 0 10px 20px rgba(0,0,0,0.1);
232
+ }
233
+
234
+ .stock-card:hover::before {
235
+ transform: rotate(45deg) translateX(50%);
236
+ }
237
+
238
+ .realtime-chart {
239
+ border: 1px solid var(--border-color);
240
+ border-radius: var(--radius-md);
241
+ padding: 1rem;
242
+ background: white;
243
+ position: relative;
244
+ }
245
+
246
+ .chart-tooltip {
247
+ position: absolute;
248
+ background: var(--main-text);
249
+ color: var(--main-bg);
250
+ padding: 0.5rem 1rem;
251
+ border-radius: var(--radius-sm);
252
+ pointer-events: none;
253
+ opacity: 0;
254
+ transition: opacity 0.2s ease;
255
+ }
256
+
257
+ .dark-mode .ticker-tape {
258
+ background: linear-gradient(90deg, #1e3a8a, #991b1b) !important;
259
+ }
260
+
261
+ .dark-mode .stock-card {
262
+ background: #2d2d2d !important;
263
+ }
264
+
265
+ /* Modern Chat Interface */
266
+ .persistent-chat-container {
267
+ position: fixed;
268
+ bottom: 0;
269
+ left: 0;
270
+ right: 0;
271
+ height: 400px;
272
+ background: var(--main-bg);
273
+ border-top: 1px solid var(--border-color);
274
+ z-index: 1000;
275
+ display: flex;
276
+ flex-direction: column;
277
+ box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
278
+ }
279
+
280
+ .dark-mode .persistent-chat-container {
281
+ background: #121212;
282
+ border-top: 1px solid #374151;
283
+ }
284
+
285
+ .chat-messages {
286
+ flex: 1;
287
+ overflow-y: auto;
288
+ padding: 1rem;
289
+ }
290
+
291
+ .chat-input-area {
292
+ padding: 1rem;
293
+ border-top: 1px solid var(--border-color);
294
+ display: flex;
295
+ gap: 0.5rem;
296
+ }
297
+
298
+ .dark-mode .chat-input-area {
299
+ border-top: 1px solid #374151;
300
+ }
301
+
302
+ .chat-input {
303
+ flex: 1;
304
+ padding: 0.75rem;
305
+ border: 1px solid var(--border-color);
306
+ border-radius: 8px;
307
+ resize: none;
308
+ }
309
+
310
+ .dark-mode .chat-input {
311
+ background: #1e1e1e;
312
+ color: white;
313
+ border-color: #4b5563;
314
+ }
315
+
316
+ .send-button {
317
+ background: var(--accent-primary);
318
+ color: white;
319
+ border: none;
320
+ border-radius: 8px;
321
+ padding: 0 1.5rem;
322
+ cursor: pointer;
323
+ font-weight: 600;
324
+ }
325
+
326
+ .content-with-chat {
327
+ padding-bottom: 400px;
328
+ }
329
+
330
+ .tab-content {
331
+ padding-bottom: 400px;
332
+ }
333
+
334
+ @media (max-width: 768px) {
335
+ .persistent-chat-container {
336
+ height: 300px;
337
+ }
338
+
339
+ .content-with-chat {
340
+ padding-bottom: 300px;
341
+ }
342
+ }
343
+
344
+ .stock-badge {
345
+ background: var(--accent-primary);
346
+ color: white;
347
+ font-size: 0.8rem;
348
+ padding: 0.3rem 0.6rem;
349
+ border-radius: 1rem;
350
+ display: inline-block;
351
+ margin-left: 0.5rem;
352
+ }
353
+
354
+ .stock-up {
355
+ color: #10b981;
356
+ }
357
+
358
+ .stock-down {
359
+ color: #ef4444;
360
+ }
361
+
362
+ .dark-mode .stock-up {
363
+ color: #34d399;
364
+ }
365
+
366
+ .dark-mode .stock-down {
367
+ color: #f87171;
368
+ }
369
+
370
+ .chart-container {
371
+ border: 1px solid var(--border-color);
372
+ border-radius: var(--radius-md);
373
+ padding: var(--spacing-md);
374
+ }
375
+
376
+ .dark-mode .chart-container {
377
+ border-color: #374151;
378
+ }
379
+
380
+ /* Add sidebar and layout CSS */
381
+ custom_css += """
382
+ /* Sidebar Navigation */
383
+ .sidebar {
384
+ position: fixed;
385
+ left: 0;
386
+ top: 0;
387
+ bottom: 400px; /* Leave space for chat */
388
+ width: 250px;
389
+ background: var(--main-bg);
390
+ border-right: 1px solid var(--border-color);
391
+ padding: 1rem;
392
+ overflow-y: auto;
393
+ z-index: 100;
394
+ transition: all 0.3s ease;
395
+ }
396
+
397
+ .dark-mode .sidebar {
398
+ background: #1a1a1a;
399
+ border-right: 1px solid #333;
400
+ }
401
+
402
+ .sidebar-header {
403
+ padding-bottom: 1rem;
404
+ margin-bottom: 1rem;
405
+ border-bottom: 1px solid var(--border-color);
406
+ }
407
+
408
+ .sidebar-nav {
409
+ display: flex;
410
+ flex-direction: column;
411
+ gap: 0.5rem;
412
+ }
413
+
414
+ .nav-item {
415
+ padding: 0.75rem 1rem;
416
+ border-radius: 8px;
417
+ cursor: pointer;
418
+ display: flex;
419
+ align-items: center;
420
+ gap: 0.75rem;
421
+ transition: all 0.2s ease;
422
+ }
423
+
424
+ .nav-item:hover {
425
+ background: rgba(0,0,0,0.05);
426
+ }
427
+
428
+ .dark-mode .nav-item:hover {
429
+ background: rgba(255,255,255,0.05);
430
+ }
431
+
432
+ .nav-item.active {
433
+ background: var(--accent-primary);
434
+ color: white;
435
+ }
436
+
437
+ .nav-icon {
438
+ font-size: 1.25rem;
439
+ width: 1.5rem;
440
+ text-align: center;
441
+ }
442
+
443
+ /* Main Content Area */
444
+ .main-content {
445
+ margin-left: 250px;
446
+ padding: 1rem;
447
+ padding-bottom: 400px; /* Space for chat */
448
+ min-height: calc(100vh - 400px);
449
+ }
450
+
451
+ .collapsed-sidebar .sidebar {
452
+ width: 60px;
453
+ }
454
+
455
+ .collapsed-sidebar .main-content {
456
+ margin-left: 60px;
457
+ }
458
+
459
+ .collapsed-sidebar .nav-text {
460
+ display: none;
461
+ }
462
+
463
+ .collapsed-sidebar .sidebar-header {
464
+ text-align: center;
465
+ }
466
+
467
+ /* Split Screen Mode */
468
+ .split-screen .main-content {
469
+ height: calc(50vh - 200px);
470
+ overflow-y: auto;
471
+ }
472
+
473
+ .split-screen .persistent-chat-container {
474
+ height: 50vh;
475
+ }
476
+
477
+ .split-screen .content-with-chat {
478
+ padding-bottom: 50vh;
479
+ }
480
+
481
+ /* Toggle Buttons */
482
+ .toggle-button {
483
+ position: fixed;
484
+ z-index: 2000;
485
+ background: var(--accent-primary);
486
+ color: white;
487
+ border: none;
488
+ border-radius: 50%;
489
+ width: 40px;
490
+ height: 40px;
491
+ display: flex;
492
+ align-items: center;
493
+ justify-content: center;
494
+ font-size: 1.25rem;
495
+ cursor: pointer;
496
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
497
+ }
498
+
499
+ .sidebar-toggle {
500
+ left: 260px;
501
+ top: 20px;
502
+ }
503
+
504
+ .split-toggle {
505
+ right: 80px;
506
+ bottom: 410px;
507
+ }
508
+
509
+ .dark-mode .toggle-button {
510
+ background: #333;
511
+ }
512
+
513
+ /* Improve responsive design */
514
+ @media (max-width: 768px) {
515
+ .sidebar {
516
+ width: 60px;
517
+ }
518
+
519
+ .main-content {
520
+ margin-left: 60px;
521
+ }
522
+
523
+ .nav-text {
524
+ display: none;
525
+ }
526
+
527
+ .sidebar-header {
528
+ text-align: center;
529
+ }
530
+
531
+ .sidebar-toggle {
532
+ left: 70px;
533
+ }
534
+ }
535
+
536
+ /* Add code viewer and PDF viewer styles */
537
+ custom_css += """
538
+ /* Tool panels */
539
+ .tool-panel {
540
+ background: var(--main-bg);
541
+ border-radius: var(--radius-md);
542
+ border: 1px solid var(--border-color);
543
+ padding: 1rem;
544
+ margin-bottom: 1rem;
545
+ }
546
+
547
+ .dark-mode .tool-panel {
548
+ background: #1a1a1a;
549
+ border: 1px solid #333;
550
+ }
551
+
552
+ /* Code Editor Styles */
553
+ .code-editor-container {
554
+ display: flex;
555
+ flex-direction: column;
556
+ height: 100%;
557
+ }
558
+
559
+ .code-editor-header {
560
+ display: flex;
561
+ justify-content: space-between;
562
+ align-items: center;
563
+ padding-bottom: 0.5rem;
564
+ margin-bottom: 1rem;
565
+ border-bottom: 1px solid var(--border-color);
566
+ }
567
+
568
+ .code-editor-filename {
569
+ font-weight: 600;
570
+ color: var(--main-text);
571
+ }
572
+
573
+ .code-editor-controls {
574
+ display: flex;
575
+ gap: 0.5rem;
576
+ }
577
+
578
+ .code-editor-area {
579
+ font-family: monospace;
580
+ line-height: 1.5;
581
+ height: 100%;
582
+ border: 1px solid var(--border-color);
583
+ border-radius: var(--radius-md);
584
+ padding: 1rem;
585
+ background: #f8f9fa;
586
+ overflow: auto;
587
+ }
588
+
589
+ .dark-mode .code-editor-area {
590
+ background: #1e1e1e;
591
+ color: #f0f0f0;
592
+ border-color: #333;
593
+ }
594
+
595
+ .code-editor-area.editable {
596
+ border-color: var(--accent-primary);
597
+ background: #f0f7f4;
598
+ }
599
+
600
+ .dark-mode .code-editor-area.editable {
601
+ background: #162922;
602
+ }
603
+
604
+ /* Enhanced PDF Viewer */
605
+ .pdf-toolbar {
606
+ display: flex;
607
+ gap: 0.5rem;
608
+ margin-bottom: 1rem;
609
+ padding-bottom: 0.5rem;
610
+ border-bottom: 1px solid var(--border-color);
611
+ }
612
+
613
+ .pdf-container {
614
+ display: flex;
615
+ flex-direction: column;
616
+ height: 100%;
617
+ }
618
+
619
+ .pdf-sidebar {
620
+ width: 25%;
621
+ border-right: 1px solid var(--border-color);
622
+ overflow-y: auto;
623
+ padding-right: 0.5rem;
624
+ }
625
+
626
+ .pdf-content {
627
+ flex: 1;
628
+ overflow: auto;
629
+ padding: 1rem;
630
+ background: #f8f9fa;
631
+ border-radius: var(--radius-md);
632
+ }
633
+
634
+ .dark-mode .pdf-content {
635
+ background: #1e1e1e;
636
+ }
637
+
638
+ .pdf-thumbnail {
639
+ cursor: pointer;
640
+ margin-bottom: 0.5rem;
641
+ border: 2px solid transparent;
642
+ border-radius: var(--radius-sm);
643
+ transition: all 0.2s ease;
644
+ }
645
+
646
+ .pdf-thumbnail:hover {
647
+ transform: translateY(-2px);
648
+ }
649
+
650
+ .pdf-thumbnail.active {
651
+ border-color: var(--accent-primary);
652
+ }
653
+
654
+ .pdf-search {
655
+ margin-bottom: 1rem;
656
+ }
657
+
658
+ .pdf-search-results {
659
+ margin-top: 0.5rem;
660
+ font-size: 0.9rem;
661
+ }
662
+
663
+ .pdf-search-highlight {
664
+ background: rgba(255, 213, 0, 0.3);
665
+ border-radius: 2px;
666
+ }
667
+
668
+ /* Initial state with just chatbot */
669
+ .tools-hidden .sidebar {
670
+ display: block;
671
+ }
672
+
673
+ .tools-hidden .main-content {
674
+ display: none;
675
+ }
676
+
677
+ .tools-visible .main-content {
678
+ display: block;
679
+ }
680
+ """
681
+
682
+ # Add JavaScript interactions
683
+ financial_js = """
684
+ <script>
685
+ document.addEventListener('DOMContentLoaded', () => {
686
+ // Real-time chart interactions
687
+ const charts = document.querySelectorAll('.realtime-chart');
688
+ charts.forEach(chart => {
689
+ chart.addEventListener('mousemove', (e) => {
690
+ const tooltip = chart.querySelector('.chart-tooltip');
691
+ if(tooltip) {
692
+ tooltip.style.left = `${e.offsetX + 10}px`;
693
+ tooltip.style.top = `${e.offsetY + 10}px`;
694
+ tooltip.style.opacity = '1';
695
+ }
696
+ });
697
+
698
+ chart.addEventListener('mouseleave', () => {
699
+ const tooltip = chart.querySelector('.chart-tooltip');
700
+ if(tooltip) tooltip.style.opacity = '0';
701
+ });
702
+ });
703
+
704
+ // Animated ticker tape
705
+ const ticker = document.querySelector('.ticker-tape');
706
+ if(ticker) {
707
+ let position = 0;
708
+ setInterval(() => {
709
+ position -= 1;
710
+ ticker.style.backgroundPosition = `${position}px 0`;
711
+ }, 50);
712
+ }
713
+ });
714
+ </script>
715
  """
716
 
717
  # Function to process PDF files
 
1061
  max_tokens=1024
1062
  )
1063
  response = completion.choices[0].message.content
1064
+ disclaimer = "\n\n*Note: This is informational only and not financial advice. Consult a professional advisor for investment decisions.*"
1065
+ response += disclaimer
1066
+
1067
  history.append((message, response))
1068
  return history
1069
  except Exception as e:
 
1309
  search_provider = "Brave Search" if (use_brave_search and BRAVE_API_KEY) else "Serper" if SERPER_API_KEY else "AI Knowledge Base"
1310
  summary = f"""
1311
  ### {ticker} Analysis (Using {search_provider})
1312
+
1313
  **Current Price:** ${stock_data['current_price']}
1314
  **52-Week High:** ${stock_data['52wk_high']}
1315
  **Market Cap:** ${stock_data['market_cap']:,}
 
1318
  **Beta:** {stock_data['beta']}
1319
  **Avg Volume:** {stock_data['average_volume']:,}
1320
  {sentiment_summary}
1321
+
1322
  For in-depth analysis of this chart, ask the chatbot by typing "/chart" or "/analyze chart".
1323
  For latest news, type "/news {ticker}".
1324
  """
 
1342
  mode = image.mode
1343
 
1344
  analysis = f"""## Technical Document Analysis
1345
+
1346
  **Image Properties:**
1347
  - Dimensions: {width}x{height} pixels
1348
  - Format: {format}
1349
  - Color Mode: {mode}
1350
+
1351
  **Technical Analysis:**
1352
  1. Document Quality:
1353
  - Resolution: {'High' if width > 2000 or height > 2000 else 'Medium' if width > 1000 or height > 1000 else 'Low'}
1354
  - Color Depth: {mode}
1355
+
1356
  2. Recommendations:
1357
  - For text extraction, consider using PDF format
1358
  - For technical diagrams, ensure high resolution
 
1364
  except Exception as e:
1365
  return f"Error analyzing image: {str(e)}\n\nPlease try using PDF format instead."
1366
 
1367
+ # Update the main interface layout with sidebar, content area, and persistent chat
1368
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
1369
+ # Shared state
1370
  current_session_id = gr.State(None)
1371
  pdf_state = gr.State({"page_images": [], "total_pages": 0, "total_words": 0})
1372
+ market_data = gr.State({})
1373
+ chat_history = gr.State([])
1374
+ selected_tool = gr.State("market-analysis") # Default selected tool
1375
+ is_split_screen = gr.State(False) # Split screen toggle state
1376
+ is_sidebar_collapsed = gr.State(False) # Sidebar collapsed state
1377
 
1378
+ # App Header
1379
  gr.HTML("""
1380
  <div class="header">
1381
+ <div class="header-title">💹 Fin-Vision AI
1382
+ <span class="stock-badge">Market Analysis</span>
1383
+ </div>
1384
+ <div class="header-subtitle">Financial Document & Market Copilot</div>
1385
  </div>
1386
  """)
1387
 
1388
+ # Sidebar Navigation
1389
+ with gr.Column(elem_classes=["sidebar"]):
1390
+ gr.HTML("""
1391
+ <div class="sidebar-header">
1392
+ <div class="sidebar-logo">📊 Fin-AI</div>
1393
+ </div>
1394
+ """)
1395
+
1396
+ with gr.Column(elem_classes=["sidebar-nav"]):
1397
+ with gr.Group():
1398
+ market_btn = gr.Button("📈 Market Analysis", elem_classes=["nav-item"])
1399
+ document_btn = gr.Button("📄 Documents", elem_classes=["nav-item"])
1400
+ economic_btn = gr.Button("📊 Economic Data", elem_classes=["nav-item"])
1401
+ news_btn = gr.Button("📰 Financial News", elem_classes=["nav-item"])
1402
+ portfolio_btn = gr.Button("💼 Portfolio", elem_classes=["nav-item"])
1403
+ pdf_viewer_btn = gr.Button("📕 PDF Viewer", elem_classes=["nav-item", "active"])
1404
+ code_editor_btn = gr.Button("💻 Code Editor", elem_classes=["nav-item"])
1405
+ settings_btn = gr.Button("⚙️ Settings", elem_classes=["nav-item"])
1406
+
1407
+ # Main Content Area
1408
+ with gr.Column(elem_classes=["main-content"]):
1409
+ # PDF Viewer Tool
1410
+ with gr.Group(visible=True) as pdf_viewer_tool:
1411
  with gr.Row():
1412
+ gr.Markdown("## 📕 PDF Viewer Tool")
1413
+
1414
+ with gr.Row():
1415
+ with gr.Column(scale=1):
1416
+ pdf_file_upload = gr.File(
1417
+ label="Upload PDF Document",
1418
+ file_types=[".pdf"],
1419
+ type="binary"
1420
+ )
1421
+ with gr.Row():
1422
+ pdf_process_btn = gr.Button("Process PDF", variant="primary")
1423
+ pdf_clear_btn = gr.Button("Clear", variant="secondary")
1424
+
1425
+ # PDF Navigation
1426
+ with gr.Box():
1427
+ gr.Markdown("### Navigation")
1428
+ with gr.Row():
1429
+ pdf_prev_btn = gr.Button("◀ Previous")
1430
+ pdf_page_slider = gr.Slider(
1431
+ minimum=1,
1432
+ maximum=1,
1433
+ step=1,
1434
+ label="Page",
1435
+ value=1
1436
+ )
1437
+ pdf_next_btn = gr.Button("Next ▶")
1438
+
1439
+ # Search functionality
1440
+ with gr.Row():
1441
+ pdf_search_input = gr.Textbox(
1442
+ label="Search in PDF",
1443
+ placeholder="Enter search term...",
1444
+ elem_classes=["pdf-search"]
1445
+ )
1446
+ pdf_search_btn = gr.Button("Search")
1447
+
1448
+ pdf_search_results = gr.Markdown(elem_classes=["pdf-search-results"])
1449
+
1450
+ # Statistics and metadata
1451
+ pdf_stats = gr.Markdown(elem_classes=["stats-box"])
1452
+
1453
+ with gr.Column(scale=2):
1454
+ # PDF Display
1455
+ with gr.Box(elem_classes=["pdf-container"]):
1456
+ with gr.Row():
1457
+ pdf_thumbnail_gallery = gr.Gallery(
1458
+ label="Thumbnails",
1459
+ elem_classes=["pdf-thumbnail-gallery"],
1460
+ columns=1,
1461
+ rows=5,
1462
+ height=500
1463
+ )
1464
+
1465
+ pdf_display = gr.Image(
1466
+ label="Document View",
1467
+ type="pil",
1468
+ elem_classes=["pdf-display"],
1469
+ height=600
1470
+ )
1471
+
1472
+ # Extracted text
1473
+ pdf_text = gr.Textbox(
1474
+ label="Extracted Text",
1475
+ lines=10,
1476
+ max_lines=20,
1477
+ interactive=False,
1478
+ elem_classes=["pdf-text"]
1479
+ )
1480
+
1481
+ # Code Editor Tool
1482
+ with gr.Group(visible=False) as code_editor_tool:
1483
+ with gr.Row():
1484
+ gr.Markdown("## 💻 Code Editor")
1485
+
1486
+ with gr.Row():
1487
+ with gr.Column(scale=1):
1488
+ # File selection and management
1489
+ code_file_upload = gr.File(
1490
+ label="Upload Code File",
1491
+ file_types=[".py", ".js", ".html", ".css", ".json", ".txt"],
1492
+ type="binary"
1493
+ )
1494
+
1495
+ with gr.Row():
1496
+ code_load_btn = gr.Button("Load File", variant="primary")
1497
+ code_clear_btn = gr.Button("Clear", variant="secondary")
1498
+
1499
+ # File browser
1500
+ with gr.Box():
1501
+ gr.Markdown("### File Browser")
1502
+ code_file_path = gr.Textbox(
1503
+ label="File Path",
1504
+ placeholder="Enter file path to open...",
1505
+ value=""
1506
+ )
1507
+ code_file_list = gr.Dropdown(
1508
+ label="Recent Files",
1509
+ choices=[],
1510
+ interactive=True
1511
  )
1512
+ with gr.Row():
1513
+ code_browse_btn = gr.Button("Browse")
1514
+ code_refresh_btn = gr.Button("↻ Refresh")
1515
+
1516
+ # Code analysis
1517
+ with gr.Box():
1518
+ gr.Markdown("### Code Analysis")
1519
+ code_analyze_btn = gr.Button("Analyze Code")
1520
+ code_analysis_results = gr.Markdown("Click 'Analyze Code' to get analysis")
1521
+
1522
+ with gr.Column(scale=2):
1523
+ # Code editor header
1524
+ with gr.Row(elem_classes=["code-editor-header"]):
1525
+ code_filename = gr.Textbox(
1526
+ label="Filename",
1527
+ value="",
1528
+ interactive=False,
1529
+ elem_classes=["code-editor-filename"]
1530
  )
1531
+ with gr.Row(elem_classes=["code-editor-controls"]):
1532
+ code_edit_toggle = gr.Checkbox(
1533
+ label="Edit Mode",
1534
+ value=False
1535
+ )
1536
+ code_save_btn = gr.Button("Save", variant="primary")
1537
+
1538
+ # Code editor
1539
+ code_content = gr.Code(
1540
+ label="",
1541
+ language="python",
1542
+ value="# No file loaded\n\n# Upload or select a file to edit",
1543
+ interactive=False,
1544
+ elem_classes=["code-editor-area"]
1545
+ )
1546
+
1547
+ # Execution results
1548
+ code_output = gr.Textbox(
1549
+ label="Output",
1550
+ lines=5,
1551
+ interactive=False
1552
+ )
1553
+
1554
+ # Market Analysis Tool (keeping as reference but initially hidden)
1555
+ with gr.Group(visible=False) as market_tool:
1556
+ gr.Markdown("## 📈 Market Analysis")
1557
+ with gr.Row():
1558
+ with gr.Column(scale=1):
1559
+ ticker_input = gr.Textbox(
1560
+ label="Stock Ticker",
1561
+ placeholder="Enter ticker symbol (e.g., AAPL, MSFT)",
1562
+ value=""
1563
+ )
1564
+ period_dropdown = gr.Dropdown(
1565
+ choices=["1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max"],
1566
+ value="1y",
1567
+ label="Time Period"
1568
+ )
1569
+ analyze_btn = gr.Button("Analyze Stock", variant="primary")
1570
+
1571
+ with gr.Column(scale=2):
1572
+ stock_chart = gr.Plot(label="Stock Price Chart")
1573
+ stock_info = gr.Markdown(elem_classes="finance-card")
1574
+
1575
+ # Document Analysis Tool (keeping as reference but initially hidden)
1576
+ with gr.Group(visible=False) as document_tool:
1577
+ gr.Markdown("## 📄 Financial Documents")
1578
+ with gr.Row():
1579
+ with gr.Column(scale=1):
1580
+ pdf_file = gr.File(
1581
+ label="Upload Financial Document",
1582
+ file_types=[".pdf"],
1583
+ type="binary"
1584
+ )
1585
+ upload_button = gr.Button("Process Document", variant="primary")
1586
+ pdf_status = gr.Markdown("Upload a document to begin analysis")
1587
+
1588
+ with gr.Column(scale=2):
1589
+ with gr.Tabs():
1590
+ with gr.TabItem("Document Viewer"):
1591
+ page_slider = gr.Slider(
1592
+ minimum=1,
1593
+ maximum=1,
1594
+ step=1,
1595
+ label="Page Navigation",
1596
+ value=1
1597
+ )
1598
+ pdf_image = gr.Image(label="Document Page", type="pil")
1599
+ stats_display = gr.Markdown(elem_classes="stats-box")
1600
+
1601
+ # Economic Data Tool (keeping as reference but initially hidden)
1602
+ with gr.Group(visible=False) as economic_tool:
1603
+ gr.Markdown("## 📊 Economic Indicators")
1604
+ with gr.Row():
1605
+ with gr.Column(scale=1):
1606
+ indicator_dropdown = gr.Dropdown(
1607
+ choices=["GDP", "Unemployment", "Inflation", "Interest Rates", "Consumer Confidence"],
1608
+ value="GDP",
1609
+ label="Economic Indicator"
1610
+ )
1611
+ indicator_btn = gr.Button("Fetch Data", variant="primary")
1612
 
1613
+ with gr.Column(scale=2):
1614
+ indicator_chart = gr.Plot(label="Economic Indicator")
1615
+ indicator_info = gr.Markdown(elem_classes="finance-card")
1616
+
1617
+ # News Tool (keeping as reference but initially hidden)
1618
+ with gr.Group(visible=False) as news_tool:
1619
+ gr.Markdown("## 📰 Financial News")
1620
+ with gr.Row():
1621
+ with gr.Column():
1622
+ news_query = gr.Textbox(
1623
+ label="Search News",
1624
+ placeholder="Enter topic, company, or leave blank for top financial news"
1625
+ )
1626
+ news_btn = gr.Button("Get News", variant="primary")
1627
+ news_results = gr.Markdown("Click 'Get News' to fetch the latest financial headlines")
1628
+
1629
+ # Portfolio Tool (keeping as reference but initially hidden)
1630
+ with gr.Group(visible=False) as portfolio_tool:
1631
+ gr.Markdown("## 💼 Portfolio Tracker")
1632
+ with gr.Row():
1633
+ gr.Markdown("Portfolio tracking features coming soon!")
1634
+
1635
+ # Settings Tool (keeping as reference but initially hidden)
1636
+ with gr.Group(visible=False) as settings_tool:
1637
+ gr.Markdown("## ⚙️ Settings")
1638
+ with gr.Row():
1639
+ with gr.Column():
1640
+ theme_toggle = gr.Checkbox(label="Dark Mode", value=False)
1641
+ api_key_input = gr.Textbox(
1642
+ label="API Key (Optional)",
1643
+ placeholder="Enter your own API key for extended functionality",
1644
+ type="password"
1645
+ )
1646
+ save_btn = gr.Button("Save Settings", variant="primary")
1647
+
1648
+ # Toggle buttons for sidebar collapse and split screen
1649
+ gr.HTML("""
1650
+ <div class="sidebar-toggle toggle-button">
1651
+ <span id="sidebar-toggle-icon">◀</span>
1652
+ </div>
1653
+ <div class="split-toggle toggle-button">
1654
+ <span id="split-toggle-icon">⬍</span>
1655
+ </div>
1656
+ """)
1657
+
1658
+ # Persistent Chat Interface
1659
+ with gr.Column(elem_classes=["persistent-chat-container"]):
1660
+ with gr.Column(elem_classes=["chat-messages"]) as chat_display:
1661
  chatbot = gr.Chatbot(
 
1662
  show_copy_button=True,
1663
+ avatar_images=(
1664
+ "assets/user.png",
1665
+ "assets/bot_finance.png"
1666
+ )
1667
  )
1668
 
1669
+ with gr.Row(elem_classes=["chat-input-area"]):
1670
+ msg = gr.Textbox(
1671
+ show_label=False,
1672
+ placeholder="Ask questions about financial data & documents...",
1673
+ elem_classes=["chat-input"]
1674
+ )
1675
+ send_btn = gr.Button("Send", elem_classes=["send-button"])
1676
+
1677
+ # Event Handlers for Navigation
1678
+ def switch_to_tool(tool_name):
1679
+ """Switch to a specific tool by name"""
1680
+ is_market = tool_name == "market"
1681
+ is_document = tool_name == "document"
1682
+ is_economic = tool_name == "economic"
1683
+ is_news = tool_name == "news"
1684
+ is_portfolio = tool_name == "portfolio"
1685
+ is_pdf_viewer = tool_name == "pdf_viewer"
1686
+ is_code_editor = tool_name == "code_editor"
1687
+ is_settings = tool_name == "settings"
1688
+
1689
+ return (
1690
+ is_market,
1691
+ is_document,
1692
+ is_economic,
1693
+ is_news,
1694
+ is_portfolio,
1695
+ is_pdf_viewer,
1696
+ is_code_editor,
1697
+ is_settings,
1698
+ "active" if is_market else "",
1699
+ "active" if is_document else "",
1700
+ "active" if is_economic else "",
1701
+ "active" if is_news else "",
1702
+ "active" if is_portfolio else "",
1703
+ "active" if is_pdf_viewer else "",
1704
+ "active" if is_code_editor else "",
1705
+ "active" if is_settings else ""
1706
+ )
1707
+
1708
+ def select_market_tool():
1709
+ return switch_to_tool("market")
1710
+
1711
+ def select_document_tool():
1712
+ return switch_to_tool("document")
1713
+
1714
+ def select_economic_tool():
1715
+ return switch_to_tool("economic")
1716
+
1717
+ def select_news_tool():
1718
+ return switch_to_tool("news")
1719
+
1720
+ def select_portfolio_tool():
1721
+ return switch_to_tool("portfolio")
1722
+
1723
+ def select_pdf_viewer_tool():
1724
+ return switch_to_tool("pdf_viewer")
1725
+
1726
+ def select_code_editor_tool():
1727
+ return switch_to_tool("code_editor")
1728
+
1729
+ def select_settings_tool():
1730
+ return switch_to_tool("settings")
1731
+
1732
+ market_btn.click(
1733
+ fn=select_market_tool,
1734
+ inputs=None,
1735
+ outputs=[
1736
+ market_tool, document_tool, economic_tool, news_tool, portfolio_tool, pdf_viewer_tool, code_editor_tool, settings_tool,
1737
+ market_btn, document_btn, economic_btn, news_btn, portfolio_btn, pdf_viewer_btn, code_editor_btn, settings_btn
1738
+ ]
1739
+ )
1740
+
1741
+ document_btn.click(
1742
+ fn=select_document_tool,
1743
+ inputs=None,
1744
+ outputs=[
1745
+ market_tool, document_tool, economic_tool, news_tool, portfolio_tool, pdf_viewer_tool, code_editor_tool, settings_tool,
1746
+ market_btn, document_btn, economic_btn, news_btn, portfolio_btn, pdf_viewer_btn, code_editor_btn, settings_btn
1747
+ ]
1748
+ )
1749
+
1750
+ economic_btn.click(
1751
+ fn=select_economic_tool,
1752
+ inputs=None,
1753
+ outputs=[
1754
+ market_tool, document_tool, economic_tool, news_tool, portfolio_tool, pdf_viewer_tool, code_editor_tool, settings_tool,
1755
+ market_btn, document_btn, economic_btn, news_btn, portfolio_btn, pdf_viewer_btn, code_editor_btn, settings_btn
1756
+ ]
1757
+ )
1758
+
1759
+ news_btn.click(
1760
+ fn=select_news_tool,
1761
+ inputs=None,
1762
+ outputs=[
1763
+ market_tool, document_tool, economic_tool, news_tool, portfolio_tool, pdf_viewer_tool, code_editor_tool, settings_tool,
1764
+ market_btn, document_btn, economic_btn, news_btn, portfolio_btn, pdf_viewer_btn, code_editor_btn, settings_btn
1765
+ ]
1766
+ )
1767
+
1768
+ portfolio_btn.click(
1769
+ fn=select_portfolio_tool,
1770
+ inputs=None,
1771
+ outputs=[
1772
+ market_tool, document_tool, economic_tool, news_tool, portfolio_tool, pdf_viewer_tool, code_editor_tool, settings_tool,
1773
+ market_btn, document_btn, economic_btn, news_btn, portfolio_btn, pdf_viewer_btn, code_editor_btn, settings_btn
1774
+ ]
1775
+ )
1776
+
1777
+ pdf_viewer_btn.click(
1778
+ fn=select_pdf_viewer_tool,
1779
+ inputs=None,
1780
+ outputs=[
1781
+ market_tool, document_tool, economic_tool, news_tool, portfolio_tool, pdf_viewer_tool, code_editor_tool, settings_tool,
1782
+ market_btn, document_btn, economic_btn, news_btn, portfolio_btn, pdf_viewer_btn, code_editor_btn, settings_btn
1783
+ ]
1784
+ )
1785
+
1786
+ code_editor_btn.click(
1787
+ fn=select_code_editor_tool,
1788
+ inputs=None,
1789
+ outputs=[
1790
+ market_tool, document_tool, economic_tool, news_tool, portfolio_tool, pdf_viewer_tool, code_editor_tool, settings_tool,
1791
+ market_btn, document_btn, economic_btn, news_btn, portfolio_btn, pdf_viewer_btn, code_editor_btn, settings_btn
1792
+ ]
1793
+ )
1794
+
1795
+ settings_btn.click(
1796
+ fn=select_settings_tool,
1797
+ inputs=None,
1798
+ outputs=[
1799
+ market_tool, document_tool, economic_tool, news_tool, portfolio_tool, pdf_viewer_tool, code_editor_tool, settings_tool,
1800
+ market_btn, document_btn, economic_btn, news_btn, portfolio_btn, pdf_viewer_btn, code_editor_btn, settings_btn
1801
+ ]
1802
+ )
1803
+
1804
+ # Existing event handlers
1805
+ def process_message(message, history, session_id, pdf_state, market_data):
1806
+ if not message:
1807
+ return history, ""
1808
+
1809
+ # Access context from document analysis if available
1810
+ context = ""
1811
+ if session_id and session_id in user_vectorstores:
1812
+ vectorstore = user_vectorstores[session_id]
1813
+ docs = vectorstore.similarity_search(message, k=3)
1814
+ if docs:
1815
+ context = "\n\nRelevant information from financial document:\n" + "\n".join(f"- {doc.page_content}" for doc in docs)
1816
+
1817
+ # Add stock market data if available
1818
+ if market_data and 'ticker' in market_data:
1819
+ ticker = market_data['ticker']
1820
+ context += f"\n\nStock data for {ticker} is available from the market analysis tab."
1821
+ if 'price' in market_data:
1822
+ context += f"\nCurrent price: ${market_data['price']}"
1823
+ if 'change' in market_data:
1824
+ context += f"\nChange: {market_data['change']}%"
1825
+
1826
+ # Check if it's a special command for stock lookup
1827
+ if message.lower().startswith("/stock "):
1828
+ ticker = message.split(" ", 1)[1].strip().upper()
1829
+ try:
1830
+ # Get stock data
1831
+ stock_data = get_stock_data(ticker)
1832
+ # Format response
1833
+ response = f"**Stock Information for {ticker}**\n\n"
1834
+ response += f"Current Price: ${stock_data['price']:.2f}\n"
1835
+ response += f"Change: {stock_data['change']:.2f}% "
1836
+ response += "📈" if stock_data['change'] > 0 else "📉"
1837
+ response += f"\nVolume: {stock_data['volume']:,}\n"
1838
+ response += f"Market Cap: ${stock_data['market_cap']:,.2f}\n"
1839
 
1840
+ # Add market data for future context
1841
+ market_data['ticker'] = ticker
1842
+ market_data['price'] = stock_data['price']
1843
+ market_data['change'] = stock_data['change']
1844
+
1845
+ history.append((message, response))
1846
+ return history, ""
1847
+ except Exception as e:
1848
+ response = f"Error retrieving stock data for {ticker}: {str(e)}"
1849
+ history.append((message, response))
1850
+ return history, ""
1851
+
1852
+ # Generate response
1853
+ system_prompt = """You are a financial assistant specializing in analyzing financial data, market trends, and investment documents.
1854
+ You provide clear, accurate information about stocks, economic indicators, and financial reports.
1855
+ Always note that you're not providing investment advice and users should consult with a qualified financial advisor."""
1856
+
1857
+ if context:
1858
+ system_prompt += f"\nUse this context to inform your response if relevant: {context}"
1859
+
1860
+ completion = client.chat.completions.create(
1861
+ model="llama3-70b-8192",
1862
+ messages=[
1863
+ {"role": "system", "content": system_prompt},
1864
+ {"role": "user", "content": message}
1865
+ ],
1866
+ temperature=0.7,
1867
+ max_tokens=1024
1868
+ )
1869
+
1870
+ response = completion.choices[0].message.content
1871
+ disclaimer = "\n\n*Note: This is informational only and not financial advice. Consult a professional advisor for investment decisions.*"
1872
+ response += disclaimer
1873
+
1874
+ history.append((message, response))
1875
+ return history, ""
1876
+
1877
+ # Keep other existing event handlers
1878
+ send_btn.click(
1879
+ process_message,
1880
+ inputs=[msg, chatbot, current_session_id, pdf_state, market_data],
1881
+ outputs=[chatbot, msg]
1882
+ )
1883
+
1884
+ msg.submit(
1885
+ process_message,
1886
+ inputs=[msg, chatbot, current_session_id, pdf_state, market_data],
1887
+ outputs=[chatbot, msg]
1888
+ )
1889
+
1890
+ upload_button.click(
1891
+ process_pdf,
1892
+ inputs=[pdf_file],
1893
+ outputs=[current_session_id, pdf_status, pdf_state]
1894
+ ).then(
1895
+ update_pdf_viewer,
1896
+ inputs=[pdf_state],
1897
+ outputs=[page_slider, pdf_image, stats_display]
1898
+ )
1899
+
1900
+ page_slider.change(
1901
+ update_image,
1902
+ inputs=[page_slider, pdf_state],
1903
+ outputs=[pdf_image]
1904
+ )
1905
+
1906
+ analyze_btn.click(
1907
+ lambda ticker, period: (create_stock_chart(ticker, period), get_stock_analysis(ticker)),
1908
+ inputs=[ticker_input, period_dropdown],
1909
+ outputs=[stock_chart, stock_info]
1910
+ ).then(
1911
+ lambda ticker: {'ticker': ticker},
1912
+ inputs=[ticker_input],
1913
+ outputs=[market_data]
1914
+ )
1915
+
1916
+ indicator_btn.click(
1917
+ lambda indicator: (get_economic_indicator_chart(indicator), get_economic_indicator_info(indicator)),
1918
+ inputs=[indicator_dropdown],
1919
+ outputs=[indicator_chart, indicator_info]
1920
+ )
1921
+
1922
+ # Add logic for news search
1923
+ news_btn.click(
1924
+ lambda query: get_financial_news(query) if query else get_financial_news("top financial news"),
1925
+ inputs=[news_query],
1926
+ outputs=[news_results]
1927
+ )
1928
+
1929
+ # Add JavaScript for toggle functionality
1930
+ gr.HTML("""
1931
+ <script>
1932
+ document.addEventListener('DOMContentLoaded', () => {
1933
+ // Dark mode toggle
1934
+ const darkModeToggle = document.querySelector('input[aria-label="Dark Mode"]');
1935
+ if (darkModeToggle) {
1936
+ darkModeToggle.addEventListener('change', () => {
1937
+ document.body.classList.toggle('dark-mode', darkModeToggle.checked);
1938
+ localStorage.setItem('dark-mode', darkModeToggle.checked);
1939
+ });
1940
+
1941
+ // Check for saved preference
1942
+ if (localStorage.getItem('dark-mode') === 'true') {
1943
+ darkModeToggle.checked = true;
1944
+ document.body.classList.add('dark-mode');
1945
+ }
1946
+ }
1947
+
1948
+ // Sidebar toggle
1949
+ const sidebarToggle = document.querySelector('.sidebar-toggle');
1950
+ const sidebarToggleIcon = document.getElementById('sidebar-toggle-icon');
1951
+
1952
+ if (sidebarToggle) {
1953
+ sidebarToggle.addEventListener('click', () => {
1954
+ document.body.classList.toggle('collapsed-sidebar');
1955
+ sidebarToggleIcon.textContent = document.body.classList.contains('collapsed-sidebar') ? '▶' : '◀';
1956
+ localStorage.setItem('sidebar-collapsed', document.body.classList.contains('collapsed-sidebar'));
1957
+ });
1958
+
1959
+ // Check for saved preference
1960
+ if (localStorage.getItem('sidebar-collapsed') === 'true') {
1961
+ document.body.classList.add('collapsed-sidebar');
1962
+ sidebarToggleIcon.textContent = '▶';
1963
+ }
1964
+ }
1965
+
1966
+ // Split screen toggle
1967
+ const splitToggle = document.querySelector('.split-toggle');
1968
+ const splitToggleIcon = document.getElementById('split-toggle-icon');
1969
+
1970
+ if (splitToggle) {
1971
+ splitToggle.addEventListener('click', () => {
1972
+ document.body.classList.toggle('split-screen');
1973
+ document.body.classList.toggle('tools-visible');
1974
+ splitToggleIcon.textContent = document.body.classList.contains('split-screen') ? '⬆' : '⬍';
1975
+ localStorage.setItem('split-screen', document.body.classList.contains('split-screen'));
1976
+ });
1977
+
1978
+ // Check for saved preference
1979
+ if (localStorage.getItem('split-screen') === 'true') {
1980
+ document.body.classList.add('split-screen');
1981
+ document.body.classList.add('tools-visible');
1982
+ splitToggleIcon.textContent = '⬆';
1983
+ } else {
1984
+ // Default: show just chat at first
1985
+ document.body.classList.add('tools-hidden');
1986
+ }
1987
+ }
1988
+
1989
+ // Code editor syntax highlighting enhancement
1990
+ const codeEditor = document.querySelector('.code-editor-area');
1991
+ if (codeEditor) {
1992
+ // Add line numbers
1993
+ const addLineNumbers = () => {
1994
+ const content = codeEditor.textContent;
1995
+ const lines = content.split('\n');
1996
+ const numberedLines = lines.map((line, i) => `${i+1} | ${line}`);
1997
+ codeEditor.textContent = numberedLines.join('\n');
1998
+ };
1999
+
2000
+ // Observe when edit mode is toggled
2001
+ const editToggle = document.querySelector('input[aria-label="Edit Mode"]');
2002
+ if (editToggle) {
2003
+ editToggle.addEventListener('change', () => {
2004
+ codeEditor.classList.toggle('editable', editToggle.checked);
2005
+ });
2006
+ }
2007
+ }
2008
+ });
2009
+ </script>
2010
+ """)
2011
+
2012
+ # Add footer
2013
+ gr.HTML("""
2014
+ <div style="text-align: center; margin-top: 20px; padding: 20px; color: #666; font-size: 0.9rem; border-top: 1px solid #eee;">
2015
+ Created by Calvin Allen-Crawford<br>
2016
+ <span style="font-style: italic; font-size: 0.8rem;">Founder of Cosmick Visions</span>
2017
+ </div>
2018
+ """)
2019
 
2020
  # Launch the app with modern UI
2021
  if __name__ == "__main__":