Spaces:
Sleeping
Sleeping
Faham
commited on
Commit
Β·
65a44fe
1
Parent(s):
97db5ba
FIX: response from LLM
Browse files
Home.py
CHANGED
|
@@ -1,12 +1,10 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
import asyncio
|
| 3 |
-
import json
|
| 4 |
import re
|
| 5 |
import os
|
| 6 |
import plotly.graph_objects as go
|
| 7 |
import yfinance as yf
|
| 8 |
import time
|
| 9 |
-
import sys
|
| 10 |
from datetime import timedelta
|
| 11 |
import gnews
|
| 12 |
from bs4 import BeautifulSoup
|
|
@@ -20,19 +18,9 @@ from sklearn.linear_model import Ridge
|
|
| 20 |
from sklearn.model_selection import GridSearchCV
|
| 21 |
from dotenv import load_dotenv
|
| 22 |
from sklearn.preprocessing import StandardScaler
|
| 23 |
-
|
| 24 |
-
# Try different import approaches
|
| 25 |
-
try:
|
| 26 |
-
from langchain_mcp_adapters.client import MultiServerMCPClient
|
| 27 |
-
except ImportError:
|
| 28 |
-
try:
|
| 29 |
-
from langchain_mcp_adapters import MultiServerMCPClient
|
| 30 |
-
except ImportError:
|
| 31 |
-
# Fallback to basic MCP client
|
| 32 |
-
MultiServerMCPClient = None
|
| 33 |
from langgraph.prebuilt import create_react_agent
|
| 34 |
from langchain_groq import ChatGroq
|
| 35 |
-
from langchain_core.tools import tool
|
| 36 |
|
| 37 |
|
| 38 |
# Load environment variables
|
|
@@ -914,24 +902,6 @@ async def initialize_mcp_agent(model, tools):
|
|
| 914 |
print(f"β MCP agent creation traceback: {traceback.format_exc()}")
|
| 915 |
return None
|
| 916 |
|
| 917 |
-
# Test the agent with LangGraph
|
| 918 |
-
try:
|
| 919 |
-
# Use LangGraph ainvoke method for async tools
|
| 920 |
-
test_result = await agent.ainvoke(
|
| 921 |
-
{
|
| 922 |
-
"messages": [
|
| 923 |
-
{"role": "user", "content": "What tools do you have available?"}
|
| 924 |
-
]
|
| 925 |
-
}
|
| 926 |
-
)
|
| 927 |
-
print(f"π Test result: {test_result}")
|
| 928 |
-
except Exception as e:
|
| 929 |
-
st.warning(f"β οΈ MCP agent test failed: {str(e)}")
|
| 930 |
-
print(f"β MCP agent test error: {str(e)}")
|
| 931 |
-
import traceback
|
| 932 |
-
|
| 933 |
-
print(f"β MCP agent test traceback: {traceback.format_exc()}")
|
| 934 |
-
|
| 935 |
return agent
|
| 936 |
|
| 937 |
except Exception as e:
|
|
@@ -944,7 +914,7 @@ async def initialize_mcp_agent(model, tools):
|
|
| 944 |
|
| 945 |
|
| 946 |
async def run_agent_with_mcp(user_query: str, selected_ticker: str = None):
|
| 947 |
-
"""Run the agent using LangGraph React agent"""
|
| 948 |
try:
|
| 949 |
# Get tools and model from session state
|
| 950 |
if "mcp_tools" not in st.session_state or "mcp_model" not in st.session_state:
|
|
@@ -989,9 +959,6 @@ Question: {user_query} for {selected_ticker}"""
|
|
| 989 |
{"messages": [{"role": "user", "content": full_query}]}
|
| 990 |
)
|
| 991 |
|
| 992 |
-
# Debug: Log the result
|
| 993 |
-
print(f"π MCP Agent Result: {result}")
|
| 994 |
-
|
| 995 |
# Extract the final answer from the result
|
| 996 |
if isinstance(result, dict) and "output" in result:
|
| 997 |
final_response = result["output"]
|
|
@@ -1000,53 +967,9 @@ Question: {user_query} for {selected_ticker}"""
|
|
| 1000 |
else:
|
| 1001 |
final_response = str(result)
|
| 1002 |
|
| 1003 |
-
# Debug: Print the result
|
| 1004 |
-
print(
|
| 1005 |
-
|
| 1006 |
-
print(f"π Result keys: {list(result.keys())}")
|
| 1007 |
-
print(f"π Final response type: {type(final_response)}")
|
| 1008 |
-
|
| 1009 |
-
# If the response contains multiple messages, extract only the final AI response
|
| 1010 |
-
if "AIMessage" in final_response:
|
| 1011 |
-
# Debug: Print the response structure
|
| 1012 |
-
print(f"π Response structure: {final_response[:500]}...")
|
| 1013 |
-
|
| 1014 |
-
# Look for the last AIMessage with content
|
| 1015 |
-
ai_messages = re.findall(
|
| 1016 |
-
r"AIMessage\(content=\'(.*?)\',", final_response, re.DOTALL
|
| 1017 |
-
)
|
| 1018 |
-
if ai_messages:
|
| 1019 |
-
final_response = ai_messages[-1] # Get the last AI message
|
| 1020 |
-
print(f"β
Extracted AI message: {final_response[:100]}...")
|
| 1021 |
-
else:
|
| 1022 |
-
# Try alternative pattern without the comma
|
| 1023 |
-
ai_messages = re.findall(
|
| 1024 |
-
r"AIMessage\(content=\'(.*?)\'", final_response, re.DOTALL
|
| 1025 |
-
)
|
| 1026 |
-
if ai_messages:
|
| 1027 |
-
final_response = ai_messages[-1]
|
| 1028 |
-
print(
|
| 1029 |
-
f"β
Extracted AI message (alt): {final_response[:100]}..."
|
| 1030 |
-
)
|
| 1031 |
-
else:
|
| 1032 |
-
print("β No AI messages found in response")
|
| 1033 |
-
# Try to find any content after the last ToolMessage
|
| 1034 |
-
if "ToolMessage" in final_response:
|
| 1035 |
-
# Split by ToolMessage and take the last part
|
| 1036 |
-
parts = final_response.split("ToolMessage")
|
| 1037 |
-
if len(parts) > 1:
|
| 1038 |
-
last_part = parts[-1]
|
| 1039 |
-
# Look for AIMessage in the last part
|
| 1040 |
-
ai_match = re.search(
|
| 1041 |
-
r"AIMessage\(content=\'(.*?)\'",
|
| 1042 |
-
last_part,
|
| 1043 |
-
re.DOTALL,
|
| 1044 |
-
)
|
| 1045 |
-
if ai_match:
|
| 1046 |
-
final_response = ai_match.group(1)
|
| 1047 |
-
print(
|
| 1048 |
-
f"β
Extracted from last part: {final_response[:100]}..."
|
| 1049 |
-
)
|
| 1050 |
|
| 1051 |
# Clean up the final response to remove escaped characters
|
| 1052 |
final_response = (
|
|
@@ -1055,112 +978,69 @@ Question: {user_query} for {selected_ticker}"""
|
|
| 1055 |
.replace('\\"', '"')
|
| 1056 |
)
|
| 1057 |
|
| 1058 |
-
#
|
| 1059 |
-
|
| 1060 |
-
|
| 1061 |
-
|
| 1062 |
-
|
| 1063 |
-
|
| 1064 |
-
|
| 1065 |
-
|
| 1066 |
-
|
| 1067 |
-
|
| 1068 |
-
|
| 1069 |
-
{"messages": [{"role": "user", "content": simple_query}]}
|
| 1070 |
)
|
| 1071 |
|
| 1072 |
-
|
| 1073 |
-
|
| 1074 |
-
|
| 1075 |
-
|
| 1076 |
-
|
| 1077 |
-
|
| 1078 |
-
|
| 1079 |
-
|
| 1080 |
-
|
| 1081 |
-
|
| 1082 |
-
|
| 1083 |
-
|
| 1084 |
-
except Exception as e2:
|
| 1085 |
-
return f"Tool execution started but final analysis failed: {str(e2)}"
|
| 1086 |
-
|
| 1087 |
-
# Look for the final answer section
|
| 1088 |
-
if "Final Answer:" in final_response:
|
| 1089 |
-
# Extract everything after "Final Answer:"
|
| 1090 |
-
final_answer = final_response.split("Final Answer:")[-1].strip()
|
| 1091 |
-
return final_answer
|
| 1092 |
-
elif "Thought:" in final_response and "Action:" in final_response:
|
| 1093 |
-
# If it contains thought process, try to extract the final analysis
|
| 1094 |
-
# Look for the last meaningful paragraph
|
| 1095 |
-
lines = final_response.split("\n")
|
| 1096 |
-
final_lines = []
|
| 1097 |
-
for line in reversed(lines):
|
| 1098 |
-
if (
|
| 1099 |
-
line.strip()
|
| 1100 |
-
and not line.startswith("Thought:")
|
| 1101 |
-
and not line.startswith("Action:")
|
| 1102 |
-
and not line.startswith("Observation:")
|
| 1103 |
-
):
|
| 1104 |
-
final_lines.insert(0, line)
|
| 1105 |
-
elif line.strip() and (
|
| 1106 |
-
"Based on" in line
|
| 1107 |
-
or "Recommendation:" in line
|
| 1108 |
-
or "Conclusion:" in line
|
| 1109 |
-
):
|
| 1110 |
-
final_lines.insert(0, line)
|
| 1111 |
-
if final_lines:
|
| 1112 |
-
return "\n".join(final_lines)
|
| 1113 |
else:
|
| 1114 |
-
|
| 1115 |
-
|
| 1116 |
-
|
| 1117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1118 |
except Exception as e:
|
| 1119 |
st.error(f"β Error during agent execution: {str(e)}")
|
| 1120 |
-
|
| 1121 |
-
import traceback
|
| 1122 |
-
|
| 1123 |
-
st.error(f"Full traceback: {traceback.format_exc()}")
|
| 1124 |
-
# Log to console
|
| 1125 |
-
print(f"β MCP Agent Execution Error: {str(e)}")
|
| 1126 |
-
print(f"Error type: {type(e).__name__}")
|
| 1127 |
-
print(f"Full traceback: {traceback.format_exc()}")
|
| 1128 |
-
|
| 1129 |
-
# Check if it's a TaskGroup error
|
| 1130 |
-
if "TaskGroup" in str(e):
|
| 1131 |
-
st.error(
|
| 1132 |
-
"β TaskGroup error - this might be due to MCP server connection issues"
|
| 1133 |
-
)
|
| 1134 |
-
st.info("π Trying to restart MCP agent...")
|
| 1135 |
-
# Clear the agent and try to reinitialize
|
| 1136 |
-
if "mcp_agent" in st.session_state:
|
| 1137 |
-
del st.session_state.mcp_agent
|
| 1138 |
-
return "Please try again - MCP agent will be reinitialized"
|
| 1139 |
-
else:
|
| 1140 |
-
# Try a different approach - use the agent's available methods
|
| 1141 |
-
st.info("π Trying alternative execution method...")
|
| 1142 |
-
try:
|
| 1143 |
-
# Try using the agent's available methods
|
| 1144 |
-
if hasattr(agent, "arun"):
|
| 1145 |
-
result = await agent.arun(full_query)
|
| 1146 |
-
elif hasattr(agent, "run"):
|
| 1147 |
-
result = await agent.run(
|
| 1148 |
-
full_query
|
| 1149 |
-
) # Always await async methods
|
| 1150 |
-
else:
|
| 1151 |
-
result = "Agent has no available execution methods"
|
| 1152 |
-
return result
|
| 1153 |
-
except Exception as e2:
|
| 1154 |
-
st.error(f"β Alternative execution also failed: {str(e2)}")
|
| 1155 |
-
st.error(f"Error type: {type(e2).__name__}")
|
| 1156 |
-
import traceback
|
| 1157 |
-
|
| 1158 |
-
st.error(f"Full traceback: {traceback.format_exc()}")
|
| 1159 |
-
# Log to console
|
| 1160 |
-
print(f"β MCP Alternative Execution Error: {str(e2)}")
|
| 1161 |
-
print(f"Error type: {type(e2).__name__}")
|
| 1162 |
-
print(f"Full traceback: {traceback.format_exc()}")
|
| 1163 |
-
return f"Error: {str(e2)}"
|
| 1164 |
|
| 1165 |
except Exception as e:
|
| 1166 |
st.error(f"β Error running MCP agent: {e}")
|
|
@@ -1261,50 +1141,6 @@ def create_trading_day_future_dataframe(model, periods=30, freq="D"):
|
|
| 1261 |
return future_df
|
| 1262 |
|
| 1263 |
|
| 1264 |
-
def test_server_availability():
|
| 1265 |
-
"""Test if the MCP servers are available and can be executed."""
|
| 1266 |
-
|
| 1267 |
-
current_dir = os.path.dirname(os.path.abspath(__file__))
|
| 1268 |
-
|
| 1269 |
-
# Test news server
|
| 1270 |
-
news_server_path = os.path.join(current_dir, "news_server.py")
|
| 1271 |
-
if not os.path.exists(news_server_path):
|
| 1272 |
-
print(f"β ERROR: news_server.py not found at {news_server_path}")
|
| 1273 |
-
return False
|
| 1274 |
-
|
| 1275 |
-
# Test stock data server
|
| 1276 |
-
stock_server_path = os.path.join(current_dir, "stock_data_server.py")
|
| 1277 |
-
if not os.path.exists(stock_server_path):
|
| 1278 |
-
print(f"β ERROR: stock_data_server.py not found at {stock_server_path}")
|
| 1279 |
-
return False
|
| 1280 |
-
|
| 1281 |
-
# Test if servers can be executed by checking if they can be imported
|
| 1282 |
-
|
| 1283 |
-
try:
|
| 1284 |
-
# Test if news_server can be imported
|
| 1285 |
-
spec = importlib.util.spec_from_file_location("news_server", news_server_path)
|
| 1286 |
-
if spec is None or spec.loader is None:
|
| 1287 |
-
print("β οΈ WARNING: Could not load news_server.py")
|
| 1288 |
-
else:
|
| 1289 |
-
print("β
SUCCESS: news_server.py is importable")
|
| 1290 |
-
except Exception as e:
|
| 1291 |
-
print(f"β οΈ WARNING: Could not import news_server.py: {e}")
|
| 1292 |
-
|
| 1293 |
-
try:
|
| 1294 |
-
# Test if stock_data_server can be imported
|
| 1295 |
-
spec = importlib.util.spec_from_file_location(
|
| 1296 |
-
"stock_data_server", stock_server_path
|
| 1297 |
-
)
|
| 1298 |
-
if spec is None or spec.loader is None:
|
| 1299 |
-
print("β οΈ WARNING: Could not load stock_data_server.py")
|
| 1300 |
-
else:
|
| 1301 |
-
print("β
SUCCESS: stock_data_server.py is importable")
|
| 1302 |
-
except Exception as e:
|
| 1303 |
-
print(f"β οΈ WARNING: Could not import stock_data_server.py: {e}")
|
| 1304 |
-
|
| 1305 |
-
return True
|
| 1306 |
-
|
| 1307 |
-
|
| 1308 |
def main():
|
| 1309 |
st.set_page_config(page_title="QueryStockAI", page_icon="π", layout="wide")
|
| 1310 |
|
|
@@ -1370,14 +1206,6 @@ def main():
|
|
| 1370 |
st.error(f"β Failed to initialize MCP client: {str(e)}")
|
| 1371 |
st.stop()
|
| 1372 |
|
| 1373 |
-
# Test server availability only once on startup
|
| 1374 |
-
if "servers_tested" not in st.session_state:
|
| 1375 |
-
st.session_state.servers_tested = False
|
| 1376 |
-
|
| 1377 |
-
if not st.session_state.servers_tested:
|
| 1378 |
-
test_server_availability()
|
| 1379 |
-
st.session_state.servers_tested = True
|
| 1380 |
-
|
| 1381 |
# Available tickers
|
| 1382 |
with st.spinner("π Loading available tickers..."):
|
| 1383 |
available_tickers = get_available_tickers()
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import asyncio
|
|
|
|
| 3 |
import re
|
| 4 |
import os
|
| 5 |
import plotly.graph_objects as go
|
| 6 |
import yfinance as yf
|
| 7 |
import time
|
|
|
|
| 8 |
from datetime import timedelta
|
| 9 |
import gnews
|
| 10 |
from bs4 import BeautifulSoup
|
|
|
|
| 18 |
from sklearn.model_selection import GridSearchCV
|
| 19 |
from dotenv import load_dotenv
|
| 20 |
from sklearn.preprocessing import StandardScaler
|
| 21 |
+
from langchain_mcp_adapters.client import MultiServerMCPClient
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
from langgraph.prebuilt import create_react_agent
|
| 23 |
from langchain_groq import ChatGroq
|
|
|
|
| 24 |
|
| 25 |
|
| 26 |
# Load environment variables
|
|
|
|
| 902 |
print(f"β MCP agent creation traceback: {traceback.format_exc()}")
|
| 903 |
return None
|
| 904 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 905 |
return agent
|
| 906 |
|
| 907 |
except Exception as e:
|
|
|
|
| 914 |
|
| 915 |
|
| 916 |
async def run_agent_with_mcp(user_query: str, selected_ticker: str = None):
|
| 917 |
+
"""Run the agent using LangGraph React agent (non-streaming version)"""
|
| 918 |
try:
|
| 919 |
# Get tools and model from session state
|
| 920 |
if "mcp_tools" not in st.session_state or "mcp_model" not in st.session_state:
|
|
|
|
| 959 |
{"messages": [{"role": "user", "content": full_query}]}
|
| 960 |
)
|
| 961 |
|
|
|
|
|
|
|
|
|
|
| 962 |
# Extract the final answer from the result
|
| 963 |
if isinstance(result, dict) and "output" in result:
|
| 964 |
final_response = result["output"]
|
|
|
|
| 967 |
else:
|
| 968 |
final_response = str(result)
|
| 969 |
|
| 970 |
+
# Debug: Print the raw result to see what we're getting
|
| 971 |
+
print("π Raw result type:", type(result))
|
| 972 |
+
print("π Raw result:", result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 973 |
|
| 974 |
# Clean up the final response to remove escaped characters
|
| 975 |
final_response = (
|
|
|
|
| 978 |
.replace('\\"', '"')
|
| 979 |
)
|
| 980 |
|
| 981 |
+
# Try to extract the final analysis more simply
|
| 982 |
+
# First, try to get the last meaningful content
|
| 983 |
+
if "AIMessage" in final_response:
|
| 984 |
+
# Look for the last AIMessage with content
|
| 985 |
+
ai_messages = re.findall(
|
| 986 |
+
r'AIMessage\(content="([^"]*)"', final_response, re.DOTALL
|
| 987 |
+
)
|
| 988 |
+
if not ai_messages:
|
| 989 |
+
# Try single quotes
|
| 990 |
+
ai_messages = re.findall(
|
| 991 |
+
r"AIMessage\(content='([^']*)'", final_response, re.DOTALL
|
|
|
|
| 992 |
)
|
| 993 |
|
| 994 |
+
if ai_messages:
|
| 995 |
+
print(f"π Found {len(ai_messages)} AIMessages")
|
| 996 |
+
for i, msg in enumerate(ai_messages):
|
| 997 |
+
print(f"π Message {i+1} length: {len(msg.strip())}")
|
| 998 |
+
print(f"π Message {i+1} preview: {msg.strip()[:100]}...")
|
| 999 |
+
print(f"π Message {i+1} starts with: {msg.strip()[:20]}")
|
| 1000 |
+
|
| 1001 |
+
# Always get the last AIMessage which contains the complete analysis
|
| 1002 |
+
# The last AIMessage is the final analysis, not the tool call
|
| 1003 |
+
final_response = ai_messages[-1].strip()
|
| 1004 |
+
print(f"π Selected message {len(ai_messages)} (the last one)")
|
| 1005 |
+
print(f"π Selected message starts with: {final_response[:50]}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1006 |
else:
|
| 1007 |
+
# If no AIMessage found, try to extract from the raw response
|
| 1008 |
+
final_response = final_response.strip()
|
| 1009 |
+
|
| 1010 |
+
# Remove any remaining tool call artifacts
|
| 1011 |
+
final_response = re.sub(r"<\|.*?\|>", "", final_response)
|
| 1012 |
+
final_response = re.sub(
|
| 1013 |
+
r"functions\.[a-zA-Z_]+:\d+", "", final_response
|
| 1014 |
+
)
|
| 1015 |
+
final_response = re.sub(r'\{[^{}]*"ticker"[^{}]*\}', "", final_response)
|
| 1016 |
+
|
| 1017 |
+
# Remove LangGraph metadata
|
| 1018 |
+
final_response = re.sub(
|
| 1019 |
+
r"\{.*?agent.*?\}", "", final_response, flags=re.DOTALL
|
| 1020 |
+
)
|
| 1021 |
+
final_response = re.sub(
|
| 1022 |
+
r"\{.*?tools.*?\}", "", final_response, flags=re.DOTALL
|
| 1023 |
+
)
|
| 1024 |
+
final_response = re.sub(
|
| 1025 |
+
r"ToolMessage.*?\]", "", final_response, flags=re.DOTALL
|
| 1026 |
+
)
|
| 1027 |
+
final_response = re.sub(
|
| 1028 |
+
r"additional_kwargs.*?usage_metadata.*?\}",
|
| 1029 |
+
"",
|
| 1030 |
+
final_response,
|
| 1031 |
+
flags=re.DOTALL,
|
| 1032 |
+
)
|
| 1033 |
+
|
| 1034 |
+
# Clean up extra whitespace and formatting
|
| 1035 |
+
final_response = re.sub(r"\n\s*\n", "\n\n", final_response)
|
| 1036 |
+
final_response = final_response.strip()
|
| 1037 |
+
|
| 1038 |
+
print("π Final cleaned response:", final_response)
|
| 1039 |
+
return final_response
|
| 1040 |
+
|
| 1041 |
except Exception as e:
|
| 1042 |
st.error(f"β Error during agent execution: {str(e)}")
|
| 1043 |
+
return f"Error during execution: {str(e)}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1044 |
|
| 1045 |
except Exception as e:
|
| 1046 |
st.error(f"β Error running MCP agent: {e}")
|
|
|
|
| 1141 |
return future_df
|
| 1142 |
|
| 1143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1144 |
def main():
|
| 1145 |
st.set_page_config(page_title="QueryStockAI", page_icon="π", layout="wide")
|
| 1146 |
|
|
|
|
| 1206 |
st.error(f"β Failed to initialize MCP client: {str(e)}")
|
| 1207 |
st.stop()
|
| 1208 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1209 |
# Available tickers
|
| 1210 |
with st.spinner("π Loading available tickers..."):
|
| 1211 |
available_tickers = get_available_tickers()
|