Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
app.py
CHANGED
|
@@ -13,13 +13,19 @@ HEADERS = {
|
|
| 13 |
}
|
| 14 |
|
| 15 |
# 格式化数值显示
|
| 16 |
-
def format_value(value,
|
| 17 |
-
"""
|
|
|
|
|
|
|
|
|
|
| 18 |
if value is None or value == 0:
|
| 19 |
return "N/A"
|
| 20 |
-
|
|
|
|
| 21 |
return f"${value:.2f}B"
|
| 22 |
-
|
|
|
|
|
|
|
| 23 |
return f"{value:.2f}"
|
| 24 |
|
| 25 |
# MCP 工具定义
|
|
@@ -100,11 +106,11 @@ def query_financial_data(company_name, query_type):
|
|
| 100 |
return f"❌ JSON Parse Error: {str(e)}\n\nResponse: {search_resp.text[:500]}"
|
| 101 |
|
| 102 |
if isinstance(company, dict) and company.get("error"):
|
| 103 |
-
return f"❌
|
| 104 |
|
| 105 |
result = f"# {company.get('name', 'Unknown')}\n\n"
|
| 106 |
-
result += f"
|
| 107 |
-
result += f"
|
| 108 |
|
| 109 |
cik = company.get('cik')
|
| 110 |
|
|
@@ -139,7 +145,7 @@ def query_financial_data(company_name, query_type):
|
|
| 139 |
|
| 140 |
result += f"- **Total Revenue**: {format_value(total_revenue)}\n"
|
| 141 |
result += f"- **Net Income**: {format_value(net_income)}\n"
|
| 142 |
-
result += f"- **Earnings Per Share**: {format_value(eps,
|
| 143 |
result += f"- **Operating Expenses**: {format_value(opex)}\n"
|
| 144 |
result += f"- **Operating Cash Flow**: {format_value(ocf)}\n"
|
| 145 |
# 使用后端返回的 source_url
|
|
@@ -182,16 +188,17 @@ def query_financial_data(company_name, query_type):
|
|
| 182 |
|
| 183 |
# 按期间降序排序,确保显示最近的3年数据
|
| 184 |
# 使用更智能的排序:先按年份,再按是否是季度
|
|
|
|
| 185 |
def sort_key(x):
|
| 186 |
period = x.get('period', '0000')
|
| 187 |
# 提取年份(前4位)
|
| 188 |
year = period[:4] if len(period) >= 4 else '0000'
|
| 189 |
-
# 如果有Q
|
| 190 |
if 'Q' in period:
|
| 191 |
quarter = period[period.index('Q')+1] if period.index('Q')+1 < len(period) else '0'
|
| 192 |
-
return (year,
|
| 193 |
else:
|
| 194 |
-
return (year,
|
| 195 |
|
| 196 |
unique_data = sorted(unique_data, key=sort_key, reverse=True)
|
| 197 |
|
|
@@ -218,7 +225,7 @@ def query_financial_data(company_name, query_type):
|
|
| 218 |
|
| 219 |
source_link = create_source_link(source_form, source_url)
|
| 220 |
|
| 221 |
-
result += f"| {display_period} | {format_value(rev)} | {format_value(inc)} | {format_value(eps_val,
|
| 222 |
|
| 223 |
elif internal_query_type == "5年趋势":
|
| 224 |
metrics_resp = requests.post(
|
|
@@ -253,16 +260,17 @@ def query_financial_data(company_name, query_type):
|
|
| 253 |
|
| 254 |
# 按期间降序排序,确保显示最近的5年数据
|
| 255 |
# 使用更智能的排序:先按年份,再按是否是季度
|
|
|
|
| 256 |
def sort_key(x):
|
| 257 |
period = x.get('period', '0000')
|
| 258 |
# 提取年份(前4位)
|
| 259 |
year = period[:4] if len(period) >= 4 else '0000'
|
| 260 |
-
# 如果有Q
|
| 261 |
if 'Q' in period:
|
| 262 |
quarter = period[period.index('Q')+1] if period.index('Q')+1 < len(period) else '0'
|
| 263 |
-
return (year,
|
| 264 |
else:
|
| 265 |
-
return (year,
|
| 266 |
|
| 267 |
unique_data = sorted(unique_data, key=sort_key, reverse=True)
|
| 268 |
|
|
@@ -290,7 +298,7 @@ def query_financial_data(company_name, query_type):
|
|
| 290 |
|
| 291 |
source_link = create_source_link(source_form, source_url)
|
| 292 |
|
| 293 |
-
result += f"| {display_period} | {format_value(rev)} | {format_value(inc)} | {format_value(eps_val,
|
| 294 |
|
| 295 |
elif internal_query_type == "公司报表列表":
|
| 296 |
# 查询公司所有报表
|
|
@@ -388,6 +396,23 @@ def chatbot_response(message, history):
|
|
| 388 |
except Exception as e:
|
| 389 |
return f"Sorry, I encountered an error: {str(e)}. Please try asking about financial data for specific companies like Apple, Microsoft, NVIDIA, Tesla, etc."
|
| 390 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 391 |
# 创建 Gradio 界面
|
| 392 |
with gr.Blocks(title="SEC Financial Data Query Assistant") as demo:
|
| 393 |
gr.Markdown("# 🤖 SEC Financial Data Query Assistant")
|
|
@@ -412,6 +437,7 @@ with gr.Blocks(title="SEC Financial Data Query Assistant") as demo:
|
|
| 412 |
|
| 413 |
with gr.Tab("Direct Query"):
|
| 414 |
gr.Markdown("## 🔍 Direct Financial Data Query")
|
|
|
|
| 415 |
|
| 416 |
with gr.Row():
|
| 417 |
company_input = gr.Textbox(
|
|
@@ -426,7 +452,12 @@ with gr.Blocks(title="SEC Financial Data Query Assistant") as demo:
|
|
| 426 |
scale=1
|
| 427 |
)
|
| 428 |
|
| 429 |
-
submit_btn = gr.Button("🔍 Query", variant="primary", size="lg")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
output = gr.Markdown(label="Query Results")
|
| 431 |
|
| 432 |
# 示例
|
|
@@ -445,9 +476,10 @@ with gr.Blocks(title="SEC Financial Data Query Assistant") as demo:
|
|
| 445 |
)
|
| 446 |
|
| 447 |
submit_btn.click(
|
| 448 |
-
fn=
|
| 449 |
inputs=[company_input, query_type],
|
| 450 |
-
outputs=output
|
|
|
|
| 451 |
)
|
| 452 |
|
| 453 |
gr.Markdown("---")
|
|
|
|
| 13 |
}
|
| 14 |
|
| 15 |
# 格式化数值显示
|
| 16 |
+
def format_value(value, value_type="money"):
|
| 17 |
+
"""
|
| 18 |
+
格式化数值:0显示为N/A,其他显示为带单位的格式
|
| 19 |
+
value_type: "money" (金额), "eps" (每股收益), "number" (普通数字)
|
| 20 |
+
"""
|
| 21 |
if value is None or value == 0:
|
| 22 |
return "N/A"
|
| 23 |
+
|
| 24 |
+
if value_type == "money":
|
| 25 |
return f"${value:.2f}B"
|
| 26 |
+
elif value_type == "eps":
|
| 27 |
+
return f"${value:.2f}"
|
| 28 |
+
else: # number
|
| 29 |
return f"{value:.2f}"
|
| 30 |
|
| 31 |
# MCP 工具定义
|
|
|
|
| 106 |
return f"❌ JSON Parse Error: {str(e)}\n\nResponse: {search_resp.text[:500]}"
|
| 107 |
|
| 108 |
if isinstance(company, dict) and company.get("error"):
|
| 109 |
+
return f"❌ Error: {company['error']}"
|
| 110 |
|
| 111 |
result = f"# {company.get('name', 'Unknown')}\n\n"
|
| 112 |
+
result += f"**Stock Symbol**: {company.get('tickers', ['N/A'])[0] if company.get('tickers') else 'N/A'}\n"
|
| 113 |
+
result += f"**Industry**: {company.get('sic_description', 'N/A')}\n\n---\n\n"
|
| 114 |
|
| 115 |
cik = company.get('cik')
|
| 116 |
|
|
|
|
| 145 |
|
| 146 |
result += f"- **Total Revenue**: {format_value(total_revenue)}\n"
|
| 147 |
result += f"- **Net Income**: {format_value(net_income)}\n"
|
| 148 |
+
result += f"- **Earnings Per Share**: {format_value(eps, 'eps')}\n"
|
| 149 |
result += f"- **Operating Expenses**: {format_value(opex)}\n"
|
| 150 |
result += f"- **Operating Cash Flow**: {format_value(ocf)}\n"
|
| 151 |
# 使用后端返回的 source_url
|
|
|
|
| 188 |
|
| 189 |
# 按期间降序排序,确保显示最近的3年数据
|
| 190 |
# 使用更智能的排序:先按年份,再按是否是季度
|
| 191 |
+
# 正确顺序:FY2024 → 2024Q3 → 2024Q2 → 2024Q1 → FY2023
|
| 192 |
def sort_key(x):
|
| 193 |
period = x.get('period', '0000')
|
| 194 |
# 提取年份(前4位)
|
| 195 |
year = period[:4] if len(period) >= 4 else '0000'
|
| 196 |
+
# 如果有Q,提取季度号
|
| 197 |
if 'Q' in period:
|
| 198 |
quarter = period[period.index('Q')+1] if period.index('Q')+1 < len(period) else '0'
|
| 199 |
+
return (year, 1, 4 - int(quarter)) # Q在FY后面:Q3, Q2, Q1 (4-3=1, 4-2=2, 4-1=3)
|
| 200 |
else:
|
| 201 |
+
return (year, 0, 0) # FY 排在同年的所有Q之前
|
| 202 |
|
| 203 |
unique_data = sorted(unique_data, key=sort_key, reverse=True)
|
| 204 |
|
|
|
|
| 225 |
|
| 226 |
source_link = create_source_link(source_form, source_url)
|
| 227 |
|
| 228 |
+
result += f"| {display_period} | {format_value(rev)} | {format_value(inc)} | {format_value(eps_val, 'eps')} | {format_value(opex)} | {format_value(ocf)} | {source_link} |\n"
|
| 229 |
|
| 230 |
elif internal_query_type == "5年趋势":
|
| 231 |
metrics_resp = requests.post(
|
|
|
|
| 260 |
|
| 261 |
# 按期间降序排序,确保显示最近的5年数据
|
| 262 |
# 使用更智能的排序:先按年份,再按是否是季度
|
| 263 |
+
# 正确顺序:FY2024 → 2024Q3 → 2024Q2 → 2024Q1 → FY2023
|
| 264 |
def sort_key(x):
|
| 265 |
period = x.get('period', '0000')
|
| 266 |
# 提取年份(前4位)
|
| 267 |
year = period[:4] if len(period) >= 4 else '0000'
|
| 268 |
+
# 如果有Q,提取季度号
|
| 269 |
if 'Q' in period:
|
| 270 |
quarter = period[period.index('Q')+1] if period.index('Q')+1 < len(period) else '0'
|
| 271 |
+
return (year, 1, 4 - int(quarter)) # Q在FY后面:Q3, Q2, Q1 (4-3=1, 4-2=2, 4-1=3)
|
| 272 |
else:
|
| 273 |
+
return (year, 0, 0) # FY 排在同年的所有Q之前
|
| 274 |
|
| 275 |
unique_data = sorted(unique_data, key=sort_key, reverse=True)
|
| 276 |
|
|
|
|
| 298 |
|
| 299 |
source_link = create_source_link(source_form, source_url)
|
| 300 |
|
| 301 |
+
result += f"| {display_period} | {format_value(rev)} | {format_value(inc)} | {format_value(eps_val, 'eps')} | {format_value(opex)} | {format_value(ocf)} | {source_link} |\n"
|
| 302 |
|
| 303 |
elif internal_query_type == "公司报表列表":
|
| 304 |
# 查询公司所有报表
|
|
|
|
| 396 |
except Exception as e:
|
| 397 |
return f"Sorry, I encountered an error: {str(e)}. Please try asking about financial data for specific companies like Apple, Microsoft, NVIDIA, Tesla, etc."
|
| 398 |
|
| 399 |
+
# 包装函数,显示加载状态
|
| 400 |
+
def query_with_status(company, query_type):
|
| 401 |
+
"""Query with loading status indicator"""
|
| 402 |
+
try:
|
| 403 |
+
# 返回加载状态和结果
|
| 404 |
+
yield "<div style='padding: 10px; background: #e3f2fd; border-left: 4px solid #2196f3; margin: 10px 0;'>🔄 <strong>Loading...</strong> Querying SEC EDGAR data for <strong>{}</strong>...</div>".format(company), ""
|
| 405 |
+
|
| 406 |
+
# 执行实际查询
|
| 407 |
+
result = query_financial_data(company, query_type)
|
| 408 |
+
|
| 409 |
+
# 返回成功状态和结果
|
| 410 |
+
yield "<div style='padding: 10px; background: #e8f5e9; border-left: 4px solid #4caf50; margin: 10px 0;'>✅ <strong>Query completed successfully!</strong></div>", result
|
| 411 |
+
|
| 412 |
+
except Exception as e:
|
| 413 |
+
# 返回错误状态
|
| 414 |
+
yield "<div style='padding: 10px; background: #ffebee; border-left: 4px solid #f44336; margin: 10px 0;'>❌ <strong>Error:</strong> {}</div>".format(str(e)), ""
|
| 415 |
+
|
| 416 |
# 创建 Gradio 界面
|
| 417 |
with gr.Blocks(title="SEC Financial Data Query Assistant") as demo:
|
| 418 |
gr.Markdown("# 🤖 SEC Financial Data Query Assistant")
|
|
|
|
| 437 |
|
| 438 |
with gr.Tab("Direct Query"):
|
| 439 |
gr.Markdown("## 🔍 Direct Financial Data Query")
|
| 440 |
+
gr.Markdown("Select a company and query type to retrieve financial information.")
|
| 441 |
|
| 442 |
with gr.Row():
|
| 443 |
company_input = gr.Textbox(
|
|
|
|
| 452 |
scale=1
|
| 453 |
)
|
| 454 |
|
| 455 |
+
submit_btn = gr.Button("🔍 Query Financial Data", variant="primary", size="lg")
|
| 456 |
+
|
| 457 |
+
# 添加加载状态指示器
|
| 458 |
+
with gr.Row():
|
| 459 |
+
status_text = gr.Markdown("")
|
| 460 |
+
|
| 461 |
output = gr.Markdown(label="Query Results")
|
| 462 |
|
| 463 |
# 示例
|
|
|
|
| 476 |
)
|
| 477 |
|
| 478 |
submit_btn.click(
|
| 479 |
+
fn=query_with_status,
|
| 480 |
inputs=[company_input, query_type],
|
| 481 |
+
outputs=[status_text, output],
|
| 482 |
+
show_progress="full" # 显示完整的进度条
|
| 483 |
)
|
| 484 |
|
| 485 |
gr.Markdown("---")
|