JC321 commited on
Commit
86e9d24
·
verified ·
1 Parent(s): 860057a

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +246 -60
app.py CHANGED
@@ -85,17 +85,17 @@ div[class*=" gradio-container-"] {
85
  display: none;
86
  }
87
 
88
- /* 自定义选项样式 */
89
  .company-list-container label {
90
  display: block;
91
- padding: 0.75rem 1rem;
92
- margin: 0.25rem 0;
93
- border-radius: 0.375rem;
94
  cursor: pointer;
95
  transition: all 0.2s ease;
96
  background-color: #f9fafb;
97
  border: 1px solid #e5e7eb;
98
- font-size: 1rem;
99
  text-align: left;
100
  width: 100%;
101
  box-sizing: border-box;
@@ -140,6 +140,42 @@ label.selected {
140
  background: #3b82f6 !important;
141
  color: white !important;
142
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  """
144
 
145
  # 全局变量用于存储公司映射关系
@@ -542,9 +578,48 @@ def update_report_section(selected_company, report_data, stock_code):
542
  if not isinstance(report_data[0], dict):
543
  return gr.update(value="<div>数据格式不正常</div>", visible=True)
544
 
 
 
 
 
545
  html_content = '<div class="report-list-box bg-white">'
546
- html_content += '<div class="report-list-title bg-white card-title left-card-title" style="border-bottom: 1px solid #e3e3e6 !important;"><h3>Financial Reports</h3></div>'
547
- for report in report_data:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  source_url = report.get('source_url', '#')
549
  period = report.get('period', 'N/A')
550
  source_form = report.get('source_form', 'N/A')
@@ -559,7 +634,38 @@ def update_report_section(selected_company, report_data, stock_code):
559
  </div>
560
  '''
561
 
562
- html_content += f'<div class="pdf-footer mt-3"><span class="text-xs text-gray-500">共{len(report_data)}份报告</span></div>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
563
  html_content += '</div>'
564
 
565
  return gr.update(value=html_content, visible=True)
@@ -582,17 +688,53 @@ def update_news_section(selected_company):
582
  # report_data = search_news(selected_company)
583
  if (report_data['articles']):
584
  report_data = report_data['articles']
 
 
 
 
 
585
  news_html = "<div class='news-list-box bg-white'>"
586
- news_html += '<div class="report-list-title bg-white card-title left-card-title" style="border-bottom: 1px solid #e3e3e6 !important;"><h3>News</h3></div>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587
  from datetime import datetime
588
 
589
- for news in report_data:
 
590
  published_at = news['published']
591
-
592
- # 解析 ISO 8601 时间字符串(注意:strptime 不直接支持 'Z',需替换或使用 fromisoformat)
593
  dt = datetime.fromisoformat(published_at.replace("Z", "+00:00"))
594
-
595
- # 格式化为 YYYY.MM.DD
596
  formatted_date = dt.strftime("%Y.%m.%d")
597
  news_html += f'''
598
  <div class="news-item bg-white hover:bg-blue-50 cursor-pointer" onclick="window.open('{news['url']}', '_blank')">
@@ -602,7 +744,37 @@ def update_news_section(selected_company):
602
  </div>
603
  </div>
604
  '''
605
- news_html += f'<div class="pdf-footer mt-3"><span class="text-xs text-gray-500">共{len(report_data)}条新闻</span></div>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
606
  news_html += '</div>'
607
  html_content += news_html
608
  except Exception as e:
@@ -673,13 +845,12 @@ def create_company_list(get_companys_state):
673
 
674
  def create_company_selector():
675
  """创建公司选择器组件"""
 
676
  company_input = gr.Textbox(
677
  show_label=False,
678
- placeholder="company name, ticker, or CIK code",
679
- elem_classes=["company-input-box"],
680
- lines=1,
681
- max_lines=1,
682
- # container=False
683
  )
684
 
685
  # 状态消息显示区域
@@ -829,7 +1000,16 @@ def create_sidebar():
829
  with gr.Column(elem_classes=["sidebar"]):
830
  # 公司选择
831
  with gr.Group(elem_classes=["card"]):
832
- gr.Markdown("### Select Company", elem_classes=["card-title", "left-card-title"])
 
 
 
 
 
 
 
 
 
833
  with gr.Column():
834
  company_list = create_company_list(get_companys_state)
835
 
@@ -845,7 +1025,7 @@ def create_sidebar():
845
  # 创建公司选择器
846
  company_input, status_message, company_modal = create_company_selector()
847
 
848
- # 绑定事件
849
  company_input.submit(
850
  fn=update_company_choices,
851
  inputs=[company_input],
@@ -990,16 +1170,18 @@ def build_income_table(table_data):
990
  table_rows += f"<tr style='{row_style}'>{cells}</tr>"
991
 
992
  html = f"""
993
- <div style="min-width: 400px;max-width: 600px;height: 300px !important;border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-family: 'Segoe UI', sans-serif;">
994
- <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 16px;">
995
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
996
- <path d="M12 2L15.09 8.26L19 9.07L16 14L16 19L12 19L8 14L8 9.07L4.91 8.26L8 2L12 2Z" fill="#0066cc"/>
997
  </svg>
998
- <div style="font-size: 18px; font-weight: 600;">Income Statement and Cash Flow</div>
 
 
 
 
 
999
  </div>
1000
- <table style="width: 100%; border-collapse: collapse; font-size: 14px;">
1001
- {table_rows}
1002
- </table>
1003
  </div>
1004
  """
1005
  return html
@@ -1060,18 +1242,18 @@ def create_metrics_dashboard():
1060
  prev_close_val = company_info.get("previous_close", "N/A")
1061
 
1062
  html = f"""
1063
- <div style="width: 250px; height: 300px !important; border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-family: 'Segoe UI', sans-serif;">
1064
- <div style="font-size: 16px; color: #555; font-weight: 500;">{company_name}</div>
1065
- <div style="font-size: 12px; color: #888;">NYSE:{symbol}</div>
1066
- <div style="display: flex; align-items: center; gap: 10px; margin: 8px 0;">
1067
- <div style="font-size: 32px; font-weight: bold;">{price}</div>
1068
- <div style="font-size: 14px;">{change_html}</div>
1069
  </div>
1070
- <div style="margin-top: 12px; display: grid; grid-template-columns: auto 1fr; gap: 8px;">
1071
- <div style="font-size: 14px; color: #555;">Open</div><div style="font-size: 14px; font-weight: 500; text-align: center;">{open_val}</div>
1072
- <div style="font-size: 14px; color: #555;">High</div><div style="font-size: 14px; font-weight: 500; text-align: center;">{high_val}</div>
1073
- <div style="font-size: 14px; color: #555;">Low</div><div style="font-size: 14px; font-weight: 500; text-align: center;">{low_val}</div>
1074
- <div style="font-size: 14px; color: #555;">Prev Close</div><div style="font-size: 14px; font-weight: 500; text-align: center;">{prev_close_val}</div>
1075
  </div>
1076
  </div>
1077
  """
@@ -1132,15 +1314,15 @@ def create_metrics_dashboard():
1132
  """
1133
 
1134
  html = f"""
1135
- <div style="min-width: 300px;max-width: 450px;height: 300px !important;border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-family: 'Segoe UI', sans-serif;">
1136
- <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 16px;justify-content: space-between;">
1137
- <div style="font-size: 18px; font-weight: 600;display: flex;align-items: center;">
1138
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1139
- <path d="M12 2L15.09 8.26L19 9.07L16 14L16 19L12 19L8 14L8 9.07L4.91 8.26L8 2L12 2Z" fill="#0066cc"/>
1140
  </svg>
1141
  <span style="margin-left: 10px;">{default_yearly_data} Financial Metrics</span>
1142
  </div>
1143
- <div style="font-size: 16px; color: #8f8f8f;">
1144
  YTD data
1145
  </div>
1146
  </div>
@@ -1297,15 +1479,15 @@ def update_metrics_dashboard(company_name):
1297
  """
1298
 
1299
  html = f"""
1300
- <div style="width: 450px;height: 300px !important;border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-family: 'Segoe UI', sans-serif;">
1301
- <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 16px;justify-content: space-between;">
1302
- <div style="font-size: 18px; font-weight: 600;display: flex;align-items: center;">
1303
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1304
- <path d="M12 2L15.09 8.26L19 9.07L16 14L16 19L12 19L8 14L8 9.07L4.91 8.26L8 2L12 2Z" fill="#0066cc"/>
1305
  </svg>
1306
  <span style="margin-left: 10px;">{yearly_data} Financial Metrics</span>
1307
- </div>
1308
- <div style="font-size: 16px; color: #8f8f8f;">
1309
  YTD data
1310
  </div>
1311
  </div>
@@ -1640,15 +1822,19 @@ def main():
1640
  # create_tab_content("comparison")
1641
  with gr.Column(scale=2, min_width=400):
1642
  # 聊天面板
1643
-
1644
- # chatbot.render()
1645
- # gr.LoginButton()
1646
  # ✅ 使用 chat_direct.chatbot_response 替代 chatbot.chat_main.respond
1647
  # 这样可以直接调用MCP函数,而不需要HTTP服务器
1648
  gr.ChatInterface(
1649
- chatbot_response, # 使用直接调用的chatbot_response
1650
- title="Easy Financial AI Assistant",
1651
- # chat_direct.chatbot_response 不需要 additional_inputs
 
 
 
 
 
 
 
1652
  )
1653
 
1654
  # 在页面加载时设置默认选中的公司并加载数据
 
85
  display: none;
86
  }
87
 
88
+ /* 自定义选项样式 - 小而精致 */
89
  .company-list-container label {
90
  display: block;
91
+ padding: 0.5rem 0.75rem;
92
+ margin: 0.2rem 0;
93
+ border-radius: 0.25rem;
94
  cursor: pointer;
95
  transition: all 0.2s ease;
96
  background-color: #f9fafb;
97
  border: 1px solid #e5e7eb;
98
+ font-size: 0.875rem;
99
  text-align: left;
100
  width: 100%;
101
  box-sizing: border-box;
 
140
  background: #3b82f6 !important;
141
  color: white !important;
142
  }
143
+
144
+ /* ✅ 搜索框样式 - 带内置图标 */
145
+ .company-input-search {
146
+ position: relative;
147
+ }
148
+
149
+ .company-input-search input {
150
+ padding-left: 36px !important;
151
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="%23999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path></svg>') !important;
152
+ background-repeat: no-repeat !important;
153
+ background-position: 12px center !important;
154
+ border: 1px solid #e5e7eb !important;
155
+ border-radius: 8px !important;
156
+ font-size: 14px !important;
157
+ transition: all 0.2s !important;
158
+ }
159
+
160
+ .company-input-search input:focus {
161
+ border-color: #667eea !important;
162
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
163
+ outline: none !important;
164
+ }
165
+
166
+ /* ✅ 对齐Tabs和ChatInterface的横线 */
167
+ .tabs {
168
+ border-bottom: 1px solid #e5e7eb !important;
169
+ }
170
+
171
+ .chatbot {
172
+ border-top: 1px solid #e5e7eb !important;
173
+ }
174
+
175
+ /* 确保tab内容和chatbot的padding一致 */
176
+ .tab-item {
177
+ padding: 16px !important;
178
+ }
179
  """
180
 
181
  # 全局变量用于存储公司映射关系
 
578
  if not isinstance(report_data[0], dict):
579
  return gr.update(value="<div>数据格式不正常</div>", visible=True)
580
 
581
+ # ✅ 可折叠的Financial Reports,默认显示5个
582
+ total_reports = len(report_data)
583
+ show_limit = 5
584
+
585
  html_content = '<div class="report-list-box bg-white">'
586
+ # 美化Financial Reports标题
587
+ html_content += '''<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
588
+ padding: 12px;
589
+ border-radius: 8px;
590
+ text-align: center;
591
+ margin-bottom: 12px;">
592
+ <h3 style="color: white; margin: 0; font-size: 16px; font-weight: 600;">Financial Reports</h3>
593
+ </div>'''
594
+
595
+ # 添加CSS样式
596
+ html_content += '''<style>
597
+ .report-toggle-btn {
598
+ background: #f3f4f6;
599
+ border: 1px solid #e5e7eb;
600
+ border-radius: 4px;
601
+ padding: 6px 12px;
602
+ cursor: pointer;
603
+ font-size: 0.875rem;
604
+ color: #374151;
605
+ text-align: center;
606
+ margin: 8px 0;
607
+ width: 100%;
608
+ transition: all 0.2s;
609
+ }
610
+ .report-toggle-btn:hover {
611
+ background: #e5e7eb;
612
+ }
613
+ .report-extra {
614
+ display: none;
615
+ }
616
+ .report-extra.show {
617
+ display: block;
618
+ }
619
+ </style>'''
620
+
621
+ # 显示前5个
622
+ for i, report in enumerate(report_data[:show_limit]):
623
  source_url = report.get('source_url', '#')
624
  period = report.get('period', 'N/A')
625
  source_form = report.get('source_form', 'N/A')
 
634
  </div>
635
  '''
636
 
637
+ # 剩余的放在可折叠区域
638
+ if total_reports > show_limit:
639
+ html_content += '<div class="report-extra" id="reportExtra">'
640
+ for i, report in enumerate(report_data[show_limit:]):
641
+ source_url = report.get('source_url', '#')
642
+ period = report.get('period', 'N/A')
643
+ source_form = report.get('source_form', 'N/A')
644
+ html_content += f'''
645
+ <div class="report-item bg-white hover:bg-blue-50 cursor-pointer" onclick="window.open('{source_url}', '_blank')">
646
+ <div class="report-item-content">
647
+ <span class="text-gray-800">{period}-{stock_code}-{source_form}</span>
648
+ <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" class="text-blue-500" viewBox="0 0 20 20" fill="currentColor">
649
+ <path fill-rule="evenodd" d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 10-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l-1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z" clip-rule="evenodd" />
650
+ </svg>
651
+ </div>
652
+ </div>
653
+ '''
654
+ html_content += '</div>'
655
+
656
+ # 添加展开/收起按钮
657
+ html_content += f'''<div class="report-toggle-btn" onclick="
658
+ var extra = document.getElementById('reportExtra');
659
+ if (extra.classList.contains('show')) {{
660
+ extra.classList.remove('show');
661
+ this.innerHTML = '↓ Show All ({total_reports} reports)';
662
+ }} else {{
663
+ extra.classList.add('show');
664
+ this.innerHTML = '↑ Show Less';
665
+ }}
666
+ ">↓ Show All ({total_reports} reports)</div>'''
667
+
668
+ html_content += f'<div class="pdf-footer mt-3"><span class="text-xs text-gray-500">共{total_reports}份报告</span></div>'
669
  html_content += '</div>'
670
 
671
  return gr.update(value=html_content, visible=True)
 
688
  # report_data = search_news(selected_company)
689
  if (report_data['articles']):
690
  report_data = report_data['articles']
691
+
692
+ # ✅ 可折叠的News,默认显示5个
693
+ total_news = len(report_data)
694
+ show_limit = 5
695
+
696
  news_html = "<div class='news-list-box bg-white'>"
697
+ # 美化News标题
698
+ news_html += '''<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
699
+ padding: 12px;
700
+ border-radius: 8px;
701
+ text-align: center;
702
+ margin-bottom: 12px;">
703
+ <h3 style="color: white; margin: 0; font-size: 16px; font-weight: 600;">News</h3>
704
+ </div>'''
705
+
706
+ # 添加CSS样式
707
+ news_html += '''<style>
708
+ .news-toggle-btn {
709
+ background: #f3f4f6;
710
+ border: 1px solid #e5e7eb;
711
+ border-radius: 4px;
712
+ padding: 6px 12px;
713
+ cursor: pointer;
714
+ font-size: 0.875rem;
715
+ color: #374151;
716
+ text-align: center;
717
+ margin: 8px 0;
718
+ width: 100%;
719
+ transition: all 0.2s;
720
+ }
721
+ .news-toggle-btn:hover {
722
+ background: #e5e7eb;
723
+ }
724
+ .news-extra {
725
+ display: none;
726
+ }
727
+ .news-extra.show {
728
+ display: block;
729
+ }
730
+ </style>'''
731
+
732
  from datetime import datetime
733
 
734
+ # 显示前5个
735
+ for news in report_data[:show_limit]:
736
  published_at = news['published']
 
 
737
  dt = datetime.fromisoformat(published_at.replace("Z", "+00:00"))
 
 
738
  formatted_date = dt.strftime("%Y.%m.%d")
739
  news_html += f'''
740
  <div class="news-item bg-white hover:bg-blue-50 cursor-pointer" onclick="window.open('{news['url']}', '_blank')">
 
744
  </div>
745
  </div>
746
  '''
747
+
748
+ # 剩余的放在可折叠区域
749
+ if total_news > show_limit:
750
+ news_html += '<div class="news-extra" id="newsExtra">'
751
+ for news in report_data[show_limit:]:
752
+ published_at = news['published']
753
+ dt = datetime.fromisoformat(published_at.replace("Z", "+00:00"))
754
+ formatted_date = dt.strftime("%Y.%m.%d")
755
+ news_html += f'''
756
+ <div class="news-item bg-white hover:bg-blue-50 cursor-pointer" onclick="window.open('{news['url']}', '_blank')">
757
+ <div class="news-item-content">
758
+ <span class="text-xs text-gray-500">[{formatted_date}]</span>
759
+ <span class="text-gray-800">{news['headline']}</span>
760
+ </div>
761
+ </div>
762
+ '''
763
+ news_html += '</div>'
764
+
765
+ # 添加展开/收起按钮
766
+ news_html += f'''<div class="news-toggle-btn" onclick="
767
+ var extra = document.getElementById('newsExtra');
768
+ if (extra.classList.contains('show')) {{
769
+ extra.classList.remove('show');
770
+ this.innerHTML = '↓ Show All ({total_news} news)';
771
+ }} else {{
772
+ extra.classList.add('show');
773
+ this.innerHTML = '↑ Show Less';
774
+ }}
775
+ ">↓ Show All ({total_news} news)</div>'''
776
+
777
+ news_html += f'<div class="pdf-footer mt-3"><span class="text-xs text-gray-500">共{total_news}条新闻</span></div>'
778
  news_html += '</div>'
779
  html_content += news_html
780
  except Exception as e:
 
845
 
846
  def create_company_selector():
847
  """创建公司选择器组件"""
848
+ # ✅ 使用HTML和CSS创建带内置图标的搜索框
849
  company_input = gr.Textbox(
850
  show_label=False,
851
+ placeholder=" Name, ticker, or CIK", # 留出空间给图标
852
+ elem_classes=["company-input-search"],
853
+ container=False
 
 
854
  )
855
 
856
  # 状态消息显示区域
 
1000
  with gr.Column(elem_classes=["sidebar"]):
1001
  # 公司选择
1002
  with gr.Group(elem_classes=["card"]):
1003
+ # 美化标题:居中对齐,添加背景色
1004
+ gr.HTML("""
1005
+ <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1006
+ padding: 12px;
1007
+ border-radius: 8px;
1008
+ text-align: center;
1009
+ margin-bottom: 16px;">
1010
+ <h3 style="color: white; margin: 0; font-size: 16px; font-weight: 600;">Select Company</h3>
1011
+ </div>
1012
+ """)
1013
  with gr.Column():
1014
  company_list = create_company_list(get_companys_state)
1015
 
 
1025
  # 创建公司选择器
1026
  company_input, status_message, company_modal = create_company_selector()
1027
 
1028
+ # 绑定事件 - 只需要submit事件
1029
  company_input.submit(
1030
  fn=update_company_choices,
1031
  inputs=[company_input],
 
1170
  table_rows += f"<tr style='{row_style}'>{cells}</tr>"
1171
 
1172
  html = f"""
1173
+ <div style="min-width: 400px;max-width: 600px;height: 300px !important;border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px; box-shadow: 0 4px 6px rgba(0,0,0,0.08); font-family: 'Segoe UI', sans-serif; background: linear-gradient(135deg, #ffffff 0%, #f9fafb 100%); overflow: hidden;">
1174
+ <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 18px; padding-bottom: 12px; border-bottom: 2px solid #e5e7eb;">
1175
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1176
+ <path d="M12 2L15.09 8.26L19 9.07L16 14L16 19L12 19L8 14L8 9.07L4.91 8.26L8 2L12 2Z" fill="#3b82f6"/>
1177
  </svg>
1178
+ <div style="font-size: 19px; font-weight: 600; color: #1f2937;">Income Statement and Cash Flow</div>
1179
+ </div>
1180
+ <div style="overflow-y: hidden; max-height: 220px;">
1181
+ <table style="width: 100%; border-collapse: collapse; font-size: 14px;">
1182
+ {table_rows}
1183
+ </table>
1184
  </div>
 
 
 
1185
  </div>
1186
  """
1187
  return html
 
1242
  prev_close_val = company_info.get("previous_close", "N/A")
1243
 
1244
  html = f"""
1245
+ <div style="width: 250px; height: 300px !important; border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px; box-shadow: 0 4px 6px rgba(0,0,0,0.08); font-family: 'Segoe UI', sans-serif; background: linear-gradient(135deg, #ffffff 0%, #f9fafb 100%);">
1246
+ <div style="font-size: 17px; color: #374151; font-weight: 600; margin-bottom: 4px;">{company_name}</div>
1247
+ <div style="font-size: 13px; color: #6b7280; margin-bottom: 12px;">NYSE:{symbol}</div>
1248
+ <div style="display: flex; align-items: center; gap: 10px; margin: 12px 0; padding: 12px; background: #f9fafb; border-radius: 8px;">
1249
+ <div style="font-size: 34px; font-weight: bold; color: #111827;">{price}</div>
1250
+ <div style="font-size: 15px;">{change_html}</div>
1251
  </div>
1252
+ <div style="margin-top: 16px; display: grid; grid-template-columns: auto 1fr; gap: 10px; padding: 12px; background: white; border-radius: 8px; border: 1px solid #e5e7eb;">
1253
+ <div style="font-size: 14px; color: #6b7280;">Open</div><div style="font-size: 14px; font-weight: 600; text-align: right; color: #111827;">{open_val}</div>
1254
+ <div style="font-size: 14px; color: #6b7280;">High</div><div style="font-size: 14px; font-weight: 600; text-align: right; color: #111827;">{high_val}</div>
1255
+ <div style="font-size: 14px; color: #6b7280;">Low</div><div style="font-size: 14px; font-weight: 600; text-align: right; color: #111827;">{low_val}</div>
1256
+ <div style="font-size: 14px; color: #6b7280;">Prev Close</div><div style="font-size: 14px; font-weight: 600; text-align: right; color: #111827;">{prev_close_val}</div>
1257
  </div>
1258
  </div>
1259
  """
 
1314
  """
1315
 
1316
  html = f"""
1317
+ <div style="min-width: 300px;max-width: 450px;height: 300px !important;border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px; box-shadow: 0 4px 6px rgba(0,0,0,0.08); font-family: 'Segoe UI', sans-serif; background: linear-gradient(135deg, #ffffff 0%, #f9fafb 100%);">
1318
+ <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 18px; padding-bottom: 12px; border-bottom: 2px solid #e5e7eb; justify-content: space-between;">
1319
+ <div style="font-size: 19px; font-weight: 600; color: #1f2937; display: flex; align-items: center;">
1320
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1321
+ <path d="M12 2L15.09 8.26L19 9.07L16 14L16 19L12 19L8 14L8 9.07L4.91 8.26L8 2L12 2Z" fill="#3b82f6"/>
1322
  </svg>
1323
  <span style="margin-left: 10px;">{default_yearly_data} Financial Metrics</span>
1324
  </div>
1325
+ <div style="font-size: 14px; color: #6b7280;">
1326
  YTD data
1327
  </div>
1328
  </div>
 
1479
  """
1480
 
1481
  html = f"""
1482
+ <div style="width: 450px;height: 300px !important;border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px; box-shadow: 0 4px 6px rgba(0,0,0,0.08); font-family: 'Segoe UI', sans-serif; background: linear-gradient(135deg, #ffffff 0%, #f9fafb 100%);">
1483
+ <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 18px; padding-bottom: 12px; border-bottom: 2px solid #e5e7eb; justify-content: space-between;">
1484
+ <div style="font-size: 19px; font-weight: 600; color: #1f2937; display: flex; align-items: center;">
1485
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1486
+ <path d="M12 2L15.09 8.26L19 9.07L16 14L16 19L12 19L8 14L8 9.07L4.91 8.26L8 2L12 2Z" fill="#3b82f6"/>
1487
  </svg>
1488
  <span style="margin-left: 10px;">{yearly_data} Financial Metrics</span>
1489
+ </div>
1490
+ <div style="font-size: 14px; color: #6b7280;">
1491
  YTD data
1492
  </div>
1493
  </div>
 
1822
  # create_tab_content("comparison")
1823
  with gr.Column(scale=2, min_width=400):
1824
  # 聊天面板
 
 
 
1825
  # ✅ 使用 chat_direct.chatbot_response 替代 chatbot.chat_main.respond
1826
  # 这样可以直接调用MCP函数,而不需要HTTP服务器
1827
  gr.ChatInterface(
1828
+ chatbot_response,
1829
+ type="messages", # 使用messages格式
1830
+ title="Easy Financial AI Assistant", # 保留标题
1831
+ chatbot=gr.Chatbot(height=550), # ✅ 增加聊天框高度
1832
+ textbox=gr.Textbox(
1833
+ placeholder="Ask a financial question...",
1834
+ container=False,
1835
+ scale=7
1836
+ ),
1837
+ submit_btn="📤 Send" # ✅ 添加发送按钮
1838
  )
1839
 
1840
  # 在页面加载时设置默认选中的公司并加载数据