Browse Source

fix: add execution handlers for all 5 new feature query types in graph.py

Made-with: Cursor
pull/6453/head
Priyanka Punukollu 1 month ago
parent
commit
194ecfb7ae
  1. 220
      graph.py

220
graph.py

@ -39,6 +39,37 @@ from tools.wealth_bridge import (
from tools.teleport_api import get_city_housing_data from tools.teleport_api import get_city_housing_data
from verification.fact_checker import verify_claims from verification.fact_checker import verify_claims
# New feature tools — wrapped in try/except so graph still loads if files missing
try:
from tools.relocation_runway import calculate_relocation_runway
_RUNWAY_AVAILABLE = True
except ImportError:
_RUNWAY_AVAILABLE = False
try:
from tools.wealth_visualizer import analyze_wealth_position
_VISUALIZER_AVAILABLE = True
except ImportError:
_VISUALIZER_AVAILABLE = False
try:
from tools.life_decision_advisor import analyze_life_decision
_LIFE_ADVISOR_AVAILABLE = True
except ImportError:
_LIFE_ADVISOR_AVAILABLE = False
try:
from tools.property_tracker import analyze_equity_options
_EQUITY_ADVISOR_AVAILABLE = True
except ImportError:
_EQUITY_ADVISOR_AVAILABLE = False
try:
from tools.family_planner import plan_family_finances
_FAMILY_PLANNER_AVAILABLE = True
except ImportError:
_FAMILY_PLANNER_AVAILABLE = False
SYSTEM_PROMPT = """You are a portfolio analysis assistant integrated with Ghostfolio wealth management software. SYSTEM_PROMPT = """You are a portfolio analysis assistant integrated with Ghostfolio wealth management software.
REASONING PROTOCOL silently reason through these four steps BEFORE writing your response. REASONING PROTOCOL silently reason through these four steps BEFORE writing your response.
@ -1463,6 +1494,195 @@ async def tools_node(state: AgentState) -> AgentState:
tool_results.append({"tool_name": "wealth_bridge", "success": True, tool_results.append({"tool_name": "wealth_bridge", "success": True,
"tool_result_id": "wealth_portfolio_summary", "result": result}) "tool_result_id": "wealth_portfolio_summary", "result": result})
# ── Relocation Runway Calculator ──────────────────────────────────────────
elif query_type == "relocation_runway":
if _RUNWAY_AVAILABLE:
# Pull portfolio value from live data if available
perf_result = await portfolio_analysis(token=state.get("bearer_token"))
portfolio_value = 94000.0 # sensible default
if perf_result.get("success"):
portfolio_snapshot = perf_result
portfolio_value = (
perf_result.get("result", {}).get("summary", {})
.get("total_current_value_usd", 94000.0)
)
# Extract cities and salaries from the query (best-effort)
current_city = _extract_real_estate_location(user_query) or "Austin"
dest_city = "Denver" # default destination
# Try to find two city names in query
for candidate in ["seattle", "san francisco", "new york", "denver",
"chicago", "miami", "boston", "los angeles",
"nashville", "dallas", "london", "berlin",
"toronto", "sydney", "tokyo", "paris"]:
if candidate in user_query.lower() and candidate.title() != current_city:
dest_city = candidate.title()
break
# Default salaries — the LLM will note these are estimates
current_salary = _extract_price(user_query) or 120000.0
offer_salary = current_salary * 1.3 # assume 30% raise if not specified
try:
result = calculate_relocation_runway(
current_salary=current_salary,
offer_salary=offer_salary,
current_city=current_city,
destination_city=dest_city,
portfolio_value=portfolio_value,
)
tool_results.append({"tool_name": "relocation_runway", "success": True,
"tool_result_id": "relocation_runway_result",
"result": result})
except Exception as e:
tool_results.append({"tool_name": "relocation_runway", "success": False,
"error": {"code": "RUNWAY_ERROR", "message": str(e)}})
else:
tool_results.append({"tool_name": "relocation_runway", "success": False,
"error": {"code": "TOOL_UNAVAILABLE",
"message": "relocation_runway tool not available"}})
# ── Wealth Gap Visualizer ─────────────────────────────────────────────────
elif query_type == "wealth_gap":
if _VISUALIZER_AVAILABLE:
perf_result = await portfolio_analysis(token=state.get("bearer_token"))
portfolio_value = 94000.0
if perf_result.get("success"):
portfolio_snapshot = perf_result
portfolio_value = (
perf_result.get("result", {}).get("summary", {})
.get("total_current_value_usd", 94000.0)
)
# Extract age from query if mentioned
age_match = re.search(r'\b(2[0-9]|[3-6][0-9]|7[0-5])\b', user_query)
age = int(age_match.group(0)) if age_match else 34
income_match = re.search(r'\$?\s*(\d{2,3})[k,]', user_query, re.I)
annual_income = float(income_match.group(1)) * 1000 if income_match else 120000.0
try:
result = analyze_wealth_position(
portfolio_value=portfolio_value,
age=age,
annual_income=annual_income,
)
tool_results.append({"tool_name": "wealth_visualizer", "success": True,
"tool_result_id": "wealth_gap_result", "result": result})
except Exception as e:
tool_results.append({"tool_name": "wealth_visualizer", "success": False,
"error": {"code": "VISUALIZER_ERROR", "message": str(e)}})
else:
tool_results.append({"tool_name": "wealth_visualizer", "success": False,
"error": {"code": "TOOL_UNAVAILABLE",
"message": "wealth_visualizer tool not available"}})
# ── Life Decision Advisor ─────────────────────────────────────────────────
elif query_type == "life_decision":
if _LIFE_ADVISOR_AVAILABLE:
perf_result = await portfolio_analysis(token=state.get("bearer_token"))
portfolio_value = 94000.0
if perf_result.get("success"):
portfolio_snapshot = perf_result
portfolio_value = (
perf_result.get("result", {}).get("summary", {})
.get("total_current_value_usd", 94000.0)
)
current_city = _extract_real_estate_location(user_query) or "Austin"
dest_city = None
for candidate in ["seattle", "san francisco", "new york", "denver",
"chicago", "miami", "boston", "los angeles",
"nashville", "dallas", "london", "berlin",
"toronto", "sydney", "tokyo", "paris"]:
if candidate in user_query.lower():
if candidate.title() != current_city:
dest_city = candidate.title()
break
# Determine decision type from query
q = user_query.lower()
if any(kw in q for kw in ["job offer", "salary", "raise", "accept"]):
decision_type = "job_offer"
elif any(kw in q for kw in ["move", "reloc", "relocat"]):
decision_type = "relocation"
elif any(kw in q for kw in ["buy", "purchase", "home", "house"]):
decision_type = "home_purchase"
elif any(kw in q for kw in ["rent or buy", "rent vs buy"]):
decision_type = "rent_or_buy"
else:
decision_type = "general"
ctx = {
"portfolio_value": portfolio_value,
"current_city": current_city,
"annual_income": 120000.0,
}
if dest_city:
ctx["destination_city"] = dest_city
try:
result = analyze_life_decision(decision_type, ctx)
tool_results.append({"tool_name": "life_decision_advisor", "success": True,
"tool_result_id": "life_decision_result", "result": result})
except Exception as e:
tool_results.append({"tool_name": "life_decision_advisor", "success": False,
"error": {"code": "LIFE_ADVISOR_ERROR", "message": str(e)}})
else:
tool_results.append({"tool_name": "life_decision_advisor", "success": False,
"error": {"code": "TOOL_UNAVAILABLE",
"message": "life_decision_advisor tool not available"}})
# ── Equity Unlock Advisor ─────────────────────────────────────────────────
elif query_type == "equity_unlock":
if _EQUITY_ADVISOR_AVAILABLE:
# Try to find a property ID in the query
prop_id_match = re.search(r'\bprop_[a-f0-9]{8}\b', user_query, re.I)
if prop_id_match:
prop_id = prop_id_match.group(0).lower()
else:
# Get first active property from DB
prop_list_result = await get_properties()
props = (prop_list_result.get("result", {})
.get("properties", []))
prop_id = props[0]["id"] if props else ""
if prop_id:
try:
result = analyze_equity_options(prop_id)
tool_results.append({"tool_name": "equity_advisor", "success": True,
"tool_result_id": "equity_unlock_result",
"result": result})
except Exception as e:
tool_results.append({"tool_name": "equity_advisor", "success": False,
"error": {"code": "EQUITY_ERROR", "message": str(e)}})
else:
tool_results.append({"tool_name": "equity_advisor", "success": False,
"error": {
"code": "NO_PROPERTY_FOUND",
"message": "No tracked property found. Add a property first with 'track my property'."
}})
else:
tool_results.append({"tool_name": "equity_advisor", "success": False,
"error": {"code": "TOOL_UNAVAILABLE",
"message": "equity_advisor tool not available"}})
# ── Family Financial Planner ──────────────────────────────────────────────
elif query_type == "family_planner":
if _FAMILY_PLANNER_AVAILABLE:
current_city = _extract_real_estate_location(user_query) or "Austin"
# Try to extract income from query
income_match = re.search(r'\$?\s*(\d{2,3})[k,]', user_query, re.I)
annual_income = float(income_match.group(1)) * 1000 if income_match else 120000.0
# Extract number of children if mentioned
children_match = re.search(r'\b([1-4])\s*(?:kid|child|baby|babies|children)', user_query, re.I)
num_children = int(children_match.group(1)) if children_match else 1
try:
result = plan_family_finances(
current_city=current_city,
annual_income=annual_income,
num_planned_children=num_children,
)
tool_results.append({"tool_name": "family_planner", "success": True,
"tool_result_id": "family_planner_result",
"result": result})
except Exception as e:
tool_results.append({"tool_name": "family_planner", "success": False,
"error": {"code": "FAMILY_PLANNER_ERROR", "message": str(e)}})
else:
tool_results.append({"tool_name": "family_planner", "success": False,
"error": {"code": "TOOL_UNAVAILABLE",
"message": "family_planner tool not available"}})
return { return {
**state, **state,
"tool_results": tool_results, "tool_results": tool_results,

Loading…
Cancel
Save