Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import requests | |
| import json | |
| import os | |
| from huggingface_hub import InferenceClient | |
| # ========== 配置两个 MCP 服务 ========== | |
| MCP_SERVICES = { | |
| "financial": { | |
| "name": "SEC Financial Reports", | |
| "url": "https://jc321-easyreportdatamcp.hf.space/mcp", | |
| "tools": [ | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "advanced_search_company", | |
| "description": "Search for US listed companies by name or ticker to get CIK and basic info", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "company_input": {"type": "string", "description": "Company name or ticker (e.g., 'Apple', 'AAPL')"} | |
| }, | |
| "required": ["company_input"] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "get_latest_financial_data", | |
| "description": "Get latest financial data (revenue, net income, EPS, etc.) for a company", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "cik": {"type": "string", "description": "10-digit CIK number"} | |
| }, | |
| "required": ["cik"] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "extract_financial_metrics", | |
| "description": "Get multi-year financial trends (3 or 5 years)", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "cik": {"type": "string", "description": "10-digit CIK number"}, | |
| "years": {"type": "integer", "enum": [3, 5]} | |
| }, | |
| "required": ["cik", "years"] | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| "market": { | |
| "name": "Market & Stock Data", | |
| "url": "https://jc321-marketandstockmcp.hf.space/mcp", | |
| "tools": [] # 需要获取实际工具列表 | |
| } | |
| } | |
| # 合并所有工具 | |
| ALL_TOOLS = [] | |
| TOOL_ROUTING = {} # tool_name -> mcp_url 的映射 | |
| for service_key, service in MCP_SERVICES.items(): | |
| for tool in service["tools"]: | |
| tool_name = tool["function"]["name"] | |
| ALL_TOOLS.append(tool) | |
| TOOL_ROUTING[tool_name] = service["url"] | |
| # ========== 初始化 LLM 客户端 ========== | |
| hf_token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN") | |
| client = InferenceClient(api_key=hf_token) if hf_token else InferenceClient() | |
| print(f"✅ LLM client initialized (Qwen/Qwen2.5-72B-Instruct:novita)") | |
| # ========== 系统提示词 ========== | |
| SYSTEM_PROMPT = """You are an intelligent financial and market analysis assistant with access to real-time data. | |
| You have access to two powerful data sources: | |
| 1. **SEC Financial Reports** - Get official financial data for US-listed companies (revenue, earnings, cash flow, etc.) | |
| 2. **Market & Stock Data** - Get real-time market data, stock prices, and market analysis | |
| When users ask about: | |
| - Company financials, earnings, revenue → Use SEC Financial Reports tools | |
| - Stock prices, market trends, trading data → Use Market & Stock Data tools | |
| Be conversational, insightful, and provide data-driven analysis. Automatically fetch data when needed.""" | |
| # ========== 核心函数:调用 MCP 工具 ========== | |
| def call_mcp_tool(tool_name, arguments): | |
| """调用 MCP 工具""" | |
| mcp_url = TOOL_ROUTING.get(tool_name) | |
| if not mcp_url: | |
| return {"error": f"Unknown tool: {tool_name}"} | |
| try: | |
| response = requests.post( | |
| mcp_url, | |
| json={ | |
| "jsonrpc": "2.0", | |
| "method": "tools/call", | |
| "params": {"name": tool_name, "arguments": arguments}, | |
| "id": 1 | |
| }, | |
| headers={"Content-Type": "application/json"}, | |
| timeout=60 | |
| ) | |
| if response.status_code == 200: | |
| return response.json() | |
| else: | |
| return {"error": f"HTTP {response.status_code}", "detail": response.text[:200]} | |
| except Exception as e: | |
| return {"error": str(e)} | |
| # ========== 核心函数:AI 助手 ========== | |
| def chatbot_response(message, history): | |
| """AI 助手主函数""" | |
| try: | |
| # 构建消息历史 | |
| messages = [{"role": "system", "content": SYSTEM_PROMPT}] | |
| # 添加对话历史(最近5轮) | |
| if history: | |
| for item in history[-5:]: | |
| if isinstance(item, dict): | |
| messages.append(item) | |
| elif isinstance(item, (list, tuple)) and len(item) == 2: | |
| user_msg, assistant_msg = item | |
| messages.append({"role": "user", "content": user_msg}) | |
| messages.append({"role": "assistant", "content": assistant_msg}) | |
| messages.append({"role": "user", "content": message}) | |
| # LLM 调用循环(支持多轮工具调用) | |
| tool_calls_log = [] | |
| max_iterations = 5 | |
| for iteration in range(max_iterations): | |
| # 调用 LLM | |
| response = client.chat_completion( | |
| messages=messages, | |
| model="Qwen/Qwen2.5-72B-Instruct:novita", | |
| tools=ALL_TOOLS, | |
| max_tokens=3000, | |
| temperature=0.7, | |
| tool_choice="auto" | |
| ) | |
| choice = response.choices[0] | |
| # 检查是否有工具调用 | |
| if choice.message.tool_calls: | |
| messages.append(choice.message) | |
| for tool_call in choice.message.tool_calls: | |
| tool_name = tool_call.function.name | |
| tool_args = json.loads(tool_call.function.arguments) | |
| # 记录工具调用 | |
| tool_calls_log.append({"name": tool_name, "arguments": tool_args}) | |
| # 调用 MCP 工具 | |
| tool_result = call_mcp_tool(tool_name, tool_args) | |
| # 添加工具结果到消息 | |
| messages.append({ | |
| "role": "tool", | |
| "name": tool_name, | |
| "content": json.dumps(tool_result), | |
| "tool_call_id": tool_call.id | |
| }) | |
| continue # 继续下一轮 | |
| else: | |
| # 无工具调用,返回最终答案 | |
| response_text = choice.message.content | |
| break | |
| # 构建最终响应 | |
| final_response = "" | |
| # 显示模型信息 | |
| final_response += f"<div style='padding: 8px; background: #e3f2fd; border-left: 3px solid #2196f3; margin-bottom: 10px; font-size: 0.9em;'>🤖 <strong>Model:</strong> Qwen/Qwen2.5-72B-Instruct:novita</div>\n\n" | |
| # 显示工具调用日志 | |
| if tool_calls_log: | |
| final_response += "**🛠️ MCP Tools Used:**\n\n" | |
| for i, tool_call in enumerate(tool_calls_log, 1): | |
| final_response += f"{i}. `{tool_call['name']}` - {json.dumps(tool_call['arguments'])}\n" | |
| final_response += "\n---\n\n" | |
| final_response += response_text | |
| return final_response | |
| except Exception as e: | |
| return f"❌ Error: {str(e)}" | |
| # ========== Gradio 界面 ========== | |
| with gr.Blocks(title="Financial & Market AI Assistant") as demo: | |
| gr.Markdown("# 🤖 Financial & Market AI Assistant") | |
| gr.Markdown(""" | |
| <div style='padding: 15px; background: #d4edda; border-left: 4px solid #28a745; margin: 10px 0; border-radius: 4px;'> | |
| <strong>✅ AI Powered by:</strong> Qwen/Qwen2.5-72B-Instruct:novita | |
| <br> | |
| <strong>📊 Data Sources:</strong> SEC Financial Reports + Market & Stock Data | |
| </div> | |
| """) | |
| chat = gr.ChatInterface( | |
| fn=chatbot_response, | |
| examples=[ | |
| "What's Apple's latest revenue and profit?", | |
| "Show me NVIDIA's 3-year financial trends", | |
| "How is Tesla's stock performing today?", | |
| "Compare Microsoft's earnings with its stock price", | |
| "Analyze Amazon's cash flow and market cap", | |
| ], | |
| title="💬 AI Assistant", | |
| description="Ask me about company financials, stock prices, or market trends. I'll automatically fetch the data you need!" | |
| ) | |
| # 启动应用 | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| show_error=True, | |
| ssr_mode=False | |
| ) | |