Spaces:
Running
Running
| # mcp_server_lifeflow.py | |
| import os | |
| import json | |
| from fastmcp import FastMCP | |
| # 引入你原本的工具包 (保持邏輯不變) | |
| from src.tools.scout_toolkit import ScoutToolkit | |
| from src.tools.optimizer_toolkit import OptimizationToolkit | |
| from src.tools.navigation_toolkit import NavigationToolkit | |
| from src.tools.weather_toolkit import WeatherToolkit | |
| from src.tools.reader_toolkit import ReaderToolkit | |
| from src.infra.context import set_session_id # 記得 import 這個 | |
| # 定義 Server | |
| mcp = FastMCP("LifeFlow Core Service") | |
| # ================= Helper: 獲取環境變數 ================= | |
| # 注意:這些 Key 是由 app.py 在啟動 subprocess 時透過 env 傳進來的 | |
| def get_env_key(name): | |
| return os.environ.get(name) | |
| # ================= Tool Definitions ================= | |
| def search_and_offload(task_list_json: str, session_id: str = None) -> str: | |
| """ | |
| Performs a proximity search for POIs based on the provided tasks and global context, then offloads results to the DB. | |
| CRITICAL: The input JSON **MUST** include the 'global_info' section containing 'start_location' (lat, lng) to ensure searches are performed nearby the user's starting point, not in a random location. | |
| Args: | |
| task_list_json (str): A JSON formatted string. The structure must be: | |
| { | |
| "global_info": { | |
| "language": str, | |
| "plan_type": str | |
| "return_to_start": bool, | |
| "start_location": ..., | |
| "departure_time": str, | |
| "deadline": str or null, | |
| }, | |
| "tasks": [ | |
| { | |
| "task_id": 1, | |
| "category": "MEAL" | "LEISURE" | "ERRAND" | "SHOPPING", | |
| "description": "Short description", | |
| "location_hint": "Clean Keyword for Google Maps", | |
| "priority": "HIGH" | "MEDIUM" | "LOW", | |
| "service_duration_min": 30, | |
| "time_window": { | |
| "earliest_time": "ISO 8601" or null, | |
| "latest_time": "ISO 8601" or null} | |
| } | |
| ..., | |
| ] | |
| } | |
| Returns: | |
| str: A Ref_id of DB system. | |
| """ | |
| if session_id: set_session_id(session_id) | |
| key = get_env_key("GOOGLE_MAPS_API_KEY") | |
| # 實例化原本的 Toolkit | |
| tool = ScoutToolkit(google_maps_api_key=key) | |
| # 執行業務邏輯 | |
| return tool.search_and_offload(task_list_json) | |
| def optimize_from_ref(ref_id: str, max_wait_time_min: int = 0, return_to_start: bool = None, session_id: str = None) -> str: | |
| """ | |
| Executes the mathematical route solver (TSPTW) to find the most efficient task sequence. | |
| This tool loads the POI data, calculates travel times, and reorders tasks to respect time windows. | |
| It returns detailed status information, allowing the caller to decide if a retry (with relaxed constraints) is needed. | |
| Args: | |
| ref_id (str): The unique reference ID returned by the Scout Agent. | |
| max_wait_time_min (int, optional): Max waiting time allowed at a location. Defaults to 0. | |
| return_to_start (bool, optional): Whether to force a round trip. Defaults to None. If None, it defaults to the value in 'global_info.return_to_start'. | |
| Returns: | |
| str: A JSON string containing the result status and reference ID. | |
| Structure: | |
| { | |
| "status": "OPTIMAL" | "FEASIBLE" | "INFEASIBLE" | "ERROR", | |
| "opt_ref_id": str, | |
| "dropped_tasks_count": int, | |
| "skipped_tasks_id": list, | |
| "message": str | |
| } | |
| """ | |
| if session_id: set_session_id(session_id) | |
| tool = OptimizationToolkit() | |
| return tool.optimize_from_ref(ref_id, max_wait_time_min, return_to_start) | |
| def calculate_traffic_and_timing(optimization_ref_id: str, session_id: str = None) -> str: | |
| """ | |
| Calculates precise travel times, traffic delays, and arrival timestamps for the optimized route. | |
| This tool acts as a 'Reality Check' by applying Google Routes data to the sequence generated by the Optimizer. | |
| It ensures the schedule is physically possible under current traffic conditions and generates the final timeline. | |
| Args: | |
| optimization_ref_id (str): The unique reference ID returned by the Optimizer Agent (e.g., "optimization_result_xyz"). | |
| This ID links to the logically sorted task list. | |
| Returns: | |
| str: A JSON string containing the 'nav_ref_id' (e.g., '{"nav_ref_id": "navigation_result_abc"}'). | |
| """ | |
| if session_id: set_session_id(session_id) | |
| key = get_env_key("GOOGLE_MAPS_API_KEY") | |
| tool = NavigationToolkit(google_maps_api_key=key) | |
| return tool.calculate_traffic_and_timing(optimization_ref_id) | |
| def check_weather_for_timeline(nav_ref_id: str, session_id: str = None) -> str: | |
| """ | |
| Enriches the solved navigation route with weather forecasts and Air Quality Index (AQI) to create the final timeline. | |
| This tool is the final post-processing step. It loads the solved route data, calculates precise local arrival times for each stop | |
| (dynamically adjusting for the destination's timezone), and fetches specific weather conditions and AQI for those times. | |
| It also resolves final location names and saves the complete itinerary for presentation. | |
| Args: | |
| nav_ref_id (str): The unique reference ID returned by the Route Solver (or Navigation) step. | |
| Returns: | |
| str: A JSON string containing the reference ID for the finalized data. | |
| Structure: | |
| { | |
| "final_ref_id": str | |
| } | |
| """ | |
| if session_id: set_session_id(session_id) | |
| key = get_env_key("OPENWEATHER_API_KEY") | |
| tool = WeatherToolkit(openweather_api_key=key) | |
| return tool.check_weather_for_timeline(nav_ref_id) | |
| def read_final_itinerary(ref_id: str, session_id: str = None) -> str: | |
| """ | |
| Retrieves the complete, enriched itinerary data for final presentation. | |
| This tool acts as the 'Data Fetcher' for the Presenter. It loads the fully processed | |
| trip plan (including Weather, Traffic, and Optimized Route) associated with the `ref_id`. | |
| Args: | |
| ref_id (str): The unique reference ID returned by the Weatherman Agent (e.g., "final_itinerary_xyz"). | |
| This ID links to the completed dataset ready for reporting. | |
| Returns: | |
| str: A structured JSON string containing the full trip details. | |
| Structure: | |
| { | |
| "status": "COMPLETE" | "INCOMPLETE", | |
| "global_info": { ... }, | |
| "traffic_summary": { "total_distance": ..., "total_drive_time": ... }, | |
| "schedule": [ | |
| { "time": "10:00", "location": "...", "weather": "...", "air_quality": "..." }, | |
| ..., | |
| ] | |
| } | |
| """ | |
| if session_id: set_session_id(session_id) | |
| tool = ReaderToolkit() | |
| return tool.read_final_itinerary(ref_id) | |
| if __name__ == "__main__": | |
| # 啟動 MCP Server | |
| mcp.run() |